Mailing List Archive

[3.12] gh-75171: Fix parsing invalid email address headers starting or ending with a dot (GH-15600) (GH-117964)
https://github.com/python/cpython/commit/03108045d25e4a75575d8a19d3187599e17277a4
commit: 03108045d25e4a75575d8a19d3187599e17277a4
branch: 3.12
author: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
committer: serhiy-storchaka <storchaka@gmail.com>
date: 2024-04-17T07:55:11Z
summary:

[3.12] gh-75171: Fix parsing invalid email address headers starting or ending with a dot (GH-15600) (GH-117964)

(cherry picked from commit 8cc9adbfddc8e37cf7d621b12754eecb0584f5da)

Co-authored-by: tsufeki <tsufeki@ymail.com>
Co-authored-by: Tim Bell <timothybell@gmail.com>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>

files:
A Misc/NEWS.d/next/Library/2019-08-29-20-26-08.bpo-30988.b-_h5O.rst
M Lib/email/_header_value_parser.py
M Lib/test/test_email/test__header_value_parser.py
M Lib/test/test_email/test_headerregistry.py

diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py
index e4a342d446f6a3..0cf23fc9f656b1 100644
--- a/Lib/email/_header_value_parser.py
+++ b/Lib/email/_header_value_parser.py
@@ -566,12 +566,14 @@ def display_name(self):
if res[0].token_type == 'cfws':
res.pop(0)
else:
- if res[0][0].token_type == 'cfws':
+ if (isinstance(res[0], TokenList) and
+ res[0][0].token_type == 'cfws'):
res[0] = TokenList(res[0][1:])
if res[-1].token_type == 'cfws':
res.pop()
else:
- if res[-1][-1].token_type == 'cfws':
+ if (isinstance(res[-1], TokenList) and
+ res[-1][-1].token_type == 'cfws'):
res[-1] = TokenList(res[-1][:-1])
return res.value

@@ -586,9 +588,13 @@ def value(self):
quote = True
if len(self) != 0 and quote:
pre = post = ''
- if self[0].token_type=='cfws' or self[0][0].token_type=='cfws':
+ if (self[0].token_type == 'cfws' or
+ isinstance(self[0], TokenList) and
+ self[0][0].token_type == 'cfws'):
pre = ' '
- if self[-1].token_type=='cfws' or self[-1][-1].token_type=='cfws':
+ if (self[-1].token_type == 'cfws' or
+ isinstance(self[-1], TokenList) and
+ self[-1][-1].token_type == 'cfws'):
post = ' '
return pre+quote_string(self.display_name)+post
else:
@@ -1772,7 +1778,10 @@ def get_name_addr(value):
raise errors.HeaderParseError(
"expected name-addr but found '{}'".format(token))
if leader is not None:
- token[0][:0] = [leader]
+ if isinstance(token[0], TokenList):
+ token[0][:0] = [leader]
+ else:
+ token[:0] = [leader]
leader = None
name_addr.append(token)
token, value = get_angle_addr(value)
diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py
index f7e80749c456f8..dd1af9954c61da 100644
--- a/Lib/test/test_email/test__header_value_parser.py
+++ b/Lib/test/test_email/test__header_value_parser.py
@@ -1805,6 +1805,32 @@ def test_get_name_addr_qs_name(self):
self.assertIsNone(name_addr.route)
self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')

+ def test_get_name_addr_ending_with_dot_without_space(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ 'John X.<jxd@example.com>',
+ 'John X.<jxd@example.com>',
+ '"John X."<jxd@example.com>',
+ [errors.ObsoleteHeaderDefect],
+ '')
+ self.assertEqual(name_addr.display_name, 'John X.')
+ self.assertEqual(name_addr.local_part, 'jxd')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'jxd@example.com')
+
+ def test_get_name_addr_starting_with_dot(self):
+ name_addr = self._test_get_x(parser.get_name_addr,
+ '. Doe <jxd@example.com>',
+ '. Doe <jxd@example.com>',
+ '". Doe" <jxd@example.com>',
+ [errors.InvalidHeaderDefect, errors.ObsoleteHeaderDefect],
+ '')
+ self.assertEqual(name_addr.display_name, '. Doe')
+ self.assertEqual(name_addr.local_part, 'jxd')
+ self.assertEqual(name_addr.domain, 'example.com')
+ self.assertIsNone(name_addr.route)
+ self.assertEqual(name_addr.addr_spec, 'jxd@example.com')
+
def test_get_name_addr_with_route(self):
name_addr = self._test_get_x(parser.get_name_addr,
'"Roy.A.Bear" <@two.example.com: dinsdale@example.com>',
diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py
index 25347ef13c2147..bb7ca8dfd8c52c 100644
--- a/Lib/test/test_email/test_headerregistry.py
+++ b/Lib/test/test_email/test_headerregistry.py
@@ -1237,6 +1237,26 @@ class TestAddressHeader(TestHeaderBase):
'example.com',
None),

+ 'name_ending_with_dot_without_space':
+ ('John X.<jxd@example.com>',
+ [errors.ObsoleteHeaderDefect],
+ '"John X." <jxd@example.com>',
+ 'John X.',
+ 'jxd@example.com',
+ 'jxd',
+ 'example.com',
+ None),
+
+ 'name_starting_with_dot':
+ ('. Doe <jxd@example.com>',
+ [errors.InvalidHeaderDefect, errors.ObsoleteHeaderDefect],
+ '". Doe" <jxd@example.com>',
+ '. Doe',
+ 'jxd@example.com',
+ 'jxd',
+ 'example.com',
+ None),
+
}

# XXX: Need many more examples, and in particular some with names in
diff --git a/Misc/NEWS.d/next/Library/2019-08-29-20-26-08.bpo-30988.b-_h5O.rst b/Misc/NEWS.d/next/Library/2019-08-29-20-26-08.bpo-30988.b-_h5O.rst
new file mode 100644
index 00000000000000..c776c73ba160e0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-08-29-20-26-08.bpo-30988.b-_h5O.rst
@@ -0,0 +1 @@
+Fix parsing of emails with invalid address headers having a leading or trailing dot. Patch by tsufeki.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-leave@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: list-python-checkins@lists.gossamer-threads.com