-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gh-102988: Detect email address parsing errors and return empty tuple…
… to indicate the parsing error (old API) (#105127) Detect email address parsing errors and return empty tuple to indicate the parsing error (old API). This fixes or at least ameliorates CVE-2023-27043. --------- Co-authored-by: Gregory P. Smith <[email protected]>
- Loading branch information
Showing
5 changed files
with
172 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,11 @@ of the new API. | |
*email address* parts. Returns a tuple of that information, unless the parse | ||
fails, in which case a 2-tuple of ``('', '')`` is returned. | ||
|
||
.. versionchanged:: 3.12 | ||
For security reasons, addresses that were ambiguous and could parse into | ||
multiple different addresses now cause ``('', '')`` to be returned | ||
instead of only one of the *potential* addresses. | ||
|
||
|
||
.. function:: formataddr(pair, charset='utf-8') | ||
|
||
|
@@ -87,7 +92,7 @@ of the new API. | |
This method returns a list of 2-tuples of the form returned by ``parseaddr()``. | ||
*fieldvalues* is a sequence of header field values as might be returned by | ||
:meth:`Message.get_all <email.message.Message.get_all>`. Here's a simple | ||
example that gets all the recipients of a message:: | ||
example that gets all the recipients of a message: | ||
|
||
from email.utils import getaddresses | ||
|
||
|
@@ -97,6 +102,25 @@ of the new API. | |
resent_ccs = msg.get_all('resent-cc', []) | ||
all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) | ||
|
||
When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')`` | ||
is returned in its place. Other errors in parsing the list of | ||
addresses such as a fieldvalue seemingly parsing into multiple | ||
addresses may result in a list containing a single empty 2-tuple | ||
``[('', '')]`` being returned rather than returning potentially | ||
invalid output. | ||
|
||
Example malformed input parsing: | ||
|
||
.. doctest:: | ||
|
||
>>> from email.utils import getaddresses | ||
>>> getaddresses(['[email protected] <[email protected]>', '[email protected]']) | ||
[('', '')] | ||
|
||
.. versionchanged:: 3.12 | ||
The 2-tuple of ``('', '')`` in the returned values when parsing | ||
fails were added as to address a security issue. | ||
|
||
|
||
.. function:: parsedate(date) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3319,15 +3319,90 @@ def test_getaddresses(self): | |
[('Al Person', '[email protected]'), | ||
('Bud Person', '[email protected]')]) | ||
|
||
def test_getaddresses_parsing_errors(self): | ||
"""Test for parsing errors from CVE-2023-27043""" | ||
eq = self.assertEqual | ||
eq(utils.getaddresses(['[email protected](<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected])<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected]<<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected]><[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected]@<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected],<[email protected]>']), | ||
[('', '[email protected]'), ('', '[email protected]')]) | ||
eq(utils.getaddresses(['[email protected];<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected]:<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected].<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected]"<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected][<[email protected]>']), | ||
[('', '')]) | ||
eq(utils.getaddresses(['[email protected]]<[email protected]>']), | ||
[('', '')]) | ||
|
||
def test_parseaddr_parsing_errors(self): | ||
"""Test for parsing errors from CVE-2023-27043""" | ||
eq = self.assertEqual | ||
eq(utils.parseaddr(['[email protected](<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected])<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected]<<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected]><[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected]@<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected],<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected];<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected]:<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected].<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected]"<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected][<[email protected]>']), | ||
('', '')) | ||
eq(utils.parseaddr(['[email protected]]<[email protected]>']), | ||
('', '')) | ||
|
||
def test_getaddresses_nasty(self): | ||
eq = self.assertEqual | ||
eq(utils.getaddresses(['foo: ;']), [('', '')]) | ||
eq(utils.getaddresses( | ||
['[]*-- =~$']), | ||
[('', ''), ('', ''), ('', '*--')]) | ||
eq(utils.getaddresses(['[]*-- =~$']), [('', '')]) | ||
eq(utils.getaddresses( | ||
['foo: ;', '"Jason R. Mastaler" <[email protected]>']), | ||
[('', ''), ('Jason R. Mastaler', '[email protected]')]) | ||
eq(utils.getaddresses( | ||
[r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>']), | ||
[('Pete (A nice ) chap his account his host)', '[email protected]')]) | ||
eq(utils.getaddresses( | ||
['(Empty list)(start)Undisclosed recipients :(nobody(I know))']), | ||
[('', '')]) | ||
eq(utils.getaddresses( | ||
['Mary <@machine.tld:[email protected]>, , jdoe@test . example']), | ||
[('Mary', '[email protected]'), ('', ''), ('', '[email protected]')]) | ||
eq(utils.getaddresses( | ||
['John Doe <jdoe@machine(comment). example>']), | ||
[('John Doe (comment)', '[email protected]')]) | ||
eq(utils.getaddresses( | ||
['"Mary Smith: Personal Account" <[email protected]>']), | ||
[('Mary Smith: Personal Account', '[email protected]')]) | ||
eq(utils.getaddresses( | ||
['Undisclosed recipients:;']), | ||
[('', '')]) | ||
eq(utils.getaddresses( | ||
[r'<[email protected]>, "Giant; \"Big\" Box" <[email protected]>']), | ||
[('', '[email protected]'), ('Giant; "Big" Box', '[email protected]')]) | ||
|
||
def test_getaddresses_embedded_comment(self): | ||
"""Test proper handling of a nested comment""" | ||
|
4 changes: 4 additions & 0 deletions
4
Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
CVE-2023-27043: Prevent :func:`email.utils.parseaddr` | ||
and :func:`email.utils.getaddresses` from returning the realname portion of an | ||
invalid RFC2822 email header in the email address portion of the 2-tuple | ||
returned after being parsed by :class:`email._parseaddr.AddressList`. |