Compare commits

..

No commits in common. 'c8-stream-3.9' and 'i8-beta-stream-3.9' have entirely different histories.

2
.gitignore vendored

@ -1 +1 @@
SOURCES/Python-3.9.20.tar.xz SOURCES/Python-3.9.18.tar.xz

@ -1 +1 @@
52902dd820d2d41c47ef927ecebb24a96a51cc4b SOURCES/Python-3.9.20.tar.xz abe4a20dcc11798495b17611ef9f8f33d6975722 SOURCES/Python-3.9.18.tar.xz

@ -0,0 +1,24 @@
From 73080f31fc4aa28ced54a2987984f93c34584dde Mon Sep 17 00:00:00 2001
From: tigro <tigro@msvsphere-os.ru>
Date: Thu, 30 May 2024 21:30:39 +0300
Subject: [PATCH] Skip test_simple_xml
---
Lib/test/test_xml_etree.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 7c346f2..257f5ef 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1392,6 +1392,7 @@ class XMLPullParserTest(unittest.TestCase):
expected)
def test_simple_xml(self):
+ self.skipTest('Skip')
for chunk_size in (None, 1, 5):
with self.subTest(chunk_size=chunk_size):
parser = ET.XMLPullParser()
--
2.45.1

@ -1,4 +1,4 @@
From 4cef6c756055a15dc33a475c1f405676fa69410c Mon Sep 17 00:00:00 2001 From ccb2659fa0ec259d4161ed84345553bf3f216531 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <encukou@gmail.com> From: Petr Viktorin <encukou@gmail.com>
Date: Wed, 11 Aug 2021 16:51:03 +0200 Date: Wed, 11 Aug 2021 16:51:03 +0200
Subject: [PATCH 01/10] Backport PyModule_AddObjectRef as Subject: [PATCH 01/10] Backport PyModule_AddObjectRef as
@ -71,10 +71,10 @@ index 13482c6..fca1083 100644
PyModule_AddIntConstant(PyObject *m, const char *name, long value) PyModule_AddIntConstant(PyObject *m, const char *name, long value)
{ {
-- --
2.45.0 2.37.2
From a115773e979f968edaed8a3419f3ccc34eef8320 Mon Sep 17 00:00:00 2001 From 794c37495d91823bd820b96382b999d84dcad58d Mon Sep 17 00:00:00 2001
From: Petr Viktorin <encukou@gmail.com> From: Petr Viktorin <encukou@gmail.com>
Date: Fri, 13 Aug 2021 13:16:43 +0200 Date: Fri, 13 Aug 2021 13:16:43 +0200
Subject: [PATCH 02/10] _hashopenssl: Uncomment and use initialization function Subject: [PATCH 02/10] _hashopenssl: Uncomment and use initialization function
@ -144,10 +144,10 @@ index 4db058c..56dfff9 100644
return m; return m;
-- --
2.45.0 2.37.2
From 28506af69b2b005bb7a8931624f97273269458a1 Mon Sep 17 00:00:00 2001 From 94b56c82b459474c3e0f9e5421fa7becbf5a1c70 Mon Sep 17 00:00:00 2001
From: Christian Heimes <christian@python.org> From: Christian Heimes <christian@python.org>
Date: Sat, 27 Mar 2021 14:55:03 +0100 Date: Sat, 27 Mar 2021 14:55:03 +0100
Subject: [PATCH 03/10] bpo-40645: use C implementation of HMAC (GH-24920, Subject: [PATCH 03/10] bpo-40645: use C implementation of HMAC (GH-24920,
@ -927,10 +927,10 @@ index 68aa765..4466ec4 100644
-/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/ -/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/
-- --
2.45.0 2.37.2
From 1f5f7d3892febb68cf96ef151beab06eae1792ce Mon Sep 17 00:00:00 2001 From b63e3fbd7c0506b5a6c00c1bb0d255054e38bbe8 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 04/10] Expose blake2b and blake2s hashes from OpenSSL Subject: [PATCH 04/10] Expose blake2b and blake2s hashes from OpenSSL
@ -944,7 +944,7 @@ used under FIPS.
3 files changed, 148 insertions(+), 1 deletion(-) 3 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index bc11a8d..9a07499 100644 index f845c7a..7aaeb76 100644
--- a/Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py
@@ -363,6 +363,12 @@ class HashLibTestCase(unittest.TestCase): @@ -363,6 +363,12 @@ class HashLibTestCase(unittest.TestCase):
@ -1137,10 +1137,10 @@ index 4466ec4..54c22b2 100644
-/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/ -/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/
+/*[clinic end generated code: output=fab05055e982f112 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fab05055e982f112 input=a9049054013a1b77]*/
-- --
2.45.0 2.37.2
From 842bd1c8c8a62c342fed848e5a5f0f1d97daeaba Mon Sep 17 00:00:00 2001 From dc8ad7b98d6d9bf14cae439acb3a99fa8f4f5020 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com> From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 1 Aug 2019 17:57:05 +0200 Date: Thu, 1 Aug 2019 17:57:05 +0200
Subject: [PATCH 05/10] Use a stronger hash in multiprocessing handshake Subject: [PATCH 05/10] Use a stronger hash in multiprocessing handshake
@ -1152,7 +1152,7 @@ https://bugs.python.org/issue17258
1 file changed, 6 insertions(+), 2 deletions(-) 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index 8e2facf..bb4acb6 100644 index 510e4b5..b68f2fb 100644
--- a/Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py
@@ -42,6 +42,10 @@ BUFSIZE = 8192 @@ -42,6 +42,10 @@ BUFSIZE = 8192
@ -1166,7 +1166,7 @@ index 8e2facf..bb4acb6 100644
_mmap_counter = itertools.count() _mmap_counter = itertools.count()
default_family = 'AF_INET' default_family = 'AF_INET'
@@ -736,7 +740,7 @@ def deliver_challenge(connection, authkey): @@ -741,7 +745,7 @@ def deliver_challenge(connection, authkey):
"Authkey must be bytes, not {0!s}".format(type(authkey))) "Authkey must be bytes, not {0!s}".format(type(authkey)))
message = os.urandom(MESSAGE_LENGTH) message = os.urandom(MESSAGE_LENGTH)
connection.send_bytes(CHALLENGE + message) connection.send_bytes(CHALLENGE + message)
@ -1175,7 +1175,7 @@ index 8e2facf..bb4acb6 100644
response = connection.recv_bytes(256) # reject large message response = connection.recv_bytes(256) # reject large message
if response == digest: if response == digest:
connection.send_bytes(WELCOME) connection.send_bytes(WELCOME)
@@ -752,7 +756,7 @@ def answer_challenge(connection, authkey): @@ -757,7 +761,7 @@ def answer_challenge(connection, authkey):
message = connection.recv_bytes(256) # reject large message message = connection.recv_bytes(256) # reject large message
assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
message = message[len(CHALLENGE):] message = message[len(CHALLENGE):]
@ -1185,10 +1185,10 @@ index 8e2facf..bb4acb6 100644
response = connection.recv_bytes(256) # reject large message response = connection.recv_bytes(256) # reject large message
if response != WELCOME: if response != WELCOME:
-- --
2.45.0 2.37.2
From 0390f1ea33f8a604467829733541791a29cee4da Mon Sep 17 00:00:00 2001 From af0c88c9d5bc4f9c127e49ed80d14e25d18813f2 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 06/10] Disable Python's hash implementations in FIPS mode, Subject: [PATCH 06/10] Disable Python's hash implementations in FIPS mode,
@ -1231,7 +1231,7 @@ index ffa3be0..3e3f4dd 100644
def __get_builtin_constructor(name): def __get_builtin_constructor(name):
cache = __builtin_constructor_cache cache = __builtin_constructor_cache
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 9a07499..56dfbaa 100644 index 7aaeb76..fa4a8d7 100644
--- a/Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py
@@ -35,14 +35,15 @@ else: @@ -35,14 +35,15 @@ else:
@ -1446,10 +1446,10 @@ index 0bec170..479f4b5 100644
)) ))
-- --
2.45.0 2.37.2
From 667781e01425308ae95f062f8596866a5af76f77 Mon Sep 17 00:00:00 2001 From 9bc3d493a3508fb82df7d24cc62315c072d9eca8 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 07/10] Use python's fall back crypto implementations only if Subject: [PATCH 07/10] Use python's fall back crypto implementations only if
@ -1565,7 +1565,7 @@ index 3e3f4dd..b842f5f 100644
for __func_name in __always_supported: for __func_name in __always_supported:
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 56dfbaa..05f4a54 100644 index fa4a8d7..ec6c883 100644
--- a/Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py
@@ -171,7 +171,13 @@ class HashLibTestCase(unittest.TestCase): @@ -171,7 +171,13 @@ class HashLibTestCase(unittest.TestCase):
@ -1604,7 +1604,7 @@ index 56dfbaa..05f4a54 100644
def test_get_builtin_constructor(self): def test_get_builtin_constructor(self):
get_builtin_constructor = getattr(hashlib, get_builtin_constructor = getattr(hashlib,
'__get_builtin_constructor') '__get_builtin_constructor')
@@ -1070,6 +1090,7 @@ class KDFTests(unittest.TestCase): @@ -1061,6 +1081,7 @@ class KDFTests(unittest.TestCase):
iterations=1, dklen=None) iterations=1, dklen=None)
self.assertEqual(out, self.pbkdf2_results['sha1'][0][0]) self.assertEqual(out, self.pbkdf2_results['sha1'][0][0])
@ -1613,10 +1613,10 @@ index 56dfbaa..05f4a54 100644
def test_pbkdf2_hmac_py(self): def test_pbkdf2_hmac_py(self):
self._test_pbkdf2_hmac(builtin_hashlib.pbkdf2_hmac, builtin_hashes) self._test_pbkdf2_hmac(builtin_hashlib.pbkdf2_hmac, builtin_hashes)
-- --
2.45.0 2.37.2
From e36df0297eca09ca1826163f39a812b45558f690 Mon Sep 17 00:00:00 2001 From 331c0d39cbc9c4df266c375bae8c1a0d27dd78d9 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 08/10] Test equivalence of hashes for the various digests with Subject: [PATCH 08/10] Test equivalence of hashes for the various digests with
@ -1659,7 +1659,7 @@ index 0000000..1f99dd7
+if __name__ == "__main__": +if __name__ == "__main__":
+ unittest.main() + unittest.main()
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 05f4a54..980c773 100644 index ec6c883..0fd036f 100644
--- a/Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py
@@ -20,6 +20,7 @@ import warnings @@ -20,6 +20,7 @@ import warnings
@ -1755,7 +1755,7 @@ index 05f4a54..980c773 100644
return return
m = hash_object_constructor(data, **kwargs) m = hash_object_constructor(data, **kwargs)
@@ -983,6 +998,15 @@ class HashLibTestCase(unittest.TestCase): @@ -974,6 +989,15 @@ class HashLibTestCase(unittest.TestCase):
): ):
HASHXOF() HASHXOF()
@ -1772,10 +1772,10 @@ index 05f4a54..980c773 100644
class KDFTests(unittest.TestCase): class KDFTests(unittest.TestCase):
-- --
2.45.0 2.37.2
From 8fc0216da1d6e148bf086c8c137ddc19a33ab642 Mon Sep 17 00:00:00 2001 From 1a3df28f95710925bc80018bcf22b7f37bbb1e17 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 09/10] Guard against Python HMAC in FIPS mode Subject: [PATCH 09/10] Guard against Python HMAC in FIPS mode
@ -1889,43 +1889,290 @@ index adf52ad..41e6a14 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.37.2
From 4271e404c9aa918368d397654d60e4e845dfc844 Mon Sep 17 00:00:00 2001 From dded0e09dd3e51998a2aa54d2ae8464d73987e51 Mon Sep 17 00:00:00 2001
From: Nikita Sobolev <mail@sobolevn.me> From: Petr Viktorin <encukou@gmail.com>
Date: Thu, 24 Nov 2022 01:47:31 +0300 Date: Wed, 25 Aug 2021 16:44:43 +0200
Subject: [PATCH 10/10] closes gh-99508: fix `TypeError` in Subject: [PATCH 10/10] Disable hash-based PYCs in FIPS mode
`Lib/importlib/_bootstrap_external.py` (GH-99635)
If FIPS mode is on, we can't use siphash-based HMAC
(_Py_KeyedHash), so:
- Unchecked hash PYCs can be imported, but not created
- Checked hash PYCs can not be imported nor created
- The default mode is timestamp-based PYCs, even if
SOURCE_DATE_EPOCH is set.
If FIPS mode is off, there are no changes in behavior.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1835169
--- ---
Lib/importlib/_bootstrap_external.py | 3 ++- Lib/py_compile.py | 2 ++
.../next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst | 2 ++ Lib/test/support/__init__.py | 14 +++++++++++++
2 files changed, 4 insertions(+), 1 deletion(-) Lib/test/test_cmd_line_script.py | 2 ++
create mode 100644 Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst Lib/test/test_compileall.py | 11 +++++++++-
Lib/test/test_imp.py | 2 ++
.../test_importlib/source/test_file_loader.py | 6 ++++++
Lib/test/test_py_compile.py | 11 ++++++++--
Lib/test/test_zipimport.py | 2 ++
Python/import.c | 20 +++++++++++++++++++
9 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py diff --git a/Lib/py_compile.py b/Lib/py_compile.py
index f0c9f8e..cccf6b2 100644 index bba3642..02db901 100644
--- a/Lib/importlib/_bootstrap_external.py --- a/Lib/py_compile.py
+++ b/Lib/importlib/_bootstrap_external.py +++ b/Lib/py_compile.py
@@ -986,7 +986,8 @@ class SourceLoader(_LoaderBasics): @@ -70,7 +70,9 @@ class PycInvalidationMode(enum.Enum):
source_mtime is not None):
if hash_based:
if source_hash is None: def _get_default_invalidation_mode():
- source_hash = _imp.source_hash(source_bytes) + import _hashlib
+ source_hash = _imp.source_hash(_RAW_MAGIC_NUMBER, if (os.environ.get('SOURCE_DATE_EPOCH') and not
+ source_bytes) + _hashlib.get_fips_mode() and not
data = _code_to_hash_pyc(code_object, source_hash, check_source) os.environ.get('RPM_BUILD_ROOT')):
else: return PycInvalidationMode.CHECKED_HASH
data = _code_to_timestamp_pyc(code_object, source_mtime, else:
diff --git a/Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst b/Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
new file mode 100644 index 6dc0813..b9d5f9a 100644
index 0000000..82720d1 --- a/Lib/test/support/__init__.py
--- /dev/null +++ b/Lib/test/support/__init__.py
+++ b/Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst @@ -3296,6 +3296,20 @@ def clear_ignored_deprecations(*tokens: object) -> None:
@@ -0,0 +1,2 @@ warnings._filters_mutated()
+Fix ``TypeError`` in ``Lib/importlib/_bootstrap_external.py`` while calling
+``_imp.source_hash()``.
+def fails_in_fips_mode(expected_error):
+ import _hashlib
+ if _hashlib.get_fips_mode():
+ def _decorator(func):
+ def _wrapper(self, *args, **kwargs):
+ with self.assertRaises(expected_error):
+ func(self, *args, **kwargs)
+ return _wrapper
+ else:
+ def _decorator(func):
+ return func
+ return _decorator
+
+
@contextlib.contextmanager
def adjust_int_max_str_digits(max_digits):
"""Temporarily change the integer string conversion length limit."""
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index 7cb1370..61df232 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -282,6 +282,7 @@ class CmdLineTest(unittest.TestCase):
self._check_script(zip_name, run_name, zip_name, zip_name, '',
zipimport.zipimporter)
+ @support.fails_in_fips_mode(ImportError)
def test_zipfile_compiled_checked_hash(self):
with support.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
@@ -292,6 +293,7 @@ class CmdLineTest(unittest.TestCase):
self._check_script(zip_name, run_name, zip_name, zip_name, '',
zipimport.zipimporter)
+ @support.fails_in_fips_mode(ImportError)
def test_zipfile_compiled_unchecked_hash(self):
with support.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py
index ab647d6..7d50f07 100644
--- a/Lib/test/test_compileall.py
+++ b/Lib/test/test_compileall.py
@@ -758,14 +758,23 @@ class CommandLineTestsBase:
out = self.assertRunOK('badfilename')
self.assertRegex(out, b"Can't list 'badfilename'")
- def test_pyc_invalidation_mode(self):
+ @support.fails_in_fips_mode(AssertionError)
+ def test_pyc_invalidation_mode_checked(self):
script_helper.make_script(self.pkgdir, 'f1', '')
pyc = importlib.util.cache_from_source(
os.path.join(self.pkgdir, 'f1.py'))
+
self.assertRunOK('--invalidation-mode=checked-hash', self.pkgdir)
with open(pyc, 'rb') as fp:
data = fp.read()
self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b11)
+
+ @support.fails_in_fips_mode(AssertionError)
+ def test_pyc_invalidation_mode_unchecked(self):
+ script_helper.make_script(self.pkgdir, 'f1', '')
+ pyc = importlib.util.cache_from_source(
+ os.path.join(self.pkgdir, 'f1.py'))
+
self.assertRunOK('--invalidation-mode=unchecked-hash', self.pkgdir)
with open(pyc, 'rb') as fp:
data = fp.read()
diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py
index fe394dc..802f0e8 100644
--- a/Lib/test/test_imp.py
+++ b/Lib/test/test_imp.py
@@ -343,6 +343,7 @@ class ImportTests(unittest.TestCase):
import _frozen_importlib
self.assertEqual(_frozen_importlib.__spec__.origin, "frozen")
+ @support.fails_in_fips_mode(ImportError)
def test_source_hash(self):
self.assertEqual(_imp.source_hash(42, b'hi'), b'\xc6\xe7Z\r\x03:}\xab')
self.assertEqual(_imp.source_hash(43, b'hi'), b'\x85\x9765\xf8\x9a\x8b9')
@@ -362,6 +363,7 @@ class ImportTests(unittest.TestCase):
res = script_helper.assert_python_ok(*args)
self.assertEqual(res.out.strip().decode('utf-8'), expected)
+ @support.fails_in_fips_mode(ImportError)
def test_find_and_load_checked_pyc(self):
# issue 34056
with support.temp_cwd():
diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py
index ab44722..480cc81 100644
--- a/Lib/test/test_importlib/source/test_file_loader.py
+++ b/Lib/test/test_importlib/source/test_file_loader.py
@@ -17,6 +17,7 @@ import types
import unittest
import warnings
+from test import support
from test.support import make_legacy_pyc, unload
from test.test_py_compile import without_source_date_epoch
@@ -239,6 +240,7 @@ class SimpleTest(abc.LoaderTests):
loader.load_module('bad name')
@util.writes_bytecode_files
+ @support.fails_in_fips_mode(ImportError)
def test_checked_hash_based_pyc(self):
with util.create_modules('_temp') as mapping:
source = mapping['_temp']
@@ -270,6 +272,7 @@ class SimpleTest(abc.LoaderTests):
)
@util.writes_bytecode_files
+ @support.fails_in_fips_mode(ImportError)
def test_overridden_checked_hash_based_pyc(self):
with util.create_modules('_temp') as mapping, \
unittest.mock.patch('_imp.check_hash_based_pycs', 'never'):
@@ -295,6 +298,7 @@ class SimpleTest(abc.LoaderTests):
self.assertEqual(mod.state, 'old')
@util.writes_bytecode_files
+ @support.fails_in_fips_mode(ImportError)
def test_unchecked_hash_based_pyc(self):
with util.create_modules('_temp') as mapping:
source = mapping['_temp']
@@ -325,6 +329,7 @@ class SimpleTest(abc.LoaderTests):
)
@util.writes_bytecode_files
+ @support.fails_in_fips_mode(ImportError)
def test_overridden_unchecked_hash_based_pyc(self):
with util.create_modules('_temp') as mapping, \
unittest.mock.patch('_imp.check_hash_based_pycs', 'always'):
@@ -434,6 +439,7 @@ class BadBytecodeTest:
del_source=del_source)
test('_temp', mapping, bc_path)
+ @support.fails_in_fips_mode(ImportError)
def _test_partial_hash(self, test, *, del_source=False):
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode(
diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py
index b2d3dcf..7e4b0c5 100644
--- a/Lib/test/test_py_compile.py
+++ b/Lib/test/test_py_compile.py
@@ -141,13 +141,16 @@ class PyCompileTestsBase:
importlib.util.cache_from_source(bad_coding)))
def test_source_date_epoch(self):
+ import _hashlib
py_compile.compile(self.source_path, self.pyc_path)
self.assertTrue(os.path.exists(self.pyc_path))
self.assertFalse(os.path.exists(self.cache_path))
with open(self.pyc_path, 'rb') as fp:
flags = importlib._bootstrap_external._classify_pyc(
fp.read(), 'test', {})
- if os.environ.get('SOURCE_DATE_EPOCH'):
+ if _hashlib.get_fips_mode():
+ expected_flags = 0b00
+ elif os.environ.get('SOURCE_DATE_EPOCH'):
expected_flags = 0b11
else:
expected_flags = 0b00
@@ -178,7 +181,8 @@ class PyCompileTestsBase:
# Specifying optimized bytecode should lead to a path reflecting that.
self.assertIn('opt-2', py_compile.compile(self.source_path, optimize=2))
- def test_invalidation_mode(self):
+ @support.fails_in_fips_mode(ImportError)
+ def test_invalidation_mode_checked(self):
py_compile.compile(
self.source_path,
invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
@@ -187,6 +191,9 @@ class PyCompileTestsBase:
flags = importlib._bootstrap_external._classify_pyc(
fp.read(), 'test', {})
self.assertEqual(flags, 0b11)
+
+ @support.fails_in_fips_mode(ImportError)
+ def test_invalidation_mode_unchecked(self):
py_compile.compile(
self.source_path,
invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH,
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
index b7347a3..09ea990 100644
--- a/Lib/test/test_zipimport.py
+++ b/Lib/test/test_zipimport.py
@@ -186,6 +186,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
TESTMOD + pyc_ext: (NOW, test_pyc)}
self.doTest(pyc_ext, files, TESTMOD)
+ @support.fails_in_fips_mode(ImportError)
def testUncheckedHashBasedPyc(self):
source = b"state = 'old'"
source_hash = importlib.util.source_hash(source)
@@ -200,6 +201,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
self.assertEqual(mod.state, 'old')
self.doTest(None, files, TESTMOD, call=check)
+ @support.fails_in_fips_mode(ImportError)
@unittest.mock.patch('_imp.check_hash_based_pycs', 'always')
def test_checked_hash_based_change_pyc(self):
source = b"state = 'old'"
diff --git a/Python/import.c b/Python/import.c
index 8358d70..1b7fb85 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -2354,6 +2354,26 @@ static PyObject *
_imp_source_hash_impl(PyObject *module, long key, Py_buffer *source)
/*[clinic end generated code: output=edb292448cf399ea input=9aaad1e590089789]*/
{
+ PyObject *_hashlib = PyImport_ImportModule("_hashlib");
+ if (_hashlib == NULL) {
+ return NULL;
+ }
+ PyObject *fips_mode_obj = PyObject_CallMethod(_hashlib, "get_fips_mode", NULL);
+ Py_DECREF(_hashlib);
+ if (fips_mode_obj == NULL) {
+ return NULL;
+ }
+ int fips_mode = PyObject_IsTrue(fips_mode_obj);
+ Py_DECREF(fips_mode_obj);
+ if (fips_mode < 0) {
+ return NULL;
+ }
+ if (fips_mode) {
+ PyErr_SetString(
+ PyExc_ImportError,
+ "hash-based PYC validation (siphash24) not available in FIPS mode");
+ return NULL;
+ };
union {
uint64_t x;
char data[sizeof(uint64_t)];
-- --
2.45.0 2.37.2

@ -1,4 +1,4 @@
From f253f1e7e8283b876d40af385d5729646f2c18b6 Mon Sep 17 00:00:00 2001 From 2513477cd909e838824f1766d81fdeff01c0790c Mon Sep 17 00:00:00 2001
From: Victor Stinner <vstinner@python.org> From: Victor Stinner <vstinner@python.org>
Date: Wed, 17 Jan 2024 14:53:23 +0100 Date: Wed, 17 Jan 2024 14:53:23 +0100
Subject: [PATCH] bpo-46623: Skip two test_zlib tests on s390x (GH-31096) Subject: [PATCH] bpo-46623: Skip two test_zlib tests on s390x (GH-31096)
@ -12,7 +12,7 @@ they fail if zlib uses the s390x hardware accelerator.
create mode 100644 Misc/NEWS.d/next/Tests/2022-02-03-09-45-26.bpo-46623.vxzuhV.rst create mode 100644 Misc/NEWS.d/next/Tests/2022-02-03-09-45-26.bpo-46623.vxzuhV.rst
diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py
index aa7943f..8945b10 100644 index 02509cd..f3654c9 100644
--- a/Lib/test/test_zlib.py --- a/Lib/test/test_zlib.py
+++ b/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py
@@ -2,6 +2,7 @@ import unittest @@ -2,6 +2,7 @@ import unittest
@ -54,12 +54,12 @@ index aa7943f..8945b10 100644
+# Make the assumption that s390x always has an accelerator to simplify the skip +# Make the assumption that s390x always has an accelerator to simplify the skip
+# condition. Windows doesn't have os.uname() but it doesn't support s390x. +# condition. Windows doesn't have os.uname() but it doesn't support s390x.
+skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x', +skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x',
+ 'skipped on s390x') + 'skipped on s390x')
+ +
def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION):
# Register "1.2.3" as "1.2.3.0" class VersionTestCase(unittest.TestCase):
# or "1.2.0-linux","1.2.0.f","1.2.0.f-linux"
@@ -187,6 +217,7 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): @@ -174,6 +204,7 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase):
bufsize=zlib.DEF_BUF_SIZE), bufsize=zlib.DEF_BUF_SIZE),
HAMLET_SCENE) HAMLET_SCENE)
@ -67,7 +67,7 @@ index aa7943f..8945b10 100644
def test_speech128(self): def test_speech128(self):
# compress more data # compress more data
data = HAMLET_SCENE * 128 data = HAMLET_SCENE * 128
@@ -238,6 +269,7 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): @@ -225,6 +256,7 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase):
class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase):
# Test compression object # Test compression object
@ -84,5 +84,5 @@ index 0000000..be085c0
+Skip test_pair() and test_speech128() of test_zlib on s390x since they fail +Skip test_pair() and test_speech128() of test_zlib on s390x since they fail
+if zlib uses the s390x hardware accelerator. Patch by Victor Stinner. +if zlib uses the s390x hardware accelerator. Patch by Victor Stinner.
-- --
2.46.0 2.43.0

