diff --git a/.gitignore b/.gitignore index 33fa1d5..d2618f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/Python-3.12.5.tar.xz +SOURCES/Python-3.12.9.tar.xz diff --git a/.python3.12.metadata b/.python3.12.metadata index ffed180..9510f08 100644 --- a/.python3.12.metadata +++ b/.python3.12.metadata @@ -1 +1 @@ -d9b83c17a717e1cbd3ab6bd14cfe3e508e6d87b2 SOURCES/Python-3.12.5.tar.xz +465d8a664e63dc5aa1f0d90cd1d0000a970ee2fb SOURCES/Python-3.12.9.tar.xz diff --git a/SOURCES/00251-change-user-install-location.patch b/SOURCES/00251-change-user-install-location.patch index 2f33b5a..dd7a07a 100644 --- a/SOURCES/00251-change-user-install-location.patch +++ b/SOURCES/00251-change-user-install-location.patch @@ -30,7 +30,7 @@ Co-authored-by: Lumír Balhar 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/Lib/site.py b/Lib/site.py -index 924cfbecec..e2871ecc89 100644 +index aed254ad50..568dbdb945 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -398,8 +398,15 @@ def getsitepackages(prefixes=None): @@ -51,7 +51,7 @@ index 924cfbecec..e2871ecc89 100644 if os.path.isdir(sitedir): addsitedir(sitedir, known_paths) diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py -index 122d441bd1..2d354a11da 100644 +index 517b13acaf..928d1a0541 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -104,6 +104,11 @@ @@ -86,7 +86,7 @@ index 122d441bd1..2d354a11da 100644 _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', 'scripts', 'data') -@@ -263,11 +281,40 @@ def _extend_dict(target_dict, other_dict): +@@ -261,11 +279,40 @@ def _extend_dict(target_dict, other_dict): target_dict[key] = value @@ -119,7 +119,7 @@ index 122d441bd1..2d354a11da 100644 + # we only change the defaults here, so explicit --prefix will take precedence + # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe + if (scheme == 'posix_prefix' and -+ _PREFIX == '/usr' and ++ sys.prefix == '/usr' and + 'RPM_BUILD_ROOT' not in os.environ): + _extend_dict(vars, _config_vars_local()) + else: @@ -129,10 +129,10 @@ index 122d441bd1..2d354a11da 100644 # On Windows we want to substitute 'lib' for schemes rather # than the native value (without modifying vars, in case it diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 1137c2032b..8fc2b84f52 100644 +index 3468d0ce02..ff31010427 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py -@@ -110,8 +110,19 @@ def test_get_path(self): +@@ -119,8 +119,19 @@ def test_get_path(self): for scheme in _INSTALL_SCHEMES: for name in _INSTALL_SCHEMES[scheme]: expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars) @@ -153,7 +153,7 @@ index 1137c2032b..8fc2b84f52 100644 os.path.normpath(expected), ) -@@ -344,7 +355,7 @@ def test_get_config_h_filename(self): +@@ -353,7 +364,7 @@ def test_get_config_h_filename(self): self.assertTrue(os.path.isfile(config_h), config_h) def test_get_scheme_names(self): @@ -162,7 +162,7 @@ index 1137c2032b..8fc2b84f52 100644 if HAS_USER_BASE: wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) -@@ -356,6 +367,8 @@ def test_symlink(self): # Issue 7880 +@@ -365,6 +376,8 @@ def test_symlink(self): # Issue 7880 cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" self.assertEqual(py.call_real(*cmd), py.call_link(*cmd)) diff --git a/SOURCES/00329-fips.patch b/SOURCES/00329-fips.patch index f95b71e..6d9944e 100644 --- a/SOURCES/00329-fips.patch +++ b/SOURCES/00329-fips.patch @@ -1,4 +1,4 @@ -From 43ce74d971fad62db6ccd723fe6b01da9c7ff407 Mon Sep 17 00:00:00 2001 +From 11deb3112bd90bc2dce2fcd4a1f5975c08b91360 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Thu, 12 Dec 2019 16:58:31 +0100 Subject: [PATCH 1/5] Expose blake2b and blake2s hashes from OpenSSL @@ -29,10 +29,10 @@ index 73d758a..5921360 100644 computed = m.hexdigest() if not shake else m.hexdigest(length) self.assertEqual( diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c -index af6d1b2..980712f 100644 +index 2998820..b96001e 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c -@@ -1079,6 +1079,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, +@@ -1128,6 +1128,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, } @@ -74,7 +74,7 @@ index af6d1b2..980712f 100644 #ifdef PY_OPENSSL_HAS_SHA3 /*[clinic input] -@@ -2067,6 +2102,8 @@ static struct PyMethodDef EVP_functions[] = { +@@ -2116,6 +2151,8 @@ static struct PyMethodDef EVP_functions[] = { _HASHLIB_OPENSSL_SHA256_METHODDEF _HASHLIB_OPENSSL_SHA384_METHODDEF _HASHLIB_OPENSSL_SHA512_METHODDEF @@ -84,7 +84,7 @@ index af6d1b2..980712f 100644 _HASHLIB_OPENSSL_SHA3_256_METHODDEF _HASHLIB_OPENSSL_SHA3_384_METHODDEF diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h -index fb61a44..1e42b87 100644 +index 84e2346..7fe03a3 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -743,6 +743,156 @@ exit: @@ -248,13 +248,13 @@ index fb61a44..1e42b87 100644 #ifndef _HASHLIB_SCRYPT_METHODDEF #define _HASHLIB_SCRYPT_METHODDEF #endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */ --/*[clinic end generated code: output=b339e255db698147 input=a9049054013a1b77]*/ -+/*[clinic end generated code: output=1d988d457a8beebe input=a9049054013a1b77]*/ +-/*[clinic end generated code: output=4734184f6555dc95 input=a9049054013a1b77]*/ ++/*[clinic end generated code: output=f0bfddb963a21208 input=a9049054013a1b77]*/ -- -2.45.0 +2.47.1 -From 6872b634078a2c69644235781ebffb07f8edcb83 Mon Sep 17 00:00:00 2001 +From ea9d5c84e25b5c04c2823e1edee4354dd6b2b7a5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 25 Jul 2019 17:19:06 +0200 Subject: [PATCH 2/5] Disable Python's hash implementations in FIPS mode, @@ -445,10 +445,10 @@ index a8bad9d..1b1d937 100644 + if (_Py_hashlib_fips_error(exc, name)) return NULL; \ +} while (0) diff --git a/configure.ac b/configure.ac -index 65ad1c2..b5f9ab5 100644 +index 9270b5f..a9eb2c9 100644 --- a/configure.ac +++ b/configure.ac -@@ -7463,7 +7463,8 @@ PY_STDLIB_MOD([_sha2], +@@ -7482,7 +7482,8 @@ PY_STDLIB_MOD([_sha2], PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes]) PY_STDLIB_MOD([_blake2], [test "$with_builtin_blake2" = yes], [], @@ -459,10 +459,10 @@ index 65ad1c2..b5f9ab5 100644 PY_STDLIB_MOD([_crypt], [], [test "$ac_cv_crypt_crypt" = yes], -- -2.45.0 +2.47.1 -From f904abdd7a607282c2cdfd18288045cedfa28414 Mon Sep 17 00:00:00 2001 +From 29a7b7ac9e18a501ed78bde7a449b90c57d44e24 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Fri, 29 Jan 2021 14:16:21 +0100 Subject: [PATCH 3/5] Use python's fall back crypto implementations only if we @@ -552,10 +552,10 @@ index dd61a9a..6031b02 100644 get_builtin_constructor = getattr(hashlib, '__get_builtin_constructor') -- -2.45.0 +2.47.1 -From 9bf0a53b7831409613c44fd7feecb56476f5e5e7 Mon Sep 17 00:00:00 2001 +From 59accf544492400c9fd32a8e682fb6f2206e932e Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Wed, 31 Jul 2019 15:43:43 +0200 Subject: [PATCH 4/5] Test equivalence of hashes for the various digests with @@ -712,21 +712,21 @@ index 6031b02..5bd5297 100644 class KDFTests(unittest.TestCase): -- -2.45.0 +2.47.1 -From 8a76571515a64a57b4ea0586ae8376cf2ef0ac60 Mon Sep 17 00:00:00 2001 +From 21efadd8b488956482bdc6ccd91c37dcef705129 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 26 Aug 2019 19:39:48 +0200 Subject: [PATCH 5/5] Guard against Python HMAC in FIPS mode --- - Lib/hmac.py | 13 +++++++++---- + Lib/hmac.py | 12 +++++++++--- Lib/test/test_hmac.py | 10 ++++++++++ - 2 files changed, 19 insertions(+), 4 deletions(-) + 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Lib/hmac.py b/Lib/hmac.py -index 8b4eb2f..e8e4864 100644 +index 8b4eb2f..8930bda 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -16,8 +16,9 @@ else: @@ -741,14 +741,7 @@ index 8b4eb2f..e8e4864 100644 # The size of the digests returned by HMAC depends on the underlying # hashing module used. Use digest_size from the instance of HMAC instead. -@@ -48,17 +49,18 @@ class HMAC: - msg argument. Passing it as a keyword argument is - recommended, though not required for legacy API reasons. - """ -- - if not isinstance(key, (bytes, bytearray)): - raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) - +@@ -55,10 +56,12 @@ class HMAC: if not digestmod: raise TypeError("Missing required argument 'digestmod'.") @@ -762,7 +755,7 @@ index 8b4eb2f..e8e4864 100644 self._init_old(key, msg, digestmod) else: self._init_old(key, msg, digestmod) -@@ -69,6 +71,9 @@ class HMAC: +@@ -69,6 +72,9 @@ class HMAC: self.block_size = self._hmac.block_size def _init_old(self, key, msg, digestmod): @@ -829,5 +822,5 @@ index 1502fba..7997073 100644 def test_realcopy_old(self): # Testing if the copy method created a real copy. -- -2.45.0 +2.47.1 diff --git a/SOURCES/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch b/SOURCES/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch index 1a202f7..1268e76 100644 --- a/SOURCES/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch +++ b/SOURCES/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch @@ -16,7 +16,7 @@ https://github.com/GrahamDumpleton/mod_wsgi/issues/730 2 files changed, 8 insertions(+), 50 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py -index 2e4b860b97..3066b23ee1 100644 +index 75a56f7830..c2509fced1 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1100,39 +1100,6 @@ def noop(): pass diff --git a/SOURCES/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch b/SOURCES/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch deleted file mode 100644 index 4274663..0000000 --- a/SOURCES/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch +++ /dev/null @@ -1,483 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Victor Stinner -Date: Fri, 15 Dec 2023 16:10:40 +0100 -Subject: [PATCH] 00415: [CVE-2023-27043] gh-102988: Reject malformed addresses - in email.parseaddr() (#111116) - -Detect email address parsing errors and return empty tuple to -indicate the parsing error (old API). Add an optional 'strict' -parameter to getaddresses() and parseaddr() functions. Patch by -Thomas Dwyer. - -Co-Authored-By: Thomas Dwyer ---- - Doc/library/email.utils.rst | 19 +- - Lib/email/utils.py | 151 +++++++++++++- - Lib/test/test_email/test_email.py | 187 +++++++++++++++++- - ...-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 + - 4 files changed, 344 insertions(+), 21 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst - -diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst -index 6ba42491d6..6bd45200d8 100644 ---- a/Doc/library/email.utils.rst -+++ b/Doc/library/email.utils.rst -@@ -58,13 +58,18 @@ of the new API. - begins with angle brackets, they are stripped off. - - --.. function:: parseaddr(address) -+.. function:: parseaddr(address, *, strict=True) - - Parse address -- which should be the value of some address-containing field such - as :mailheader:`To` or :mailheader:`Cc` -- into its constituent *realname* and - *email address* parts. Returns a tuple of that information, unless the parse - fails, in which case a 2-tuple of ``('', '')`` is returned. - -+ If *strict* is true, use a strict parser which rejects malformed inputs. -+ -+ .. versionchanged:: 3.13 -+ Add *strict* optional parameter and reject malformed inputs by default. -+ - - .. function:: formataddr(pair, charset='utf-8') - -@@ -82,12 +87,15 @@ of the new API. - Added the *charset* option. - - --.. function:: getaddresses(fieldvalues) -+.. function:: getaddresses(fieldvalues, *, strict=True) - - 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:: -+ :meth:`Message.get_all `. -+ -+ If *strict* is true, use a strict parser which rejects malformed inputs. -+ -+ Here's a simple example that gets all the recipients of a message:: - - from email.utils import getaddresses - -@@ -97,6 +105,9 @@ of the new API. - resent_ccs = msg.get_all('resent-cc', []) - all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) - -+ .. versionchanged:: 3.13 -+ Add *strict* optional parameter and reject malformed inputs by default. -+ - - .. function:: parsedate(date) - -diff --git a/Lib/email/utils.py b/Lib/email/utils.py -index 1de547a011..e53abc8b84 100644 ---- a/Lib/email/utils.py -+++ b/Lib/email/utils.py -@@ -48,6 +48,7 @@ - specialsre = re.compile(r'[][\\()<>@,:;".]') - escapesre = re.compile(r'[\\"]') - -+ - def _has_surrogates(s): - """Return True if s may contain surrogate-escaped binary data.""" - # This check is based on the fact that unless there are surrogates, utf8 -@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'): - return address - - -+def _iter_escaped_chars(addr): -+ pos = 0 -+ escape = False -+ for pos, ch in enumerate(addr): -+ if escape: -+ yield (pos, '\\' + ch) -+ escape = False -+ elif ch == '\\': -+ escape = True -+ else: -+ yield (pos, ch) -+ if escape: -+ yield (pos, '\\') - --def getaddresses(fieldvalues): -- """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" -- all = COMMASPACE.join(str(v) for v in fieldvalues) -- a = _AddressList(all) -- return a.addresslist -+ -+def _strip_quoted_realnames(addr): -+ """Strip real names between quotes.""" -+ if '"' not in addr: -+ # Fast path -+ return addr -+ -+ start = 0 -+ open_pos = None -+ result = [] -+ for pos, ch in _iter_escaped_chars(addr): -+ if ch == '"': -+ if open_pos is None: -+ open_pos = pos -+ else: -+ if start != open_pos: -+ result.append(addr[start:open_pos]) -+ start = pos + 1 -+ open_pos = None -+ -+ if start < len(addr): -+ result.append(addr[start:]) -+ -+ return ''.join(result) -+ -+ -+supports_strict_parsing = True -+ -+def getaddresses(fieldvalues, *, strict=True): -+ """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 strict is true, use a strict parser which rejects malformed inputs. -+ """ -+ -+ # If strict is true, if the resulting list of parsed addresses is greater -+ # than the number of fieldvalues in the input list, a parsing error has -+ # occurred and consequently a list containing a single empty 2-tuple [('', -+ # '')] is returned in its place. This is done to avoid invalid output. -+ # -+ # Malformed input: getaddresses(['alice@example.com ']) -+ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')] -+ # Safe output: [('', '')] -+ -+ if not strict: -+ all = COMMASPACE.join(str(v) for v in fieldvalues) -+ a = _AddressList(all) -+ return a.addresslist -+ -+ fieldvalues = [str(v) for v in fieldvalues] -+ fieldvalues = _pre_parse_validation(fieldvalues) -+ addr = COMMASPACE.join(fieldvalues) -+ a = _AddressList(addr) -+ result = _post_parse_validation(a.addresslist) -+ -+ # Treat output as invalid if the number of addresses is not equal to the -+ # expected number of addresses. -+ n = 0 -+ for v in fieldvalues: -+ # When a comma is used in the Real Name part it is not a deliminator. -+ # So strip those out before counting the commas. -+ v = _strip_quoted_realnames(v) -+ # Expected number of addresses: 1 + number of commas -+ n += 1 + v.count(',') -+ if len(result) != n: -+ return [('', '')] -+ -+ return result -+ -+ -+def _check_parenthesis(addr): -+ # Ignore parenthesis in quoted real names. -+ addr = _strip_quoted_realnames(addr) -+ -+ opens = 0 -+ for pos, ch in _iter_escaped_chars(addr): -+ if ch == '(': -+ opens += 1 -+ elif ch == ')': -+ opens -= 1 -+ if opens < 0: -+ return False -+ return (opens == 0) -+ -+ -+def _pre_parse_validation(email_header_fields): -+ accepted_values = [] -+ for v in email_header_fields: -+ if not _check_parenthesis(v): -+ 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 _format_timetuple_and_zone(timetuple, zone): -@@ -205,16 +321,33 @@ def parsedate_to_datetime(data): - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) - - --def parseaddr(addr): -+def parseaddr(addr, *, strict=True): - """ - Parse addr into its constituent realname and email address parts. - - Return a tuple of realname and email address, unless the parse fails, in - which case return a 2-tuple of ('', ''). -+ -+ If strict is True, use a strict parser which rejects malformed inputs. - """ -- addrs = _AddressList(addr).addresslist -- if not addrs: -- return '', '' -+ if not strict: -+ addrs = _AddressList(addr).addresslist -+ if not addrs: -+ return ('', '') -+ return addrs[0] -+ -+ 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] - - -diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py -index fc8d87974e..ef8aa0d53c 100644 ---- a/Lib/test/test_email/test_email.py -+++ b/Lib/test/test_email/test_email.py -@@ -16,6 +16,7 @@ - - import email - import email.policy -+import email.utils - - from email.charset import Charset - from email.generator import Generator, DecodedGenerator, BytesGenerator -@@ -3352,15 +3353,137 @@ def test_getaddresses_comma_in_name(self): - ], - ) - -+ def test_parsing_errors(self): -+ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056""" -+ alice = 'alice@example.org' -+ bob = 'bob@example.com' -+ empty = ('', '') -+ -+ # Test utils.getaddresses() and utils.parseaddr() on malformed email -+ # addresses: default behavior (strict=True) rejects malformed address, -+ # and strict=False which tolerates malformed address. -+ for invalid_separator, expected_non_strict in ( -+ ('(', [(f'<{bob}>', alice)]), -+ (')', [('', alice), empty, ('', bob)]), -+ ('<', [('', alice), empty, ('', bob), empty]), -+ ('>', [('', alice), empty, ('', bob)]), -+ ('[', [('', f'{alice}[<{bob}>]')]), -+ (']', [('', alice), empty, ('', bob)]), -+ ('@', [empty, empty, ('', bob)]), -+ (';', [('', alice), empty, ('', bob)]), -+ (':', [('', alice), ('', bob)]), -+ ('.', [('', alice + '.'), ('', bob)]), -+ ('"', [('', alice), ('', f'<{bob}>')]), -+ ): -+ address = f'{alice}{invalid_separator}<{bob}>' -+ with self.subTest(address=address): -+ self.assertEqual(utils.getaddresses([address]), -+ [empty]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ expected_non_strict) -+ -+ self.assertEqual(utils.parseaddr([address]), -+ empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Comma (',') is treated differently depending on strict parameter. -+ # Comma without quotes. -+ address = f'{alice},<{bob}>' -+ self.assertEqual(utils.getaddresses([address]), -+ [('', alice), ('', bob)]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('', alice), ('', bob)]) -+ self.assertEqual(utils.parseaddr([address]), -+ empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Real name between quotes containing comma. -+ address = '"Alice, alice@example.org" ' -+ expected_strict = ('Alice, alice@example.org', 'bob@example.com') -+ self.assertEqual(utils.getaddresses([address]), [expected_strict]) -+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) -+ self.assertEqual(utils.parseaddr([address]), expected_strict) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Valid parenthesis in comments. -+ address = 'alice@example.org (Alice)' -+ expected_strict = ('Alice', 'alice@example.org') -+ self.assertEqual(utils.getaddresses([address]), [expected_strict]) -+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) -+ self.assertEqual(utils.parseaddr([address]), expected_strict) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Invalid parenthesis in comments. -+ address = 'alice@example.org )Alice(' -+ self.assertEqual(utils.getaddresses([address]), [empty]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')]) -+ self.assertEqual(utils.parseaddr([address]), empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Two addresses with quotes separated by comma. -+ address = '"Jane Doe" , "John Doe" ' -+ self.assertEqual(utils.getaddresses([address]), -+ [('Jane Doe', 'jane@example.net'), -+ ('John Doe', 'john@example.net')]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('Jane Doe', 'jane@example.net'), -+ ('John Doe', 'john@example.net')]) -+ self.assertEqual(utils.parseaddr([address]), empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Test email.utils.supports_strict_parsing attribute -+ self.assertEqual(email.utils.supports_strict_parsing, True) -+ - def test_getaddresses_nasty(self): -- eq = self.assertEqual -- eq(utils.getaddresses(['foo: ;']), [('', '')]) -- eq(utils.getaddresses( -- ['[]*-- =~$']), -- [('', ''), ('', ''), ('', '*--')]) -- eq(utils.getaddresses( -- ['foo: ;', '"Jason R. Mastaler" ']), -- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) -+ for addresses, expected in ( -+ (['"Sürname, Firstname" '], -+ [('Sürname, Firstname', 'to@example.com')]), -+ -+ (['foo: ;'], -+ [('', '')]), -+ -+ (['foo: ;', '"Jason R. Mastaler" '], -+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]), -+ -+ ([r'Pete(A nice \) chap) '], -+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]), -+ -+ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'], -+ [('', '')]), -+ -+ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'], -+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]), -+ -+ (['John Doe '], -+ [('John Doe (comment)', 'jdoe@machine.example')]), -+ -+ (['"Mary Smith: Personal Account" '], -+ [('Mary Smith: Personal Account', 'smith@home.example')]), -+ -+ (['Undisclosed recipients:;'], -+ [('', '')]), -+ -+ ([r', "Giant; \"Big\" Box" '], -+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]), -+ ): -+ with self.subTest(addresses=addresses): -+ self.assertEqual(utils.getaddresses(addresses), -+ expected) -+ self.assertEqual(utils.getaddresses(addresses, strict=False), -+ expected) -+ -+ addresses = ['[]*-- =~$'] -+ self.assertEqual(utils.getaddresses(addresses), -+ [('', '')]) -+ self.assertEqual(utils.getaddresses(addresses, strict=False), -+ [('', ''), ('', ''), ('', '*--')]) - - def test_getaddresses_embedded_comment(self): - """Test proper handling of a nested comment""" -@@ -3551,6 +3674,54 @@ def test_mime_classes_policy_argument(self): - m = cls(*constructor, policy=email.policy.default) - self.assertIs(m.policy, email.policy.default) - -+ def test_iter_escaped_chars(self): -+ self.assertEqual(list(utils._iter_escaped_chars(r'a\\b\"c\\"d')), -+ [(0, 'a'), -+ (2, '\\\\'), -+ (3, 'b'), -+ (5, '\\"'), -+ (6, 'c'), -+ (8, '\\\\'), -+ (9, '"'), -+ (10, 'd')]) -+ self.assertEqual(list(utils._iter_escaped_chars('a\\')), -+ [(0, 'a'), (1, '\\')]) -+ -+ def test_strip_quoted_realnames(self): -+ def check(addr, expected): -+ self.assertEqual(utils._strip_quoted_realnames(addr), expected) -+ -+ check('"Jane Doe" , "John Doe" ', -+ ' , ') -+ check(r'"Jane \"Doe\"." ', -+ ' ') -+ -+ # special cases -+ check(r'before"name"after', 'beforeafter') -+ check(r'before"name"', 'before') -+ check(r'b"name"', 'b') # single char -+ check(r'"name"after', 'after') -+ check(r'"name"a', 'a') # single char -+ check(r'"name"', '') -+ -+ # no change -+ for addr in ( -+ 'Jane Doe , John Doe ', -+ 'lone " quote', -+ ): -+ self.assertEqual(utils._strip_quoted_realnames(addr), addr) -+ -+ -+ def test_check_parenthesis(self): -+ addr = 'alice@example.net' -+ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice(')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)')) -+ -+ # Ignore real name between quotes -+ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}')) -+ - - # Test the iterator/generators - class TestIterators(TestEmailBase): -diff --git a/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst -new file mode 100644 -index 0000000000..3d0e9e4078 ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst -@@ -0,0 +1,8 @@ -+:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now -+return ``('', '')`` 2-tuples in more situations where invalid email -+addresses are encountered instead of potentially inaccurate values. Add -+optional *strict* parameter to these two functions: use ``strict=False`` to -+get the old behavior, accept malformed inputs. -+``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check -+if the *strict* paramater is available. Patch by Thomas Dwyer and Victor -+Stinner to improve the CVE-2023-27043 fix. diff --git a/SOURCES/00436-cve-2024-8088-gh-122905-sanitize-names-in-zipfile-path.patch b/SOURCES/00436-cve-2024-8088-gh-122905-sanitize-names-in-zipfile-path.patch deleted file mode 100644 index c0552ce..0000000 --- a/SOURCES/00436-cve-2024-8088-gh-122905-sanitize-names-in-zipfile-path.patch +++ /dev/null @@ -1,121 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Mon, 12 Aug 2024 02:35:17 +0200 -Subject: [PATCH] 00436: [CVE-2024-8088] gh-122905: Sanitize names in - zipfile.Path. - ---- - Lib/test/test_zipfile/_path/test_path.py | 17 +++++ - Lib/zipfile/_path/__init__.py | 64 ++++++++++++++++++- - ...-08-11-14-08-04.gh-issue-122905.7tDsxA.rst | 1 + - 3 files changed, 81 insertions(+), 1 deletion(-) - create mode 100644 Misc/NEWS.d/next/Library/2024-08-11-14-08-04.gh-issue-122905.7tDsxA.rst - -diff --git a/Lib/test/test_zipfile/_path/test_path.py b/Lib/test/test_zipfile/_path/test_path.py -index 06d5aab69b..90885dbbe3 100644 ---- a/Lib/test/test_zipfile/_path/test_path.py -+++ b/Lib/test/test_zipfile/_path/test_path.py -@@ -577,3 +577,20 @@ def test_getinfo_missing(self, alpharep): - zipfile.Path(alpharep) - with self.assertRaises(KeyError): - alpharep.getinfo('does-not-exist') -+ -+ def test_malformed_paths(self): -+ """ -+ Path should handle malformed paths. -+ """ -+ data = io.BytesIO() -+ zf = zipfile.ZipFile(data, "w") -+ zf.writestr("/one-slash.txt", b"content") -+ zf.writestr("//two-slash.txt", b"content") -+ zf.writestr("../parent.txt", b"content") -+ zf.filename = '' -+ root = zipfile.Path(zf) -+ assert list(map(str, root.iterdir())) == [ -+ 'one-slash.txt', -+ 'two-slash.txt', -+ 'parent.txt', -+ ] -diff --git a/Lib/zipfile/_path/__init__.py b/Lib/zipfile/_path/__init__.py -index 78c413563b..42f9fded21 100644 ---- a/Lib/zipfile/_path/__init__.py -+++ b/Lib/zipfile/_path/__init__.py -@@ -83,7 +83,69 @@ def __setstate__(self, state): - super().__init__(*args, **kwargs) - - --class CompleteDirs(InitializedState, zipfile.ZipFile): -+class SanitizedNames: -+ """ -+ ZipFile mix-in to ensure names are sanitized. -+ """ -+ -+ def namelist(self): -+ return list(map(self._sanitize, super().namelist())) -+ -+ @staticmethod -+ def _sanitize(name): -+ r""" -+ Ensure a relative path with posix separators and no dot names. -+ -+ Modeled after -+ https://github.com/python/cpython/blob/bcc1be39cb1d04ad9fc0bd1b9193d3972835a57c/Lib/zipfile/__init__.py#L1799-L1813 -+ but provides consistent cross-platform behavior. -+ -+ >>> san = SanitizedNames._sanitize -+ >>> san('/foo/bar') -+ 'foo/bar' -+ >>> san('//foo.txt') -+ 'foo.txt' -+ >>> san('foo/.././bar.txt') -+ 'foo/bar.txt' -+ >>> san('foo../.bar.txt') -+ 'foo../.bar.txt' -+ >>> san('\\foo\\bar.txt') -+ 'foo/bar.txt' -+ >>> san('D:\\foo.txt') -+ 'D/foo.txt' -+ >>> san('\\\\server\\share\\file.txt') -+ 'server/share/file.txt' -+ >>> san('\\\\?\\GLOBALROOT\\Volume3') -+ '?/GLOBALROOT/Volume3' -+ >>> san('\\\\.\\PhysicalDrive1\\root') -+ 'PhysicalDrive1/root' -+ -+ Retain any trailing slash. -+ >>> san('abc/') -+ 'abc/' -+ -+ Raises a ValueError if the result is empty. -+ >>> san('../..') -+ Traceback (most recent call last): -+ ... -+ ValueError: Empty filename -+ """ -+ -+ def allowed(part): -+ return part and part not in {'..', '.'} -+ -+ # Remove the drive letter. -+ # Don't use ntpath.splitdrive, because that also strips UNC paths -+ bare = re.sub('^([A-Z]):', r'\1', name, flags=re.IGNORECASE) -+ clean = bare.replace('\\', '/') -+ parts = clean.split('/') -+ joined = '/'.join(filter(allowed, parts)) -+ if not joined: -+ raise ValueError("Empty filename") -+ return joined + '/' * name.endswith('/') -+ -+ -+class CompleteDirs(InitializedState, SanitizedNames, zipfile.ZipFile): - """ - A ZipFile subclass that ensures that implied directories - are always included in the namelist. -diff --git a/Misc/NEWS.d/next/Library/2024-08-11-14-08-04.gh-issue-122905.7tDsxA.rst b/Misc/NEWS.d/next/Library/2024-08-11-14-08-04.gh-issue-122905.7tDsxA.rst -new file mode 100644 -index 0000000000..1be44c906c ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2024-08-11-14-08-04.gh-issue-122905.7tDsxA.rst -@@ -0,0 +1 @@ -+:class:`zipfile.Path` objects now sanitize names from the zipfile. diff --git a/SOURCES/Python-3.12.5.tar.xz.asc b/SOURCES/Python-3.12.5.tar.xz.asc deleted file mode 100644 index b8c4cf1..0000000 --- a/SOURCES/Python-3.12.5.tar.xz.asc +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQKTBAABCgB9FiEEcWlgX2LHUTVtBUomqCHmgOX6YwUFAmayiFtfFIAAAAAALgAo -aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDcx -Njk2MDVGNjJDNzUxMzU2RDA1NEEyNkE4MjFFNjgwRTVGQTYzMDUACgkQqCHmgOX6 -YwUr4g//VyVs9tvbtiSp8pGe8f1gYErEw54r124sL/CBuNii8Irts1j5ymGxcm+l -hshPK5UlqRnhd5dCJWFTvLTXa5Ko2R1L3JyyxfGd1hmDuMhrWsDHijI0R7L/mGM5 -6X2LTaadBVNvk8HaNKvR8SEWvo68rdnOuYElFA9ir7uqwjO26ZWz9FfH80YDGwo8 -Blef2NYw8rNhiaZMFV0HYV7D+YyUAZnFNfW8M7Fd4oskUyj1tD9J89T9FFLYN09d -BcCIf+EdiEfqRpKxH89bW2g52kDrm4jYGONtpyF8eruyS3YwYSbvbuWioBYKmlxC -s51mieXz6G325GTZnmPxLek3ywPv6Gil9y0wH3fIr2BsWsmXust4LBpjDGt56Fy6 -seokGBg8xzsBSk3iEqNoFmNsy/QOiuCcDejX4XqBDNodOlETQPJb07TkTI2iOmg9 -NG4Atiz1HvGVxK68UuK9IIcNHyaWUmH8h4VQFGvc6KV6feP5Nm21Y12PZ5XIqJBO -Y8M/VJIJ5koaNPQfnBbbI5YBkUr4BVpIXIpY5LM/L5sUo2C3R7hMi0VGK88HGfSQ -KV4JmZgf6RMBNmrWY12sryS1QQ6q3P110GTUGQWB3sxxNbhmfcrK+4viqHc83yDz -ifmk33HuqaQGU7OzUMHeNcoCJIPo3H1FpoHOn9wLLCtA1pT+as4= -=t0Rk ------END PGP SIGNATURE----- diff --git a/SOURCES/Python-3.12.9.tar.xz.asc b/SOURCES/Python-3.12.9.tar.xz.asc new file mode 100644 index 0000000..75a8cde --- /dev/null +++ b/SOURCES/Python-3.12.9.tar.xz.asc @@ -0,0 +1,18 @@ +-----BEGIN PGP SIGNATURE----- + +iQKTBAABCgB9FiEEcWlgX2LHUTVtBUomqCHmgOX6YwUFAmeiX7JfFIAAAAAALgAo +aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDcx +Njk2MDVGNjJDNzUxMzU2RDA1NEEyNkE4MjFFNjgwRTVGQTYzMDUACgkQqCHmgOX6 +YwXTqw//VlGJA5CRDfljMwN9BmG2hdXB1B7Lj0PssuAo4A/lH99gb4DRVDS9LNjr +99WdH/fQQovx6rTbtyJnN8Vh7SSduBi/vOc5n5VOXZB0buqR0l+0wu4m43Slu6xP +fXO349Hr6585lemU8x54TrP756rSVUhy3T+krUuNDL9W1Wrp2yDCpt4tUoEhNXGw +DoYS8MrK/ygLNV/7p2DeMWOHNdbjKNH6rfzl60IAwAp7oANcyoj6Pho960bbeUDo +tb47Pw0WWZv3EuITP6bPa8+Z6dj096cFL3AQJ3ap16OduwiaOsGhqTfe4+kbp6ut +Gp/1HeIHzPbEV0E5K78RWHuzBYgU1oPGiMjlp7WkA7bP2OSTF7nM4EBkiiihk2qx +3d5VF9wpVRJ4AuR/aWcWcMnvD2ziSWfzZM3Z3VLnTaWYpuRkQp8TTiFr1vHqxMYm +p/8AozzBJMfOS6u/Q0WNAdk6x3VB0DXnTAETXQVIrex4DXqX/3WSMWK5/x/OyCh9 +ytdreIQYbv1KvlNQJkgpPb7jlUSXp8t9fHCXt4hszhJgtjwIj/+CuSeAgX0bhopV +XsqOBseDNhATg38mhwBVaeFKGRpxsKdpxcdqSEGKuhXtEI/hJmkpZGw49gy3xWxB +KlgRgKjCPw+BGAIVV9qvdtJzam8a09SKVcslqgF619q0byQoBmo= +=1TbP +-----END PGP SIGNATURE----- diff --git a/SPECS/python3.12.spec b/SPECS/python3.12.spec index ff1c0cd..c6086ce 100644 --- a/SPECS/python3.12.spec +++ b/SPECS/python3.12.spec @@ -16,11 +16,11 @@ URL: https://www.python.org/ # WARNING When rebasing to a new Python version, # remember to update the python3-docs package as well -%global general_version %{pybasever}.5 +%global general_version %{pybasever}.9 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -66,7 +66,7 @@ License: Python-2.0.1 # If the rpmwheels condition is disabled, we use the bundled wheel packages # from Python with the versions below. # This needs to be manually updated when we update Python. -%global pip_version 24.2 +%global pip_version 24.3.1 %global setuptools_version 67.6.1 %global wheel_version 0.40.0 # All of those also include a list of indirect bundled libs: @@ -74,8 +74,8 @@ License: Python-2.0.1 # $ %%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/ensurepip/_bundled/pip-*.whl pip/_vendor/vendor.txt) %global pip_bundled_provides %{expand: Provides: bundled(python3dist(cachecontrol)) = 0.14 -Provides: bundled(python3dist(certifi)) = 2024.7.4 -Provides: bundled(python3dist(distlib)) = 0.3.8 +Provides: bundled(python3dist(certifi)) = 2024.8.30 +Provides: bundled(python3dist(distlib)) = 0.3.9 Provides: bundled(python3dist(distro)) = 1.9 Provides: bundled(python3dist(idna)) = 3.7 Provides: bundled(python3dist(msgpack)) = 1.0.8 @@ -88,9 +88,9 @@ Provides: bundled(python3dist(resolvelib)) = 1.0.1 Provides: bundled(python3dist(rich)) = 13.7.1 Provides: bundled(python3dist(setuptools)) = 70.3 Provides: bundled(python3dist(tomli)) = 2.0.1 -Provides: bundled(python3dist(truststore)) = 0.9.1 +Provides: bundled(python3dist(truststore)) = 0.10 Provides: bundled(python3dist(typing-extensions)) = 4.12.2 -Provides: bundled(python3dist(urllib3)) = 1.26.18 +Provides: bundled(python3dist(urllib3)) = 1.26.20 } # setuptools # vendor.txt files not in .whl @@ -329,7 +329,7 @@ Source11: idle3.appdata.xml # (Patches taken from github.com/fedora-python/cpython) -# 00251 # cae5a6abc5df08239c85b83e4e250b6f2702e4f5 +# 00251 # 6a4ec74157aa01f1ada9f29f30a371cd9e5369e8 # Change user install location # # Set values of base and platbase in sysconfig from /usr @@ -378,15 +378,6 @@ Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-g # - https://access.redhat.com/articles/7004769 Patch397: 00397-tarfile-filter.patch -# 00415 # 83e0fc3ec7bc38055c536f482578a10f6efcc08c -# [CVE-2023-27043] gh-102988: Reject malformed addresses in email.parseaddr() (#111116) -# -# Detect email address parsing errors and return empty tuple to -# indicate the parsing error (old API). Add an optional 'strict' -# parameter to getaddresses() and parseaddr() functions. Patch by -# Thomas Dwyer. -Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch - # 00422 # a353cebef737c41420dc7ae2469dd657371b8881 # Fix tests for XMLPullParser with Expat 2.6.0 # @@ -394,10 +385,6 @@ Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-par # CVE-2023-52425. Future versions of Expat may be more reactive. Patch422: 00422-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch -# 00436 # c76cc2aa3a2c30375ade4859b732ada851cc89ed -# [CVE-2024-8088] gh-122905: Sanitize names in zipfile.Path. -Patch436: 00436-cve-2024-8088-gh-122905-sanitize-names-in-zipfile-path.patch - # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1712,6 +1699,20 @@ CheckPython optimized # ====================================================== %changelog +* Tue Feb 04 2025 Charalampos Stratakis - 3.12.9-1 +- Update to 3.12.9 +- Security fix for CVE-2025-0938 +Resolves: RHEL-77261 + +* Tue Dec 03 2024 Charalampos Stratakis - 3.12.8-1 +- Update to 3.12.8 +- Security fix for CVE-2024-9287 and CVE-2024-12254 +Resolves: RHEL-64886, RHEL-70320 + +* Mon Sep 09 2024 Tomáš Hrnčiar - 3.12.6-1 +- Update to 3.12.6 +Resolves: RHEL-57417 + * Fri Aug 23 2024 Charalampos Stratakis - 3.12.5-2 - Security fix for CVE-2024-8088 Resolves: RHEL-55963