aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_rdap.py85
1 files changed, 85 insertions, 0 deletions
diff --git a/tests/test_rdap.py b/tests/test_rdap.py
index 14a1fd4..b7baa21 100644
--- a/tests/test_rdap.py
+++ b/tests/test_rdap.py
@@ -176,5 +176,90 @@ class ServerSelection(unittest.TestCase):
self.assertIsNone(rdap.server_for_tld("example", self.DNS))
+def _entity(roles, emails, entities=None):
+ """Build an RDAP entity in real jCard shape."""
+ properties = [["version", {}, "text", "4.0"]]
+ for address in emails:
+ properties.append(["email", {}, "text", address])
+ entity = {"roles": roles, "vcardArray": ["vcard", properties]}
+ if entities:
+ entity["entities"] = entities
+ return entity
+
+
+class AbuseExtraction(unittest.TestCase):
+ def test_an_abuse_entity_yields_its_address(self):
+ response = {"entities": [_entity(["abuse"], ["abuse@example.invalid"])]}
+ self.assertEqual(
+ rdap.abuse_addresses(response), ["abuse@example.invalid"]
+ )
+
+ def test_a_nested_abuse_entity_is_found(self):
+ """The abuse entity is usually a child of the organisation entity."""
+ response = {
+ "entities": [
+ _entity(
+ ["registrant"], [],
+ entities=[_entity(["abuse"], ["abuse@example.invalid"])],
+ )
+ ]
+ }
+ self.assertEqual(
+ rdap.abuse_addresses(response), ["abuse@example.invalid"]
+ )
+
+ def test_a_technical_only_response_yields_nothing(self):
+ """A technical contact is a named human who never volunteered to
+ receive abuse mail. Mailing them is useless and is a small privacy
+ harm to an uninvolved third party."""
+ response = {"entities": [_entity(["technical"], ["someone@example.invalid"])]}
+ self.assertEqual(rdap.abuse_addresses(response), [])
+
+ def test_every_abuse_address_is_kept(self):
+ """Some netblocks publish two desks, and picking one arbitrarily
+ can drop the one that would have answered."""
+ response = {
+ "entities": [
+ _entity(["abuse"], ["one@example.invalid", "two@example.invalid"])
+ ]
+ }
+ self.assertEqual(
+ rdap.abuse_addresses(response),
+ ["one@example.invalid", "two@example.invalid"],
+ )
+
+ def test_a_newline_in_an_address_is_rejected(self):
+ """The address becomes a mail recipient in report and submit, so a
+ CRLF here is header injection into mail this tool sends."""
+ response = {
+ "entities": [
+ _entity(["abuse"], ["abuse@example.invalid\r\nBcc: victim@example.org"])
+ ]
+ }
+ self.assertEqual(rdap.abuse_addresses(response), [])
+
+ def test_a_non_address_is_rejected(self):
+ response = {"entities": [_entity(["abuse"], ["not an address"])]}
+ self.assertEqual(rdap.abuse_addresses(response), [])
+
+ def test_recursion_is_depth_capped(self):
+ """Remote JSON must not be able to hang the tool."""
+ deep = _entity(["abuse"], ["deep@example.invalid"])
+ for _ in range(10):
+ deep = _entity(["registrant"], [], entities=[deep])
+ self.assertEqual(rdap.abuse_addresses({"entities": [deep]}), [])
+
+ def test_duplicate_addresses_collapse(self):
+ response = {
+ "entities": [
+ _entity(["abuse"], ["abuse@example.invalid"]),
+ _entity(["abuse"], ["abuse@example.invalid"]),
+ ]
+ }
+ self.assertEqual(
+ rdap.abuse_addresses(response), ["abuse@example.invalid"]
+ )
+
+
if __name__ == "__main__":
unittest.main()