import CS python3.12-3.12.12-5.el10
This commit is contained in:
parent
630b9e05a2
commit
27c7d1307d
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
Python-3.12.5.tar.xz
|
Python-3.12.12.tar.xz
|
||||||
|
|||||||
@ -30,7 +30,7 @@ Co-authored-by: Lumír Balhar <frenzy.madness@gmail.com>
|
|||||||
3 files changed, 71 insertions(+), 4 deletions(-)
|
3 files changed, 71 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
diff --git a/Lib/site.py b/Lib/site.py
|
diff --git a/Lib/site.py b/Lib/site.py
|
||||||
index 924cfbecec..e2871ecc89 100644
|
index aed254ad50..568dbdb945 100644
|
||||||
--- a/Lib/site.py
|
--- a/Lib/site.py
|
||||||
+++ b/Lib/site.py
|
+++ b/Lib/site.py
|
||||||
@@ -398,8 +398,15 @@ def getsitepackages(prefixes=None):
|
@@ -398,8 +398,15 @@ def getsitepackages(prefixes=None):
|
||||||
@ -51,7 +51,7 @@ index 924cfbecec..e2871ecc89 100644
|
|||||||
if os.path.isdir(sitedir):
|
if os.path.isdir(sitedir):
|
||||||
addsitedir(sitedir, known_paths)
|
addsitedir(sitedir, known_paths)
|
||||||
diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py
|
diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py
|
||||||
index 122d441bd1..2d354a11da 100644
|
index acc8d4d182..6355669f62 100644
|
||||||
--- a/Lib/sysconfig.py
|
--- a/Lib/sysconfig.py
|
||||||
+++ b/Lib/sysconfig.py
|
+++ b/Lib/sysconfig.py
|
||||||
@@ -104,6 +104,11 @@
|
@@ -104,6 +104,11 @@
|
||||||
@ -86,7 +86,7 @@ index 122d441bd1..2d354a11da 100644
|
|||||||
_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
|
_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
|
||||||
'scripts', 'data')
|
'scripts', 'data')
|
||||||
|
|
||||||
@@ -263,11 +281,40 @@ def _extend_dict(target_dict, other_dict):
|
@@ -268,11 +286,40 @@ def _extend_dict(target_dict, other_dict):
|
||||||
target_dict[key] = value
|
target_dict[key] = value
|
||||||
|
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ index 122d441bd1..2d354a11da 100644
|
|||||||
+ # we only change the defaults here, so explicit --prefix will take precedence
|
+ # we only change the defaults here, so explicit --prefix will take precedence
|
||||||
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
+ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe
|
||||||
+ if (scheme == 'posix_prefix' and
|
+ if (scheme == 'posix_prefix' and
|
||||||
+ _PREFIX == '/usr' and
|
+ sys.prefix == '/usr' and
|
||||||
+ 'RPM_BUILD_ROOT' not in os.environ):
|
+ 'RPM_BUILD_ROOT' not in os.environ):
|
||||||
+ _extend_dict(vars, _config_vars_local())
|
+ _extend_dict(vars, _config_vars_local())
|
||||||
+ else:
|
+ else:
|
||||||
@ -129,10 +129,10 @@ index 122d441bd1..2d354a11da 100644
|
|||||||
# On Windows we want to substitute 'lib' for schemes rather
|
# On Windows we want to substitute 'lib' for schemes rather
|
||||||
# than the native value (without modifying vars, in case it
|
# than the native value (without modifying vars, in case it
|
||||||
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
|
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
|
||||||
index 1137c2032b..8fc2b84f52 100644
|
index 67647e1b78..7baddaa9d6 100644
|
||||||
--- a/Lib/test/test_sysconfig.py
|
--- a/Lib/test/test_sysconfig.py
|
||||||
+++ b/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 scheme in _INSTALL_SCHEMES:
|
||||||
for name in _INSTALL_SCHEMES[scheme]:
|
for name in _INSTALL_SCHEMES[scheme]:
|
||||||
expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
|
expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
|
||||||
@ -153,7 +153,7 @@ index 1137c2032b..8fc2b84f52 100644
|
|||||||
os.path.normpath(expected),
|
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)
|
self.assertTrue(os.path.isfile(config_h), config_h)
|
||||||
|
|
||||||
def test_get_scheme_names(self):
|
def test_get_scheme_names(self):
|
||||||
@ -162,7 +162,7 @@ index 1137c2032b..8fc2b84f52 100644
|
|||||||
if HAS_USER_BASE:
|
if HAS_USER_BASE:
|
||||||
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
|
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
|
||||||
self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
|
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())"
|
cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
|
||||||
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
From d307f5706434e0cb445fb48291852bd7ec46ddbd Mon Sep 17 00:00:00 2001
|
From 11deb3112bd90bc2dce2fcd4a1f5975c08b91360 Mon Sep 17 00:00:00 2001
|
||||||
From: Charalampos Stratakis <cstratak@redhat.com>
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
||||||
Date: Thu, 12 Dec 2019 16:58:31 +0100
|
Date: Thu, 12 Dec 2019 16:58:31 +0100
|
||||||
Subject: [PATCH 1/5] Expose blake2b and blake2s hashes from OpenSSL
|
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)
|
computed = m.hexdigest() if not shake else m.hexdigest(length)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
||||||
index af6d1b2..980712f 100644
|
index 2998820..b96001e 100644
|
||||||
--- a/Modules/_hashopenssl.c
|
--- a/Modules/_hashopenssl.c
|
||||||
+++ b/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
|
#ifdef PY_OPENSSL_HAS_SHA3
|
||||||
|
|
||||||
/*[clinic input]
|
/*[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_SHA256_METHODDEF
|
||||||
_HASHLIB_OPENSSL_SHA384_METHODDEF
|
_HASHLIB_OPENSSL_SHA384_METHODDEF
|
||||||
_HASHLIB_OPENSSL_SHA512_METHODDEF
|
_HASHLIB_OPENSSL_SHA512_METHODDEF
|
||||||
@ -84,7 +84,7 @@ index af6d1b2..980712f 100644
|
|||||||
_HASHLIB_OPENSSL_SHA3_256_METHODDEF
|
_HASHLIB_OPENSSL_SHA3_256_METHODDEF
|
||||||
_HASHLIB_OPENSSL_SHA3_384_METHODDEF
|
_HASHLIB_OPENSSL_SHA3_384_METHODDEF
|
||||||
diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h
|
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
|
--- a/Modules/clinic/_hashopenssl.c.h
|
||||||
+++ b/Modules/clinic/_hashopenssl.c.h
|
+++ b/Modules/clinic/_hashopenssl.c.h
|
||||||
@@ -743,6 +743,156 @@ exit:
|
@@ -743,6 +743,156 @@ exit:
|
||||||
@ -248,13 +248,13 @@ index fb61a44..1e42b87 100644
|
|||||||
#ifndef _HASHLIB_SCRYPT_METHODDEF
|
#ifndef _HASHLIB_SCRYPT_METHODDEF
|
||||||
#define _HASHLIB_SCRYPT_METHODDEF
|
#define _HASHLIB_SCRYPT_METHODDEF
|
||||||
#endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */
|
#endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */
|
||||||
-/*[clinic end generated code: output=b339e255db698147 input=a9049054013a1b77]*/
|
-/*[clinic end generated code: output=4734184f6555dc95 input=a9049054013a1b77]*/
|
||||||
+/*[clinic end generated code: output=1d988d457a8beebe input=a9049054013a1b77]*/
|
+/*[clinic end generated code: output=f0bfddb963a21208 input=a9049054013a1b77]*/
|
||||||
--
|
--
|
||||||
2.45.0
|
2.47.1
|
||||||
|
|
||||||
|
|
||||||
From c247ff164269fb68236a79a1359cc37c1a8a0004 Mon Sep 17 00:00:00 2001
|
From ea9d5c84e25b5c04c2823e1edee4354dd6b2b7a5 Mon Sep 17 00:00:00 2001
|
||||||
From: Petr Viktorin <pviktori@redhat.com>
|
From: Petr Viktorin <pviktori@redhat.com>
|
||||||
Date: Thu, 25 Jul 2019 17:19:06 +0200
|
Date: Thu, 25 Jul 2019 17:19:06 +0200
|
||||||
Subject: [PATCH 2/5] Disable Python's hash implementations in FIPS mode,
|
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; \
|
+ if (_Py_hashlib_fips_error(exc, name)) return NULL; \
|
||||||
+} while (0)
|
+} while (0)
|
||||||
diff --git a/configure.ac b/configure.ac
|
diff --git a/configure.ac b/configure.ac
|
||||||
index 384718d..c4a1198 100644
|
index 9270b5f..a9eb2c9 100644
|
||||||
--- a/configure.ac
|
--- a/configure.ac
|
||||||
+++ b/configure.ac
|
+++ b/configure.ac
|
||||||
@@ -7445,7 +7445,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([_sha3], [test "$with_builtin_sha3" = yes])
|
||||||
PY_STDLIB_MOD([_blake2],
|
PY_STDLIB_MOD([_blake2],
|
||||||
[test "$with_builtin_blake2" = yes], [],
|
[test "$with_builtin_blake2" = yes], [],
|
||||||
@ -459,10 +459,10 @@ index 384718d..c4a1198 100644
|
|||||||
PY_STDLIB_MOD([_crypt],
|
PY_STDLIB_MOD([_crypt],
|
||||||
[], [test "$ac_cv_crypt_crypt" = yes],
|
[], [test "$ac_cv_crypt_crypt" = yes],
|
||||||
--
|
--
|
||||||
2.45.0
|
2.47.1
|
||||||
|
|
||||||
|
|
||||||
From e58b32f238f1d4503248f3a8b1489f7567bdbd6d Mon Sep 17 00:00:00 2001
|
From 29a7b7ac9e18a501ed78bde7a449b90c57d44e24 Mon Sep 17 00:00:00 2001
|
||||||
From: Charalampos Stratakis <cstratak@redhat.com>
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
||||||
Date: Fri, 29 Jan 2021 14:16:21 +0100
|
Date: Fri, 29 Jan 2021 14:16:21 +0100
|
||||||
Subject: [PATCH 3/5] Use python's fall back crypto implementations only if we
|
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 = getattr(hashlib,
|
||||||
'__get_builtin_constructor')
|
'__get_builtin_constructor')
|
||||||
--
|
--
|
||||||
2.45.0
|
2.47.1
|
||||||
|
|
||||||
|
|
||||||
From 2b14d347948dc01af587b9e21cd448833a38c7b5 Mon Sep 17 00:00:00 2001
|
From 59accf544492400c9fd32a8e682fb6f2206e932e Mon Sep 17 00:00:00 2001
|
||||||
From: Charalampos Stratakis <cstratak@redhat.com>
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
||||||
Date: Wed, 31 Jul 2019 15:43:43 +0200
|
Date: Wed, 31 Jul 2019 15:43:43 +0200
|
||||||
Subject: [PATCH 4/5] Test equivalence of hashes for the various digests with
|
Subject: [PATCH 4/5] Test equivalence of hashes for the various digests with
|
||||||
@ -712,10 +712,10 @@ index 6031b02..5bd5297 100644
|
|||||||
class KDFTests(unittest.TestCase):
|
class KDFTests(unittest.TestCase):
|
||||||
|
|
||||||
--
|
--
|
||||||
2.45.0
|
2.47.1
|
||||||
|
|
||||||
|
|
||||||
From b98c72b356a529a68cb4216526b838a57937cf6f Mon Sep 17 00:00:00 2001
|
From 21efadd8b488956482bdc6ccd91c37dcef705129 Mon Sep 17 00:00:00 2001
|
||||||
From: Petr Viktorin <pviktori@redhat.com>
|
From: Petr Viktorin <pviktori@redhat.com>
|
||||||
Date: Mon, 26 Aug 2019 19:39:48 +0200
|
Date: Mon, 26 Aug 2019 19:39:48 +0200
|
||||||
Subject: [PATCH 5/5] Guard against Python HMAC in FIPS mode
|
Subject: [PATCH 5/5] Guard against Python HMAC in FIPS mode
|
||||||
@ -766,7 +766,7 @@ index 8b4eb2f..8930bda 100644
|
|||||||
digest_cons = digestmod
|
digest_cons = digestmod
|
||||||
elif isinstance(digestmod, str):
|
elif isinstance(digestmod, str):
|
||||||
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
|
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
|
||||||
index a39a2c4..b7b24ab 100644
|
index 1502fba..7997073 100644
|
||||||
--- a/Lib/test/test_hmac.py
|
--- a/Lib/test/test_hmac.py
|
||||||
+++ b/Lib/test/test_hmac.py
|
+++ b/Lib/test/test_hmac.py
|
||||||
@@ -5,6 +5,7 @@ import hashlib
|
@@ -5,6 +5,7 @@ import hashlib
|
||||||
@ -805,7 +805,7 @@ index a39a2c4..b7b24ab 100644
|
|||||||
@unittest.skipUnless(sha256_module is not None, 'need _sha256')
|
@unittest.skipUnless(sha256_module is not None, 'need _sha256')
|
||||||
def test_with_sha256_module(self):
|
def test_with_sha256_module(self):
|
||||||
h = hmac.HMAC(b"key", b"hash this!", digestmod=sha256_module.sha256)
|
h = hmac.HMAC(b"key", b"hash this!", digestmod=sha256_module.sha256)
|
||||||
@@ -481,6 +489,7 @@ class SanityTestCase(unittest.TestCase):
|
@@ -489,6 +497,7 @@ class UpdateTestCase(unittest.TestCase):
|
||||||
|
|
||||||
class CopyTestCase(unittest.TestCase):
|
class CopyTestCase(unittest.TestCase):
|
||||||
|
|
||||||
@ -813,7 +813,7 @@ index a39a2c4..b7b24ab 100644
|
|||||||
@hashlib_helper.requires_hashdigest('sha256')
|
@hashlib_helper.requires_hashdigest('sha256')
|
||||||
def test_attributes_old(self):
|
def test_attributes_old(self):
|
||||||
# Testing if attributes are of same type.
|
# Testing if attributes are of same type.
|
||||||
@@ -492,6 +501,7 @@ class CopyTestCase(unittest.TestCase):
|
@@ -500,6 +509,7 @@ class CopyTestCase(unittest.TestCase):
|
||||||
self.assertEqual(type(h1._outer), type(h2._outer),
|
self.assertEqual(type(h1._outer), type(h2._outer),
|
||||||
"Types of outer don't match.")
|
"Types of outer don't match.")
|
||||||
|
|
||||||
@ -822,5 +822,5 @@ index a39a2c4..b7b24ab 100644
|
|||||||
def test_realcopy_old(self):
|
def test_realcopy_old(self):
|
||||||
# Testing if the copy method created a real copy.
|
# Testing if the copy method created a real copy.
|
||||||
--
|
--
|
||||||
2.45.0
|
2.47.1
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@ https://github.com/GrahamDumpleton/mod_wsgi/issues/730
|
|||||||
2 files changed, 8 insertions(+), 50 deletions(-)
|
2 files changed, 8 insertions(+), 50 deletions(-)
|
||||||
|
|
||||||
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
|
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
|
--- a/Lib/test/test_threading.py
|
||||||
+++ b/Lib/test/test_threading.py
|
+++ b/Lib/test/test_threading.py
|
||||||
@@ -1100,39 +1100,6 @@ def noop(): pass
|
@@ -1100,39 +1100,6 @@ def noop(): pass
|
||||||
@ -60,7 +60,7 @@ index 2e4b860b97..3066b23ee1 100644
|
|||||||
code = """if 1:
|
code = """if 1:
|
||||||
import _thread
|
import _thread
|
||||||
diff --git a/Lib/threading.py b/Lib/threading.py
|
diff --git a/Lib/threading.py b/Lib/threading.py
|
||||||
index 0bba85d08a..b256e3273f 100644
|
index 064c74d40f..9e3abacd42 100644
|
||||||
--- a/Lib/threading.py
|
--- a/Lib/threading.py
|
||||||
+++ b/Lib/threading.py
|
+++ b/Lib/threading.py
|
||||||
@@ -1587,29 +1587,20 @@ def _shutdown():
|
@@ -1587,29 +1587,20 @@ def _shutdown():
|
||||||
|
|||||||
@ -1,483 +0,0 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Victor Stinner <vstinner@python.org>
|
|
||||||
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 <github@tomd.tel>
|
|
||||||
---
|
|
||||||
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 <email.message.Message.get_all>`. Here's a simple
|
|
||||||
- example that gets all the recipients of a message::
|
|
||||||
+ :meth:`Message.get_all <email.message.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 <bob@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 a373c53c7c..c616398eb1 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" <bob@example.com>'
|
|
||||||
+ 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" <jane@example.net>, "John Doe" <john@example.net>'
|
|
||||||
+ 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@dom.ain>']),
|
|
||||||
- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
|
|
||||||
+ for addresses, expected in (
|
|
||||||
+ (['"Sürname, Firstname" <to@example.com>'],
|
|
||||||
+ [('Sürname, Firstname', 'to@example.com')]),
|
|
||||||
+
|
|
||||||
+ (['foo: ;'],
|
|
||||||
+ [('', '')]),
|
|
||||||
+
|
|
||||||
+ (['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>'],
|
|
||||||
+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]),
|
|
||||||
+
|
|
||||||
+ ([r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>'],
|
|
||||||
+ [('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 <jdoe@machine(comment). example>'],
|
|
||||||
+ [('John Doe (comment)', 'jdoe@machine.example')]),
|
|
||||||
+
|
|
||||||
+ (['"Mary Smith: Personal Account" <smith@home.example>'],
|
|
||||||
+ [('Mary Smith: Personal Account', 'smith@home.example')]),
|
|
||||||
+
|
|
||||||
+ (['Undisclosed recipients:;'],
|
|
||||||
+ [('', '')]),
|
|
||||||
+
|
|
||||||
+ ([r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>'],
|
|
||||||
+ [('', '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" <jane@example.net>, "John Doe" <john@example.net>',
|
|
||||||
+ ' <jane@example.net>, <john@example.net>')
|
|
||||||
+ check(r'"Jane \"Doe\"." <jane@example.net>',
|
|
||||||
+ ' <jane@example.net>')
|
|
||||||
+
|
|
||||||
+ # 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 <jane@example.net>, John Doe <john@example.net>',
|
|
||||||
+ '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.
|
|
||||||
@ -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.
|
|
||||||
196
00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch
Normal file
196
00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: yevgeny hong <hongyevgeny@gmail.com>
|
||||||
|
Date: Tue, 26 Mar 2024 16:45:43 +0900
|
||||||
|
Subject: 00462: Fix PySSL_SetError handling SSL_ERROR_SYSCALL
|
||||||
|
|
||||||
|
Python 3.10 changed from using SSL_write() and SSL_read() to SSL_write_ex() and
|
||||||
|
SSL_read_ex(), but did not update handling of the return value.
|
||||||
|
|
||||||
|
Change error handling so that the return value is not examined.
|
||||||
|
OSError (not EOF) is now returned when retval is 0.
|
||||||
|
|
||||||
|
This resolves the issue of failing tests when a system is
|
||||||
|
stressed on OpenSSL 3.5.
|
||||||
|
|
||||||
|
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
Co-authored-by: Petr Viktorin <encukou@gmail.com>
|
||||||
|
---
|
||||||
|
Lib/test/test_ssl.py | 28 ++++++-----
|
||||||
|
...-02-18-09-50-31.gh-issue-115627.HGchj0.rst | 2 +
|
||||||
|
Modules/_ssl.c | 48 +++++++------------
|
||||||
|
3 files changed, 35 insertions(+), 43 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
|
||||||
|
index b13e37d0cd..daeb8cba74 100644
|
||||||
|
--- a/Lib/test/test_ssl.py
|
||||||
|
+++ b/Lib/test/test_ssl.py
|
||||||
|
@@ -2427,16 +2427,18 @@ def run(self):
|
||||||
|
self.write(msg.lower())
|
||||||
|
except OSError as e:
|
||||||
|
# handles SSLError and socket errors
|
||||||
|
+ if isinstance(e, ConnectionError):
|
||||||
|
+ # OpenSSL 1.1.1 sometimes raises
|
||||||
|
+ # ConnectionResetError when connection is not
|
||||||
|
+ # shut down gracefully.
|
||||||
|
+ if self.server.chatty and support.verbose:
|
||||||
|
+ print(f" Connection reset by peer: {self.addr}")
|
||||||
|
+
|
||||||
|
+ self.close()
|
||||||
|
+ self.running = False
|
||||||
|
+ return
|
||||||
|
if self.server.chatty and support.verbose:
|
||||||
|
- if isinstance(e, ConnectionError):
|
||||||
|
- # OpenSSL 1.1.1 sometimes raises
|
||||||
|
- # ConnectionResetError when connection is not
|
||||||
|
- # shut down gracefully.
|
||||||
|
- print(
|
||||||
|
- f" Connection reset by peer: {self.addr}"
|
||||||
|
- )
|
||||||
|
- else:
|
||||||
|
- handle_error("Test server failure:\n")
|
||||||
|
+ handle_error("Test server failure:\n")
|
||||||
|
try:
|
||||||
|
self.write(b"ERROR\n")
|
||||||
|
except OSError:
|
||||||
|
@@ -3148,8 +3150,8 @@ def test_wrong_cert_tls13(self):
|
||||||
|
suppress_ragged_eofs=False) as s:
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
- ssl.SSLError,
|
||||||
|
- 'alert unknown ca|EOF occurred|TLSV1_ALERT_UNKNOWN_CA'
|
||||||
|
+ OSError,
|
||||||
|
+ 'alert unknown ca|EOF occurred|TLSV1_ALERT_UNKNOWN_CA|closed by the remote host|Connection reset by peer'
|
||||||
|
):
|
||||||
|
# TLS 1.3 perform client cert exchange after handshake
|
||||||
|
s.write(b'data')
|
||||||
|
@@ -4422,8 +4424,8 @@ def msg_cb(conn, direction, version, content_type, msg_type, data):
|
||||||
|
# test sometimes fails with EOF error. Test passes as long as
|
||||||
|
# server aborts connection with an error.
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
- ssl.SSLError,
|
||||||
|
- '(certificate required|EOF occurred)'
|
||||||
|
+ OSError,
|
||||||
|
+ 'certificate required|EOF occurred|closed by the remote host|Connection reset by peer'
|
||||||
|
):
|
||||||
|
# receive CertificateRequest
|
||||||
|
data = s.recv(1024)
|
||||||
|
diff --git a/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst b/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..75d926ab59
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+Fix the :mod:`ssl` module error handling of connection terminate by peer.
|
||||||
|
+It now throws an OSError with the appropriate error code instead of an EOFError.
|
||||||
|
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
|
||||||
|
index 0b8cf0b6df..42a4c95890 100644
|
||||||
|
--- a/Modules/_ssl.c
|
||||||
|
+++ b/Modules/_ssl.c
|
||||||
|
@@ -573,7 +573,7 @@ PySSL_ChainExceptions(PySSLSocket *sslsock) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
-PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
|
||||||
|
+PySSL_SetError(PySSLSocket *sslsock, const char *filename, int lineno)
|
||||||
|
{
|
||||||
|
PyObject *type;
|
||||||
|
char *errstr = NULL;
|
||||||
|
@@ -586,7 +586,6 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
|
||||||
|
_sslmodulestate *state = get_state_sock(sslsock);
|
||||||
|
type = state->PySSLErrorObject;
|
||||||
|
|
||||||
|
- assert(ret <= 0);
|
||||||
|
e = ERR_peek_last_error();
|
||||||
|
|
||||||
|
if (sslsock->ssl != NULL) {
|
||||||
|
@@ -619,32 +618,21 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
|
||||||
|
case SSL_ERROR_SYSCALL:
|
||||||
|
{
|
||||||
|
if (e == 0) {
|
||||||
|
- PySocketSockObject *s = GET_SOCKET(sslsock);
|
||||||
|
- if (ret == 0 || (((PyObject *)s) == Py_None)) {
|
||||||
|
+ /* underlying BIO reported an I/O error */
|
||||||
|
+ ERR_clear_error();
|
||||||
|
+#ifdef MS_WINDOWS
|
||||||
|
+ if (err.ws) {
|
||||||
|
+ return PyErr_SetFromWindowsErr(err.ws);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ if (err.c) {
|
||||||
|
+ errno = err.c;
|
||||||
|
+ return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
+ }
|
||||||
|
+ else {
|
||||||
|
p = PY_SSL_ERROR_EOF;
|
||||||
|
type = state->PySSLEOFErrorObject;
|
||||||
|
errstr = "EOF occurred in violation of protocol";
|
||||||
|
- } else if (s && ret == -1) {
|
||||||
|
- /* underlying BIO reported an I/O error */
|
||||||
|
- ERR_clear_error();
|
||||||
|
-#ifdef MS_WINDOWS
|
||||||
|
- if (err.ws) {
|
||||||
|
- return PyErr_SetFromWindowsErr(err.ws);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
- if (err.c) {
|
||||||
|
- errno = err.c;
|
||||||
|
- return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
- }
|
||||||
|
- else {
|
||||||
|
- p = PY_SSL_ERROR_EOF;
|
||||||
|
- type = state->PySSLEOFErrorObject;
|
||||||
|
- errstr = "EOF occurred in violation of protocol";
|
||||||
|
- }
|
||||||
|
- } else { /* possible? */
|
||||||
|
- p = PY_SSL_ERROR_SYSCALL;
|
||||||
|
- type = state->PySSLSyscallErrorObject;
|
||||||
|
- errstr = "Some I/O error occurred";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ERR_GET_LIB(e) == ERR_LIB_SSL &&
|
||||||
|
@@ -1007,7 +995,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
|
||||||
|
err.ssl == SSL_ERROR_WANT_WRITE);
|
||||||
|
Py_XDECREF(sock);
|
||||||
|
if (ret < 1)
|
||||||
|
- return PySSL_SetError(self, ret, __FILE__, __LINE__);
|
||||||
|
+ return PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
if (PySSL_ChainExceptions(self) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
@@ -2424,7 +2412,7 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
|
||||||
|
|
||||||
|
Py_XDECREF(sock);
|
||||||
|
if (retval == 0)
|
||||||
|
- return PySSL_SetError(self, retval, __FILE__, __LINE__);
|
||||||
|
+ return PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
if (PySSL_ChainExceptions(self) < 0)
|
||||||
|
return NULL;
|
||||||
|
return PyLong_FromSize_t(count);
|
||||||
|
@@ -2454,7 +2442,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
|
||||||
|
self->err = err;
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
- return PySSL_SetError(self, count, __FILE__, __LINE__);
|
||||||
|
+ return PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
else
|
||||||
|
return PyLong_FromLong(count);
|
||||||
|
}
|
||||||
|
@@ -2577,7 +2565,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
|
||||||
|
err.ssl == SSL_ERROR_WANT_WRITE);
|
||||||
|
|
||||||
|
if (retval == 0) {
|
||||||
|
- PySSL_SetError(self, retval, __FILE__, __LINE__);
|
||||||
|
+ PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (self->exc != NULL)
|
||||||
|
@@ -2703,7 +2691,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
Py_XDECREF(sock);
|
||||||
|
- PySSL_SetError(self, ret, __FILE__, __LINE__);
|
||||||
|
+ PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (self->exc != NULL)
|
||||||
102
00464-enable-pac-and-bti-protections-for-aarch64.patch
Normal file
102
00464-enable-pac-and-bti-protections-for-aarch64.patch
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
||||||
|
Date: Tue, 3 Jun 2025 03:02:15 +0200
|
||||||
|
Subject: 00464: Enable PAC and BTI protections for aarch64
|
||||||
|
|
||||||
|
Apply protection against ROP/JOP attacks for aarch64 on asm_trampoline.S
|
||||||
|
|
||||||
|
The BTI flag must be applied in the assembler sources for this class
|
||||||
|
of attacks to be mitigated on newer aarch64 processors.
|
||||||
|
|
||||||
|
Upstream PR: https://github.com/python/cpython/pull/130864/files
|
||||||
|
|
||||||
|
The upstream patch is incomplete but only for the case where
|
||||||
|
frame pointers are not used on 3.13+.
|
||||||
|
|
||||||
|
Since on Fedora we always compile with frame pointers the BTI/PAC
|
||||||
|
hardware protections can be enabled without losing Perf unwinding.
|
||||||
|
---
|
||||||
|
Python/asm_trampoline.S | 4 +++
|
||||||
|
Python/asm_trampoline_aarch64.h | 50 +++++++++++++++++++++++++++++++++
|
||||||
|
2 files changed, 54 insertions(+)
|
||||||
|
create mode 100644 Python/asm_trampoline_aarch64.h
|
||||||
|
|
||||||
|
diff --git a/Python/asm_trampoline.S b/Python/asm_trampoline.S
|
||||||
|
index 341d0bbe51..ae882660b5 100644
|
||||||
|
--- a/Python/asm_trampoline.S
|
||||||
|
+++ b/Python/asm_trampoline.S
|
||||||
|
@@ -1,3 +1,5 @@
|
||||||
|
+#include "asm_trampoline_aarch64.h"
|
||||||
|
+
|
||||||
|
.text
|
||||||
|
.globl _Py_trampoline_func_start
|
||||||
|
# The following assembly is equivalent to:
|
||||||
|
@@ -20,10 +22,12 @@ _Py_trampoline_func_start:
|
||||||
|
#if defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__)
|
||||||
|
// ARM64 little endian, 64bit ABI
|
||||||
|
// generate with aarch64-linux-gnu-gcc 12.1
|
||||||
|
+ SIGN_LR
|
||||||
|
stp x29, x30, [sp, -16]!
|
||||||
|
mov x29, sp
|
||||||
|
blr x3
|
||||||
|
ldp x29, x30, [sp], 16
|
||||||
|
+ VERIFY_LR
|
||||||
|
ret
|
||||||
|
#endif
|
||||||
|
.globl _Py_trampoline_func_end
|
||||||
|
diff --git a/Python/asm_trampoline_aarch64.h b/Python/asm_trampoline_aarch64.h
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..4b0ec4a7dc
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Python/asm_trampoline_aarch64.h
|
||||||
|
@@ -0,0 +1,50 @@
|
||||||
|
+#ifndef ASM_TRAMPOLINE_AARCH_64_H_
|
||||||
|
+#define ASM_TRAMPOLINE_AARCH_64_H_
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * References:
|
||||||
|
+ * - https://developer.arm.com/documentation/101028/0012/5--Feature-test-macros
|
||||||
|
+ * - https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1
|
||||||
|
+ #define BTI_J hint 36 /* bti j: for jumps, IE br instructions */
|
||||||
|
+ #define BTI_C hint 34 /* bti c: for calls, IE bl instructions */
|
||||||
|
+ #define GNU_PROPERTY_AARCH64_BTI 1 /* bit 0 GNU Notes is for BTI support */
|
||||||
|
+#else
|
||||||
|
+ #define BTI_J
|
||||||
|
+ #define BTI_C
|
||||||
|
+ #define GNU_PROPERTY_AARCH64_BTI 0
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#if defined(__ARM_FEATURE_PAC_DEFAULT)
|
||||||
|
+ #if __ARM_FEATURE_PAC_DEFAULT & 1
|
||||||
|
+ #define SIGN_LR hint 25 /* paciasp: sign with the A key */
|
||||||
|
+ #define VERIFY_LR hint 29 /* autiasp: verify with the A key */
|
||||||
|
+ #elif __ARM_FEATURE_PAC_DEFAULT & 2
|
||||||
|
+ #define SIGN_LR hint 27 /* pacibsp: sign with the b key */
|
||||||
|
+ #define VERIFY_LR hint 31 /* autibsp: verify with the b key */
|
||||||
|
+ #endif
|
||||||
|
+ #define GNU_PROPERTY_AARCH64_POINTER_AUTH 2 /* bit 1 GNU Notes is for PAC support */
|
||||||
|
+#else
|
||||||
|
+ #define SIGN_LR BTI_C
|
||||||
|
+ #define VERIFY_LR
|
||||||
|
+ #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+/* Add the BTI and PAC support to GNU Notes section */
|
||||||
|
+#if GNU_PROPERTY_AARCH64_BTI != 0 || GNU_PROPERTY_AARCH64_POINTER_AUTH != 0
|
||||||
|
+ .pushsection .note.gnu.property, "a"; /* Start a new allocatable section */
|
||||||
|
+ .balign 8; /* align it on a byte boundry */
|
||||||
|
+ .long 4; /* size of "GNU\0" */
|
||||||
|
+ .long 0x10; /* size of descriptor */
|
||||||
|
+ .long 0x5; /* NT_GNU_PROPERTY_TYPE_0 */
|
||||||
|
+ .asciz "GNU";
|
||||||
|
+ .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */
|
||||||
|
+ .long 4; /* Four bytes of data */
|
||||||
|
+ .long (GNU_PROPERTY_AARCH64_BTI|GNU_PROPERTY_AARCH64_POINTER_AUTH); /* BTI or PAC is enabled */
|
||||||
|
+ .long 0; /* padding for 8 byte alignment */
|
||||||
|
+ .popsection; /* end the section */
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#endif
|
||||||
139
00471-cve-2025-12084.patch
Normal file
139
00471-cve-2025-12084.patch
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Mon, 22 Dec 2025 14:48:49 +0100
|
||||||
|
Subject: 00471: CVE-2025-12084
|
||||||
|
|
||||||
|
* gh-142145: Remove quadratic behavior in node ID cache clearing (GH-142146)
|
||||||
|
* gh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (GH-142794)
|
||||||
|
(cherry picked from commit 1cc7551b3f9f71efbc88d96dce90f82de98b2454)
|
||||||
|
(cherry picked from commit 08d8e18ad81cd45bc4a27d6da478b51ea49486e4)
|
||||||
|
(cherry picked from commit 8d2d7bb2e754f8649a68ce4116271a4932f76907)
|
||||||
|
|
||||||
|
Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>
|
||||||
|
Co-authored-by: Seth Michael Larson <seth@python.org>
|
||||||
|
Co-authored-by: Petr Viktorin <encukou@gmail.com>
|
||||||
|
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
|
||||||
|
Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
|
||||||
|
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
|
||||||
|
Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
|
||||||
|
Co-authored-by: Gregory P. Smith <greg@krypto.org>
|
||||||
|
---
|
||||||
|
Lib/test/test_minidom.py | 33 ++++++++++++++++++-
|
||||||
|
Lib/xml/dom/minidom.py | 11 ++-----
|
||||||
|
...-12-01-09-36-45.gh-issue-142145.tcAUhg.rst | 6 ++++
|
||||||
|
3 files changed, 41 insertions(+), 9 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py
|
||||||
|
index 699265ccad..ab4823c831 100644
|
||||||
|
--- a/Lib/test/test_minidom.py
|
||||||
|
+++ b/Lib/test/test_minidom.py
|
||||||
|
@@ -2,13 +2,14 @@
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import pickle
|
||||||
|
+import time
|
||||||
|
import io
|
||||||
|
from test import support
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import xml.dom.minidom
|
||||||
|
|
||||||
|
-from xml.dom.minidom import parse, Attr, Node, Document, parseString
|
||||||
|
+from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString
|
||||||
|
from xml.dom.minidom import getDOMImplementation
|
||||||
|
from xml.parsers.expat import ExpatError
|
||||||
|
|
||||||
|
@@ -176,6 +177,36 @@ def testAppendChild(self):
|
||||||
|
self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
|
||||||
|
dom.unlink()
|
||||||
|
|
||||||
|
+ @support.requires_resource('cpu')
|
||||||
|
+ def testAppendChildNoQuadraticComplexity(self):
|
||||||
|
+ impl = getDOMImplementation()
|
||||||
|
+
|
||||||
|
+ newdoc = impl.createDocument(None, "some_tag", None)
|
||||||
|
+ top_element = newdoc.documentElement
|
||||||
|
+ children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)]
|
||||||
|
+ element = top_element
|
||||||
|
+
|
||||||
|
+ start = time.monotonic()
|
||||||
|
+ for child in children:
|
||||||
|
+ element.appendChild(child)
|
||||||
|
+ element = child
|
||||||
|
+ end = time.monotonic()
|
||||||
|
+
|
||||||
|
+ # This example used to take at least 30 seconds.
|
||||||
|
+ # Conservative assertion due to the wide variety of systems and
|
||||||
|
+ # build configs timing based tests wind up run under.
|
||||||
|
+ # A --with-address-sanitizer --with-pydebug build on a rpi5 still
|
||||||
|
+ # completes this loop in <0.5 seconds.
|
||||||
|
+ self.assertLess(end - start, 4)
|
||||||
|
+
|
||||||
|
+ def testSetAttributeNodeWithoutOwnerDocument(self):
|
||||||
|
+ # regression test for gh-142754
|
||||||
|
+ elem = Element("test")
|
||||||
|
+ attr = Attr("id")
|
||||||
|
+ attr.value = "test-id"
|
||||||
|
+ elem.setAttributeNode(attr)
|
||||||
|
+ self.assertEqual(elem.getAttribute("id"), "test-id")
|
||||||
|
+
|
||||||
|
def testAppendChildFragment(self):
|
||||||
|
dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
|
||||||
|
dom.documentElement.appendChild(frag)
|
||||||
|
diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py
|
||||||
|
index ef8a159833..cada981f39 100644
|
||||||
|
--- a/Lib/xml/dom/minidom.py
|
||||||
|
+++ b/Lib/xml/dom/minidom.py
|
||||||
|
@@ -292,13 +292,6 @@ def _append_child(self, node):
|
||||||
|
childNodes.append(node)
|
||||||
|
node.parentNode = self
|
||||||
|
|
||||||
|
-def _in_document(node):
|
||||||
|
- # return True iff node is part of a document tree
|
||||||
|
- while node is not None:
|
||||||
|
- if node.nodeType == Node.DOCUMENT_NODE:
|
||||||
|
- return True
|
||||||
|
- node = node.parentNode
|
||||||
|
- return False
|
||||||
|
|
||||||
|
def _write_data(writer, data):
|
||||||
|
"Writes datachars to writer."
|
||||||
|
@@ -355,6 +348,7 @@ class Attr(Node):
|
||||||
|
def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
|
||||||
|
prefix=None):
|
||||||
|
self.ownerElement = None
|
||||||
|
+ self.ownerDocument = None
|
||||||
|
self._name = qName
|
||||||
|
self.namespaceURI = namespaceURI
|
||||||
|
self._prefix = prefix
|
||||||
|
@@ -680,6 +674,7 @@ class Element(Node):
|
||||||
|
|
||||||
|
def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
|
||||||
|
localName=None):
|
||||||
|
+ self.ownerDocument = None
|
||||||
|
self.parentNode = None
|
||||||
|
self.tagName = self.nodeName = tagName
|
||||||
|
self.prefix = prefix
|
||||||
|
@@ -1539,7 +1534,7 @@ def _clear_id_cache(node):
|
||||||
|
if node.nodeType == Node.DOCUMENT_NODE:
|
||||||
|
node._id_cache.clear()
|
||||||
|
node._id_search_stack = None
|
||||||
|
- elif _in_document(node):
|
||||||
|
+ elif node.ownerDocument:
|
||||||
|
node.ownerDocument._id_cache.clear()
|
||||||
|
node.ownerDocument._id_search_stack= None
|
||||||
|
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst b/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..05c7df35d1
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
|
||||||
|
@@ -0,0 +1,6 @@
|
||||||
|
+Remove quadratic behavior in ``xml.minidom`` node ID cache clearing. In order
|
||||||
|
+to do this without breaking existing users, we also add the *ownerDocument*
|
||||||
|
+attribute to :mod:`xml.dom.minidom` elements and attributes created by directly
|
||||||
|
+instantiating the ``Element`` or ``Attr`` class. Note that this way of creating
|
||||||
|
+nodes is not supported; creator functions like
|
||||||
|
+:py:meth:`xml.dom.Document.documentElement` should be used instead.
|
||||||
159
00472-cve-2025-13836.patch
Normal file
159
00472-cve-2025-13836.patch
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Mon, 22 Dec 2025 14:50:18 +0100
|
||||||
|
Subject: 00472: CVE-2025-13836
|
||||||
|
|
||||||
|
[3.12] gh-119451: Fix a potential denial of service in http.client (GH-119454) (#142140)
|
||||||
|
|
||||||
|
gh-119451: Fix a potential denial of service in http.client (GH-119454)
|
||||||
|
|
||||||
|
Reading the whole body of the HTTP response could cause OOM if
|
||||||
|
the Content-Length value is too large even if the server does not send
|
||||||
|
a large amount of data. Now the HTTP client reads large data by chunks,
|
||||||
|
therefore the amount of consumed memory is proportional to the amount
|
||||||
|
of sent data.
|
||||||
|
(cherry picked from commit 5a4c4a033a4a54481be6870aa1896fad732555b5)
|
||||||
|
|
||||||
|
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
---
|
||||||
|
Lib/http/client.py | 28 ++++++--
|
||||||
|
Lib/test/test_httplib.py | 66 +++++++++++++++++++
|
||||||
|
...-05-23-11-47-48.gh-issue-119451.qkJe9-.rst | 5 ++
|
||||||
|
3 files changed, 95 insertions(+), 4 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/http/client.py b/Lib/http/client.py
|
||||||
|
index fb29923d94..70451d67d4 100644
|
||||||
|
--- a/Lib/http/client.py
|
||||||
|
+++ b/Lib/http/client.py
|
||||||
|
@@ -111,6 +111,11 @@
|
||||||
|
_MAXLINE = 65536
|
||||||
|
_MAXHEADERS = 100
|
||||||
|
|
||||||
|
+# Data larger than this will be read in chunks, to prevent extreme
|
||||||
|
+# overallocation.
|
||||||
|
+_MIN_READ_BUF_SIZE = 1 << 20
|
||||||
|
+
|
||||||
|
+
|
||||||
|
# Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2)
|
||||||
|
#
|
||||||
|
# VCHAR = %x21-7E
|
||||||
|
@@ -639,10 +644,25 @@ def _safe_read(self, amt):
|
||||||
|
reading. If the bytes are truly not available (due to EOF), then the
|
||||||
|
IncompleteRead exception can be used to detect the problem.
|
||||||
|
"""
|
||||||
|
- data = self.fp.read(amt)
|
||||||
|
- if len(data) < amt:
|
||||||
|
- raise IncompleteRead(data, amt-len(data))
|
||||||
|
- return data
|
||||||
|
+ cursize = min(amt, _MIN_READ_BUF_SIZE)
|
||||||
|
+ data = self.fp.read(cursize)
|
||||||
|
+ if len(data) >= amt:
|
||||||
|
+ return data
|
||||||
|
+ if len(data) < cursize:
|
||||||
|
+ raise IncompleteRead(data, amt - len(data))
|
||||||
|
+
|
||||||
|
+ data = io.BytesIO(data)
|
||||||
|
+ data.seek(0, 2)
|
||||||
|
+ while True:
|
||||||
|
+ # This is a geometric increase in read size (never more than
|
||||||
|
+ # doubling out the current length of data per loop iteration).
|
||||||
|
+ delta = min(cursize, amt - cursize)
|
||||||
|
+ data.write(self.fp.read(delta))
|
||||||
|
+ if data.tell() >= amt:
|
||||||
|
+ return data.getvalue()
|
||||||
|
+ cursize += delta
|
||||||
|
+ if data.tell() < cursize:
|
||||||
|
+ raise IncompleteRead(data.getvalue(), amt - data.tell())
|
||||||
|
|
||||||
|
def _safe_readinto(self, b):
|
||||||
|
"""Same as _safe_read, but for reading into a buffer."""
|
||||||
|
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
|
||||||
|
index 01f5a10190..e46dac0077 100644
|
||||||
|
--- a/Lib/test/test_httplib.py
|
||||||
|
+++ b/Lib/test/test_httplib.py
|
||||||
|
@@ -1452,6 +1452,72 @@ def run_server():
|
||||||
|
thread.join()
|
||||||
|
self.assertEqual(result, b"proxied data\n")
|
||||||
|
|
||||||
|
+ def test_large_content_length(self):
|
||||||
|
+ serv = socket.create_server((HOST, 0))
|
||||||
|
+ self.addCleanup(serv.close)
|
||||||
|
+
|
||||||
|
+ def run_server():
|
||||||
|
+ [conn, address] = serv.accept()
|
||||||
|
+ with conn:
|
||||||
|
+ while conn.recv(1024):
|
||||||
|
+ conn.sendall(
|
||||||
|
+ b"HTTP/1.1 200 Ok\r\n"
|
||||||
|
+ b"Content-Length: %d\r\n"
|
||||||
|
+ b"\r\n" % size)
|
||||||
|
+ conn.sendall(b'A' * (size//3))
|
||||||
|
+ conn.sendall(b'B' * (size - size//3))
|
||||||
|
+
|
||||||
|
+ thread = threading.Thread(target=run_server)
|
||||||
|
+ thread.start()
|
||||||
|
+ self.addCleanup(thread.join, 1.0)
|
||||||
|
+
|
||||||
|
+ conn = client.HTTPConnection(*serv.getsockname())
|
||||||
|
+ try:
|
||||||
|
+ for w in range(15, 27):
|
||||||
|
+ size = 1 << w
|
||||||
|
+ conn.request("GET", "/")
|
||||||
|
+ with conn.getresponse() as response:
|
||||||
|
+ self.assertEqual(len(response.read()), size)
|
||||||
|
+ finally:
|
||||||
|
+ conn.close()
|
||||||
|
+ thread.join(1.0)
|
||||||
|
+
|
||||||
|
+ def test_large_content_length_truncated(self):
|
||||||
|
+ serv = socket.create_server((HOST, 0))
|
||||||
|
+ self.addCleanup(serv.close)
|
||||||
|
+
|
||||||
|
+ def run_server():
|
||||||
|
+ while True:
|
||||||
|
+ [conn, address] = serv.accept()
|
||||||
|
+ with conn:
|
||||||
|
+ conn.recv(1024)
|
||||||
|
+ if not size:
|
||||||
|
+ break
|
||||||
|
+ conn.sendall(
|
||||||
|
+ b"HTTP/1.1 200 Ok\r\n"
|
||||||
|
+ b"Content-Length: %d\r\n"
|
||||||
|
+ b"\r\n"
|
||||||
|
+ b"Text" % size)
|
||||||
|
+
|
||||||
|
+ thread = threading.Thread(target=run_server)
|
||||||
|
+ thread.start()
|
||||||
|
+ self.addCleanup(thread.join, 1.0)
|
||||||
|
+
|
||||||
|
+ conn = client.HTTPConnection(*serv.getsockname())
|
||||||
|
+ try:
|
||||||
|
+ for w in range(18, 65):
|
||||||
|
+ size = 1 << w
|
||||||
|
+ conn.request("GET", "/")
|
||||||
|
+ with conn.getresponse() as response:
|
||||||
|
+ self.assertRaises(client.IncompleteRead, response.read)
|
||||||
|
+ conn.close()
|
||||||
|
+ finally:
|
||||||
|
+ conn.close()
|
||||||
|
+ size = 0
|
||||||
|
+ conn.request("GET", "/")
|
||||||
|
+ conn.close()
|
||||||
|
+ thread.join(1.0)
|
||||||
|
+
|
||||||
|
def test_putrequest_override_domain_validation(self):
|
||||||
|
"""
|
||||||
|
It should be possible to override the default validation
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst b/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..6d6f25cd2f
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
|
||||||
|
@@ -0,0 +1,5 @@
|
||||||
|
+Fix a potential memory denial of service in the :mod:`http.client` module.
|
||||||
|
+When connecting to a malicious server, it could cause
|
||||||
|
+an arbitrary amount of memory to be allocated.
|
||||||
|
+This could have led to symptoms including a :exc:`MemoryError`, swapping, out
|
||||||
|
+of memory (OOM) killed processes or containers, or even system crashes.
|
||||||
90
00473-cve-2026-0865.patch
Normal file
90
00473-cve-2026-0865.patch
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Seth Michael Larson <seth@python.org>
|
||||||
|
Date: Sat, 17 Jan 2026 11:46:21 -0600
|
||||||
|
Subject: 00473: CVE-2026-0865
|
||||||
|
|
||||||
|
gh-143916: Reject control characters in wsgiref.headers.Headers (GH-143917)
|
||||||
|
|
||||||
|
* Add 'test.support' fixture for C0 control characters
|
||||||
|
* gh-143916: Reject control characters in wsgiref.headers.Headers
|
||||||
|
---
|
||||||
|
Lib/test/support/__init__.py | 7 +++++++
|
||||||
|
Lib/test/test_wsgiref.py | 12 +++++++++++-
|
||||||
|
Lib/wsgiref/headers.py | 3 +++
|
||||||
|
.../2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst | 2 ++
|
||||||
|
4 files changed, 23 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
|
||||||
|
index 4c42234ccc..26c0af4b13 100644
|
||||||
|
--- a/Lib/test/support/__init__.py
|
||||||
|
+++ b/Lib/test/support/__init__.py
|
||||||
|
@@ -2599,3 +2599,10 @@ def __iter__(self):
|
||||||
|
if self.iter_raises:
|
||||||
|
1/0
|
||||||
|
return self
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def control_characters_c0() -> list[str]:
|
||||||
|
+ """Returns a list of C0 control characters as strings.
|
||||||
|
+ C0 control characters defined as the byte range 0x00-0x1F, and 0x7F.
|
||||||
|
+ """
|
||||||
|
+ return [chr(c) for c in range(0x00, 0x20)] + ["\x7F"]
|
||||||
|
diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py
|
||||||
|
index 9316d0ecbc..28e3656632 100644
|
||||||
|
--- a/Lib/test/test_wsgiref.py
|
||||||
|
+++ b/Lib/test/test_wsgiref.py
|
||||||
|
@@ -1,6 +1,6 @@
|
||||||
|
from unittest import mock
|
||||||
|
from test import support
|
||||||
|
-from test.support import socket_helper
|
||||||
|
+from test.support import socket_helper, control_characters_c0
|
||||||
|
from test.test_httpservers import NoLogRequestHandler
|
||||||
|
from unittest import TestCase
|
||||||
|
from wsgiref.util import setup_testing_defaults
|
||||||
|
@@ -503,6 +503,16 @@ def testExtras(self):
|
||||||
|
'\r\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
+ def testRaisesControlCharacters(self):
|
||||||
|
+ headers = Headers()
|
||||||
|
+ for c0 in control_characters_c0():
|
||||||
|
+ self.assertRaises(ValueError, headers.__setitem__, f"key{c0}", "val")
|
||||||
|
+ self.assertRaises(ValueError, headers.__setitem__, "key", f"val{c0}")
|
||||||
|
+ self.assertRaises(ValueError, headers.add_header, f"key{c0}", "val", param="param")
|
||||||
|
+ self.assertRaises(ValueError, headers.add_header, "key", f"val{c0}", param="param")
|
||||||
|
+ self.assertRaises(ValueError, headers.add_header, "key", "val", param=f"param{c0}")
|
||||||
|
+
|
||||||
|
+
|
||||||
|
class ErrorHandler(BaseCGIHandler):
|
||||||
|
"""Simple handler subclass for testing BaseHandler"""
|
||||||
|
|
||||||
|
diff --git a/Lib/wsgiref/headers.py b/Lib/wsgiref/headers.py
|
||||||
|
index fab851c5a4..fd98e85d75 100644
|
||||||
|
--- a/Lib/wsgiref/headers.py
|
||||||
|
+++ b/Lib/wsgiref/headers.py
|
||||||
|
@@ -9,6 +9,7 @@
|
||||||
|
# existence of which force quoting of the parameter value.
|
||||||
|
import re
|
||||||
|
tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
|
||||||
|
+_control_chars_re = re.compile(r'[\x00-\x1F\x7F]')
|
||||||
|
|
||||||
|
def _formatparam(param, value=None, quote=1):
|
||||||
|
"""Convenience function to format and return a key=value pair.
|
||||||
|
@@ -41,6 +42,8 @@ def __init__(self, headers=None):
|
||||||
|
def _convert_string_type(self, value):
|
||||||
|
"""Convert/check value type."""
|
||||||
|
if type(value) is str:
|
||||||
|
+ if _control_chars_re.search(value):
|
||||||
|
+ raise ValueError("Control characters not allowed in headers")
|
||||||
|
return value
|
||||||
|
raise AssertionError("Header names/values must be"
|
||||||
|
" of type str (got {0})".format(repr(value)))
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..44bd0b2705
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+Reject C0 control characters within wsgiref.headers.Headers fields, values,
|
||||||
|
+and parameters.
|
||||||
61
00474-cve-2025-15366.patch
Normal file
61
00474-cve-2025-15366.patch
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Seth Michael Larson <seth@python.org>
|
||||||
|
Date: Tue, 20 Jan 2026 14:45:42 -0600
|
||||||
|
Subject: 00474: CVE-2025-15366
|
||||||
|
|
||||||
|
gh-143921: Reject control characters in IMAP commands
|
||||||
|
|
||||||
|
(cherry-picked from commit 6262704b134db2a4ba12e85ecfbd968534f28b45)
|
||||||
|
---
|
||||||
|
Lib/imaplib.py | 4 +++-
|
||||||
|
Lib/test/test_imaplib.py | 6 ++++++
|
||||||
|
.../Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst | 1 +
|
||||||
|
3 files changed, 10 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
|
||||||
|
index e337fe6471..c7f44f05b1 100644
|
||||||
|
--- a/Lib/imaplib.py
|
||||||
|
+++ b/Lib/imaplib.py
|
||||||
|
@@ -132,7 +132,7 @@
|
||||||
|
# We compile these in _mode_xxx.
|
||||||
|
_Literal = br'.*{(?P<size>\d+)}$'
|
||||||
|
_Untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'
|
||||||
|
-
|
||||||
|
+_control_chars = re.compile(b'[\x00-\x1F\x7F]')
|
||||||
|
|
||||||
|
|
||||||
|
class IMAP4:
|
||||||
|
@@ -994,6 +994,8 @@ def _command(self, name, *args):
|
||||||
|
if arg is None: continue
|
||||||
|
if isinstance(arg, str):
|
||||||
|
arg = bytes(arg, self._encoding)
|
||||||
|
+ if _control_chars.search(arg):
|
||||||
|
+ raise ValueError("Control characters not allowed in commands")
|
||||||
|
data = data + b' ' + arg
|
||||||
|
|
||||||
|
literal = self.literal
|
||||||
|
diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py
|
||||||
|
index 4429a90050..73c25bc733 100644
|
||||||
|
--- a/Lib/test/test_imaplib.py
|
||||||
|
+++ b/Lib/test/test_imaplib.py
|
||||||
|
@@ -504,6 +504,12 @@ def test_login(self):
|
||||||
|
self.assertEqual(data[0], b'LOGIN completed')
|
||||||
|
self.assertEqual(client.state, 'AUTH')
|
||||||
|
|
||||||
|
+ def test_control_characters(self):
|
||||||
|
+ client, _ = self._setup(SimpleIMAPHandler)
|
||||||
|
+ for c0 in support.control_characters_c0():
|
||||||
|
+ with self.assertRaises(ValueError):
|
||||||
|
+ client.login(f'user{c0}', 'pass')
|
||||||
|
+
|
||||||
|
def test_logout(self):
|
||||||
|
client, _ = self._setup(SimpleIMAPHandler)
|
||||||
|
typ, data = client.login('user', 'pass')
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..4e13fe92bc
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Reject control characters in IMAP commands.
|
||||||
61
00475-cve-2025-15367.patch
Normal file
61
00475-cve-2025-15367.patch
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Seth Michael Larson <seth@python.org>
|
||||||
|
Date: Tue, 20 Jan 2026 14:46:32 -0600
|
||||||
|
Subject: 00475: CVE-2025-15367
|
||||||
|
|
||||||
|
gh-143923: Reject control characters in POP3 commands
|
||||||
|
|
||||||
|
(cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7)
|
||||||
|
---
|
||||||
|
Lib/poplib.py | 2 ++
|
||||||
|
Lib/test/test_poplib.py | 8 ++++++++
|
||||||
|
.../2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst | 1 +
|
||||||
|
3 files changed, 11 insertions(+)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/poplib.py b/Lib/poplib.py
|
||||||
|
index 9eb662d000..5c83522504 100644
|
||||||
|
--- a/Lib/poplib.py
|
||||||
|
+++ b/Lib/poplib.py
|
||||||
|
@@ -122,6 +122,8 @@ def _putline(self, line):
|
||||||
|
def _putcmd(self, line):
|
||||||
|
if self._debugging: print('*cmd*', repr(line))
|
||||||
|
line = bytes(line, self.encoding)
|
||||||
|
+ if re.search(b'[\x00-\x1F\x7F]', line):
|
||||||
|
+ raise ValueError('Control characters not allowed in commands')
|
||||||
|
self._putline(line)
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py
|
||||||
|
index f1ebbeafe0..50d8c255d6 100644
|
||||||
|
--- a/Lib/test/test_poplib.py
|
||||||
|
+++ b/Lib/test/test_poplib.py
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
import unittest
|
||||||
|
from unittest import TestCase, skipUnless
|
||||||
|
from test import support as test_support
|
||||||
|
+from test.support import control_characters_c0
|
||||||
|
from test.support import hashlib_helper
|
||||||
|
from test.support import socket_helper
|
||||||
|
from test.support import threading_helper
|
||||||
|
@@ -395,6 +396,13 @@ def test_quit(self):
|
||||||
|
self.assertIsNone(self.client.sock)
|
||||||
|
self.assertIsNone(self.client.file)
|
||||||
|
|
||||||
|
+ def test_control_characters(self):
|
||||||
|
+ for c0 in control_characters_c0():
|
||||||
|
+ with self.assertRaises(ValueError):
|
||||||
|
+ self.client.user(f'user{c0}')
|
||||||
|
+ with self.assertRaises(ValueError):
|
||||||
|
+ self.client.pass_(f'{c0}pass')
|
||||||
|
+
|
||||||
|
@requires_ssl
|
||||||
|
def test_stls_capa(self):
|
||||||
|
capa = self.client.capa()
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..3cde4df3e0
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Reject control characters in POP3 commands.
|
||||||
110
00476-cve-2026-1299.patch
Normal file
110
00476-cve-2026-1299.patch
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Fri, 13 Feb 2026 17:04:54 +0100
|
||||||
|
Subject: [PATCH] [3.12] gh-144125: email: verify headers are sound in
|
||||||
|
BytesGenerator
|
||||||
|
|
||||||
|
gh-144125: email: verify headers are sound in BytesGenerator
|
||||||
|
(cherry picked from commit 052e55e7d44718fe46cbba0ca995cb8fcc359413)
|
||||||
|
|
||||||
|
Co-authored-by: Seth Michael Larson <seth@python.org>
|
||||||
|
Co-authored-by: Denis Ledoux <dle@odoo.com>
|
||||||
|
Co-authored-by: Denis Ledoux <5822488+beledouxdenis@users.noreply.github.com>
|
||||||
|
Co-authored-by: Petr Viktorin <302922+encukou@users.noreply.github.com>
|
||||||
|
Co-authored-by: Bas Bloemsaat <1586868+basbloemsaat@users.noreply.github.com>
|
||||||
|
Co-authored-by: Petr Viktorin <encukou@gmail.com>
|
||||||
|
---
|
||||||
|
Lib/email/generator.py | 12 +++++++++++-
|
||||||
|
Lib/test/test_email/test_generator.py | 4 +++-
|
||||||
|
Lib/test/test_email/test_policy.py | 6 +++++-
|
||||||
|
.../2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst | 4 ++++
|
||||||
|
4 files changed, 23 insertions(+), 3 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/email/generator.py b/Lib/email/generator.py
|
||||||
|
index 47b9df8f4e6090..8cbc43ef5bc647 100644
|
||||||
|
--- a/Lib/email/generator.py
|
||||||
|
+++ b/Lib/email/generator.py
|
||||||
|
@@ -22,6 +22,7 @@
|
||||||
|
NLCRE = re.compile(r'\r\n|\r|\n')
|
||||||
|
fcre = re.compile(r'^From ', re.MULTILINE)
|
||||||
|
NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
|
||||||
|
+NEWLINE_WITHOUT_FWSP_BYTES = re.compile(br'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
|
||||||
|
|
||||||
|
|
||||||
|
class Generator:
|
||||||
|
@@ -429,7 +430,16 @@ def _write_headers(self, msg):
|
||||||
|
# This is almost the same as the string version, except for handling
|
||||||
|
# strings with 8bit bytes.
|
||||||
|
for h, v in msg.raw_items():
|
||||||
|
- self._fp.write(self.policy.fold_binary(h, v))
|
||||||
|
+ folded = self.policy.fold_binary(h, v)
|
||||||
|
+ if self.policy.verify_generated_headers:
|
||||||
|
+ linesep = self.policy.linesep.encode()
|
||||||
|
+ if not folded.endswith(linesep):
|
||||||
|
+ raise HeaderWriteError(
|
||||||
|
+ f'folded header does not end with {linesep!r}: {folded!r}')
|
||||||
|
+ if NEWLINE_WITHOUT_FWSP_BYTES.search(folded.removesuffix(linesep)):
|
||||||
|
+ raise HeaderWriteError(
|
||||||
|
+ f'folded header contains newline: {folded!r}')
|
||||||
|
+ self._fp.write(folded)
|
||||||
|
# A blank line always separates headers from body
|
||||||
|
self.write(self._NL)
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py
|
||||||
|
index c75a842c33578e..3ca79edf6a65d9 100644
|
||||||
|
--- a/Lib/test/test_email/test_generator.py
|
||||||
|
+++ b/Lib/test/test_email/test_generator.py
|
||||||
|
@@ -313,7 +313,7 @@ def test_flatten_unicode_linesep(self):
|
||||||
|
self.assertEqual(s.getvalue(), self.typ(expected))
|
||||||
|
|
||||||
|
def test_verify_generated_headers(self):
|
||||||
|
- """gh-121650: by default the generator prevents header injection"""
|
||||||
|
+ # gh-121650: by default the generator prevents header injection
|
||||||
|
class LiteralHeader(str):
|
||||||
|
name = 'Header'
|
||||||
|
def fold(self, **kwargs):
|
||||||
|
@@ -334,6 +334,8 @@ def fold(self, **kwargs):
|
||||||
|
|
||||||
|
with self.assertRaises(email.errors.HeaderWriteError):
|
||||||
|
message.as_string()
|
||||||
|
+ with self.assertRaises(email.errors.HeaderWriteError):
|
||||||
|
+ message.as_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
class TestBytesGenerator(TestGeneratorBase, TestEmailBase):
|
||||||
|
diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py
|
||||||
|
index baa35fd68e49c5..71ec0febb0fd86 100644
|
||||||
|
--- a/Lib/test/test_email/test_policy.py
|
||||||
|
+++ b/Lib/test/test_email/test_policy.py
|
||||||
|
@@ -296,7 +296,7 @@ def test_short_maxlen_error(self):
|
||||||
|
policy.fold("Subject", subject)
|
||||||
|
|
||||||
|
def test_verify_generated_headers(self):
|
||||||
|
- """Turning protection off allows header injection"""
|
||||||
|
+ # Turning protection off allows header injection
|
||||||
|
policy = email.policy.default.clone(verify_generated_headers=False)
|
||||||
|
for text in (
|
||||||
|
'Header: Value\r\nBad: Injection\r\n',
|
||||||
|
@@ -319,6 +319,10 @@ def fold(self, **kwargs):
|
||||||
|
message.as_string(),
|
||||||
|
f"{text}\nBody",
|
||||||
|
)
|
||||||
|
+ self.assertEqual(
|
||||||
|
+ message.as_bytes(),
|
||||||
|
+ f"{text}\nBody".encode(),
|
||||||
|
+ )
|
||||||
|
|
||||||
|
# XXX: Need subclassing tests.
|
||||||
|
# For adding subclassed objects, make sure the usual rules apply (subclass
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst b/Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000000000..e6333e724972c5
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst
|
||||||
|
@@ -0,0 +1,4 @@
|
||||||
|
+:mod:`~email.generator.BytesGenerator` will now refuse to serialize (write) headers
|
||||||
|
+that are unsafely folded or delimited; see
|
||||||
|
+:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas
|
||||||
|
+Bloemsaat and Petr Viktorin in :gh:`121650`).
|
||||||
18
Python-3.12.12.tar.xz.asc
Normal file
18
Python-3.12.12.tar.xz.asc
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQKTBAABCgB9FiEEcWlgX2LHUTVtBUomqCHmgOX6YwUFAmjnnr1fFIAAAAAALgAo
|
||||||
|
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDcx
|
||||||
|
Njk2MDVGNjJDNzUxMzU2RDA1NEEyNkE4MjFFNjgwRTVGQTYzMDUACgkQqCHmgOX6
|
||||||
|
YwXF3Q//VrreGa+P8lvp9UMjoj/YquKPwLqjzzAWf5vzHipkebdiESsB1HfGu04k
|
||||||
|
Jw+ctTnXHf/12u0W7ijv+56JtcJFqEzh8yGokWqOzc99rpCeCY9qtuwaVYtZrTNx
|
||||||
|
wepRaDAHdhP4Z2kLPDiE6pCXu2NIR5wHqHjQ8JGmprhASc07uxEhNN/gucVR2Sbr
|
||||||
|
cCfC9rHfHkdhoPpZRRbcraAaxPGL3VyBXf7HuYbHhf4GuF9EVDlFg5I0BzHCKJDd
|
||||||
|
ebPXYHvsoDgrMMqPXiX/YkGNByf3Ze6KZTNSGICy8SDzIzZgpmtOe5rzvlOXJBZZ
|
||||||
|
SVfX8SqP4Ufml+MfJrGEx30S9reYYvnyTSmttpbDznonROKPEZOuDt08+CG3yR+T
|
||||||
|
o5RdIneWmGXRf1mBrFKH9Br5tfOd+YeldfxdoQgla2fFHFVRnab1lsZFOC/HZ5z2
|
||||||
|
Q3rPfVMDYKO8yoIKqv0BUzlkn9wYphCWoPHq0Y+SGjcP+Zh5qRTMqZYIaGekhWmx
|
||||||
|
86egHHVqedMI0Q9hvgIEirupVJ1q34FZn2+3sEka9hdOie9aNHXWTmgWCGDm46qj
|
||||||
|
qC9tT/jkMzWIY2Y4RdVDMdSCb7HkBEl1eAANq511gJ+eSWAXbP1sVrQoiAQY+EkC
|
||||||
|
Yu2ceZYsl9i6zm7i/QaU/mOGB7xMZhMQLZBnZTHSzAZo/pBN7y8=
|
||||||
|
=RuLK
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
@ -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-----
|
|
||||||
228
python3.12.spec
228
python3.12.spec
@ -13,11 +13,11 @@ URL: https://www.python.org/
|
|||||||
|
|
||||||
# WARNING When rebasing to a new Python version,
|
# WARNING When rebasing to a new Python version,
|
||||||
# remember to update the python3-docs package as well
|
# remember to update the python3-docs package as well
|
||||||
%global general_version %{pybasever}.5
|
%global general_version %{pybasever}.12
|
||||||
#global prerel ...
|
#global prerel ...
|
||||||
%global upstream_version %{general_version}%{?prerel}
|
%global upstream_version %{general_version}%{?prerel}
|
||||||
Version: %{general_version}%{?prerel:~%{prerel}}
|
Version: %{general_version}%{?prerel:~%{prerel}}
|
||||||
Release: 2%{?dist}
|
Release: 5%{?dist}
|
||||||
License: Python-2.0.1
|
License: Python-2.0.1
|
||||||
|
|
||||||
|
|
||||||
@ -71,49 +71,52 @@ License: Python-2.0.1
|
|||||||
# If the rpmwheels condition is disabled, we use the bundled wheel packages
|
# If the rpmwheels condition is disabled, we use the bundled wheel packages
|
||||||
# from Python with the versions below.
|
# from Python with the versions below.
|
||||||
# This needs to be manually updated when we update Python.
|
# This needs to be manually updated when we update Python.
|
||||||
%global pip_version 24.2
|
%global pip_version 25.0.1
|
||||||
%global setuptools_version 67.6.1
|
%global setuptools_version 79.0.1
|
||||||
%global wheel_version 0.40.0
|
%global wheel_version 0.40.0
|
||||||
# All of those also include a list of indirect bundled libs:
|
# All of those also include a list of indirect bundled libs:
|
||||||
# pip
|
# pip
|
||||||
# $ %%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/ensurepip/_bundled/pip-*.whl pip/_vendor/vendor.txt)
|
# $ %%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/ensurepip/_bundled/pip-*.whl pip/_vendor/vendor.txt)
|
||||||
%global pip_bundled_provides %{expand:
|
%global pip_bundled_provides %{expand:
|
||||||
Provides: bundled(python3dist(cachecontrol)) = 0.14
|
Provides: bundled(python3dist(cachecontrol)) = 0.14.1
|
||||||
Provides: bundled(python3dist(certifi)) = 2024.7.4
|
Provides: bundled(python3dist(certifi)) = 2024.8.30
|
||||||
Provides: bundled(python3dist(distlib)) = 0.3.8
|
Provides: bundled(python3dist(distlib)) = 0.3.9
|
||||||
Provides: bundled(python3dist(distro)) = 1.9
|
Provides: bundled(python3dist(distro)) = 1.9
|
||||||
Provides: bundled(python3dist(idna)) = 3.7
|
Provides: bundled(python3dist(idna)) = 3.10
|
||||||
Provides: bundled(python3dist(msgpack)) = 1.0.8
|
Provides: bundled(python3dist(msgpack)) = 1.1
|
||||||
Provides: bundled(python3dist(packaging)) = 24.1
|
Provides: bundled(python3dist(packaging)) = 24.2
|
||||||
Provides: bundled(python3dist(platformdirs)) = 4.2.2
|
Provides: bundled(python3dist(platformdirs)) = 4.3.6
|
||||||
Provides: bundled(python3dist(pygments)) = 2.18
|
Provides: bundled(python3dist(pygments)) = 2.18
|
||||||
Provides: bundled(python3dist(pyproject-hooks)) = 1
|
Provides: bundled(python3dist(pyproject-hooks)) = 1.2
|
||||||
Provides: bundled(python3dist(requests)) = 2.32.3
|
Provides: bundled(python3dist(requests)) = 2.32.3
|
||||||
Provides: bundled(python3dist(resolvelib)) = 1.0.1
|
Provides: bundled(python3dist(resolvelib)) = 1.0.1
|
||||||
Provides: bundled(python3dist(rich)) = 13.7.1
|
Provides: bundled(python3dist(rich)) = 13.9.4
|
||||||
Provides: bundled(python3dist(setuptools)) = 70.3
|
Provides: bundled(python3dist(setuptools)) = 70.3
|
||||||
Provides: bundled(python3dist(tomli)) = 2.0.1
|
Provides: bundled(python3dist(tomli)) = 2.2.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(typing-extensions)) = 4.12.2
|
||||||
Provides: bundled(python3dist(urllib3)) = 1.26.18
|
Provides: bundled(python3dist(urllib3)) = 1.26.20
|
||||||
}
|
}
|
||||||
# setuptools
|
# setuptools
|
||||||
# vendor.txt files not in .whl
|
# vendor.txt not in .whl
|
||||||
# $ %%{_rpmconfigdir}/pythonbundles.py \
|
# %%{_rpmconfigdir}/pythonbundles.py <(unzip -l Lib/test/wheeldata/setuptools-*.whl | grep -E '_vendor/.+dist-info/RECORD' | sed -E 's@^.*/([^-]+)-([^-]+)\.dist-info/.*$@\1==\2@')
|
||||||
# <(curl -L https://github.com/pypa/setuptools/raw/v%%{setuptools_version}/setuptools/_vendor/vendored.txt) \
|
|
||||||
# <(curl -L https://github.com/pypa/setuptools/raw/v%%{setuptools_version}/pkg_resources/_vendor/vendored.txt)
|
|
||||||
%global setuptools_bundled_provides %{expand:
|
%global setuptools_bundled_provides %{expand:
|
||||||
Provides: bundled(python3dist(importlib-metadata)) = 6
|
Provides: bundled(python3dist(autocommand)) = 2.2.2
|
||||||
Provides: bundled(python3dist(importlib-resources)) = 5.10.2
|
Provides: bundled(python3dist(backports-tarfile)) = 1.2
|
||||||
Provides: bundled(python3dist(jaraco-text)) = 3.7
|
Provides: bundled(python3dist(importlib-metadata)) = 8
|
||||||
Provides: bundled(python3dist(more-itertools)) = 8.8
|
Provides: bundled(python3dist(inflect)) = 7.3.1
|
||||||
Provides: bundled(python3dist(ordered-set)) = 3.1.1
|
Provides: bundled(python3dist(jaraco-collections)) = 5.1
|
||||||
Provides: bundled(python3dist(packaging)) = 23
|
Provides: bundled(python3dist(jaraco-context)) = 5.3
|
||||||
Provides: bundled(python3dist(platformdirs)) = 2.6.2
|
Provides: bundled(python3dist(jaraco-functools)) = 4.0.1
|
||||||
|
Provides: bundled(python3dist(jaraco-text)) = 3.12.1
|
||||||
|
Provides: bundled(python3dist(more-itertools)) = 10.3
|
||||||
|
Provides: bundled(python3dist(packaging)) = 24.2
|
||||||
|
Provides: bundled(python3dist(platformdirs)) = 4.2.2
|
||||||
Provides: bundled(python3dist(tomli)) = 2.0.1
|
Provides: bundled(python3dist(tomli)) = 2.0.1
|
||||||
Provides: bundled(python3dist(typing-extensions)) = 4.0.1
|
Provides: bundled(python3dist(typeguard)) = 4.3
|
||||||
Provides: bundled(python3dist(typing-extensions)) = 4.4
|
Provides: bundled(python3dist(typing-extensions)) = 4.12.2
|
||||||
Provides: bundled(python3dist(zipp)) = 3.7
|
Provides: bundled(python3dist(wheel)) = 0.45.1
|
||||||
|
Provides: bundled(python3dist(zipp)) = 3.19.2
|
||||||
}
|
}
|
||||||
# wheel
|
# wheel
|
||||||
# $ %%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/test/wheeldata/wheel-*.whl wheel/vendored/vendor.txt)
|
# $ %%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/test/wheeldata/wheel-*.whl wheel/vendored/vendor.txt)
|
||||||
@ -301,6 +304,7 @@ BuildRequires: valgrind-devel
|
|||||||
BuildRequires: xz-devel
|
BuildRequires: xz-devel
|
||||||
BuildRequires: zlib-devel
|
BuildRequires: zlib-devel
|
||||||
|
|
||||||
|
BuildRequires: systemtap-sdt-devel
|
||||||
BuildRequires: /usr/bin/dtrace
|
BuildRequires: /usr/bin/dtrace
|
||||||
|
|
||||||
# workaround http://bugs.python.org/issue19804 (test_uuid requires ifconfig)
|
# workaround http://bugs.python.org/issue19804 (test_uuid requires ifconfig)
|
||||||
@ -351,7 +355,7 @@ Source11: idle3.appdata.xml
|
|||||||
|
|
||||||
# (Patches taken from github.com/fedora-python/cpython)
|
# (Patches taken from github.com/fedora-python/cpython)
|
||||||
|
|
||||||
# 00251 # cae5a6abc5df08239c85b83e4e250b6f2702e4f5
|
# 00251 # 6a4ec74157aa01f1ada9f29f30a371cd9e5369e8
|
||||||
# Change user install location
|
# Change user install location
|
||||||
#
|
#
|
||||||
# Set values of base and platbase in sysconfig from /usr
|
# Set values of base and platbase in sysconfig from /usr
|
||||||
@ -400,18 +404,87 @@ Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-g
|
|||||||
# - https://access.redhat.com/articles/7004769
|
# - https://access.redhat.com/articles/7004769
|
||||||
Patch397: 00397-tarfile-filter.patch
|
Patch397: 00397-tarfile-filter.patch
|
||||||
|
|
||||||
# 00415 # 5b830b814be638d1a167802780b5f498a4a5e97c
|
# 00462 # 5324dc5f57e0068f7e4f7b2f20006e88ff5f4e47
|
||||||
# [CVE-2023-27043] gh-102988: Reject malformed addresses in email.parseaddr() (#111116)
|
# Fix PySSL_SetError handling SSL_ERROR_SYSCALL
|
||||||
#
|
#
|
||||||
# Detect email address parsing errors and return empty tuple to
|
# Python 3.10 changed from using SSL_write() and SSL_read() to SSL_write_ex() and
|
||||||
# indicate the parsing error (old API). Add an optional 'strict'
|
# SSL_read_ex(), but did not update handling of the return value.
|
||||||
# parameter to getaddresses() and parseaddr() functions. Patch by
|
#
|
||||||
# Thomas Dwyer.
|
# Change error handling so that the return value is not examined.
|
||||||
Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch
|
# OSError (not EOF) is now returned when retval is 0.
|
||||||
|
#
|
||||||
|
# This resolves the issue of failing tests when a system is
|
||||||
|
# stressed on OpenSSL 3.5.
|
||||||
|
Patch462: 00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch
|
||||||
|
|
||||||
# 00436 # c76cc2aa3a2c30375ade4859b732ada851cc89ed
|
# 00464 # 1c713e02a26bf8865bb6421749d19d0766cac178
|
||||||
# [CVE-2024-8088] gh-122905: Sanitize names in zipfile.Path.
|
# Enable PAC and BTI protections for aarch64
|
||||||
Patch436: 00436-cve-2024-8088-gh-122905-sanitize-names-in-zipfile-path.patch
|
#
|
||||||
|
# Apply protection against ROP/JOP attacks for aarch64 on asm_trampoline.S
|
||||||
|
#
|
||||||
|
# The BTI flag must be applied in the assembler sources for this class
|
||||||
|
# of attacks to be mitigated on newer aarch64 processors.
|
||||||
|
#
|
||||||
|
# Upstream PR: https://github.com/python/cpython/pull/130864/
|
||||||
|
#
|
||||||
|
# The upstream patch is incomplete but only for the case where
|
||||||
|
# frame pointers are not used on 3.13+.
|
||||||
|
#
|
||||||
|
# Since we don't utilize frame pointers on RHEL and CS, Perf profiling
|
||||||
|
# will not show the Python functions, irrespective of this patch.
|
||||||
|
Patch464: 00464-enable-pac-and-bti-protections-for-aarch64.patch
|
||||||
|
|
||||||
|
# 00471 # 37c05f26d11e8e24f2a760167015a267996b1d69
|
||||||
|
# CVE-2025-12084
|
||||||
|
#
|
||||||
|
# * gh-142145: Remove quadratic behavior in node ID cache clearing (GH-142146)
|
||||||
|
# * gh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (GH-142794)
|
||||||
|
Patch471: 00471-cve-2025-12084.patch
|
||||||
|
|
||||||
|
# 00472 # 2ba215eaba508b2cdd7c3acfdf3b9a6e32872274
|
||||||
|
# CVE-2025-13836
|
||||||
|
#
|
||||||
|
# [3.12] gh-119451: Fix a potential denial of service in http.client (GH-119454) (#142140)
|
||||||
|
#
|
||||||
|
# gh-119451: Fix a potential denial of service in http.client (GH-119454)
|
||||||
|
#
|
||||||
|
# Reading the whole body of the HTTP response could cause OOM if
|
||||||
|
# the Content-Length value is too large even if the server does not send
|
||||||
|
# a large amount of data. Now the HTTP client reads large data by chunks,
|
||||||
|
# therefore the amount of consumed memory is proportional to the amount
|
||||||
|
# of sent data.
|
||||||
|
Patch472: 00472-cve-2025-13836.patch
|
||||||
|
|
||||||
|
# 00473 # dd705786aa0c1ccfde913858598e34e1f196be2e
|
||||||
|
# CVE-2026-0865
|
||||||
|
#
|
||||||
|
# gh-143916: Reject control characters in wsgiref.headers.Headers (GH-143917)
|
||||||
|
#
|
||||||
|
# * Add 'test.support' fixture for C0 control characters
|
||||||
|
# * gh-143916: Reject control characters in wsgiref.headers.Headers
|
||||||
|
Patch473: 00473-cve-2026-0865.patch
|
||||||
|
|
||||||
|
# 00474 # 837ddca0372fa87ff9cee47142200caa21e77def
|
||||||
|
# CVE-2025-15366
|
||||||
|
#
|
||||||
|
# gh-143921: Reject control characters in IMAP commands
|
||||||
|
#
|
||||||
|
# (cherry-picked from commit 6262704b134db2a4ba12e85ecfbd968534f28b45)
|
||||||
|
Patch474: 00474-cve-2025-15366.patch
|
||||||
|
|
||||||
|
# 00475 # 3748209a316662d4e85981ca1a7418547a1d25c6
|
||||||
|
# CVE-2025-15367
|
||||||
|
#
|
||||||
|
# gh-143923: Reject control characters in POP3 commands
|
||||||
|
#
|
||||||
|
# (cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7)
|
||||||
|
Patch475: 00475-cve-2025-15367.patch
|
||||||
|
|
||||||
|
# 00476
|
||||||
|
# CVE-2026-1299
|
||||||
|
#
|
||||||
|
# gh-144125: email: verify headers are sound in BytesGenerator
|
||||||
|
Patch476: 00476-cve-2026-1299.patch
|
||||||
|
|
||||||
# (New patches go here ^^^)
|
# (New patches go here ^^^)
|
||||||
#
|
#
|
||||||
@ -761,6 +834,7 @@ The debug runtime additionally supports debug builds of C-API extensions
|
|||||||
if [ -f %{_rpmconfigdir}/pythonbundles.py ]; then
|
if [ -f %{_rpmconfigdir}/pythonbundles.py ]; then
|
||||||
%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/ensurepip/_bundled/pip-*.whl pip/_vendor/vendor.txt) --compare-with '%pip_bundled_provides'
|
%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/ensurepip/_bundled/pip-*.whl pip/_vendor/vendor.txt) --compare-with '%pip_bundled_provides'
|
||||||
%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/test/wheeldata/wheel-*.whl wheel/vendored/vendor.txt) --compare-with '%wheel_bundled_provides'
|
%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/test/wheeldata/wheel-*.whl wheel/vendored/vendor.txt) --compare-with '%wheel_bundled_provides'
|
||||||
|
%{_rpmconfigdir}/pythonbundles.py <(unzip -l Lib/test/wheeldata/setuptools-*.whl | grep -E '_vendor/.+dist-info/RECORD' | sed -E 's@^.*/([^-]+)-([^-]+)\.dist-info/.*$@\1==\2@') --compare-with '%setuptools_bundled_provides'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
%if %{with rpmwheels}
|
%if %{with rpmwheels}
|
||||||
@ -862,7 +936,6 @@ BuildPython() {
|
|||||||
--with-computed-gotos=%{computed_gotos_flag} \
|
--with-computed-gotos=%{computed_gotos_flag} \
|
||||||
--with-dbmliborder=gdbm:ndbm:bdb \
|
--with-dbmliborder=gdbm:ndbm:bdb \
|
||||||
--with-system-expat \
|
--with-system-expat \
|
||||||
--with-system-ffi \
|
|
||||||
--with-system-libmpdec \
|
--with-system-libmpdec \
|
||||||
--enable-loadable-sqlite-extensions \
|
--enable-loadable-sqlite-extensions \
|
||||||
--with-dtrace \
|
--with-dtrace \
|
||||||
@ -1778,6 +1851,77 @@ CheckPython optimized
|
|||||||
# ======================================================
|
# ======================================================
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Mar 09 2026 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.12.12-5
|
||||||
|
- Rebuilding previous fixes for different build target
|
||||||
|
Related: RHEL-143057, RHEL-143109, RHEL-144854
|
||||||
|
|
||||||
|
* Fri Feb 27 2026 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.12.12-4
|
||||||
|
- Security fixes for CVE-2026-0865, CVE-2025-15366, CVE-2025-15367 and CVE-2026-1299
|
||||||
|
Resolves: RHEL-143057
|
||||||
|
Resolves: RHEL-143109
|
||||||
|
Resolves: RHEL-144854
|
||||||
|
|
||||||
|
* Fri Jan 16 2026 Lumír Balhar <lbalhar@redhat.com> - 3.12.12-3
|
||||||
|
- Security fix for CVE-2025-13836
|
||||||
|
Resolves: RHEL-140978
|
||||||
|
|
||||||
|
* Tue Jan 06 2026 Lumír Balhar <lbalhar@redhat.com> - 3.12.12-2
|
||||||
|
- Security fix for CVE-2025-12084
|
||||||
|
Resolves: RHEL-135387
|
||||||
|
|
||||||
|
* Fri Oct 10 2025 Karolina Surma <ksurma@redhat.com> - 3.12.12-1
|
||||||
|
- Update to 3.12.12
|
||||||
|
- Security fix for CVE-2025-8291
|
||||||
|
Resolves: RHEL-128361
|
||||||
|
|
||||||
|
* Thu Aug 14 2025 Lumír Balhar <lbalhar@redhat.com> - 3.12.11-3
|
||||||
|
- Security fix for CVE-2025-8194
|
||||||
|
Resolves: RHEL-106329
|
||||||
|
|
||||||
|
* Thu Jun 12 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.12.11-2
|
||||||
|
- Enable PAC and BTI hardware protections for aarch64
|
||||||
|
Resolves: RHEL-84196
|
||||||
|
|
||||||
|
* Wed Jun 04 2025 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.12.11-1
|
||||||
|
- Update to 3.12.11
|
||||||
|
- Security fixes for CVE-2025-4517, CVE-2025-4330, CVE-2025-4138, CVE-2024-12718, CVE-2025-4435
|
||||||
|
Resolves: RHEL-98060, RHEL-98047, RHEL-97813, RHEL-98062, RHEL-98180
|
||||||
|
|
||||||
|
* Fri May 09 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.12.10-3
|
||||||
|
- Fix PySSL_SetError handling SSL_ERROR_SYSCALL
|
||||||
|
- This fixes random flakiness of test_ssl on stressed machines
|
||||||
|
Resolves: RHEL-90555
|
||||||
|
|
||||||
|
* Tue Apr 22 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.12.10-2
|
||||||
|
- Apply Intel's CET for mitigation against control-flow hijacking attacks
|
||||||
|
Resolves: RHEL-67040
|
||||||
|
|
||||||
|
* Wed Apr 09 2025 Miro Hrončok <mhroncok@redhat.com> - 3.12.10-1
|
||||||
|
- Update to 3.12.10
|
||||||
|
Resolves: RHEL-86605
|
||||||
|
|
||||||
|
* Mon Mar 31 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.12.9-2
|
||||||
|
- Properly apply exported CFLAGS for dtrace/systemtap builds
|
||||||
|
Resolves: RHEL-85727
|
||||||
|
|
||||||
|
* Tue Feb 04 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.12.9-1
|
||||||
|
- Update to 3.12.9
|
||||||
|
- Security fix for CVE-2025-0938
|
||||||
|
Resolves: RHEL-77264
|
||||||
|
|
||||||
|
* Tue Dec 03 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.12.8-1
|
||||||
|
- Update to 3.12.8
|
||||||
|
- Security fix for CVE-2024-9287 and CVE-2024-12254
|
||||||
|
Resolves: RHEL-64877, RHEL-70450
|
||||||
|
|
||||||
|
* Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 3.12.6-2
|
||||||
|
- Bump release for October 2024 mass rebuild:
|
||||||
|
Resolves: RHEL-64018
|
||||||
|
|
||||||
|
* Mon Sep 09 2024 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.12.6-1
|
||||||
|
- Update to 3.12.6
|
||||||
|
Resolves: RHEL-57397
|
||||||
|
|
||||||
* Fri Aug 23 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.12.5-2
|
* Fri Aug 23 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.12.5-2
|
||||||
- Security fix for CVE-2024-8088
|
- Security fix for CVE-2024-8088
|
||||||
Resolves: RHEL-55923
|
Resolves: RHEL-55923
|
||||||
|
|||||||
2
sources
2
sources
@ -1 +1 @@
|
|||||||
SHA512 (Python-3.12.5.tar.xz) = 7a1c30d798434fe24697bc253f6010d75145e7650f66803328425c8525331b9fa6b63d12a652687582db205f8d4c8279c8f73c338168592481517b063351c921
|
SHA512 (Python-3.12.12.tar.xz) = 4b99d240dd96a6e154909dcffe87f8bb38193d634cd80a1c3d9e819b7a63af2afa46d5e6423e81f00dd388840dc29a4a71580f6aa1ce9a12e559c1d63f65a205
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user