From 4d0cce20586ffad09467a8d9ba25dc60d761596b61f13ef4ee6e91a77128851f Mon Sep 17 00:00:00 2001 From: Matej Cepl Date: Fri, 14 Jul 2023 14:05:14 +0000 Subject: [PATCH] Accepting request 1098688 from devel:languages:python:Factory Revert faulty fix for CVE-2023-27043 (gh#python/cpython#106669) OBS-URL: https://build.opensuse.org/request/show/1098688 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python38?expand=0&rev=134 --- CVE-2023-27043-email-parsing-errors.patch | 249 ---------------------- python38.changes | 8 - python38.spec | 5 - 3 files changed, 262 deletions(-) delete mode 100644 CVE-2023-27043-email-parsing-errors.patch diff --git a/CVE-2023-27043-email-parsing-errors.patch b/CVE-2023-27043-email-parsing-errors.patch deleted file mode 100644 index 072c332..0000000 --- a/CVE-2023-27043-email-parsing-errors.patch +++ /dev/null @@ -1,249 +0,0 @@ ---- - Doc/library/email.utils.rst | 26 +++ - Lib/email/utils.py | 62 ++++++- - Lib/test/test_email/test_email.py | 81 +++++++++- - Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst | 4 - 4 files changed, 163 insertions(+), 10 deletions(-) - -Index: Python-3.8.17/Doc/library/email.utils.rst -=================================================================== ---- Python-3.8.17.orig/Doc/library/email.utils.rst -+++ Python-3.8.17/Doc/library/email.utils.rst -@@ -67,6 +67,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') - -@@ -89,7 +94,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 `. 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 - -@@ -99,6 +104,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(['alice@example.com ', 'me@example.com']) -+ [('', '')] -+ -+ .. 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) - -Index: Python-3.8.17/Lib/email/utils.py -=================================================================== ---- Python-3.8.17.orig/Lib/email/utils.py -+++ Python-3.8.17/Lib/email/utils.py -@@ -105,13 +105,54 @@ def formataddr(pair, charset='utf-8'): - return '%s%s%s <%s>' % (quotes, name, quotes, address) - return address - -+def _pre_parse_validation(email_header_fields): -+ accepted_values = [] -+ for v in email_header_fields: -+ s = v.replace('\\(', '').replace('\\)', '') -+ if s.count('(') != s.count(')'): -+ v = "('', '')" -+ accepted_values.append(v) -+ -+ return accepted_values -+ -+ -+def _post_parse_validation(parsed_email_header_tuples): -+ accepted_values = [] -+ # The parser would have parsed a correctly formatted domain-literal -+ # The existence of an [ after parsing indicates a parsing failure -+ for v in parsed_email_header_tuples: -+ if '[' in v[1]: -+ v = ('', '') -+ accepted_values.append(v) -+ -+ return accepted_values - - - def getaddresses(fieldvalues): -- """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" -- all = COMMASPACE.join(fieldvalues) -+ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. -+ -+ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in -+ its place. -+ -+ If the resulting list of parsed address is not the same as the number of -+ fieldvalues in the input list a parsing error has occurred. A list -+ containing a single empty 2-tuple [('', '')] is returned in its place. -+ This is done to avoid invalid output. -+ """ -+ fieldvalues = [str(v) for v in fieldvalues] -+ fieldvalues = _pre_parse_validation(fieldvalues) -+ all = COMMASPACE.join(v for v in fieldvalues) - a = _AddressList(all) -- return a.addresslist -+ result = _post_parse_validation(a.addresslist) -+ -+ n = 0 -+ for v in fieldvalues: -+ n += v.count(',') + 1 -+ -+ if len(result) != n: -+ return [('', '')] -+ -+ return result - - - def _format_timetuple_and_zone(timetuple, zone): -@@ -209,9 +250,18 @@ def parseaddr(addr): - Return a tuple of realname and email address, unless the parse fails, in - which case return a 2-tuple of ('', ''). - """ -- addrs = _AddressList(addr).addresslist -- if not addrs: -- return '', '' -+ if isinstance(addr, list): -+ addr = addr[0] -+ -+ if not isinstance(addr, str): -+ return ('', '') -+ -+ addr = _pre_parse_validation([addr])[0] -+ addrs = _post_parse_validation(_AddressList(addr).addresslist) -+ -+ if not addrs or len(addrs) > 1: -+ return ('', '') -+ - return addrs[0] - - -Index: Python-3.8.17/Lib/test/test_email/test_email.py -=================================================================== ---- Python-3.8.17.orig/Lib/test/test_email/test_email.py -+++ Python-3.8.17/Lib/test/test_email/test_email.py -@@ -3248,15 +3248,90 @@ Foo - [('Al Person', 'aperson@dom.ain'), - ('Bud Person', 'bperson@dom.ain')]) - -+ def test_getaddresses_parsing_errors(self): -+ """Test for parsing errors from CVE-2023-27043""" -+ eq = self.assertEqual -+ eq(utils.getaddresses(['alice@example.org(']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org)']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org<']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org>']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org@']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org,']), -+ [('', 'alice@example.org'), ('', 'bob@example.com')]) -+ eq(utils.getaddresses(['alice@example.org;']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org:']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org.']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org"']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org[']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org]']), -+ [('', '')]) -+ -+ def test_parseaddr_parsing_errors(self): -+ """Test for parsing errors from CVE-2023-27043""" -+ eq = self.assertEqual -+ eq(utils.parseaddr(['alice@example.org(']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org)']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org<']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org>']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org@']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org,']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org;']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org:']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org.']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org"']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org[']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org]']), -+ ('', '')) -+ - 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" ']), - [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) -+ eq(utils.getaddresses( -+ [r'Pete(A nice \) chap) ']), -+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]) -+ eq(utils.getaddresses( -+ ['(Empty list)(start)Undisclosed recipients :(nobody(I know))']), -+ [('', '')]) -+ eq(utils.getaddresses( -+ ['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']), -+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]) -+ eq(utils.getaddresses( -+ ['John Doe ']), -+ [('John Doe (comment)', 'jdoe@machine.example')]) -+ eq(utils.getaddresses( -+ ['"Mary Smith: Personal Account" ']), -+ [('Mary Smith: Personal Account', 'smith@home.example')]) -+ eq(utils.getaddresses( -+ ['Undisclosed recipients:;']), -+ [('', '')]) -+ eq(utils.getaddresses( -+ [r', "Giant; \"Big\" Box" ']), -+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]) - - def test_getaddresses_embedded_comment(self): - """Test proper handling of a nested comment""" -Index: Python-3.8.17/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst -=================================================================== ---- /dev/null -+++ Python-3.8.17/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst -@@ -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`. diff --git a/python38.changes b/python38.changes index a8c467d..b02a431 100644 --- a/python38.changes +++ b/python38.changes @@ -1,11 +1,3 @@ -------------------------------------------------------------------- -Tue Jul 11 07:35:18 UTC 2023 - Matej Cepl - -- (bsc#1210638, CVE-2023-27043) Add - CVE-2023-27043-email-parsing-errors.patch, which detects email - address parsing errors and returns empty tuple to indicate the - parsing error (old API). - ------------------------------------------------------------------- Wed Jun 28 16:57:46 UTC 2023 - Matej Cepl diff --git a/python38.spec b/python38.spec index 4ded8f0..2dc999a 100644 --- a/python38.spec +++ b/python38.spec @@ -179,10 +179,6 @@ Patch38: 98437-sphinx.locale._-as-gettext-in-pyspecific.patch # PATCH-FIX-UPSTREAM 99366-patch.dict-can-decorate-async.patch bsc#[0-9]+ mcepl@suse.com # Patch for gh#python/cpython#98086 Patch41: 99366-patch.dict-can-decorate-async.patch -# PATCH-FIX-UPSTREAM CVE-2023-27043-email-parsing-errors.patch bsc#1210638 mcepl@suse.com -# Detect email address parsing errors and return empty tuple to -# indicate the parsing error (old API) -Patch42: CVE-2023-27043-email-parsing-errors.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -456,7 +452,6 @@ other applications. %patch37 -p1 %patch38 -p1 %patch41 -p1 -%patch42 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac