aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_cli.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-09 19:00:26 +0200
committerDanilo M. <danix@danix.xyz>2026-09-09 19:00:26 +0200
commitb35d6e504719da97f8d2ef19b300d5d8ee45beb0 (patch)
tree1350f13796d3bafd210a749ab207359699a00b00 /tests/test_cli.py
parent7cdeb7f2d8a92fce0355de70290cb7bbab6fb363 (diff)
downloadabusectl-b35d6e504719da97f8d2ef19b300d5d8ee45beb0.tar.gz
abusectl-b35d6e504719da97f8d2ef19b300d5d8ee45beb0.zip
feat: store a whitelist of publishable headers in the manifest
report must never open source.eml, so parse decides once what may be published and report formats only what it is given. To, Cc, Delivered-To and X-Original-To are absent by construction rather than stripped. Received is cut to the boundary hop alone, in both directions. Above it are our own relays; below it is the attacker's own writing, and a forged chain names an innocent third party there, so publishing a hop below the boundary puts someone else's address into a report a desk will act on. That is the third property applied to disclosure rather than to sending_ip(). Truncating the chain was not sufficient on its own: the surviving line is written by our own relay and records the envelope recipient in its optional "for <addr>" clause, so the whitelist alone would have published the victim's address verbatim in the one header a report reproduces in full. The clause is removed and the rest of the hop kept. The manifest-wide "no example.org" assertion is narrowed to the headers block only, where the whitelist deliberately publishes our receiving relay's name in a by/authserv-id clause. The address itself is still barred there, asserted separately. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xj1ayFRSUQ2u7cwb3S4axE
Diffstat (limited to 'tests/test_cli.py')
-rw-r--r--tests/test_cli.py20
1 files changed, 19 insertions, 1 deletions
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 1c023c3..425bce9 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -145,7 +145,25 @@ class TestParse(unittest.TestCase):
str(FIXTURES / "simple.eml"),
)
text = (pathlib.Path(out.strip()) / "manifest.json").read_text()
- self.assertNotIn("example.org", text)
+ self.assertNotIn("you@example.org", text)
+ # The bare domain is still barred everywhere the IOCs live. The
+ # headers block is the one exception and it is a NARROW one: the
+ # whitelist publishes the boundary Received line and
+ # Authentication-Results, and both name our own receiving relay in a
+ # "by"/authserv-id clause. That is the user's mail host, not the
+ # user's identity, and a desk learns it from the report's own From
+ # regardless. The address itself must still be absent, which the
+ # assertion above and report_headers' own tests cover.
+ import json
+
+ manifest = json.loads(text)
+ headers = manifest.pop("headers")
+ self.assertNotIn("example.org", json.dumps(manifest))
+ # And nothing shaped like an address survives in the exception.
+ self.assertNotIn("@example.org", json.dumps(headers))
+ names = [name for name, _ in headers]
+ for name in ("To", "Cc", "Delivered-To", "X-Original-To"):
+ self.assertNotIn(name, names)
def test_a_missing_config_points_at_init(self):
code, _, err = self._run(