diff --git a/src/mailparser/utils.py b/src/mailparser/utils.py index be6e4d5..ae92ab0 100644 --- a/src/mailparser/utils.py +++ b/src/mailparser/utils.py @@ -134,7 +134,18 @@ def get_addresses( elif not isinstance(raw_header, str): raw_header = str(raw_header) - parsed = email.utils.getaddresses([raw_header], strict=True) + # ``strict`` was only added to ``getaddresses()`` as part of the + # CVE-2023-27043 hardening, and was backported to patch releases + # >=3.10.15, >=3.11.10, >=3.12.6 rather than to every 3.10-3.12 + # release. On an older patch release within one of those minor + # versions, passing ``strict=True`` raises ``TypeError: getaddresses() + # got an unexpected keyword argument 'strict'``. + # ``email.utils.supports_strict_parsing`` is the stdlib's own + # capability flag for this, so use it instead of a version check. + if getattr(email.utils, "supports_strict_parsing", False): + parsed = email.utils.getaddresses([raw_header], strict=True) + else: + parsed = email.utils.getaddresses([raw_header]) # If every result from the strict parser has an empty address — while the # raw header is non-empty — fall back to regex extraction so that the diff --git a/tests/test_utils.py b/tests/test_utils.py index 734900d..af7faaf 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -647,6 +647,51 @@ def test_get_addresses_plain_string_unchanged(self): result = get_addresses("Plain Name ") self.assertEqual(result, [("Plain Name", "plain@example.com")]) + def test_get_addresses_omits_strict_kwarg_when_unsupported(self): + """ + Regression for TypeError on Python patch releases that predate + the CVE-2023-27043 backport (< 3.10.15, < 3.11.10, < 3.12.6): + ``email.utils.getaddresses()`` doesn't accept ``strict=`` there, + and previously get_addresses() passed it unconditionally, raising + + TypeError: getaddresses() got an unexpected keyword + argument 'strict' + + on every affected interpreter. get_addresses() should now check + ``email.utils.supports_strict_parsing`` and omit ``strict=`` + when it's not set, instead of assuming every supported Python + version has it. + """ + with patch("mailparser.utils.email.utils.supports_strict_parsing", False): + with patch( + "mailparser.utils.email.utils.getaddresses", + return_value=[("Plain Name", "plain@example.com")], + ) as mock_getaddresses: + result = get_addresses("Plain Name ") + + mock_getaddresses.assert_called_once_with(["Plain Name "]) + self.assertEqual(result, [("Plain Name", "plain@example.com")]) + + def test_get_addresses_uses_strict_kwarg_when_supported(self): + """ + On interpreters that do support it (the common case in CI and + in production), get_addresses() should still pass + ``strict=True`` as before, so the CVE-2023-27043 hardening and + the existing regex fallback for non-compliant display names + keep working unchanged. + """ + with patch("mailparser.utils.email.utils.supports_strict_parsing", True): + with patch( + "mailparser.utils.email.utils.getaddresses", + return_value=[("Plain Name", "plain@example.com")], + ) as mock_getaddresses: + result = get_addresses("Plain Name ") + + mock_getaddresses.assert_called_once_with( + ["Plain Name "], strict=True + ) + self.assertEqual(result, [("Plain Name", "plain@example.com")]) + def test_mailparser_from_bytes_preserves_unicode_display_name(self): """ Regression: Header objects from Message.get(name) must round-trip