8000 [3.12] gh-106669: Revert "gh-102988: Detect email address parsing err… · python/cpython@656f624 · GitHub
[go: up one dir, main page]

Skip to content

Commit 656f624

Browse files
authored
[3.12] gh-106669: Revert "gh-102988: Detect email address parsing errors ... (GH-105127)" (GH-106733) (#106941)
This reverts commit 18dfbd0. Adds a regression test from the issue. See #106669.. (cherry picked from commit a31dea1)
1 parent c1fd76e commit 656f624

File tree

5 files changed

+30
-167
lines changed

5 files changed

+30
-167
lines changed

Doc/library/email.utils.rst

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,6 @@ of the new API.
6565
*email address* parts. Returns a tuple of that information, unless the parse
6666
fails, in which case a 2-tuple of ``('', '')`` is returned.
6767

68-
.. versionchanged:: 3.12
69-
For security reasons, addresses that were ambiguous and could parse into
70-
multiple different addresses now cause ``('', '')`` to be returned
71-
instead of only one of the *potential* addresses.
72-
7368

7469
.. function:: formataddr(pair, charset='utf-8')
7570

@@ -92,7 +87,7 @@ of the new API.
9287
This method returns a list of 2-tuples of the form returned by ``parseaddr()``.
9388
*fieldvalues* is a sequence of header field values as might be returned by
9489
:meth:`Message.get_all <email.message.Message.get_all>`. Here's a simple
95-
example that gets all the recipients of a message:
90+
example that gets all the recipients of a message::
9691

9792
from email.utils import getaddresses
9893

@@ -102,25 +97,6 @@ of the new API.
10297
resent_ccs = msg.get_all('resent-cc', [])
10398
all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
10499

105-
When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')``
106-
is returned in its place. Other errors in parsing the list of
107-
addresses such as a fieldvalue seemingly parsing into multiple
108-
addresses may result in a list containing a single empty 2-tuple
109-
``[('', '')]`` being returned rather than returning potentially
110-
invalid output.
111-
112-
Example malformed input parsing:
113-
114-
.. doctest::
115-
116-
>>> from email.utils import getaddresses
117-
>>> getaddresses(['alice@example.com <bob@example.com>', 'me@example.com'])
118-
[('', '')]
119-
120-
.. versionchanged:: 3.12
121-
The 2-tuple of ``('', '')`` in the returned values when parsing
122-
fails were added as to address a security issue.
123-
124100

125101
.. function:: parsedate(date)
126102

Doc/whatsnew/3.12.rst

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -570,14 +570,6 @@ dis
570570
:data:`~dis.hasarg` collection instead.
571571
(Contributed by Irit Katriel in :gh:`94216`.)
572572

573-
email
574-
-----
575-
576-
* :func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now return
577-
``('', '')`` 2-tuples in more situations where invalid email addresses are
578-
encountered instead of potentially inaccurate values.
579-
(Contributed by Thomas Dwyer for :gh:`102988` to ameliorate CVE-2023-27043.)
580-
581573
fractions
582574
---------
583575

Lib/email/utils.py

Lines changed: 6 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -106,54 +106,12 @@ def formataddr(pair, charset='utf-8'):
106106
return address
107107

108108

109-
def _pre_parse_validation(email_header_fields):
110-
accepted_values = []
111-
for v in email_header_fields:
112-
s = v.replace('\\(', '').replace('\\)', '')
113-
if s.count('(') != s.count(')'):
114-
v = "('', '')"
115-
accepted_values.append(v)
116-
117-
return accepted_values
118-
119-
120-
def _post_parse_validation(parsed_email_header_tuples):
121-
accepted_values = []
122-
# The parser would have parsed a correctly formatted domain-literal
123-
# The existence of an [ after parsing indicates a parsing failure
124-
for v in parsed_email_header_tuples:
125-
if '[' in v[1]:
126-
v = ('', '')
127-
accepted_values.append(v)
128-
129-
return accepted_values
130-
131109

132110
def getaddresses(fieldvalues):
133-
"""Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue.
134-
135-
When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in
136-
its place.
137-
138-
If the resulting list of parsed address is not the same as the number of
139-
fieldvalues in the input list a parsing error has occurred. A list
140-
containing a single empty 2-tuple [('', '')] is returned in its place.
141-
This is done to avoid invalid output.
142-
"""
143-
fieldvalues = [str(v) for v in fieldvalues]
144-
fieldvalues = _pre_parse_validation(fieldvalues)
145-
all = COMMASPACE.join(v for v in fieldvalues)
111+
"""Return a list of (REALNAME, EMAIL) for each fieldvalue."""
112+
all = COMMASPACE.join(str(v) for v in fieldvalues)
146113
a = _AddressList(all)
147-
result = _post_parse_validation(a.addresslist)
148-
149-
n = 0
150-
for v in fieldvalues:
151-
n += v.count(',') + 1
152-
153-
if len(result) != n:
154-
return [('', '')]
155-
156-
return result
114+
return a.addresslist
157115

158116

159117
def _format_timetuple_and_zone(timetuple, zone):
@@ -254,18 +212,9 @@ def parseaddr(addr):
254212
Return a tuple of realname and email address, unless the parse fails, in
255213
which case return a 2-tuple of ('', '').
256214
"""
257-
if isinstance(addr, list):
258-
addr = addr[0]
259-
260-
if not isinstance(addr, str):
261-
return ('', '')
262-
263-
addr = _pre_parse_validation([addr])[0]
264-
addrs = _post_parse_validation(_AddressList(addr).addresslist)
265-
266-
if not addrs or len(addrs) > 1:
267-
return ('', '')
268-
215+
addrs = _AddressList(addr).addresslist
216+
if not addrs:
217+
return '', ''
269218
return addrs[0]
270219

271220

Lib/test/test_email/test_email.py

Lines changed: 19 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -3319,90 +3319,32 @@ def test_getaddresses(self):
33193319
[('Al Person', 'aperson@dom.ain'),
33203320
('Bud Person', 'bperson@dom.ain')])
33213321

3322-
def test_getaddresses_parsing_errors(self):
3323-
"""Test for parsing errors from CVE-2023-27043"""
3324-
eq = self.assertEqual
3325-
eq(utils.getaddresses(['alice@example.org(<bob@example.com>']),
3326-
[('', '')])
3327-
eq(utils.getaddresses(['alice@example.org)<bob@example.com>']),
3328-
[('', '')])
3329-
eq(utils.getaddresses(['alice@example.org<<bob@example.com>']),
3330-
[('', '')])
3331-
eq(utils.getaddresses(['alice@example.org><bob@example.com>']),
3332-
[('', '')])
3333-
eq(utils.getaddresses(['alice@example.org@<bob@example.com>']),
3334-
[('', '')])
3335-
eq(utils.getaddresses(['alice@example.org,<bob@example.com>']),
3336-
[('', 'alice@example.org'), ('', 'bob@example.com')])
3337-
eq(utils.getaddresses(['alice@example.org;<bob@example.com>']),
3338-
[('', '')])
3339-
eq(utils.getaddresses(['alice@example.org:<bob@example.com>']),
3340-
[('', '')])
3341-
eq(utils.getaddresses(['alice@example.org.<bob@example.com>']),
3342-
[('', '')])
3343-
eq(utils.getaddresses(['alice@example.org"<bob@example.com>']),
3344-
[('', '')])
3345-
eq(utils.getaddresses(['alice@example.org[<bob@example.com>']),
3346-
[('', '')])
3347-
eq(utils.getaddresses(['alice@example.org]<bob@example.com>']),
3348-
[('', '')])
3349-
3350-
def test_parseaddr_parsing_errors(self):
3351-
"""Test for parsing errors from CVE-2023-27043"""
3352-
eq = self.assertEqual
3353-
eq(utils.parseaddr(['alice@example.org(<bob@example.com>']),
3354-
('', ''))
3355-
eq(utils.parseaddr(['alice@example.org)<bob@example.com>']),
3356-
('', ''))
3357-
eq(utils.parseaddr(['alice@example.org<<bob@example.com>']),
3358-
('', ''))
3359-
eq(utils.parseaddr(['alice@example.org><bob@example.com>']),
3360-
('', ''))
3361-
eq(utils.parseaddr(['alice@example.org@<bob@example.com>']),
3362-
('', ''))
3363-
eq(utils.parseaddr(['alice@example.org,<bob@example.com>']),
3364-
('', ''))
3365-
eq(utils.parseaddr(['alice@example.org;<bob@example.com>']),
3366-
('', ''))
3367-
eq(utils.parseaddr(['alice@example.org:<bob@example.com>']),
3368-
('', ''))
3369-
eq(utils.parseaddr(['alice@example.org.<bob@example.com>']),
3370-
('', ''))
3371-
eq(utils.parseaddr(['alice@example.org"<bob@example.com>']),
3372-
('', ''))
3373-
eq(utils.parseaddr(['alice@example.org[<bob@example.com>']),
3374-
('', ''))
3375-
eq(utils.parseaddr(['alice@example.org]<bob@example.com>']),
3376-
('', ''))
3322+
def test_getaddresses_comma_in_name(self):
3323+
"""GH-106669 regression test."""
3324+
self.assertEqual(
3325+
utils.getaddresses(
3326+
[
3327+
'"Bud, Person" <bperson@dom.ain>',
3328+
'aperson@dom.ain (Al Person)',
3329+
'"Mariusz Felisiak" <to@example.com>',
3330+
]
3331+
),
3332+
[
3333+
('Bud, Person', 'bperson@dom.ain'),
3334+
('Al Person', 'aperson@dom.ain'),
3335+
('Mariusz Felisiak', 'to@example.com'),
3336+
],
3337+
)
33773338

33783339
def test_getaddresses_nasty(self):
33793340
eq = self.assertEqual
33803341
eq(utils.getaddresses(['foo: ;']), [('', '')])
3381-
eq(utils.getaddresses(['[]*-- =~$']), [('', '')])
3342+
eq(utils.getaddresses(
3343+
['[]*-- =~$']),
3344+
[('', ''), ('', ''), ('', '*--')])
33823345
eq(utils.getaddresses(
33833346
['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
33843347
[('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
3385-
eq(utils.getaddresses(
3386-
[r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>']),
3387-
[('Pete (A nice ) chap his account his host)', 'pete@silly.test')])
3388-
eq(utils.getaddresses(
3389-
['(Empty list)(start)Undisclosed recipients :(nobody(I know))']),
3390-
[('', '')])
3391-
eq(utils.getaddresses(
3392-
['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']),
3393-
[('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')])
3394-
eq(utils.getaddresses(
3395-
['John Doe <jdoe@machine(comment). example>']),
3396-
[('John Doe (comment)', 'jdoe@machine.example')])
3397-
eq(utils.getaddresses(
3398-
['"Mary Smith: Personal Account" <smith@home.example>']),
3399-
[('Mary Smith: Personal Account', 'smith@home.example')])
3400-
eq(utils.getaddresses(
3401-
['Undisclosed recipients:;']),
3402-
[('', '')])
3403-
eq(utils.getaddresses(
3404-
[r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>']),
3405-
[('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')])
34063348

34073349
def test_getaddresses_embedded_comment(self):
34083350
"""Test proper handling of a nested comment"""
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Reverted the :mod:`email.utils` security improvement change released in
2+
3.12beta4 that unintentionally caused :mod:`email.utils.getaddresses` to fail
3+
to parse email addresses with a comma in the quoted name field.
4+
See :gh:`106669`.

0 commit comments

Comments
 (0)
0