@ -1,3 +1,505 @@
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 | 204 +++++++++++++++++-
...-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 +
4 files changed, 361 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 4d0e920eb0..104229e9e5 100644
--- a/Doc/library/email.utils.rst
+++ b/Doc/library/email.utils.rst
@@ -60,13 +60,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')
@@ -84,12 +89,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
@@ -99,6 +107,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 48d30160aa..7ca7a7c886 100644
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -48,6 +48,7 @@ TICK = "'"
specialsre = re.compile(r'[][\\()<>@,:;".]')
escapesre = re.compile(r'[\\"]')
+
def _has_surrogates(s):
"""Return True if s contains 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):
@@ -202,16 +318,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 761ea90b78..0c689643de 100644
--- a/Lib/test/test_email/test_email.py
+++ b/Lib/test/test_email/test_email.py
@@ -16,6 +16,7 @@ from unittest.mock import patch
import email
import email.policy
+import email.utils
from email.charset import Charset
from email.header import Header, decode_header, make_header
@@ -3263,15 +3264,154 @@ Foo
[('Al Person', 'aperson@dom.ain'),
('Bud Person', 'bperson@dom.ain')])
+ def test_getaddresses_comma_in_name(self):
+ """GH-106669 regression test."""
+ self.assertEqual(
+ utils.getaddresses(
+ [
+ '"Bud, Person" <bperson@dom.ain>',
+ 'aperson@dom.ain (Al Person)',
+ '"Mariusz Felisiak" <to@example.com>',
+ ]
+ ),
+ [
+ ('Bud, Person', 'bperson@dom.ain'),
+ ('Al Person', 'aperson@dom.ain'),
+ ('Mariusz Felisiak', 'to@example.com'),
+ ],
+ )
+
+ 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"""
@@ -3460,6 +3600,54 @@ multipart/report
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.
From 4df4fad359c280f2328b98ea9b4414f244624a58 Mon Sep 17 00:00:00 2001 From 4df4fad359c280f2328b98ea9b4414f244624a58 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com> From: Lumir Balhar <lbalhar@redhat.com>
Date: Mon, 18 Dec 2023 20:15:33 +0100 Date: Mon, 18 Dec 2023 20:15:33 +0100
@ -30,7 +532,7 @@ index d1e1898591..7aef773b5f 100644
+ [email_addr_parsing] + [email_addr_parsing]
+ PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true + PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true
+ +
.. versionchanged:: 3.9.20 .. versionchanged:: 3.13
Add *strict* optional parameter and reject malformed inputs by default. Add *strict* optional parameter and reject malformed inputs by default.
@@ -97,6 +110,19 @@ of the new API. @@ -97,6 +110,19 @@ of the new API.

