From 02d02417800184fb76cd0245cdaa94c437aa4081 Mon Sep 17 00:00:00 2001 From: blasty Date: Fri, 21 Aug 2026 12:14:46 +0200 Subject: reformat: ruff format + import sort, mechanically (see ruff.toml) No behavior. Listed in .git-blame-ignore-revs (next commit). --- tests/test_findings.py | 219 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 148 insertions(+), 71 deletions(-) (limited to 'tests/test_findings.py') diff --git a/tests/test_findings.py b/tests/test_findings.py index cf813c2..205b005 100644 --- a/tests/test_findings.py +++ b/tests/test_findings.py @@ -18,7 +18,12 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from idatui.domain import Comment, NamedItem, Struct # noqa: E402 from idatui.findings import ( # noqa: E402 - Findings, default_path, from_loader, gather, is_dummy, render, + Findings, + default_path, + from_loader, + gather, + is_dummy, + render, ) PASS = FAIL = 0 @@ -36,34 +41,62 @@ def check(name, cond, detail=""): def sample() -> Findings: return Findings( - binary="echo", path="/tmp/echo", + binary="echo", + path="/tmp/echo", sections=[(0x1000, 0x2000, ".text"), (0x2000, 0x2100, ".data")], n_functions=128, comments=[ - Comment(addr=0x1100, text="length is attacker controlled", - line="mov edi, [rbp+len]", func="parse", func_addr=0x1000), - Comment(addr=0x1010, text="entry", line="push rbp", - func="parse", func_addr=0x1000), - Comment(addr=0x1000, text="parses the header", whole_func=True, - func="parse", func_addr=0x1000), + Comment( + addr=0x1100, + text="length is attacker controlled", + line="mov edi, [rbp+len]", + func="parse", + func_addr=0x1000, + ), + Comment( + addr=0x1010, + text="entry", + line="push rbp", + func="parse", + func_addr=0x1000, + ), + Comment( + addr=0x1000, + text="parses the header", + whole_func=True, + func="parse", + func_addr=0x1000, + ), Comment(addr=0x2004, text="magic", line="dd 0DEADBEEFh"), # What the ELF loader writes into every database, through the very # same set_cmt a person uses. - Comment(addr=0x4, text="File class: 64-bit", line="db 2", - seg="LOAD"), + Comment(addr=0x4, text="File class: 64-bit", line="db 2", seg="LOAD"), ], names=[ - NamedItem(addr=0x1000, name="parse", is_func=True, size=0x120, - proto="int __fastcall parse(char *)"), + NamedItem( + addr=0x1000, + name="parse", + is_func=True, + size=0x120, + proto="int __fastcall parse(char *)", + ), NamedItem(addr=0x1200, name="sub_1200", is_func=True, size=0x30), NamedItem(addr=0x2004, name="hdr_magic", seg=".data"), NamedItem(addr=0x1400, name="memcpy", is_func=True, size=0x40), NamedItem(addr=0x390, name="elf_gnu_hash_nbuckets", seg="LOAD"), ], - types=[(Struct(name="hdr", size=0x10, is_union=False, members=3, - ordinal=42), "struct hdr\n{\n int magic;\n};\n"), - (Struct(name="Elf64_Dyn", size=0x10, is_union=False, members=2, - ordinal=3), "struct Elf64_Dyn\n{\n int d_tag;\n};\n")], + types=[ + ( + Struct(name="hdr", size=0x10, is_union=False, members=3, ordinal=42), + "struct hdr\n{\n int magic;\n};\n", + ), + ( + Struct( + name="Elf64_Dyn", size=0x10, is_union=False, members=2, ordinal=3 + ), + "struct Elf64_Dyn\n{\n int d_tag;\n};\n", + ), + ], linked={"memcpy"}, stripped=True, ) @@ -74,9 +107,11 @@ def main() -> int: check("the report names the binary", doc.startswith("# Findings — echo"), doc[:40]) # 1 function, not 3: sub_1200 is IDA's invention and memcpy is the linker's. - check("the summary counts only what a person contributed", - "1 named functions · 1 named data · 4 comments · 2 local types" in doc, - doc.splitlines()[2] if len(doc.splitlines()) > 2 else "") + check( + "the summary counts only what a person contributed", + "1 named functions · 1 named data · 4 comments · 2 local types" in doc, + doc.splitlines()[2] if len(doc.splitlines()) > 2 else "", + ) # A name IDA invented is not a finding, and neither is one the linker gave. check("dummy names are excluded", "sub_1200" not in doc) @@ -86,75 +121,111 @@ def main() -> int: # The loader annotates every database it makes; none of it is a finding. check("the loader's own comments are left out", "File class" not in doc) check("the loader's own names are left out", "elf_gnu_hash" not in doc) - check("but the report says how many it dropped", - "4 annotations left out as the loader's own" in doc, # 1 comment + 3 names - [l for l in doc.splitlines() if "left out" in l]) - check("from_loader knows both shapes", - from_loader("LOAD") and from_loader("", "elf_gnu_hash_x") - and not from_loader(".text", "parse")) - check("is_dummy knows the shapes IDA invents", - all(is_dummy(n) for n in ("sub_1234", "loc_A0", "unk_4000", "j_free")) - and not any(is_dummy(n) for n in ("parse", "sub_parse", "main", "")), - "") + check( + "but the report says how many it dropped", + "4 annotations left out as the loader's own" in doc, # 1 comment + 3 names + [l for l in doc.splitlines() if "left out" in l], + ) + check( + "from_loader knows both shapes", + from_loader("LOAD") + and from_loader("", "elf_gnu_hash_x") + and not from_loader(".text", "parse"), + ) + check( + "is_dummy knows the shapes IDA invents", + all(is_dummy(n) for n in ("sub_1234", "loc_A0", "unk_4000", "j_free")) + and not any(is_dummy(n) for n in ("parse", "sub_parse", "main", "")), + "", + ) # Comments lead, grouped by function, address-ordered within a group. - check("comments come before the name tables", - doc.index("## Comments") < doc.index("## Named functions")) - body = doc[doc.index("## Comments"):doc.index("## Named functions")] + check( + "comments come before the name tables", + doc.index("## Comments") < doc.index("## Named functions"), + ) + body = doc[doc.index("## Comments") : doc.index("## Named functions")] check("comments are grouped under their function", "### `parse`" in body) - check("a commentless region is grouped separately", - "### outside any function" in body) - check("comments are ordered by address inside a group", - body.index("0x1010") < body.index("0x1100")) - check("a function comment says that is what it is", - "*whole function*: parses the header" in body) - check("an instruction comment carries the line it annotates", - "`mov edi, [rbp+len]`" in body) + check( + "a commentless region is grouped separately", "### outside any function" in body + ) + check( + "comments are ordered by address inside a group", + body.index("0x1010") < body.index("0x1100"), + ) + check( + "a function comment says that is what it is", + "*whole function*: parses the header" in body, + ) + check( + "an instruction comment carries the line it annotates", + "`mov edi, [rbp+len]`" in body, + ) # Types: newest ordinal first, because that is the one you just wrote. - types = doc[doc.index("## Local types"):] - check("your newest type is first", - types.index("hdr") < types.index("Elf64_Dyn")) + types = doc[doc.index("## Local types") :] + check("your newest type is first", types.index("hdr") < types.index("Elf64_Dyn")) check("type source is fenced as C", "```c\nstruct hdr" in types) # Escaping. - hostile = Findings(binary="x", comments=[ - Comment(addr=1, text="a | b", line="mov | rax"), - ], names=[NamedItem(addr=2, name="a|b")]) + hostile = Findings( + binary="x", + comments=[ + Comment(addr=1, text="a | b", line="mov | rax"), + ], + names=[NamedItem(addr=2, name="a|b")], + ) hdoc = render(hostile) check("a pipe cannot break a table row", "a\\|b" in hdoc, hdoc) check("a pipe in a comment is escaped too", "a \\| b" in hdoc) # The empty database must still produce a document that says something. empty = render(Findings(binary="nothing")) - check("an empty report is still a document", - empty.startswith("# Findings — nothing") and "## Comments" in empty) + check( + "an empty report is still a document", + empty.startswith("# Findings — nothing") and "## Comments" in empty, + ) check("and it says why it is empty", "Comments are the part" in empty) - check("an empty report has no dangling type section", - "## Local types" not in empty) + check("an empty report has no dangling type section", "## Local types" not in empty) # Provenance must be stated, not implied. Without a journal the report is a # scan and says so; with one it is exactly what idatui recorded doing. - scanned = render(Findings(binary="x", stripped=False, - names=[NamedItem(addr=1, name="main", is_func=True)])) - check("a scanned report admits it cannot know who wrote what", - "**source**: a scan of the database" in scanned - and "include its work as well as yours" in scanned) - check("and warns when the binary brought its own symbols", - "include ones it shipped with" in scanned) + scanned = render( + Findings( + binary="x", + stripped=False, + names=[NamedItem(addr=1, name="main", is_func=True)], + ) + ) + check( + "a scanned report admits it cannot know who wrote what", + "**source**: a scan of the database" in scanned + and "include its work as well as yours" in scanned, + ) + check( + "and warns when the binary brought its own symbols", + "include ones it shipped with" in scanned, + ) j = sample() j.recorded = {0x1100, 0x1000} j.n_recorded = 7 jdoc = render(j) - check("a journalled report says so", "idatui's edit journal" in jdoc - and "7 recorded edits" in jdoc, "") - jbody = jdoc[jdoc.index("## Comments"):jdoc.index("## Named functions")] - check("and lists only the comments it recorded", - "length is attacker controlled" in jbody and "0x2004" not in jbody, - jbody) - check("a journalled report drops names it did not record", - "`parse`" in jdoc and "hdr_magic" not in jdoc) + check( + "a journalled report says so", + "idatui's edit journal" in jdoc and "7 recorded edits" in jdoc, + "", + ) + jbody = jdoc[jdoc.index("## Comments") : jdoc.index("## Named functions")] + check( + "and lists only the comments it recorded", + "length is attacker controlled" in jbody and "0x2004" not in jbody, + jbody, + ) + check( + "a journalled report drops names it did not record", + "`parse`" in jdoc and "hdr_magic" not in jdoc, + ) # -- gather ------------------------------------------------------------- # class FakeProgram: @@ -162,8 +233,10 @@ def main() -> int: return [(0x1000, 0x2000, ".text")] def annotations(self, limit=4000): - return ([Comment(addr=1, text="hi")], - [NamedItem(addr=1, name="parse", is_func=True)]) + return ( + [Comment(addr=1, text="hi")], + [NamedItem(addr=1, name="parse", is_func=True)], + ) def linkage(self): return ([], []) @@ -180,11 +253,15 @@ def main() -> int: f = gather(FakeProgram(), "/tmp/echo") check("gather reads the annotations", len(f.comments) == 1 and len(f.names) == 1) check("gather takes the binary name from the path", f.binary == "echo", f.binary) - check("a failing backend degrades the report instead of raising", - f.n_functions == 0 and f.types == [] and "# Findings" in render(f)) + check( + "a failing backend degrades the report instead of raising", + f.n_functions == 0 and f.types == [] and "# Findings" in render(f), + ) - check("the default path sits beside the binary", - default_path("/tmp/echo") == "/tmp/echo.findings.md") + check( + "the default path sits beside the binary", + default_path("/tmp/echo") == "/tmp/echo.findings.md", + ) print(f"\n{PASS} passed, {FAIL} failed") return 1 if FAIL else 0 -- cgit v1.3.1-sl0p