diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 792072ab9f6128a..ab136d36cebfc1d 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -580,6 +580,8 @@ def display_name(self): return res.value if res[0].token_type == 'cfws': res.pop(0) + if len(res) == 0: + return res.value else: if (isinstance(res[0], TokenList) and res[0][0].token_type == 'cfws'): @@ -2511,7 +2513,7 @@ def get_parameter(value): param.append(ValueTerminal('*', 'extended-parameter-marker')) value = value[1:] param.extended = True - if value[0] != '=': + if not value or value[0] != '=': raise errors.HeaderParseError("Parameter not followed by '='") param.append(ValueTerminal('=', 'parameter-separator')) value = value[1:] diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 9d9fe418ee4d067..4becab32bf06170 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -1862,6 +1862,15 @@ def test_get_display_name_for_invalid_address_field(self): ':Foo ', '', '', [errors.InvalidHeaderDefect], ':Foo ') self.assertEqual(display_name.value, '') + def test_get_display_name_comment_only(self): + # gh-151857: a comment-only (CFWS) display name raised IndexError. + display_name = self._test_get_x( + parser.get_display_name, + '(c)', '(c)', ' "" ', + [errors.InvalidHeaderDefect, errors.ObsoleteHeaderDefect], '') + self.assertEqual(display_name.display_name, '') + self.assertEqual(display_name.value, ' "" ') + # get_name_addr def test_get_name_addr_angle_addr_only(self): @@ -3144,6 +3153,29 @@ def mime_parameters_as_value(self, 'r*=\'a\'"', [('r', '"')], [errors.InvalidHeaderDefect]*2), + + # gh-151857: a parameter name ending in the extended marker '*' with + # no value used to raise an uncaught IndexError instead of a defect. + 'extended_marker_no_value': ( + 'name*', + '', + 'name*', + [], + [errors.InvalidHeaderDefect]), + + 'extended_marker_no_value_sectioned': ( + 'name*0*', + '', + 'name*0*', + [], + [errors.InvalidHeaderDefect]), + + 'extended_marker_no_value_after_param': ( + 'x=1; name*', + ' x="1"', + 'x=1; name*', + [('x', '1')], + [errors.InvalidHeaderDefect]), } @parameterize diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index aa918255d15c37e..19ce176c374cc87 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -1426,6 +1426,15 @@ def test_groups_types(self): self.assertIsInstance(h.groups, tuple) self.assertIsInstance(h.groups[0], Group) + def test_comment_only_group_display_name(self): + # gh-151857: a comment-only group display name raised IndexError. + h = self.make_header('to', '(c):') + self.assertEqual(h.groups[0].display_name, '') + self.assertEqual(h.addresses, ()) + h = self.make_header('cc', '(x): a@b.com;') + self.assertEqual(h.groups[0].display_name, '') + self.assertEqual(h.addresses[0].addr_spec, 'a@b.com') + def test_set_from_Address(self): h = self.make_header('to', Address('me', 'foo', 'example.com')) self.assertEqual(h, 'me ') diff --git a/Misc/NEWS.d/next/Library/2026-06-22-10-00-00.gh-issue-151857.3j9GAn.rst b/Misc/NEWS.d/next/Library/2026-06-22-10-00-00.gh-issue-151857.3j9GAn.rst new file mode 100644 index 000000000000000..a6446a40482c349 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-22-10-00-00.gh-issue-151857.3j9GAn.rst @@ -0,0 +1,4 @@ +Fixed two cases where the :mod:`email` header parser (under +:class:`~email.policy.EmailPolicy`) raised an uncaught :exc:`IndexError` on +malformed input: a MIME parameter name ending with ``*``, and an address +header whose display name is only a comment. Both now degrade gracefully.