Repository: mandiant/capa
File: tests/test_render.py (Python)

Code before:

    # capture the output of render_maec
    output_stream = capa.render.utils.StringIO()
    capa.render.default.render_maec(mock_doc, output_stream)
    output = output_stream.getvalue()

    # create the expected output
    maec_fields = [
        (capa.render.utils.bold("analysis-conclusion"), rule_meta.maec.analysis_conclusion),
        (capa.render.utils.bold("malware-category"), rule_meta.maec.malware_category),
        (capa.render.utils.bold("malware-family"), rule_meta.maec.malware_family),
    ]

    expected_output = (
        tabulate.tabulate(
            maec_fields,
            headers=[capa.render.default.width("MAEC Category", 25), capa.render.default.width("MAEC Value", 75)],
            tablefmt="mixed_grid",
        )
        + "\n"
    )

    # check if render_maec output is expected
    assert output == expected_output


@fixtures.parametrize(
    "feature,expected",
    [
        (capa.features.common.OS("windows"), "os: windows"),
        (capa.features.common.Arch("i386"), "arch: i386"),
        (capa.features.common.Format("pe"), "format: pe"),
        (capa.features.common.MatchedRule("foo"), "match: foo @ 0x401000"),
        (capa.features.common.Characteristic("foo"), "characteristic: foo @ 0x401000"),
        (capa.features.file.Export("SvcMain"), "export: SvcMain @ 0x401000"),
        (capa.features.file.Import("CreateFileW"), "import: CreateFileW @ 0x401000"),
        (capa.features.file.Section(".detours"), "section: .detours @ 0x401000"),
        (capa.features.file.FunctionName("memcmp"), "function name: memcmp @ 0x401000"),
        (capa.features.common.Substring("foo"), "substring: foo"),
        (capa.features.common.Regex("^foo"), "regex: ^foo"),
        (capa.features.common.String("foo"), 'string: "foo" @ 0x401000'),
        (capa.features.common.Class("BeanFactory"), "class: BeanFactory @ 0x401000"),
        (capa.features.common.Namespace("std::enterprise"), "namespace: std::enterprise @ 0x401000"),
        (capa.features.insn.API("CreateFileW"), "api: CreateFileW @ 0x401000"),
        (capa.features.insn.Property("foo"), "property: foo @ 0x401000"),
        (capa.features.insn.Property("foo", "read"), "property/read: foo @ 0x401000"),
        (capa.features.insn.Property("foo", "write"), "property/write: foo @ 0x401000"),
        (capa.features.insn.Number(12), "number: 0xC @ 0x401000"),
        (capa.features.common.Bytes(b"AAAA"), "bytes: 41414141 @ 0x401000"),
        (capa.features.insn.Offset(12), "offset: 0xC @ 0x401000"),
        (capa.features.insn.Mnemonic("call"), "mnemonic: call @ 0x401000"),

Review comment (suggestion):
```suggestion
    rm = capa.render.result_document.RuleMatches(
        meta=capa.render.result_document.RuleMetadata.from_capa(rule),
        source=rule_yaml,
        matches=(),
    )

    # create a mock ResultDocument
    mock_rd = Mock(spec=rd.ResultDocument)
    mock_rd.rules = {"test rule": rm}

    # capture the output of render_maec
    output_stream = capa.render.utils.StringIO()
    capa.render.default.render_maec(mock_rd, output_stream)
    output = output_stream.getvalue()

    assert "analysis-conclusion" in output
    assert analysis_conclusion in output
    assert "malware-category" in output
    assert malware_category in output
    assert "malware-family" in output
    assert malware_family in output
```

Code after:

    assert "analysis-conclusion" in output
    assert analysis_conclusion in output
    assert "malware-category" in output
    assert malware_category in output
    assert "malware-family" in output
    assert malware_family in output


@fixtures.parametrize(
    "feature,expected",
    [
        (capa.features.common.OS("windows"), "os: windows"),
        (capa.features.common.Arch("i386"), "arch: i386"),
        (capa.features.common.Format("pe"), "format: pe"),
        (capa.features.common.MatchedRule("foo"), "match: foo @ 0x401000"),
        (capa.features.common.Characteristic("foo"), "characteristic: foo @ 0x401000"),
        (capa.features.file.Export("SvcMain"), "export: SvcMain @ 0x401000"),
        (capa.features.file.Import("CreateFileW"), "import: CreateFileW @ 0x401000"),
        (capa.features.file.Section(".detours"), "section: .detours @ 0x401000"),
        (capa.features.file.FunctionName("memcmp"), "function name: memcmp @ 0x401000"),
        (capa.features.common.Substring("foo"), "substring: foo"),
        (capa.features.common.Regex("^foo"), "regex: ^foo"),
        (capa.features.common.String("foo"), 'string: "foo" @ 0x401000'),
        (capa.features.common.Class("BeanFactory"), "class: BeanFactory @ 0x401000"),
        (capa.features.common.Namespace("std::enterprise"), "namespace: std::enterprise @ 0x401000"),
        (capa.features.insn.API("CreateFileW"), "api: CreateFileW @ 0x401000"),
        (capa.features.insn.Property("foo"), "property: foo @ 0x401000"),
        (capa.features.insn.Property("foo", "read"), "property/read: foo @ 0x401000"),
        (capa.features.insn.Property("foo", "write"), "property/write: foo @ 0x401000"),
        (capa.features.insn.Number(12), "number: 0xC @ 0x401000"),
        (capa.features.common.Bytes(b"AAAA"), "bytes: 41414141 @ 0x401000"),
        (capa.features.insn.Offset(12), "offset: 0xC @ 0x401000"),
        (capa.features.insn.Mnemonic("call"), "mnemonic: call @ 0x401000"),
        (capa.features.insn.OperandNumber(0, 12), "operand[0].number: 0xC @ 0x401000"),
        (capa.features.insn.OperandOffset(0, 12), "operand[0].offset: 0xC @ 0x401000"),
        # unsupported
        # (capa.features.basicblock.BasicBlock(), "basic block @ 0x401000"),
    ],
)
def test_render_vverbose_feature(feature, expected):
    ostream = capa.render.utils.StringIO()

    addr = capa.features.freeze.Address.from_capa(capa.features.address.AbsoluteVirtualAddress(0x401000))
    feature = capa.features.freeze.features.feature_from_capa(feature)

    matches = capa.render.result_document.Match(
        success=True,
        node=capa.render.result_document.FeatureNode(feature=feature),
        children=(),
        locations=(addr,),