@ -1,63 +0,0 @@
From 60d40d7095983e0bc23a103b2050adc519dc7fe3 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Fri, 3 May 2024 14:17:48 +0200
Subject: [PATCH] Expect failures in tests not working properly with expat with
a fixed CVE in RHEL
---
Lib/test/test_pyexpat.py | 1 +
Lib/test/test_sax.py | 1 +
Lib/test/test_xml_etree.py | 3 +++
3 files changed, 5 insertions(+)
diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py
index 43cbd27..27b1502 100644
--- a/Lib/test/test_pyexpat.py
+++ b/Lib/test/test_pyexpat.py
@@ -793,6 +793,7 @@ class ReparseDeferralTest(unittest.TestCase):
self.assertEqual(started, ['doc'])
+ @unittest.expectedFailure
def test_reparse_deferral_disabled(self):
started = []
diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py
index 9b3014a..646c92d 100644
--- a/Lib/test/test_sax.py
+++ b/Lib/test/test_sax.py
@@ -1240,6 +1240,7 @@ class ExpatReaderTest(XmlTestBase):
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
+ @unittest.expectedFailure
def test_flush_reparse_deferral_disabled(self):
result = BytesIO()
xmlgen = XMLGenerator(result)
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 9c382d1..62f2871 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1424,9 +1424,11 @@ class XMLPullParserTest(unittest.TestCase):
self.assert_event_tags(parser, [('end', 'root')])
self.assertIsNone(parser.close())
+ @unittest.expectedFailure
def test_simple_xml_chunk_1(self):
self.test_simple_xml(chunk_size=1, flush=True)
+ @unittest.expectedFailure
def test_simple_xml_chunk_5(self):
self.test_simple_xml(chunk_size=5, flush=True)
@@ -1651,6 +1653,7 @@ class XMLPullParserTest(unittest.TestCase):
self.assert_event_tags(parser, [('end', 'doc')])
+ @unittest.expectedFailure
def test_flush_reparse_deferral_disabled(self):
parser = ET.XMLPullParser(events=('start', 'end'))
--
2.44.0

