aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-08 13:39:40 +0200
committerDanilo M. <danix@danix.xyz>2026-09-08 13:39:40 +0200
commit498282f9d89cdc391781b9aadfe329452122fa98 (patch)
treed2276e0b54c21055a9303d4e773b7bc9d7220861 /tests
parent69c49e4616bc3375964f90aae408376b20484d8f (diff)
downloadabusectl-498282f9d89cdc391781b9aadfe329452122fa98.tar.gz
abusectl-498282f9d89cdc391781b9aadfe329452122fa98.zip
feat: extract URLs and attachment hashes, fetching nothing
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KphFXTc2QajxXsHWyvGJ4R
Diffstat (limited to 'tests')
-rw-r--r--tests/test_parse.py61
1 files changed, 61 insertions, 0 deletions
diff --git a/tests/test_parse.py b/tests/test_parse.py
index 1083f1d..0fac304 100644
--- a/tests/test_parse.py
+++ b/tests/test_parse.py
@@ -96,5 +96,66 @@ class TestAuthResults(unittest.TestCase):
self.assertEqual(parse.auth_results(load("with-attachment.eml")), {})
+class TestUrls(unittest.TestCase):
+ def test_an_href_is_found_and_redacted(self):
+ urls = parse.urls(load("simple.eml"))
+ self.assertEqual(
+ urls,
+ ["http://login.bank-verify.example.invalid/verify?id=REDACTED"],
+ )
+
+ def test_a_plain_text_url_is_found_and_redacted(self):
+ urls = parse.urls(load("forged-chain.eml"))
+ self.assertEqual(urls, ["http://evil.example.invalid/go?u=REDACTED"])
+
+ def test_urls_are_deduplicated_and_ordered(self):
+ raw = (
+ b"From: <a@b.example.invalid>\r\n"
+ b"Subject: t\r\n"
+ b"Content-Type: text/plain\r\n\r\n"
+ b"http://z.example.invalid/ and http://a.example.invalid/ and "
+ b"http://z.example.invalid/ again\r\n"
+ )
+ self.assertEqual(
+ parse.urls(raw),
+ ["http://a.example.invalid/", "http://z.example.invalid/"],
+ )
+
+
+class TestRedirectChains(unittest.TestCase):
+ def test_a_declared_target_is_recovered_as_a_hop(self):
+ chains = parse.redirect_chains(load("redirector.eml"))
+ self.assertEqual(len(chains), 1)
+ source, target = chains[0]
+ self.assertTrue(source.startswith("http://t.example.invalid/c"))
+ self.assertTrue(target.startswith("http://evil.example.invalid/pay"))
+
+ def test_the_recovered_target_is_itself_redacted(self):
+ _, target = parse.redirect_chains(load("redirector.eml"))[0]
+ self.assertEqual(target, "http://evil.example.invalid/pay?ref=REDACTED")
+
+ def test_the_recipient_token_does_not_survive(self):
+ # The whole point: the destination is an indicator, the token is not.
+ chains = parse.redirect_chains(load("redirector.eml"))
+ self.assertNotIn("dGVzdEBleGFtcGxlLm9yZw", repr(chains))
+
+ def test_a_message_with_no_redirector_reports_none(self):
+ self.assertEqual(parse.redirect_chains(load("simple.eml")), [])
+
+
+class TestAttachments(unittest.TestCase):
+ def test_filename_and_sha256_are_recorded(self):
+ found = parse.attachments(load("with-attachment.eml"))
+ self.assertEqual(len(found), 1)
+ self.assertEqual(found[0].filename, "invoice.pdf")
+ self.assertEqual(
+ found[0].sha256,
+ "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3",
+ )
+
+ def test_a_message_with_no_attachment_reports_none(self):
+ self.assertEqual(parse.attachments(load("simple.eml")), [])
+
+
if __name__ == "__main__":
unittest.main()