diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-10 16:49:33 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-10 16:49:33 +0200 |
| commit | 6593d2596f5d3df0da60a4e083b8097c8539b14e (patch) | |
| tree | 06ba4eb3de22d3cd46e369a34b84a13a7ce9736b /tests | |
| parent | 88d07d352e84b0c32012c6989e8402980e0a3b94 (diff) | |
| download | abusectl-6593d2596f5d3df0da60a4e083b8097c8539b14e.tar.gz abusectl-6593d2596f5d3df0da60a4e083b8097c8539b14e.zip | |
feat: write vendor rows into the manifest
generate() gains a `configured` argument and appends vendor_destinations()
after the email bodies are written. It is an ARGUMENT rather than a config
read, the way the identity already is: report.py stays pure and testable
with no files on disk. Defaulting to nothing configured is the safe
direction, a caller that forgets it writes no vendor row rather than
promising a submission nobody set up.
cli.py passes settings.destinations down, so a config carrying a vendor's
keys produces that vendor's row in the case manifest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176FYdVfpzUq8S9jecqQqL6
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_report.py | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/tests/test_report.py b/tests/test_report.py index d0e8239..c0fe0b0 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -2106,3 +2106,82 @@ class VendorDestinations(unittest.TestCase): [row["id"] for row in second]) self.assertEqual([row["id"] for row in first], ["misp", "abusedb", "urlhaus"]) + + +class GenerateWritesVendorRows(unittest.TestCase): + """The seam: vendor rows land in the manifest beside the email rows. + + generate() takes `configured` as an ARGUMENT rather than reading the + config, the way it already takes the identity and parse.py takes the + trust boundary. That is what keeps this module testable with no files + on disk beyond the case directory it writes into. + """ + + def _manifest(self): + return { + "format": 1, + "case_id": "2026-09-10-test", + "headers": [["From", "phish@example.invalid"], + ["Subject", "test"], + ["Date", "Wed, 09 Sep 2026 09:12:44 +0000"]], + "iocs": [ + {"id": "ioc-1", "type": "ipv4", "value": "198.51.100.7"}, + {"id": "ioc-2", "type": "url", + "value": "http://example.invalid/a"}, + ], + "contacts": [{"ioc": "ioc-1", "iocs": ["ioc-1"], + "abuse": ["abuse@example.invalid"]}], + } + + def test_configured_destinations_appear_alongside_the_email_rows(self): + with tempfile.TemporaryDirectory() as tmp: + out = report.generate(self._manifest(), Path(tmp), + {"email": "r@example.org"}, + configured={"misp", "abusedb"}) + kinds = [row["kind"] for row in out["destinations"]] + self.assertIn("email", kinds) + self.assertIn("misp", kinds) + self.assertIn("api", kinds) + + def test_nothing_configured_leaves_the_email_rows_untouched(self): + with tempfile.TemporaryDirectory() as tmp: + out = report.generate(self._manifest(), Path(tmp), + {"email": "r@example.org"}, + configured=set()) + self.assertEqual([row["kind"] for row in out["destinations"]], + ["email"]) + + def test_a_second_run_produces_the_same_rows(self): + with tempfile.TemporaryDirectory() as tmp: + first = report.generate(self._manifest(), Path(tmp), + {"email": "r@example.org"}, + configured={"misp"}) + ids = [row["id"] for row in first["destinations"]] + second = report.generate(first, Path(tmp), + {"email": "r@example.org"}, + configured={"misp"}) + self.assertEqual([row["id"] for row in second["destinations"]], ids) + + def test_vendor_rows_get_no_body_written_to_disk(self): + with tempfile.TemporaryDirectory() as tmp: + out = report.generate(self._manifest(), Path(tmp), + {"email": "r@example.org"}, + configured={"misp", "abusedb"}) + written = sorted(p.name + for p in (Path(tmp) / "bodies").iterdir()) + self.assertTrue(all(name.endswith(".xarf") for name in written)) + self.assertEqual(len(written), 1) + for row in out["destinations"]: + if row["kind"] != "email": + self.assertIsNone(row["body"]) + self.assertNotIn("body_sha256", row) + + def test_configured_defaults_to_nothing(self): + # An older caller that does not pass it must not crash, and must not + # silently gain destinations it never configured. + with tempfile.TemporaryDirectory() as tmp: + out = report.generate(self._manifest(), Path(tmp), + {"email": "r@example.org"}) + self.assertEqual([row["kind"] for row in out["destinations"]], + ["email"]) + |
