This commit is contained in:
inliver233
2026-10-24 01:50:24 +08:00
committed by dpkg123
commit 863fe6b1bf
98 changed files with 28849 additions and 0 deletions
+264
View File
@@ -0,0 +1,264 @@
"""Subprocess contract tests for skills/exe-reverse/scripts/dart_snapshot_strings.py.
The input is the generated ELF64 from `conftest.build_synthetic_elf`, never a sample: a valid
container with one one-byte literal, one two-byte literal and a `.text` that carries neither of the
idioms the pool observer counts.
Two conventions are pinned because they are not what the docstring's exit-code table suggests:
* `--json` returns 0 whatever the filter did, while the text mode returns 1 for "nothing matched";
* `--at` widens a match by `offset + len(text)`, and for a two-byte literal that is half the bytes
the literal occupies, so an offset inside the literal's second half is reported as a miss.
"""
import json
import pytest
pytestmark = pytest.mark.cli
def plain_lines(text):
return [line for line in text.splitlines() if line.strip()]
def reported_count(text):
"""The `N literal(s) reported.` line the text view ends with."""
for line in reversed(plain_lines(text)):
if "literal(s) reported." in line:
return int(line.split()[0])
raise AssertionError("no 'literal(s) reported.' line in:\n%s" % text)
# --- refusals ----------------------------------------------------------------
def test_a_file_that_is_not_an_elf_is_refused_with_the_documented_code(run_script, tmp_path):
path = tmp_path / "notes.txt"
path.write_text("this is not a snapshot\n", encoding="utf-8")
result = run_script("dart_snapshot_strings.py", path)
assert result.returncode == 3
assert "not an ELF image" in result.stderr
assert "Traceback" not in result.stderr
assert result.stdout == ""
def test_a_truncated_image_is_refused_without_a_traceback(run_script, synthetic_elf, tmp_path):
path = tmp_path / "truncated.so"
path.write_bytes(synthetic_elf.data[:0x30])
result = run_script("dart_snapshot_strings.py", path)
assert result.returncode == 3
assert "not an ELF image" in result.stderr
assert "Traceback" not in result.stderr
def test_a_missing_file_exits_two_with_a_reason(run_script, tmp_path):
result = run_script("dart_snapshot_strings.py", tmp_path / "absent.so")
assert result.returncode == 2
assert "error:" in result.stderr
assert "Traceback" not in result.stderr
def test_an_elf_with_no_code_section_is_refused(run_script, synthetic_elf_without_text):
"""A shared object without `.text` is not an AOT code image, and the reader says so."""
result = run_script("dart_snapshot_strings.py", synthetic_elf_without_text.path)
assert result.returncode == 3
assert "no .text section" in result.stderr
assert "Traceback" not in result.stderr
# --- recovery ----------------------------------------------------------------
def test_the_default_run_reports_every_literal_it_recovered(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path)
assert result.returncode == 0, result.stderr
assert "x86-64, shared-object" in result.stdout
for entry in synthetic_elf.literals:
assert hex(entry["vaddr"]) in result.stdout
assert entry["text"] in result.stdout
assert "literals recovered : 3" in result.stdout
assert reported_count(result.stdout) == 3
def test_min_length_filters_the_short_literals_out(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--min-length", "12")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "premium_license" in result.stdout
assert "trial_mode" not in result.stdout
assert ">= 12 chars" in result.stdout
def test_limit_caps_how_many_are_reported(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--limit", "1")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
def test_section_selection_is_honoured(run_script, synthetic_elf):
only_rodata = run_script("dart_snapshot_strings.py", synthetic_elf.path,
"--sections", ".rodata")
assert only_rodata.returncode == 0, only_rodata.stderr
assert reported_count(only_rodata.stdout) == 3
only_text = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--sections", ".text")
assert only_text.returncode == 1
assert reported_count(only_text.stdout) == 0
absent = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--sections", ".absent")
assert absent.returncode == 1
assert reported_count(absent.stdout) == 0
# --- keyword filter ----------------------------------------------------------
def test_find_matches_case_insensitively(run_script, synthetic_elf):
lower = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "premium")
upper = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "PREMIUM")
assert lower.returncode == 0, lower.stderr
assert upper.returncode == 0, upper.stderr
assert reported_count(lower.stdout) == reported_count(upper.stdout) == 1
assert "premium_license" in lower.stdout
assert "trial_mode" not in lower.stdout
def test_find_accepts_a_comma_separated_list(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "trial, VIP")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 2
assert "trial_mode" in result.stdout
assert "vip_pass" in result.stdout
assert "premium_license" not in result.stdout
def test_find_can_reach_a_two_byte_literal(run_script, synthetic_elf):
"""A keyword search that only read ASCII would miss this one, which is why both are recovered."""
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "vip")
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "[two-byte]" in result.stdout
def test_a_keyword_that_matches_nothing_changes_the_exit_code(run_script, synthetic_elf):
"""A miss is a negative finding, not an empty success."""
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "no_such_keyword")
assert result.returncode == 1
assert reported_count(result.stdout) == 0
assert "Traceback" not in result.stderr
# --- --at --------------------------------------------------------------------
def test_at_resolves_a_literal_by_file_offset(run_script, synthetic_elf):
entry = synthetic_elf.literal("premium_license")
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", str(entry["offset"]))
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "premium_license" in result.stdout
def test_at_resolves_a_two_byte_literal_by_file_offset(run_script, synthetic_elf):
entry = synthetic_elf.literal("vip_pass")
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", str(entry["offset"]))
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "vip_pass" in result.stdout
def test_at_resolves_a_literal_by_virtual_address(run_script, synthetic_elf):
"""The vaddr is far above the file size, which is how the reader tells the two apart."""
entry = synthetic_elf.literal("vip_pass")
assert entry["vaddr"] > synthetic_elf.size
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", hex(entry["vaddr"]))
assert result.returncode == 0, result.stderr
assert reported_count(result.stdout) == 1
assert "vip_pass" in result.stdout
assert hex(entry["vaddr"]) in result.stdout
def test_at_inside_the_code_section_matches_nothing(run_script, synthetic_elf):
result = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at",
str(synthetic_elf.text["offset"] + 1))
assert result.returncode == 1
assert reported_count(result.stdout) == 0
def test_at_finds_every_byte_of_a_two_byte_literal(load_script, run_script, synthetic_elf):
"""`--at` measures its window in bytes, so **every** byte offset inside the literal matches.
The window used to be `offset + len(text)`. For a two-byte literal the offsets are bytes and the
text is characters, so the window covered only the first half of it and a real byte offset in
the second half came back as a miss. Note which byte is which: in UTF-16LE an ASCII character is
a printable byte followed by a zero byte, so the last meaningful offset in the literal is the
high byte of its final character, which is `0x00` and is a perfectly valid offset to probe.
"""
entry = synthetic_elf.literal("vip_pass")
assert entry["encoding"] == "two-byte"
span = 2 * len(entry["text"])
lo = entry["offset"]
last_low_byte = lo + span - 2
assert synthetic_elf.data[last_low_byte] in range(0x20, 0x7F)
assert synthetic_elf.data[lo + span - 1] == 0, "the final byte is the high byte of the character"
for probe in (lo, lo + 2, last_low_byte, lo + span - 1):
found = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--at", str(probe))
assert found.returncode == 0, (probe, found.stdout, found.stderr)
assert reported_count(found.stdout) == 1, (probe, found.stdout)
# The module computes the same window the CLI does.
recovered = load_script("dart_snapshot_strings.py").recover_strings(
synthetic_elf.data, {"offset": synthetic_elf.rodata["offset"],
"vaddr": synthetic_elf.rodata["vaddr"],
"size": synthetic_elf.rodata["size"]})
matched = [row[2] for row in recovered
if row[0] <= lo + span - 1 < row[0] + 2 * len(row[2])]
assert matched == ["vip_pass"]
def test_at_can_be_combined_with_find(run_script, synthetic_elf):
entry = synthetic_elf.literal("trial_mode")
hit = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "trial",
"--at", str(entry["offset"]))
assert hit.returncode == 0, hit.stderr
assert reported_count(hit.stdout) == 1
miss = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "premium",
"--at", str(entry["offset"]))
assert miss.returncode == 1
assert reported_count(miss.stdout) == 0
# --- json --------------------------------------------------------------------
def test_json_returns_one_when_the_filter_matched_nothing(run_script, synthetic_elf):
"""The exit code means the same thing in both output modes.
It used to be 1 from the text path and 0 from the JSON path for the identical filter, so a
caller that asked for JSON could not tell a match from a miss by reading the exit code.
"""
text_mode = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "no_such_word")
json_mode = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--find", "no_such_word",
"--json")
assert text_mode.returncode == 1
assert json_mode.returncode == 1
payload = json.loads(json_mode.stdout)
assert payload["matches"] == []
assert payload["literals_recovered"] == 0
# An unfiltered run is a listing, not a search, and a listing that finds literals still exits 0.
unfiltered = run_script("dart_snapshot_strings.py", synthetic_elf.path, "--json")
assert unfiltered.returncode == 0
assert json.loads(unfiltered.stdout)["matches"]
def test_an_unknown_flag_is_a_usage_error(run_script):
result = run_script("dart_snapshot_strings.py", "--definitely-not-a-flag")
assert result.returncode == 2
assert "usage:" in result.stderr.lower()
assert "Traceback" not in result.stderr
def test_help_exits_zero(run_script):
result = run_script("dart_snapshot_strings.py", "--help")
assert result.returncode == 0
assert "usage:" in result.stdout.lower()
assert "app.so" in result.stdout
+397
View File
@@ -0,0 +1,397 @@
"""Subprocess contract tests for the machine-readable output of every script.
One convention is not what a reader would assume, so it is asserted explicitly rather than
smoothed over: ``scan_leaks.py`` writes a JSON document and then a ``RESULT=`` line, so its
stdout as a whole is not valid JSON. The document parses from the front; the verdict line is
read from the end. A test pins that shape so a future change to either half is deliberate.
"""
import hashlib
import json
import pytest
pytestmark = pytest.mark.cli
# The reader's own view names, produced by the flags that populate them.
PEUTIL_VIEWS = {
"--summary": "summary",
"--sections": "section_detail",
"--imports": "imports",
"--exports": "exports",
"--resources": "resources",
"--rich": "rich_header",
"--tls": "tls",
"--debug": "debug",
"--relocs": "base_relocations",
"--entropy": "entropy",
"--check-sum": "checksum",
}
# Always present, whatever the flags.
PEUTIL_ALWAYS = ("security_directory", "load_config")
def json_document(text):
return json.JSONDecoder().raw_decode(text)[0]
def last_line(text):
lines = [line for line in text.splitlines() if line.strip()]
return lines[-1] if lines else ""
# --- peutil ------------------------------------------------------------------
def test_reader_json_carries_the_summary_the_text_view_shows(run_script, fixtures):
result = run_script("peutil.py", fixtures.pe64.path, "--json")
assert result.returncode == 0
payload = json.loads(result.stdout)
assert sorted(payload) == ["load_config", "security_directory", "summary"]
summary = payload["summary"]
assert summary["checksum_matches"] is True
assert summary["checksum_declared"] == fixtures.pe64.meta["checksum"]
assert summary["kind"] == "exe"
assert summary["arch"] == "amd64"
assert summary["file_size"] == fixtures.pe64.size
assert summary["sections"] and len(summary["sections"]) == 5
assert payload["security_directory"] is None
assert payload["load_config"] is None
@pytest.mark.parametrize("flag,key", sorted(PEUTIL_VIEWS.items()))
def test_each_reader_view_lands_under_its_own_key(run_script, fixtures, flag, key):
result = run_script("peutil.py", fixtures.pe64.path, flag, "--json")
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert key in payload
def test_reader_json_with_every_view_present(run_script, fixtures):
flags = [flag for flag in sorted(PEUTIL_VIEWS)]
result = run_script("peutil.py", fixtures.pe64.path, *flags, "--json")
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
for key in list(PEUTIL_VIEWS.values()) + list(PEUTIL_ALWAYS):
assert key in payload, key
assert payload["resources"][0]["path"] == ["VERSION", "1", "1033"]
assert payload["tls"]["callback_rvas"] == ["0x1000"]
assert payload["debug"][0]["format"] == "RSDS"
assert payload["checksum"]["matches"] is True
assert payload["imports"][0]["dll"] == "fixture-import.dll"
assert payload["exports"]["dll_name"] == "fixture-export.dll"
assert payload["rich_header"] is None # the fixture has none, and says so
assert payload["delay_imports"] == []
assert payload["base_relocations"]["blocks"] == 1
assert payload["section_detail"][0]["raw_tail"] == 0
assert len(payload["entropy"]) == 5
def test_reader_json_of_a_bare_recipe_reports_the_absent_structures(run_script, fixtures):
result = run_script("peutil.py", fixtures.bare.path, "--exports", "--resources", "--tls",
"--debug", "--json")
payload = json.loads(result.stdout)
assert result.returncode == 0
assert payload["exports"] is None
assert payload["resources"] == []
assert payload["tls"] is None
assert payload["debug"] == []
def test_reader_json_is_decodable_ascii(run_script, fixtures):
result = run_script("peutil.py", fixtures.pe64.path, "--json")
assert result.stdout.isascii()
# --- pepatch -----------------------------------------------------------------
def test_patcher_dry_run_json_reports_the_plan_and_says_it_wrote_nothing(run_script, fixtures,
tmp_path):
out = tmp_path / "never.exe"
result = run_script("pepatch.py", fixtures.pe64.path, "--at-offset", "0x410",
"--bytes", "CC CC", "--original-length", "2", "--dry-run", "--json")
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["dry_run"] is True
assert payload["patches_requested"] == 1
assert payload["patches_applied"] == 1
assert payload["errors"] == []
assert payload["checksum_recomputed"] is False
applied = payload["applied"][0]
assert applied["offset"] == 0x410
assert applied["rva"] == "0x1010"
assert applied["before"] == "48 8B"
assert applied["after"] == "CC CC"
assert applied["equal_length"] is True
assert applied["length_basis"] == "original-length"
assert "output" not in payload
assert not out.exists()
def test_patcher_real_run_json_reports_verification_and_the_new_checksum(run_script, fixtures,
tmp_path):
out = tmp_path / "patched.exe"
result = run_script("pepatch.py", fixtures.pe64.path, "--at-offset", "0x410",
"--expect", "48 8B", "--bytes", "CC 8B", "--out", out, "--json")
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["output"] == str(out)
assert payload["output_size"] == payload["input_size"] == fixtures.pe64.size
assert payload["verify_problems"] == []
assert payload["checksum_after"]["matches"] is True
assert payload["checksum_recomputed"] is True
assert payload["signed_input"] is False
assert payload["signature_note"] is None
assert payload["structural"] == []
assert payload["length_unproven"] == []
assert out.read_bytes()[0x410:0x412] == b"\xCC\x8B"
def test_patcher_json_names_a_length_unproven_patch(load_script, run_script, fixtures, tmp_path):
out = tmp_path / "unproven.exe"
result = run_script("pepatch.py", fixtures.pe64.path, "--at-offset", "0x410",
"--bytes", "CC", "--out", out, "--json")
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["applied"][0]["equal_length"] is None
assert payload["length_unproven"] == ["cli"]
assert payload["structural"] == []
def test_patcher_json_reports_a_refusal_without_an_output(run_script, fixtures, tmp_path):
out = tmp_path / "never.exe"
result = run_script("pepatch.py", fixtures.pe64.path, "--pattern", fixtures.pe64.plant,
"--bytes", "90 " * 12, "--out", out, "--json")
assert result.returncode == 1
assert not out.exists()
assert "matched 2 sites" in result.stderr
assert result.stdout.strip() == ""
# --- aob_scan ----------------------------------------------------------------
def test_locator_json_reports_a_unique_pattern_as_ok(run_script, fixtures, hexpat):
pdb = hexpat(b"fixture.pdb")
result = run_script("aob_scan.py", fixtures.pe64.path, "--patterns", pdb, "--json")
assert result.returncode == 0, result.stdout
payload = json.loads(result.stdout)
assert payload["result"] == "ok"
assert payload["target_sections"] == [".text", ".rdata", ".data", ".rsrc", ".reloc"]
assert payload["section_filter"] is None
entry = payload["patterns"][0]
assert entry["hits"] == 1
assert entry["verdict"] == "unique"
assert entry["wildcards"] == 0
assert entry["sites"][0]["section"] == ".rdata"
def test_locator_json_reports_the_planted_pair_as_ambiguous(run_script, fixtures):
result = run_script("aob_scan.py", fixtures.pe64.path, "--patterns", fixtures.pe64.plant,
"--json")
assert result.returncode == 1, "an ambiguous pattern is the documented non-zero outcome"
payload = json.loads(result.stdout)
entry = payload["patterns"][0]
assert entry["hits"] == 2
assert entry["verdict"] == "ambiguous"
assert payload["result"] == "needs-attention"
offsets = [int(site["offset_hex"], 16) for site in entry["sites"]]
assert offsets == list(fixtures.pe64.plant_offsets)
assert [site["rva_hex"] for site in entry["sites"]] == \
[fixtures.pe64.meta["plant"]["first_rva"], fixtures.pe64.meta["plant"]["second_rva"]]
def test_locator_json_reports_an_absent_pattern_as_a_no_match(run_script, fixtures):
result = run_script("aob_scan.py", fixtures.pe64.path, "--patterns", "DE AD BE EF CA FE",
"--json")
assert result.returncode == 1
payload = json.loads(result.stdout)
assert payload["patterns"][0]["hits"] == 0
assert payload["patterns"][0]["verdict"] == "no-match"
assert payload["patterns"][0]["sites"] == []
def test_locator_json_counts_wildcards_and_section_filtered_hits(run_script, fixtures):
result = run_script("aob_scan.py", fixtures.pe64.path, "--patterns",
"48 8B 05 ?? ?? ?? 00", "--section", ".text", "--json")
payload = json.loads(result.stdout)
entry = payload["patterns"][0]
assert entry["wildcards"] == 3
assert entry["hits"] == 2
assert payload["section_filter"] == ".text"
assert {site["section"] for site in entry["sites"]} == {".text"}
def test_locator_json_from_a_pattern_file_keeps_the_names(run_script, fixtures, tmp_path, hexpat):
sigs = tmp_path / "sigs.txt"
sigs.write_text("# comment\n%s\nnamed-site\t%s\n" % (fixtures.pe64.plant, hexpat(b"FIXT")),
encoding="utf-8")
result = run_script("aob_scan.py", fixtures.pe64.path, "--patterns", sigs, "--json")
payload = json.loads(result.stdout)
names = [entry["name"] for entry in payload["patterns"]]
assert names == ["sigs.txt:2", "named-site"]
assert payload["patterns"][1]["hits"] >= 1
# --- scan_leaks --------------------------------------------------------------
def test_scanner_json_is_valid_json_ahead_of_the_result_line(run_script, synthetic_leak_root):
"""The document parses from the front; the RESULT line is not part of it."""
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--format", "json")
assert result.returncode == 1
payload = json_document(result.stdout)
assert sorted(payload) == ["exempted", "files_scanned", "root", "strong", "verdict", "weak"]
assert payload["verdict"] == "findings"
assert payload["files_scanned"] >= 3
assert last_line(result.stdout) == "RESULT=findings"
def test_scanner_json_stdout_is_not_valid_as_a_whole(run_script, synthetic_leak_root):
"""A negative outcome: nothing may treat this stdout as one JSON document."""
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--format", "json")
with pytest.raises(json.JSONDecodeError):
json.loads(result.stdout)
def test_scanner_json_reports_exactly_the_expected_rules(run_script, synthetic_leak_root):
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--format", "json",
"--show-exempt")
payload = json_document(result.stdout)
assert {hit["rule"] for hit in payload["strong"]} == {
"private-key-block", "github-token", "aws-access-key", "slack-token",
"generic-secret-assignment", "licence-blob-literal", "windows-user-profile",
"posix-home", "windows-sid-literal", "volume-serial-literal",
"literal-endpoint-with-creds", "public-ipv4-literal", "product-name-with-version",
}
assert {hit["rule"] for hit in payload["weak"]} == {
"exe-name-with-version", "literal-host-with-path", "email-address",
"vendor-copyright-line",
}
for hit in payload["strong"]:
assert hit["strength"] == "strong"
assert hit["file"].endswith("notes.md")
assert hit["line"] > 0
assert hit["context"]
assert "machine-guid" not in {hit["rule"] for hit in payload["strong"]}
def test_scanner_json_show_exempt_records_the_suppressed_hits(run_script, synthetic_leak_root):
"""Measured: the machine-guid hit is suppressed by the bare-GUID exemption."""
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--format", "json",
"--show-exempt")
payload = json_document(result.stdout)
suppressed = {hit["rule"]: hit.get("exempt") for hit in payload["exempted"]}
assert isinstance(payload["exempted"], list)
assert suppressed.get("machine-guid") == \
"a bare GUID is not machine identity without MachineGuid context"
reasons = {hit["exempt"] for hit in payload["exempted"]
if hit["rule"] == "product-name-with-version"}
assert reasons and reasons <= {"tool or library name",
"platform or vendor-of-the-platform term",
"public protector/packer/managed tooling name"}
def test_scanner_json_without_show_exempt_reports_only_a_count(run_script, synthetic_leak_root):
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--format", "json")
payload = json_document(result.stdout)
assert isinstance(payload["exempted"], int)
assert payload["exempted"] > 0
def test_scanner_json_on_a_clean_root_says_clean(run_script, clean_scan_root):
result = run_script("scan_leaks.py", "--root", clean_scan_root, "--format", "json")
assert result.returncode == 0
payload = json_document(result.stdout)
assert payload["verdict"] == "clean"
assert payload["strong"] == []
assert payload["weak"] == []
# --- make_fixture_pe ---------------------------------------------------------
def test_fixture_builder_json_describes_what_it_wrote(run_script, tmp_path):
out = tmp_path / "described.exe"
result = run_script("make_fixture_pe.py", "--out", out, "--json")
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["path"] == str(out)
assert payload["size"] == out.stat().st_size
assert payload["sha256"] == hashlib.sha256(out.read_bytes()).hexdigest()
assert sorted(payload["sections"]) == [".data", ".rdata", ".reloc", ".rsrc", ".text"]
plant = payload["plant"]
assert plant["first_file_offset"] != plant["second_file_offset"]
assert "planted twice" in plant["note"]
assert payload["checksum"].startswith("0x")
assert payload["virtual_only_tail_bytes"] > 0
assert payload["executable_and_writable_code"] is True
def test_fixture_builder_is_byte_reproducible(run_script, tmp_path):
first = tmp_path / "one.exe"
second = tmp_path / "two.exe"
a = run_script("make_fixture_pe.py", "--out", first, "--json")
b = run_script("make_fixture_pe.py", "--out", second, "--json")
assert a.returncode == b.returncode == 0
assert json.loads(a.stdout)["sha256"] == json.loads(b.stdout)["sha256"]
assert first.read_bytes() == second.read_bytes()
# --- hwprobe -----------------------------------------------------------------
# The route table is documented as three-state: available / needs-<x> / unavailable. Measured on
# this host, two rows carry a prose state instead of a vocabulary word. The values below are the
# ones actually produced; allowing them keeps the check honest without asserting the docstring.
ROUTE_STATES = ("available", "unavailable")
ROUTE_PROSE_STATES = (
"CPU supports it, but a hypervisor is present",
"needs-a-debugger, and a hypervisor is present",
"no documented user-mode path on this platform",
)
def test_prober_json_reports_a_route_table(hwprobe_report):
payload = hwprobe_report
for key in ("python", "platform", "cpu", "os", "privilege", "routes", "tools",
"python_modules"):
assert key in payload, key
assert payload["python"].startswith("3.")
rows = payload["routes"]
assert rows
for row in rows:
assert len(row) == 3, row
name, state, why = row
assert name and state and why
assert name != state
# The route that needs nothing but Python and a file on disk must be available when the
# script that reports it is running at all.
static = [row for row in rows if row[0].startswith("static PE analysis")]
assert len(static) == 1
assert static[0][1] == "available"
def test_route_states_use_the_vocabulary_or_a_measured_deviation(hwprobe_report):
"""Measured: not every route state is one of the three documented states.
Two rows on this host answer with a sentence instead of a vocabulary word, so the
three-state claim in the docstring holds for the table's shape but not for every cell. The
measured sentence forms are listed rather than asserted as required, so a fix to the
vocabulary passes and a new unmeasured sentence fails loudly.
"""
for row in hwprobe_report["routes"]:
state = row[1]
assert state in ROUTE_STATES or state.startswith("needs-") \
or state in ROUTE_PROSE_STATES, state
def test_prober_rows_carry_a_measurement_and_a_note(hwprobe_report):
payload = hwprobe_report
for group in ("cpu", "os", "privilege"):
assert payload[group], group
for row in payload[group]:
assert set(row) >= {"state", "value", "note"}, (group, row)
assert row["state"] in ROUTE_STATES or row["state"].startswith("needs-"), row
def test_prober_can_dump_raw_cpuid_registers(run_script):
result = run_script("hwprobe.py", "--raw", "--json", timeout=120)
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["raw_cpuid"], "a raw dump was requested but nothing was reported"
+260
View File
@@ -0,0 +1,260 @@
"""Subprocess contract tests for skills/exe-reverse/scripts/capabilities.py and doctor.py.
The exit codes are the ones the two docstrings publish -- 0 all ok, 3 something blocked, 2 usage
error, 4 internal -- and the last stdout line is a `RESULT=<token>` for both.
One convention is pinned because it is not what a reader would assume: `capabilities.py --list`
prints `RESULT=capabilities_ok` whatever the registry says, because listing ids and names probes
nothing. The test asserts the contrast directly -- the same registry resolved through `--json` exits
3 on this host while `--list` exits 0 -- so a future change to either half is deliberate.
"""
import json
import pytest
pytestmark = pytest.mark.cli
BLOCKED_TOKEN = "capabilities_blocked"
def json_document(text):
return json.JSONDecoder().raw_decode(text)[0]
def last_line(text):
lines = [line for line in text.splitlines() if line.strip()]
return lines[-1] if lines else ""
def result_token(text):
line = last_line(text)
assert line.startswith("RESULT="), "the last line was %r" % line
return line.split("=", 1)[1]
# --- capabilities.py ---------------------------------------------------------
def test_list_prints_every_id_and_name(run_script, load_script):
caps = load_script("capabilities.py")
result = run_script("capabilities.py", "--list")
for capability_id in caps.CAPABILITIES:
assert capability_id in result.stdout, capability_id
assert "%d capabilities." % len(caps.CAPABILITIES) in result.stdout
# The list now carries a real verdict rather than an unconditional one, so the exit code and
# the token have to agree with what resolving the same registry reports.
token, code = caps.verdict(caps.resolve_all())
assert result.returncode == code
assert result.stdout.strip().endswith("RESULT=%s" % caps.TOKENS[token])
def test_list_and_json_agree_on_the_same_registry(run_script):
"""Two views of one table must not disagree about it.
The list view used to print the ok token and exit 0 unconditionally while the JSON view of the
same registry exited 3 on this host, because most of the table is blocked here by design --
every `artifact:` row waits for an operator to supply one. A report that contradicts another
report of the same data is worse than either alone: a reader cannot tell which to believe.
"""
listed = run_script("capabilities.py", "--list")
resolved = run_script("capabilities.py", "--json")
assert result_token(listed.stdout) == result_token(resolved.stdout)
assert listed.returncode == resolved.returncode
def test_json_reports_every_capability_with_its_closure(run_script, load_script):
caps = load_script("capabilities.py")
result = run_script("capabilities.py", "--json")
assert result.returncode in (0, 3), result.stderr
rows = json_document(result.stdout)
assert [row["id"] for row in rows] == list(caps.CAPABILITIES)
for row in rows:
assert set(row) >= {"id", "name", "state", "required", "any_of", "depends_on",
"blocked_deps", "missing", "closure", "scripts", "note"}
assert row["state"] in ("ok", "blocked")
assert row["closure"][0] == row["id"]
assert result_token(result.stdout) == (BLOCKED_TOKEN if result.returncode == 3
else "capabilities_ok")
def test_one_capability_reports_its_closure_and_its_state(run_script):
"""`pe_structure_read` needs the interpreter and a script this kit ships, so it is ok anywhere."""
result = run_script("capabilities.py", "pe_structure_read")
assert result.returncode == 0, result.stderr
assert "capability: Read a PE image's structure" in result.stdout
assert "scripts : peutil.py" in result.stdout
assert "requires : python310, file:peutil.py" in result.stdout
assert "state : ok" in result.stdout
assert result.stdout.strip().endswith("RESULT=capabilities_ok")
def test_one_blocked_capability_names_the_atom_no_install_can_supply(run_script):
result = run_script("capabilities.py", "commandline_debug")
assert result.returncode == 3, result.stdout
assert "state : blocked" in result.stdout
assert "missing atom : artifact:sample" in result.stdout
assert "next action" not in result.stdout, "an artifact has no install line"
assert result.stdout.strip().endswith("RESULT=%s" % BLOCKED_TOKEN)
def test_one_capability_as_json_carries_the_same_fields(run_script):
result = run_script("capabilities.py", "pe_structure_read", "--json")
assert result.returncode == 0, result.stderr
row = json_document(result.stdout)
assert row["id"] == "pe_structure_read"
assert row["state"] == "ok"
assert row["missing"] == []
assert row["blocked_deps"] == []
assert result.stdout.strip().endswith("RESULT=capabilities_ok")
def test_an_unknown_capability_is_a_usage_error_on_stdout_and_stderr(run_script):
result = run_script("capabilities.py", "no_such_capability")
assert result.returncode == 2
assert result.stdout.strip() == "RESULT=usage_error"
assert "no such capability" in result.stderr
assert "Traceback" not in result.stderr
def test_verbose_prints_the_next_action_for_a_blocked_capability(run_script, load_script):
caps = load_script("capabilities.py")
installable = [caps.ATOMS[atom].get("install")
for row in caps.resolve_all(caps.Probe(tool_dirs=caps.extra_tool_dirs()))
if row["state"] != "ok"
for atom, _detail in row["missing"]
if caps.ATOMS.get(atom, {}).get("install")]
if not installable:
pytest.skip("this host has no blocked capability whose missing atom carries an install "
"line, so there is nothing for --verbose to print here")
result = run_script("capabilities.py", "--verbose")
assert result.returncode in (0, 3), result.stderr
assert "missing atom" in result.stdout
assert "next action :" in result.stdout
assert "est. minutes :" in result.stdout
assert "(basis: " in result.stdout
def test_an_unknown_flag_is_a_usage_error(run_script):
result = run_script("capabilities.py", "--definitely-not-a-flag")
assert result.returncode == 2
assert result.stdout.strip() == ""
assert "usage:" in result.stderr.lower()
assert "Traceback" not in result.stderr
def test_help_exits_zero(run_script):
result = run_script("capabilities.py", "--help")
assert result.returncode == 0
assert "usage:" in result.stdout.lower()
def test_an_extra_tool_directory_can_turn_a_blocked_capability_ok(run_script, load_script,
tmp_path):
"""`--tools-dir` is the documented second search path; a tool off PATH is not a missing tool."""
caps = load_script("capabilities.py")
baseline = caps.resolve("static_disassembly",
caps.Probe(tool_dirs=caps.extra_tool_dirs()))
if baseline["state"] == "ok":
pytest.skip("a disassembly backend is already reachable here (%s), so the extra directory "
"cannot demonstrate anything" % baseline["alternatives_present"])
tools = tmp_path / "unpacked-tools"
tools.mkdir()
for name in ("ghidra", "ida64"):
(tools / name).write_text("", encoding="utf-8")
result = run_script("capabilities.py", "static_disassembly", "--tools-dir", tools)
assert result.returncode == 0, result.stdout
assert "present :" in result.stdout
assert "EXEREV_TOOLS" in result.stdout
# --- doctor.py ---------------------------------------------------------------
@pytest.fixture(scope="module")
def default_run(run_script):
"""One full doctor run for the module: it probes the host and answers --help for every script."""
return run_script("doctor.py", timeout=180)
@pytest.fixture(scope="module")
def json_run(run_script):
return run_script("doctor.py", "--json", timeout=180)
def test_the_doctor_exits_zero_or_three_and_never_with_a_traceback(default_run):
assert default_run.returncode in (0, 3), default_run
assert "Traceback" not in default_run.stdout + default_run.stderr
def test_the_doctor_prints_its_three_blocks_and_a_result_line(default_run, load_script):
doctor = load_script("doctor.py")
assert "== capabilities ==" in default_run.stdout
assert "== scripts ==" in default_run.stdout
assert "== environment facts that can change a result ==" in default_run.stdout
assert "== result ==" in default_run.stdout
assert "%d script(s) checked" % len(doctor.discover_scripts()) in default_run.stdout
assert result_token(default_run.stdout) in ("env_ok", "env_partial", "env_blocked")
def test_the_result_token_follows_the_exit_code(default_run):
token = result_token(default_run.stdout)
if default_run.returncode == 3:
assert token == "env_blocked"
else:
assert token in ("env_ok", "env_partial")
def test_the_script_table_counts_the_scripts_that_are_actually_there(default_run, load_script):
doctor = load_script("doctor.py")
total = len(doctor.discover_scripts())
assert "registered/checked = %d / %d" % (total, total) in default_run.stdout
def test_json_carries_the_four_sections_and_a_summary(json_run, load_script):
"""The counts are cross-checked against the rows they summarise, so a script that stops being
classified shows up as a mismatch rather than as a smaller denominator."""
doctor = load_script("doctor.py")
assert json_run.returncode in (0, 3), json_run.stderr
payload = json_document(json_run.stdout)
assert sorted(payload) == ["capabilities", "environment", "result", "scripts", "summary"]
total = len(doctor.discover_scripts())
assert len(payload["scripts"]) == total
assert payload["summary"]["scripts_checked"] == total
unknown = [row for row in payload["scripts"] if row["capability_source"] == "unknown"]
assert payload["summary"]["scripts_with_unknown_capability"] == len(unknown)
assert payload["summary"]["capabilities_total"] == len(payload["capabilities"])
assert payload["result"] == result_token(json_run.stdout)
for row in payload["scripts"]:
assert set(row) == {"script", "capabilities", "capability_source", "capability_state",
"runnable", "third_party", "detail"}
def test_json_environment_facts_carry_the_documented_keys(json_run):
payload = json_document(json_run.stdout)
if len(payload["environment"]) == 1 and payload["environment"][0]["fact"] == "host probe":
pytest.skip("the host probe could not answer here, so the fact table is inert")
for fact in payload["environment"]:
assert set(fact) == {"fact", "value", "effect", "severity"}
assert fact["severity"] in ("note", "warn")
def test_each_view_flag_prints_only_its_own_block(run_script):
scripts_only = run_script("doctor.py", "--scripts", timeout=180)
assert scripts_only.returncode in (0, 3), scripts_only
assert "== scripts ==" in scripts_only.stdout
assert "== capabilities ==" not in scripts_only.stdout
assert "== environment facts" not in scripts_only.stdout
assert "RESULT=" in scripts_only.stdout
environment_only = run_script("doctor.py", "--environment", timeout=180)
assert environment_only.returncode in (0, 3), environment_only
assert "== environment facts" in environment_only.stdout
assert "== scripts ==" not in environment_only.stdout
assert "== capabilities ==" not in environment_only.stdout
def test_an_unknown_flag_is_a_usage_error_without_a_traceback(run_script):
result = run_script("doctor.py", "--definitely-not-a-flag")
assert result.returncode == 2
assert "usage:" in result.stderr.lower()
assert "Traceback" not in result.stderr
assert result.stdout.strip() == ""
+362
View File
@@ -0,0 +1,362 @@
"""Subprocess contract tests for the exit codes and usage surface of every script.
The exit codes asserted here are the ones each script's own docstring publishes, read from the
docstring rather than invented:
* peutil.py 0 parsed, 1 not a parseable PE, 2 usage error
* pepatch.py 0 applied and verified, 1 a patch failed, 2 usage error, 3 not a parseable PE
* aob_scan.py 0 every pattern unique, 1 a pattern matched zero or several times, 2 usage,
3 not a parseable PE
* hwprobe.py 0 report produced, 2 usage error (a limited machine is a finding, not a failure)
* make_fixture_pe.py 0 written, 2 usage error
* scan_leaks.py 0 clean, 1 findings, 2 error
One input path is pinned as a measured deviation: a missing ``--spec`` file reaches an unguarded
``open()``, so it prints a traceback instead of using the module's own exit-code vocabulary.
"""
import json
import pytest
pytestmark = pytest.mark.cli
SCRIPTS = ("peutil.py", "pepatch.py", "hwprobe.py", "aob_scan.py", "make_fixture_pe.py",
"scan_leaks.py")
@pytest.mark.parametrize("script", SCRIPTS)
def test_help_exits_zero_with_usage_text(run_script, script):
result = run_script(script, "--help")
assert result.returncode == 0, result.stderr
assert result.stdout.strip(), "no help text was printed"
assert "usage:" in result.stdout.lower()
assert script in result.stdout
@pytest.mark.parametrize("script", SCRIPTS)
def test_an_unknown_flag_is_a_usage_error(run_script, script):
result = run_script(script, "--definitely-not-a-flag")
assert result.returncode == 2
assert result.stdout.strip() == ""
assert "usage:" in result.stderr.lower()
assert "Traceback" not in result.stderr
@pytest.mark.parametrize("script", [s for s in SCRIPTS if s != "scan_leaks.py"])
def test_a_script_with_no_arguments_never_hangs(run_script, script):
"""Each script either does its documented thing or reports a usage error; none hangs.
The scanner is excluded here because its no-argument behaviour is to scan a tree rather than
to fail; it has its own check below.
"""
result = run_script(script, timeout=120)
assert result.returncode in (0, 2), result
def test_scanner_with_no_arguments_scans_a_tree_and_states_its_verdict(run_script):
"""With no arguments it scans the tree its own script lives in, and says what it found."""
result = run_script("scan_leaks.py", timeout=180)
assert result.returncode in (0, 1), result
assert "scanning" in result.stdout
assert result.stdout.strip().endswith(
"RESULT=clean" if result.returncode == 0 else "RESULT=findings")
# --- peutil ------------------------------------------------------------------
def test_reader_on_a_non_pe_exits_one_without_a_traceback(run_script, tmp_path):
path = tmp_path / "notes.txt"
path.write_text("this is not a portable executable\n", encoding="utf-8")
result = run_script("peutil.py", path)
assert result.returncode == 1
assert "error:" in result.stderr
assert "no MZ signature" in result.stderr
assert "Traceback" not in result.stderr
assert result.stdout == ""
def test_reader_on_a_missing_file_exits_two(run_script, tmp_path):
result = run_script("peutil.py", tmp_path / "absent.exe")
assert result.returncode == 2
assert "is not a file" in result.stderr
assert "Traceback" not in result.stderr
def test_reader_on_a_truncated_image_reports_instead_of_raising(run_script, tmp_path, fixtures):
"""A file cut off mid-header goes through the exit-code contract, not through struct.
A truncated image reaches an analyst as an interrupted download or a carved region far more
often than as a malformed program, so what matters is the contract: one line on stderr and a
documented code. Pinned after out-of-range reads were turned into a ParseError -- before that
the caller got a traceback, which reads as a defect in the tool rather than a fact about the
file.
"""
path = tmp_path / "truncated.exe"
path.write_bytes(fixtures.pe64.data[:0x100])
result = run_script("peutil.py", path)
assert result.returncode == 1
assert "error:" in result.stderr
assert "ends before" in result.stderr
assert "Traceback" not in result.stderr
def test_reader_defaults_to_the_summary_view(run_script, fixtures):
result = run_script("peutil.py", fixtures.pe64.path)
assert result.returncode == 0
assert "checksum" in result.stdout
assert "sections" in result.stdout
assert "amd64" in result.stdout
# --- pepatch -----------------------------------------------------------------
def test_patcher_on_a_non_pe_exits_three(run_script, tmp_path):
path = tmp_path / "notes.txt"
path.write_text("not a PE\n", encoding="utf-8")
result = run_script("pepatch.py", path, "--at-offset", "0", "--bytes", "CC", "--out",
tmp_path / "out.exe")
assert result.returncode == 3
assert "not a parseable PE" in result.stderr
assert "Traceback" not in result.stderr
assert not (tmp_path / "out.exe").exists()
def test_patcher_on_a_truncated_image_reports_instead_of_raising(run_script, tmp_path, fixtures):
"""Same contract as the reader's: a truncated image is an input, not a crash."""
path = tmp_path / "truncated.exe"
path.write_bytes(fixtures.pe64.data[:0x100])
result = run_script("pepatch.py", path, "--at-offset", "0", "--bytes", "CC", "--out",
tmp_path / "out.exe")
assert result.returncode == 3
assert "not a parseable PE" in result.stderr
assert "Traceback" not in result.stderr
assert not (tmp_path / "out.exe").exists()
def test_patcher_on_a_missing_file_exits_two(run_script, tmp_path):
result = run_script("pepatch.py", tmp_path / "absent.exe", "--at-offset", "0",
"--bytes", "CC", "--out", tmp_path / "out.exe")
assert result.returncode == 2
assert "is not a file" in result.stderr
assert "Traceback" not in result.stderr
@pytest.mark.parametrize("extra", [
(),
("--bytes", "CC"),
("--at-offset", "0x410"),
])
def test_patcher_without_a_usable_spec_exits_two(run_script, fixtures, extra):
result = run_script("pepatch.py", fixtures.pe64.path, *extra)
assert result.returncode == 2
assert "error:" in result.stderr
assert "Traceback" not in result.stderr
def test_patcher_with_an_empty_patch_list_exits_two(run_script, fixtures, tmp_path):
spec = tmp_path / "empty.json"
spec.write_text('{"patches": []}', encoding="utf-8")
result = run_script("pepatch.py", fixtures.pe64.path, "--spec", spec, "--out",
tmp_path / "out.exe")
assert result.returncode == 2
assert 'has no "patches" array' in result.stderr
assert not (tmp_path / "out.exe").exists()
def test_patcher_with_a_missing_spec_file_uses_the_exit_code_vocabulary(run_script,
fixtures, tmp_path):
"""A spec file that is not there is a usage error, and it says so in one line."""
result = run_script("pepatch.py", fixtures.pe64.path, "--spec", tmp_path / "absent.json",
"--out", tmp_path / "out.exe")
assert result.returncode == 2
assert "is not a readable file" in result.stderr
assert "Traceback" not in result.stderr
assert not (tmp_path / "out.exe").exists()
# --- aob_scan ----------------------------------------------------------------
def test_locator_on_a_non_pe_exits_three(run_script, tmp_path):
path = tmp_path / "notes.txt"
path.write_text("not a PE\n", encoding="utf-8")
result = run_script("aob_scan.py", path, "--patterns", "48 8B")
assert result.returncode == 3
assert "error:" in result.stderr
assert "Traceback" not in result.stderr
def test_locator_without_patterns_is_a_usage_error(run_script, fixtures):
result = run_script("aob_scan.py", fixtures.pe64.path)
assert result.returncode == 2
assert "Traceback" not in result.stderr
def test_locator_on_a_malformed_pattern_exits_nonzero_with_a_message(run_script, fixtures):
result = run_script("aob_scan.py", fixtures.pe64.path, "--patterns", "ZZ QQ")
assert result.returncode == 1
assert "not a byte" in result.stderr
assert "Traceback" not in result.stderr
def test_locator_on_a_missing_file_exits_three(run_script, tmp_path):
result = run_script("aob_scan.py", tmp_path / "absent.exe", "--patterns", "48 8B")
assert result.returncode == 3
assert "Traceback" not in result.stderr
# --- make_fixture_pe ---------------------------------------------------------
def test_fixture_builder_lists_its_recipes(run_script):
result = run_script("make_fixture_pe.py", "--list-recipes")
assert result.returncode == 0
for recipe in ("default", "pe32", "bare", "fat-align", "thin-align", "no-virtual-tail"):
assert recipe in result.stdout
def test_fixture_builder_without_an_output_path_is_a_usage_error(run_script):
result = run_script("make_fixture_pe.py")
assert result.returncode == 2
assert "--out is required" in result.stderr
assert "Traceback" not in result.stderr
def test_fixture_builder_rejects_an_unknown_recipe(run_script, tmp_path):
result = run_script("make_fixture_pe.py", "--out", tmp_path / "x.exe",
"--recipe", "no-such-recipe")
assert result.returncode == 2
assert "Traceback" not in result.stderr
def test_the_named_no_virtual_tail_recipe_builds(run_script, tmp_path):
"""The recipe names a property it removes, and it can be used to remove it.
An earlier guard compared the .rdata payload against the requested tail itself, which
inverted the relationship and made a zero-byte tail -- the whole point of the recipe --
impossible to ask for. Pinned after the fix, because a recipe that is listed and cannot be
built is a trap for whoever reads the list next.
"""
out = tmp_path / "novtail.exe"
result = run_script("make_fixture_pe.py", "--out", out, "--recipe", "no-virtual-tail")
assert result.returncode == 0
assert out.exists()
assert "virtual_only_tail_bytes" in result.stdout or "wrote" in result.stdout
# --- scan_leaks --------------------------------------------------------------
def test_scanner_lists_its_rules_and_exemptions(run_script):
result = run_script("scan_leaks.py", "--list-rules")
assert result.returncode == 0
assert "rules (18):" in result.stdout
assert "exemptions (" in result.stdout
assert "private-key-block" in result.stdout
assert "tool or library name" in result.stdout
def test_scanner_on_a_missing_root_exits_two_and_states_the_result(run_script, tmp_path):
result = run_script("scan_leaks.py", "--root", tmp_path / "absent")
assert result.returncode == 2
assert result.stdout.strip().endswith("RESULT=error")
assert "is not a directory" in result.stderr
assert "Traceback" not in result.stderr
def test_scanner_on_a_clean_root_exits_zero(run_script, clean_scan_root):
result = run_script("scan_leaks.py", "--root", clean_scan_root)
assert result.returncode == 0
assert "RESULT=clean" in result.stdout
def test_scanner_quiet_mode_prints_only_the_result_line(run_script, clean_scan_root):
result = run_script("scan_leaks.py", "--root", clean_scan_root, "--quiet")
assert result.returncode == 0
assert result.stdout.strip() == "RESULT=clean"
def test_scanner_exit_code_three_is_documented_but_unreachable(run_script, tmp_path):
"""Measured deviation: the docstring promises 3 for an empty scan surface.
No code path returns 3, so an empty surface is reported as `clean` with exit 0 -- the exact
distinction the docstring says it is making. Pinned so the mismatch is visible.
"""
empty = tmp_path / "empty"
empty.mkdir()
result = run_script("scan_leaks.py", "--root", empty)
assert result.returncode == 0
assert "RESULT=clean" in result.stdout
assert "0 files" in result.stdout
def test_scanner_on_a_root_with_findings_exits_one(run_script, synthetic_leak_root):
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--quiet")
assert result.returncode == 1
assert result.stdout.strip() == "RESULT=findings"
def test_scanner_fail_on_any_raises_a_weak_only_surface_to_findings(run_script, tmp_path,
weak_line):
root = tmp_path / "weak"
root.mkdir()
(root / "note.md").write_text(weak_line + "\n", encoding="utf-8")
default = run_script("scan_leaks.py", "--root", root, "--quiet")
strict = run_script("scan_leaks.py", "--root", root, "--quiet", "--fail-on", "any")
assert default.returncode == 0
assert default.stdout.strip() == "RESULT=clean"
assert strict.returncode == 1
assert strict.stdout.strip() == "RESULT=findings"
def test_scanner_only_filter_selects_one_category(run_script, synthetic_leak_root):
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--only", "token",
"--format", "json")
payload = json.JSONDecoder().raw_decode(result.stdout)[0]
assert result.returncode == 1
assert {hit["category"] for hit in payload["strong"]} == {"token"}
assert payload["weak"] == []
def test_scanner_skip_filter_drops_a_category(run_script, synthetic_leak_root):
result = run_script("scan_leaks.py", "--root", synthetic_leak_root, "--skip",
"token,path,device,endpoint,package,identity", "--format", "json")
payload = json.JSONDecoder().raw_decode(result.stdout)[0]
assert payload["strong"] == []
assert payload["weak"] == []
assert payload["verdict"] == "clean"
assert result.returncode == 0
# --- hwprobe -----------------------------------------------------------------
def test_prober_reports_routes_without_failing_on_a_limited_host(run_script):
"""A missing capability is a finding, not a non-zero exit."""
result = run_script("hwprobe.py", "--json", timeout=120)
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["routes"]
assert "Traceback" not in result.stderr
def test_prober_tool_inventory_mode_skips_the_cpu_rows(run_script):
result = run_script("hwprobe.py", "--tools", "--json", timeout=120)
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert "cpu" not in payload and "routes" not in payload
assert payload["tools"] and payload["python_modules"]
def test_inspect_only_runs_leave_their_working_directory_clean(run_script, fixtures,
clean_scan_root):
"""A negative outcome: none of these inspect-only runs may write into the cwd or the input.
The dry-run patcher is the case that matters: without ``--out`` it must report and stop.
"""
workdir = fixtures.pe64.path.parent
before = sorted(p.name for p in workdir.iterdir())
digest = fixtures.pe64.sha256
run_script("peutil.py", fixtures.pe64.path, cwd=workdir)
run_script("aob_scan.py", fixtures.pe64.path, "--patterns", "48 8B", cwd=workdir)
run_script("pepatch.py", fixtures.pe64.path, "--at-offset", "0x410", "--bytes", "CC",
"--dry-run", cwd=workdir)
run_script("scan_leaks.py", "--root", clean_scan_root, cwd=workdir)
assert sorted(p.name for p in workdir.iterdir()) == before
assert fixtures.pe64.sha256 == digest