From 715f29471245ec3474b074c2d431a360431a3e7a Mon Sep 17 00:00:00 2001 From: SATOH Fumiyasu Date: Sat, 15 Nov 2025 01:21:49 +0900 Subject: [PATCH] fix: authenticate_message: Do not set None to spf_result.smtp_mailfrom Closes issue #33. ``` self = def test_authenticate_dmarc_prev_spf(self): prev = "Authentication-Results: example.com; spf=pass smtp.mailfrom=gmail.com" > res = authenticate_message(self.message2, "example.com", prev=prev, spf=False, dkim=True, dmarc=True, dnsfunc=self.dnsfunc) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ authheaders/test/test_authentication.py:155: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ authheaders/__init__.py:434: in authenticate_message return str(auth_res) ^^^^^^^^^^^^^ /usr/lib/python3/dist-packages/authres/core.py:476: in __str__ return ''.join((self.HEADER_FIELD_NAME, ': ', self.header_value())) ^^^^^^^^^^^^^^^^^^^ /usr/lib/python3/dist-packages/authres/core.py:492: in header_value strs.append(str(result)) ^^^^^^^^^^^ /usr/lib/python3/dist-packages/authres/core.py:222: in __str__ strs.append(str(property_)) ^^^^^^^^^^^^^^ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = def __str__(self): if self.comment: return '%s.%s=%s (%s)' % (self.type, self.name, self.value.quote_if_needed(), self.comment) else: > return '%s.%s=%s' % (self.type, self.name, self.value.quote_if_needed()) ^^^^^^^^^^^^^^^^^^^^^^^^^^ E AttributeError: 'NoneType' object has no attribute 'quote_if_needed' /usr/lib/python3/dist-packages/authres/core.py:132: AttributeError ``` --- authheaders/__init__.py | 12 +++++++----- authheaders/test/test_authentication.py | 21 ++++++++++++++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/authheaders/__init__.py b/authheaders/__init__.py index 37a2c00..6031ced 100644 --- a/authheaders/__init__.py +++ b/authheaders/__init__.py @@ -232,11 +232,13 @@ def dmarc_per_from(from_domain, spf_result=None, dkim_result=None, dnsfunc=None, if not policy_only and spf_result and spf_result.result == "pass": # The domain in SPF results often includes the local part, even though # generally it SHOULD NOT (RFC 7601, Section 2.7.2, last paragraph). - try: - mail_from_domain = get_domain_part(spf_result.smtp_mailfrom) - except IndexError: - mail_from_domain = None - spf_result.smtp_mailfrom = mail_from_domain + mail_from_domain = spf_result.smtp_mailfrom + if '@' in mail_from_domain: + try: + mail_from_domain = get_domain_part(mail_from_domain) + spf_result.smtp_mailfrom = mail_from_domain + except IndexError: + mail_from_domain = None if aspf == "s" and from_domain == mail_from_domain: result = "pass" elif aspf == "r" and get_org_domain(from_domain) == get_org_domain(mail_from_domain): diff --git a/authheaders/test/test_authentication.py b/authheaders/test/test_authentication.py index 486b4c6..81a39df 100755 --- a/authheaders/test/test_authentication.py +++ b/authheaders/test/test_authentication.py @@ -145,10 +145,25 @@ def test_authenticate_dmarc_comma(self): res = authenticate_message(self.message8, "example.com", spf=False, dnsfunc=self.dnsfunc) self.assertEqual(res, "Authentication-Results: example.com; dkim=pass header.d=example.com header.i=@example.com; dmarc=pass (Used From Domain Record) header.from=example.com policy.dmarc=reject") - def test_prev(self): + def test_authenticate_dkim_prev_spf(self): prev = "Authentication-Results: example.com; spf=pass smtp.mailfrom=gmail.com" - res = authenticate_message(self.message2, "example.com", prev=prev, spf=False, dmarc=False, dnsfunc=self.dnsfunc) - self.assertEqual(res, "Authentication-Results: example.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=example.com header.i=@example.com") + res = authenticate_message(self.message2, "example.com", prev=prev, spf=False, dkim=True, dmarc=False, dnsfunc=self.dnsfunc) + self.assertEqual(res, f"{prev}; dkim=pass header.d=example.com header.i=@example.com") + + def test_authenticate_dmarc_prev_spf(self): + prev = "Authentication-Results: example.com; spf=pass smtp.mailfrom=gmail.com" + res = authenticate_message(self.message2, "example.com", prev=prev, spf=False, dkim=True, dmarc=True, dnsfunc=self.dnsfunc) + self.assertEqual(res, f"{prev}; dkim=pass header.d=example.com header.i=@example.com; dmarc=pass (Used From Domain Record) header.from=example.com policy.dmarc=reject") + + def test_authenticate_dmarc_prev_spf_mailfrom_at_domain(self): + prev = "Authentication-Results: example.com; spf=pass smtp.mailfrom=bogus@gmail.com" + res = authenticate_message(self.message2, "example.com", prev=prev, spf=False, dkim=True, dmarc=True, dnsfunc=self.dnsfunc) + self.assertEqual(res, f"{prev.replace('bogus@', '')}; dkim=pass header.d=example.com header.i=@example.com; dmarc=pass (Used From Domain Record) header.from=example.com policy.dmarc=reject") + + def test_authenticate_dmarc_prev_spf_mailfrom_at_invalid(self): + prev = "Authentication-Results: example.com; spf=pass smtp.mailfrom=bogus@" + res = authenticate_message(self.message2, "example.com", prev=prev, spf=False, dkim=True, dmarc=True, dnsfunc=self.dnsfunc) + self.assertEqual(res, f"{prev}; dkim=pass header.d=example.com header.i=@example.com; dmarc=pass (Used From Domain Record) header.from=example.com policy.dmarc=reject") def test_get_domain_part(self): froms_to_test = [['test@example.com', 'example.com'], [""""Test, User" """, 'example.com'], ["""Test User """, 'sub2.example.biz'], ["""=?UTF-8?B?QmVkIEJhdGggJiBCZXlvbmQ=?=""", 'example.com'], ]