Skip to content

Commit 52f0205

Browse files
ripe data: handle whitespace in localparts with multiple addresses
handle a special case with abuse-mailbox: if there are multiple addresses and one of them has a white space in the local part, splitting by white space does not work getaddresses logic does not handle whitespace-separated email addresses so use our only simple email address pattern to extract the addresses and make the proper parsing and validation with getaddresses
1 parent f6cfbe8 commit 52f0205

4 files changed

Lines changed: 35 additions & 4 deletions

File tree

intelmq_certbund_contact/ripe/ripe_data.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@
2828
import gzip
2929
import ipaddress
3030

31+
from re import compile as re_compile
32+
from itertools import chain
33+
3134
from email.utils import getaddresses
3235

3336

37+
# Basic regular expression pattern for email addresses. Local part can either be quoted or it must not contain spaces.
38+
# More reasoning in function sanitize_role_entry and in 2auto/issue354
39+
RE_EMAIL_ADDRESS = re_compile('((".*?"|[^ ]+)@[^ ]+)')
40+
41+
3442
def add_db_args(parser):
3543
parser.add_argument("--conninfo",
3644
default='dbname=contactdb',
@@ -626,12 +634,20 @@ def sanitize_role_entry(entry):
626634

627635
if 'abuse-mailbox' in entry:
628636
# extract email addresses using email.utils.parseaddr
629-
# role contained also multiple addresses in the past, this is no longer the case as od 2025-03-05
630-
# getaddresses may return the same email address twice if the address is in display name format, therefore make a list of a set
631-
# getaddresses does not detect email addresses separated by a simple whitespace, so replace it with a comma
637+
# role contained also multiple addresses in the past, this is no longer the case and forbidden as of 2025-03-05
638+
# needs to be handled anyway (2auto/issue354)
639+
# getaddresses may return the same email address twice if the address is in display name format, therefore use a set
640+
# getaddresses does not detect email addresses separated by a simple whitespace, so use a simple regular expression to first find all strings looking like an email address
641+
# the regular expression is necessary to handle special cases like:
642+
# abuse-mailbox: "abuse contact"@example.com abuse@example.com
643+
# lines with multiple addresses containing white spaces in the email addresses
632644
# getaddresses may return empty strings, so filter out zero-length strings
633645
# sort the list of addresses for reproducability and consistency
634-
entry['abuse-mailbox'] = list(filter(len, sorted({address for _, address in getaddresses([element.replace(' ', ', ') for element in entry['abuse-mailbox']], strict=False)})))
646+
entry['abuse-mailbox'] = [address for address in
647+
sorted({address for _, address in
648+
getaddresses(chain.from_iterable([[finding[0] for finding in RE_EMAIL_ADDRESS.findall(element)] for element in entry['abuse-mailbox']]),
649+
strict=False)})
650+
if len(address)]
635651
return entry
636652

637653

49 Bytes
Binary file not shown.
75 Bytes
Binary file not shown.

tests/ripe/test_ripe_data.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ def test_role_special_characters(self):
7979
abuse-mailbox: N&INetworkOperations@homeoffice.gov.uk
8080
abuse-mailbox: sdm.n&c@frieslandcampina.com
8181
abuse-mailbox: kevino'connor@merseyfire.gov.uk
82+
No examples in RIPE data, but allowed: whitespaces in the local part
83+
abuse-mailbox: "foo bar"@example.com
8284
"""
8385
role_list = ripe_data.parse_file(
8486
f"{dirname(__file__)}/role_special_characters.txt.gz",
@@ -95,6 +97,11 @@ def test_role_special_characters(self):
9597
"role": ["Abuse-C Role"],
9698
'org': ['ORG-EFHB1-RIPE'],
9799
},
100+
{
101+
"abuse-mailbox": ['"abuse contact"@example.com'],
102+
"nic-hdl": ["DUMY1-RIPE"],
103+
"role": ["Non-existing contact with white space"],
104+
},
98105
],
99106
)
100107

@@ -131,6 +138,9 @@ def test_multiple_addresses(self):
131138
"""
132139
There are no examples in RIPE data as of 2025-03-05 but it should still be parseable:
133140
abuse-mailbox: abuse@example.com abuse@example.net
141+
abuse-mailbox: abuse@example.com, abuse@example.net
142+
One address with whitespace in local part and also a second address separated by a white space:
143+
abuse-mailbox: "abuse contact"@example.com abuse@example.com
134144
"""
135145
role_list = ripe_data.parse_file(
136146
f"{dirname(__file__)}/role_multiple_addresses.txt.gz",
@@ -151,6 +161,11 @@ def test_multiple_addresses(self):
151161
"nic-hdl": ["DUMY2-RIPE"],
152162
"role": ["Non-existing contact 2 with comma"],
153163
},
164+
{
165+
"abuse-mailbox": ['"abuse contact"@example.com', 'abuse@example.com'],
166+
"nic-hdl": ["DUMY3-RIPE"],
167+
"role": ["Non-existing contacts with white-space"],
168+
},
154169
],
155170
)
156171

0 commit comments

Comments
 (0)