@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmTnntEACgkQsmmV4xAl
BWgmQw/9EFWMXtSfWBV93AQF37r0nbUnOBvrOcubkO7ygt+GfHKzN8EPuNeO2It7
yNZDuCmwepnNGaIkO7UkgbwYyNw3YaoHQqxG8izAfJAVqK6BSk8UAET/YKWFXbLv
cZBfgxSa0tTEkwq3BAY4vDewRXnLkUq7k6JRRCKFGLNSi/ygC56SijxyAV2g4Vio
Qcwr9VhsTvz6ujoWuPrfVpUY4I81LBJxKK7n9zBreYzh5uUXRu5k4lN2W8HrE4q0
7tTdsccB9j1CJAiUacYLxTFsvwd/hBs9+g9Eu5kqGeChqEU56Gd8wR96TEu8cVIZ
Bv5UEo9MgT1KsJwk0FMfV8qVScqZrGG3QaoMtNAeAm/tUrhhZO9ANYsC9dey03ut
tU6s5GAeh6i17bqW5WfvzCdhY9ayCInndzkq7SPi9F7fYx79PgdsofqPdyCSBXUo
Ozfn1VQkYQJTmYtrwqLfdAivubaEPIf1+fLqMOXbrI85Ujuy5xzlgVrrqO2K9rbE
DYyPgGZjPtss/yZGRCUdJX6rbW8Tq0HKt/8HpbW5fCt9o0wCSawR71GhzPA1fpNs
0mkAGvvoNGdiSizTLLPvNCaecw4kSzeBNViyP6oRCv69ifNqHPErItsMZ0YIMU14
w4/d9yI9kUa2bvE3cmx6G+9OS8PYip9MsJbQgP7kJsZ8wgt9rQU=
=aw+P
-----END PGP SIGNATURE-----

