aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_report.py54
1 files changed, 43 insertions, 11 deletions
diff --git a/tests/test_report.py b/tests/test_report.py
index 9a19d00..c8c39f8 100644
--- a/tests/test_report.py
+++ b/tests/test_report.py
@@ -961,19 +961,51 @@ class FeedbackPart(unittest.TestCase):
self.lookup = dict(self.fields)
def test_the_three_rfc5965_required_fields_are_present(self):
- self.assertEqual(self.lookup["Feedback-Type"], "abuse")
+ self.assertEqual(self.lookup["Feedback-Type"], "fraud")
self.assertEqual(self.lookup["Version"], "1")
self.assertTrue(self.lookup["User-Agent"].startswith("abusectl/"))
- def test_the_xarf_report_type_is_phishing(self):
- self.assertEqual(self.lookup["Report-Type"], "phishing")
+ def test_the_feedback_type_is_the_registered_value_naming_phishing(self):
+ """Checked against the IANA MARF registry, not a worked example.
+
+ RFC 5965 registers fraud as "indicates some kind of fraud or
+ phishing activity" and abuse as "unsolicited email or some other
+ kind of email abuse". This tool reports phishing.
+ """
+ self.assertEqual(self.lookup["Feedback-Type"], "fraud")
+
+ def test_no_unregistered_field_is_emitted(self):
+ """Every name here appears in the IANA MARF registry.
+
+ Report-Type: phishing used to be emitted and is NOT registered. It
+ was harmless, since RFC 5965 section 6 makes ignoring an unknown
+ field a MUST, but that same section requires an extension field be
+ registered, and a desk should be able to look up every field in a
+ document this tool sends.
+ """
+ registered = {
+ "Arrival-Date", "Auth-Failure", "Authentication-Results",
+ "Delivery-Result", "DKIM-ADSP-DNS", "DKIM-Canonicalized-Body",
+ "DKIM-Canonicalized-Header", "DKIM-Domain", "DKIM-Identity",
+ "DKIM-Selector", "DKIM-Selector-DNS", "Feedback-Type",
+ "Identity-Alignment", "Incidents", "Original-Mail-From",
+ "Original-Rcpt-To", "Received-Date", "Reported-Domain",
+ "Reported-URI", "Reporting-MTA", "Source-IP", "Source-Port",
+ "SPF-DNS", "User-Agent", "Version",
+ }
+ # Source is x-arf's, deliberately: RFC 5965 has no field for "this
+ # host is the thing being reported", which is why the envelope
+ # carries x-arf fields at all. It is the one exception and is named
+ # here so a NEW unregistered field cannot slip in unnoticed.
+ emitted = {name for name, _ in self.fields}
+ self.assertEqual(emitted - registered - {"Source"}, set())
def test_source_is_the_primary_indicator(self):
self.assertEqual(self.lookup["Source"], "203.0.113.42")
self.assertEqual(self.lookup["Source-IP"], "203.0.113.42")
def test_every_url_appears_as_a_reported_uri(self):
- uris = [value for name, value in self.fields if name == "Reported-Uri"]
+ uris = [value for name, value in self.fields if name == "Reported-URI"]
self.assertEqual(
uris, ["http://login.sender.invalid/verify?id=REDACTED"]
)
@@ -1048,11 +1080,11 @@ class FeedbackPart(unittest.TestCase):
seen: dict[str, int] = {}
for name, _ in fields:
seen[name] = seen.get(name, 0) + 1
- repeatable = {"Reported-Uri", "Reported-Domain"}
+ repeatable = {"Reported-URI", "Reported-Domain"}
for name, count in seen.items():
if name not in repeatable:
self.assertEqual(count, 1, f"{name} appeared {count} times")
- self.assertEqual(seen["Reported-Uri"], 2)
+ self.assertEqual(seen["Reported-URI"], 2)
self.assertEqual(seen["Reported-Domain"], 2)
def test_an_ipv6_indicator_fills_source_ip_too(self):
@@ -1086,7 +1118,7 @@ class FeedbackPart(unittest.TestCase):
self.assertNotIn("Source", lookup)
self.assertNotIn("Source-IP", lookup)
# The envelope is still well formed: a desk gets a valid part.
- self.assertEqual(lookup["Feedback-Type"], "abuse")
+ self.assertEqual(lookup["Feedback-Type"], "fraud")
self.assertEqual(lookup["Version"], "1")
def test_a_type_this_module_does_not_place_is_not_invented_into_one(self):
@@ -1133,7 +1165,7 @@ class FeedbackPart(unittest.TestCase):
"target": "abuse@host.invalid", "body": None,
"status": "pending"}
lookup = dict(report.feedback_fields(MANIFEST, destination))
- self.assertEqual(lookup["Feedback-Type"], "abuse")
+ self.assertEqual(lookup["Feedback-Type"], "fraud")
def test_arrival_date_is_not_taken_from_the_senders_date_header(self):
"""RFC 5965: Arrival-Date is when the generating ADMD's MTA received
@@ -1189,7 +1221,7 @@ class FeedbackInjection(unittest.TestCase):
destination = report.email_destinations(manifest["contacts"])[0]
fields = report.feedback_fields(manifest, destination)
for name, out in fields:
- if name == "Reported-Uri":
+ if name == "Reported-URI":
return out
return None
@@ -1386,7 +1418,7 @@ class FeedbackInjection(unittest.TestCase):
fields = report.feedback_fields(manifest, destination)
self.assertEqual(
{name for name, _ in fields},
- {"Feedback-Type", "User-Agent", "Version", "Report-Type"})
+ {"Feedback-Type", "User-Agent", "Version"})
def test_a_non_string_value_does_not_crash_the_report(self):
"""A manifest is edited by hand and JSON has numbers.
@@ -1800,7 +1832,7 @@ class Writing(unittest.TestCase):
self.assertNotIn(b"\n", header.replace(b"\r\n", b""))
# And the bare LFs inside the feedback part survive untranslated,
# which is the same defect seen from the other side.
- self.assertIn(b"Feedback-Type: abuse\nUser-Agent:", raw)
+ self.assertIn(b"Feedback-Type: fraud\nUser-Agent:", raw)
def test_an_untouched_case_regenerates_without_complaint(self):
"""The regression that the plan's read_text() caused.