@ -1,16 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmbcKf0ACgkQsmmV4xAl
BWh4rg//R5E1EjsifYqhLeIyT+JnrBvbTZeEcdxPXevsgilojYmrxBUKuXXViul0
YZFaoDf6wjbHh6NMNgUpqcOH/5S/LsFZvuEcrw0jyGlMr0AMA4KLmNvQ9Wxf+wp4
mUmhymQx555nVivsdPiziNnDwubZeA870ZllYEMWP5vXw7p2LbnlZvn7A+LSKjqM
S/6xbiKYVexK3vHY/uG0xo4z24FySfvs0/PF11JfRJCxm9+bli7FmHOoFMwpOO6S
caZLok4987YWOcPIPY6h+o2sFhDqHs8POGKd8k+0KQNQs5UbEQ4t/eKgnaoATkGn
nfcAGXSjX5RSv5uXPzBUc0PulYo6EalIn1b5fu96La/FEg9GLMR/n9g75Fgm/j9L
QGYu/DSaastY/c7Ot4QVyB6pxbQKjM438yneQrjhKBILGla4Crh1k6yRCx93j/TH
hF9kiuRf7jtLIGTp0cnquELGnatmL1RhOySn/1Y+asMR+oK8d+XQab//w4VsAt7C
SIfVXg25PUgZoaiYj/qIjLK9vkcj/EZ1IacivP5qBWb3O1E8gzSV8Z9duGT8Ef3P
ch4M/pd6hefVVVfyCoazB3gwDs68O6U2BIRdYLRlet8AuKTBysQKFwOo3EcCMmJV
W20KutPnERCzt8jeJdzFd0z3po9mvxNTKDLYaABtNI6NN00LcsM=
=svjf
-----END PGP SIGNATURE-----

@ -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}.20 %global general_version %{pybasever}.18
#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: 1%{?dist} Release: 3%{?dist}.inferit
License: Python License: Python
# Exclude i686 arch. Due to a modularity issue it's being added to the # Exclude i686 arch. Due to a modularity issue it's being added to the
@ -182,13 +182,6 @@ ExcludeArch: i686
%global py_INSTSONAME_optimized libpython%{LDVERSION_optimized}.so.%{py_SOVERSION} %global py_INSTSONAME_optimized libpython%{LDVERSION_optimized}.so.%{py_SOVERSION}
%global py_INSTSONAME_debug libpython%{LDVERSION_debug}.so.%{py_SOVERSION} %global py_INSTSONAME_debug libpython%{LDVERSION_debug}.so.%{py_SOVERSION}
# The -O flag for the compiler, optimized builds
# https://fedoraproject.org/wiki/Changes/Python_built_with_gcc_O3
%global optflags_optimized -O3
# The -O flag for the compiler, debug builds
# -Wno-cpp avoids some warnings with -O0
%global optflags_debug -O0 -Wno-cpp
# Disable automatic bytecompilation. The python3 binary is not yet be # Disable automatic bytecompilation. The python3 binary is not yet be
# available in /usr/bin when Python is built. Also, the bytecompilation fails # available in /usr/bin when Python is built. Also, the bytecompilation fails
# on files that test invalid syntax. # on files that test invalid syntax.
@ -448,17 +441,10 @@ Patch414: 00414-skip_test_zlib_s390x.patch
# #
# Upstream PR: https://github.com/python/cpython/pull/111116 # Upstream PR: https://github.com/python/cpython/pull/111116
# #
# This patch implements the possibility to restore the old behavior via # Second patch implmenets the possibility to restore the old behavior via
# config file or environment variable. # config file or environment variable.
Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch
# 00422 # a353cebef737c41420dc7ae2469dd657371b8881
# Fix tests for XMLPullParser with Expat 2.6.0
#
# Feeding the parser by too small chunks defers parsing to prevent
# CVE-2023-52425. Future versions of Expat may be more reactive.
Patch422: 00422-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch
# (New patches go here ^^^) # (New patches go here ^^^)
# #
# When adding new patches to "python" and "python3" in Fedora, EL, etc., # When adding new patches to "python" and "python3" in Fedora, EL, etc.,
@ -472,6 +458,8 @@ Patch422: 00422-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch
# #
# https://github.com/fedora-python/cpython # https://github.com/fedora-python/cpython
# MSVSphere
Patch500: 0001-Skip-test_simple_xml.patch
# ========================================== # ==========================================
# Descriptions, and metadata for subpackages # Descriptions, and metadata for subpackages
@ -873,7 +861,7 @@ rm Lib/ensurepip/_bundled/*.whl
%apply_patch -q %{PATCH397} %apply_patch -q %{PATCH397}
%apply_patch -q %{PATCH414} %apply_patch -q %{PATCH414}
%apply_patch -q %{PATCH415} %apply_patch -q %{PATCH415}
%apply_patch -q %{PATCH422} %apply_patch -q %{PATCH500}
# Remove all exe files to ensure we are not shipping prebuilt binaries # Remove all exe files to ensure we are not shipping prebuilt binaries
# note that those are only used to create Microsoft Windows installers # note that those are only used to create Microsoft Windows installers
@ -950,7 +938,6 @@ BuildPython() {
ConfName=$1 ConfName=$1
ExtraConfigArgs=$2 ExtraConfigArgs=$2
MoreCFlags=$3 MoreCFlags=$3
MoreCFlagsNodist=$4
# Each build is done in its own directory # Each build is done in its own directory
ConfDir=build/$ConfName ConfDir=build/$ConfName
@ -985,7 +972,7 @@ BuildPython() {
$ExtraConfigArgs \ $ExtraConfigArgs \
%{nil} %{nil}
%global flags_override EXTRA_CFLAGS="$MoreCFlags" CFLAGS_NODIST="$CFLAGS_NODIST $MoreCFlags $MoreCFlagsNodist" %global flags_override EXTRA_CFLAGS="$MoreCFlags" CFLAGS_NODIST="$CFLAGS_NODIST $MoreCFlags"
%if %{without bootstrap} %if %{without bootstrap}
# Regenerate generated files (needs python3) # Regenerate generated files (needs python3)
@ -1008,14 +995,12 @@ BuildPython() {
# See also: https://bugzilla.redhat.com/show_bug.cgi?id=1818857 # See also: https://bugzilla.redhat.com/show_bug.cgi?id=1818857
BuildPython debug \ BuildPython debug \
"--without-ensurepip --with-pydebug" \ "--without-ensurepip --with-pydebug" \
"%{optflags_debug}" \ "-O0 -Wno-cpp"
""
%endif # with debug_build %endif # with debug_build
BuildPython optimized \ BuildPython optimized \
"--without-ensurepip %{optimizations_flag}" \ "--without-ensurepip %{optimizations_flag}" \
"" \ ""
"%{optflags_optimized}"
# ====================================================== # ======================================================
# Installing the built code: # Installing the built code:
@ -1114,7 +1099,7 @@ EOF
%if %{with debug_build} %if %{with debug_build}
InstallPython debug \ InstallPython debug \
%{py_INSTSONAME_debug} \ %{py_INSTSONAME_debug} \
"%{optflags_debug}" \ -O0 \
%{LDVERSION_debug} %{LDVERSION_debug}
%endif # with debug_build %endif # with debug_build
@ -2048,38 +2033,11 @@ fi
# ====================================================== # ======================================================
%changelog %changelog
* Mon Sep 09 2024 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.9.20-1 * Thu May 30 2024 Arkady L. Shane <tigro@msvsphere-os.ru> - 3.9.18-3.inferit
- Update to 3.9.20 - Skip test_simple_xml
Resolves: RHEL-60007
* Wed Apr 03 2024 MSVSphere Packaging Team <packager@msvsphere-os.ru> - 3.9.18-3
* Fri Aug 23 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.9.19-7 - Rebuilt for MSVSphere 8.10 beta
- Security fix for CVE-2024-8088
Resolves: RHEL-55954
* Tue Aug 13 2024 Lumír Balhar <lbalhar@redhat.com> - 3.9.19-6
- Security fix for CVE-2024-6923
Resolves: RHEL-53102
* Thu Jul 25 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.9.19-5
- Properly propagate the optimization flags to C extensions
* Thu Jul 18 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.9.19-4
- Build Python with -O3
- https://fedoraproject.org/wiki/Changes/Python_built_with_gcc_O3
* Thu Jul 18 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.9.19-3
- Security fix for CVE-2024-4032
Resolves: RHEL-44094
* Tue Jun 11 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.9.19-2
- Enable importing of hash-based .pyc files under FIPS mode
Resolves: RHEL-40786
* Mon Apr 22 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.9.19-1
- Update to 3.9.19
- Security fixes for CVE-2023-6597 and CVE-2024-0450
- Fix tests for XMLPullParser with Expat with fixed CVE
Resolves: RHEL-33676, RHEL-33688
* Wed Jan 17 2024 Lumír Balhar <lbalhar@redhat.com> - 3.9.18-3 * Wed Jan 17 2024 Lumír Balhar <lbalhar@redhat.com> - 3.9.18-3
- Skip tests failing on s390x - Skip tests failing on s390x

Loading…
Cancel
Save