From c7631a779fc7b79bc21b4e3a2b30646af5acdbe5 Mon Sep 17 00:00:00 2001
From: rockyautomation <rockyautomation@rockylinux.org>
Date: Tue, 2 Jul 2024 20:36:03 +0000
Subject: [PATCH] import python3.11-3.11.9-2.el8_10

---
 .python3.11.checksum                          |   2 +-
 .python3.11.metadata                          |   2 +-
 SOURCES/00329-fips.patch                      | 386 ++-------
 SOURCES/00397-tarfile-filter.patch            |  35 +-
 ...-addresses-in-email-parseaddr-111116.patch | 748 ++++++++++++++++++
 SOURCES/00422-fix-expat-tests.patch           |  75 ++
 SOURCES/Python-3.11.9.tar.xz.asc              |  16 +
 SOURCES/check-pyc-timestamps.py               |   6 +-
 SPECS/python3.11.spec                         |  72 +-
 9 files changed, 984 insertions(+), 358 deletions(-)
 create mode 100644 SOURCES/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch
 create mode 100644 SOURCES/00422-fix-expat-tests.patch
 create mode 100644 SOURCES/Python-3.11.9.tar.xz.asc

diff --git a/.python3.11.checksum b/.python3.11.checksum
index 82d16af..bf707f5 100644
--- a/.python3.11.checksum
+++ b/.python3.11.checksum
@@ -1 +1 @@
-e5feea809aad509f99943b68d44ac7c89eb943fbefb0f0ced5050675a6d1d16e
+6146ef29ae002c4abb3e07316c079dde832739c187a57b35123cfacfb314df1d
diff --git a/.python3.11.metadata b/.python3.11.metadata
index 7802c5c..6856768 100644
--- a/.python3.11.metadata
+++ b/.python3.11.metadata
@@ -1,2 +1,2 @@
-85cd12e9cf1d6d5a45f17f7afe1cebe7ee628d3282281c492e86adf636defa3f SOURCES/Python-3.11.5.tar.xz
+9b1e896523fc510691126c864406d9360a3d1e986acbda59cda57b5abda45b87 SOURCES/Python-3.11.9.tar.xz
 fb28243ffeb9725b14b60586a9a123682a89604c025b7a9d4bcdeb67078203c6 SOURCES/pgp_keys.asc
diff --git a/SOURCES/00329-fips.patch b/SOURCES/00329-fips.patch
index b4763dd..e2f1cf8 100644
--- a/SOURCES/00329-fips.patch
+++ b/SOURCES/00329-fips.patch
@@ -1,4 +1,4 @@
-From c96f1bea2ffc5c0ca849d5406236c07ea229a64f Mon Sep 17 00:00:00 2001
+From 4345f8ea8a56a58ef8a48439c0e201702d1012a2 Mon Sep 17 00:00:00 2001
 From: Charalampos Stratakis <cstratak@redhat.com>
 Date: Thu, 12 Dec 2019 16:58:31 +0100
 Subject: [PATCH 1/7] Expose blake2b and blake2s hashes from OpenSSL
@@ -29,10 +29,10 @@ index 67becdd..6607ef7 100644
              computed = m.hexdigest() if not shake else m.hexdigest(length)
              self.assertEqual(
 diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
-index 3c40f09..e819d02 100644
+index 57d64bd..d0c3b9e 100644
 --- a/Modules/_hashopenssl.c
 +++ b/Modules/_hashopenssl.c
-@@ -1077,6 +1077,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj,
+@@ -1078,6 +1078,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj,
  }
  
  
@@ -74,7 +74,7 @@ index 3c40f09..e819d02 100644
  #ifdef PY_OPENSSL_HAS_SHA3
  
  /*[clinic input]
-@@ -2065,6 +2100,8 @@ static struct PyMethodDef EVP_functions[] = {
+@@ -2066,6 +2101,8 @@ static struct PyMethodDef EVP_functions[] = {
      _HASHLIB_OPENSSL_SHA256_METHODDEF
      _HASHLIB_OPENSSL_SHA384_METHODDEF
      _HASHLIB_OPENSSL_SHA512_METHODDEF
@@ -205,10 +205,10 @@ index 5d84f4a..011026a 100644
 -/*[clinic end generated code: output=69f2374071bff707 input=a9049054013a1b77]*/
 +/*[clinic end generated code: output=c6a9af5563972eda input=a9049054013a1b77]*/
 -- 
-2.39.1
+2.45.0
 
 
-From 9a7e164840aa35602e1c6dddadd461fafc666a63 Mon Sep 17 00:00:00 2001
+From 1f79be1a11ad6811913c239da980c5bab0f1c538 Mon Sep 17 00:00:00 2001
 From: Petr Viktorin <pviktori@redhat.com>
 Date: Thu, 1 Aug 2019 17:57:05 +0200
 Subject: [PATCH 2/7] Use a stronger hash in multiprocessing handshake
@@ -220,10 +220,10 @@ https://bugs.python.org/issue17258
  1 file changed, 6 insertions(+), 2 deletions(-)
 
 diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
-index b08144f..0497557 100644
+index 59c61d2..7fc594e 100644
 --- a/Lib/multiprocessing/connection.py
 +++ b/Lib/multiprocessing/connection.py
-@@ -42,6 +42,10 @@ BUFSIZE = 8192
+@@ -43,6 +43,10 @@ BUFSIZE = 8192
  # A very generous timeout when it comes to local connections...
  CONNECTION_TIMEOUT = 20.
  
@@ -234,7 +234,7 @@ index b08144f..0497557 100644
  _mmap_counter = itertools.count()
  
  default_family = 'AF_INET'
-@@ -735,7 +739,7 @@ def deliver_challenge(connection, authkey):
+@@ -753,7 +757,7 @@ def deliver_challenge(connection, authkey):
              "Authkey must be bytes, not {0!s}".format(type(authkey)))
      message = os.urandom(MESSAGE_LENGTH)
      connection.send_bytes(CHALLENGE + message)
@@ -243,7 +243,7 @@ index b08144f..0497557 100644
      response = connection.recv_bytes(256)        # reject large message
      if response == digest:
          connection.send_bytes(WELCOME)
-@@ -751,7 +755,7 @@ def answer_challenge(connection, authkey):
+@@ -769,7 +773,7 @@ def answer_challenge(connection, authkey):
      message = connection.recv_bytes(256)         # reject large message
      assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
      message = message[len(CHALLENGE):]
@@ -253,10 +253,10 @@ index b08144f..0497557 100644
      response = connection.recv_bytes(256)        # reject large message
      if response != WELCOME:
 -- 
-2.39.1
+2.45.0
 
 
-From 10b91783a2f22153738c5658a98daf7475ad9a8c Mon Sep 17 00:00:00 2001
+From e069ed2dcd0edf0de489eb387267fb35a92ed506 Mon Sep 17 00:00:00 2001
 From: Petr Viktorin <pviktori@redhat.com>
 Date: Thu, 25 Jul 2019 17:19:06 +0200
 Subject: [PATCH 3/7] Disable Python's hash implementations in FIPS mode,
@@ -359,7 +359,7 @@ index c2cac98..55b1677 100644
  
      if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
 diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c
-index 44d783b..d247e44 100644
+index 93478f5..e3a024d 100644
 --- a/Modules/_blake2/blake2module.c
 +++ b/Modules/_blake2/blake2module.c
 @@ -13,6 +13,7 @@
@@ -370,7 +370,7 @@ index 44d783b..d247e44 100644
  #include "blake2module.h"
  
  extern PyType_Spec blake2b_type_spec;
-@@ -77,6 +78,7 @@ _blake2_free(void *module)
+@@ -83,6 +84,7 @@ _blake2_free(void *module)
  static int
  blake2_exec(PyObject *m)
  {
@@ -378,7 +378,7 @@ index 44d783b..d247e44 100644
      Blake2State* st = blake2_get_state(m);
  
      st->blake2b_type = (PyTypeObject *)PyType_FromModuleAndSpec(
-@@ -145,5 +147,6 @@ static struct PyModuleDef blake2_module = {
+@@ -154,5 +156,6 @@ static struct PyModuleDef blake2_module = {
  PyMODINIT_FUNC
  PyInit__blake2(void)
  {
@@ -446,10 +446,10 @@ index 56ae7a5..45fb403 100644
 +    if (_Py_hashlib_fips_error(exc, name)) return NULL; \
 +} while (0)
 diff --git a/configure.ac b/configure.ac
-index c62a565..861f7a0 100644
+index 7b4000f..8e2f0ad 100644
 --- a/configure.ac
 +++ b/configure.ac
-@@ -7044,7 +7044,8 @@ PY_STDLIB_MOD([_sha512], [test "$with_builtin_sha512" = yes])
+@@ -7070,7 +7070,8 @@ PY_STDLIB_MOD([_sha512], [test "$with_builtin_sha512" = yes])
  PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes])
  PY_STDLIB_MOD([_blake2],
    [test "$with_builtin_blake2" = yes], [],
@@ -460,10 +460,10 @@ index c62a565..861f7a0 100644
  PY_STDLIB_MOD([_crypt],
    [], [test "$ac_cv_crypt_crypt" = yes],
 -- 
-2.39.1
+2.45.0
 
 
-From e26066b1c05c9768e38cb6f45d6a01058de55b3f Mon Sep 17 00:00:00 2001
+From 2e0c5086f4a52803595e19795111278c3c80ee2f Mon Sep 17 00:00:00 2001
 From: Charalampos Stratakis <cstratak@redhat.com>
 Date: Fri, 29 Jan 2021 14:16:21 +0100
 Subject: [PATCH 4/7] Use python's fall back crypto implementations only if we
@@ -623,10 +623,10 @@ index 01d12f5..a7cdb07 100644
      def test_pbkdf2_hmac_py(self):
          with warnings_helper.check_warnings():
 -- 
-2.39.1
+2.45.0
 
 
-From 9ccbd22b8538fee379717c8b2916dc1ff8b96f07 Mon Sep 17 00:00:00 2001
+From 0e1d2a67ef66cccc9afa4a515dc34ce587946f22 Mon Sep 17 00:00:00 2001
 From: Charalampos Stratakis <cstratak@redhat.com>
 Date: Wed, 31 Jul 2019 15:43:43 +0200
 Subject: [PATCH 5/7] Test equivalence of hashes for the various digests with
@@ -783,21 +783,21 @@ index a7cdb07..c071f28 100644
  class KDFTests(unittest.TestCase):
  
 -- 
-2.39.1
+2.45.0
 
 
-From c3b8d6ecc76c87e8b05fd2cb212d5dece50ce0b1 Mon Sep 17 00:00:00 2001
+From f1c9ecbb2e2f08d792fb0557058824eed23abb7b Mon Sep 17 00:00:00 2001
 From: Petr Viktorin <pviktori@redhat.com>
 Date: Mon, 26 Aug 2019 19:39:48 +0200
 Subject: [PATCH 6/7] Guard against Python HMAC in FIPS mode
 
 ---
- Lib/hmac.py           | 13 +++++++++----
+ Lib/hmac.py           | 12 +++++++++---
  Lib/test/test_hmac.py | 10 ++++++++++
- 2 files changed, 19 insertions(+), 4 deletions(-)
+ 2 files changed, 19 insertions(+), 3 deletions(-)
 
 diff --git a/Lib/hmac.py b/Lib/hmac.py
-index 8b4f920..20ef96c 100644
+index 8b4eb2f..8930bda 100644
 --- a/Lib/hmac.py
 +++ b/Lib/hmac.py
 @@ -16,8 +16,9 @@ else:
@@ -812,16 +812,9 @@ index 8b4f920..20ef96c 100644
  
  # The size of the digests returned by HMAC depends on the underlying
  # hashing module used.  Use digest_size from the instance of HMAC instead.
-@@ -48,17 +49,18 @@ class HMAC:
-                    msg argument.  Passing it as a keyword argument is
-                    recommended, though not required for legacy API reasons.
-         """
--
-         if not isinstance(key, (bytes, bytearray)):
-             raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
- 
+@@ -55,10 +56,12 @@ class HMAC:
          if not digestmod:
-             raise TypeError("Missing required parameter 'digestmod'.")
+             raise TypeError("Missing required argument 'digestmod'.")
  
 -        if _hashopenssl and isinstance(digestmod, (str, _functype)):
 +        if _hashopenssl.get_fips_mode() or (_hashopenssl and isinstance(digestmod, (str, _functype))):
@@ -833,7 +826,7 @@ index 8b4f920..20ef96c 100644
                  self._init_old(key, msg, digestmod)
          else:
              self._init_old(key, msg, digestmod)
-@@ -69,6 +71,9 @@ class HMAC:
+@@ -69,6 +72,9 @@ class HMAC:
          self.block_size = self._hmac.block_size
  
      def _init_old(self, key, msg, digestmod):
@@ -844,7 +837,7 @@ index 8b4f920..20ef96c 100644
              digest_cons = digestmod
          elif isinstance(digestmod, str):
 diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
-index 7cf9973..a9e4e39 100644
+index 1502fba..e40ca4b 100644
 --- a/Lib/test/test_hmac.py
 +++ b/Lib/test/test_hmac.py
 @@ -5,6 +5,7 @@ import hashlib
@@ -875,7 +868,7 @@ index 7cf9973..a9e4e39 100644
          with warnings.catch_warnings():
              warnings.simplefilter('error', RuntimeWarning)
              with self.assertRaises(RuntimeWarning):
-@@ -443,6 +450,7 @@ class ConstructorTestCase(unittest.TestCase):
+@@ -453,6 +460,7 @@ class ConstructorTestCase(unittest.TestCase):
          with self.assertRaisesRegex(TypeError, "immutable type"):
              C_HMAC.value = None
  
@@ -883,7 +876,7 @@ index 7cf9973..a9e4e39 100644
      @unittest.skipUnless(sha256_module is not None, 'need _sha256')
      def test_with_sha256_module(self):
          h = hmac.HMAC(b"key", b"hash this!", digestmod=sha256_module.sha256)
-@@ -471,6 +479,7 @@ class SanityTestCase(unittest.TestCase):
+@@ -489,6 +497,7 @@ class UpdateTestCase(unittest.TestCase):
  
  class CopyTestCase(unittest.TestCase):
  
@@ -891,7 +884,7 @@ index 7cf9973..a9e4e39 100644
      @hashlib_helper.requires_hashdigest('sha256')
      def test_attributes_old(self):
          # Testing if attributes are of same type.
-@@ -482,6 +491,7 @@ class CopyTestCase(unittest.TestCase):
+@@ -500,6 +509,7 @@ class CopyTestCase(unittest.TestCase):
          self.assertEqual(type(h1._outer), type(h2._outer),
              "Types of outer don't match.")
  
@@ -900,290 +893,43 @@ index 7cf9973..a9e4e39 100644
      def test_realcopy_old(self):
          # Testing if the copy method created a real copy.
 -- 
-2.39.1
-
+2.45.0
 
-From 2b06ee89344e8735cdc8435aadbdf83fe289e934 Mon Sep 17 00:00:00 2001
-From: Petr Viktorin <encukou@gmail.com>
-Date: Wed, 25 Aug 2021 16:44:43 +0200
-Subject: [PATCH 7/7] Disable hash-based PYCs in FIPS mode
 
-If FIPS mode is on, we can't use siphash-based HMAC
-(_Py_KeyedHash), so:
+From a0c3f9ac5a4e60ab22418a3196ae46ba34e9477b Mon Sep 17 00:00:00 2001
+From: Nikita Sobolev <mail@sobolevn.me>
+Date: Thu, 24 Nov 2022 01:47:31 +0300
+Subject: [PATCH 7/7] closes gh-99508: fix `TypeError` in
+ `Lib/importlib/_bootstrap_external.py` (GH-99635)
 
-- 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/py_compile.py                             |  2 ++
- Lib/test/support/__init__.py                  | 14 +++++++++++++
- Lib/test/test_cmd_line_script.py              |  2 ++
- 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(-)
+ Lib/importlib/_bootstrap_external.py                           | 3 ++-
+ .../next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst | 2 ++
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+ create mode 100644 Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst
 
-diff --git a/Lib/py_compile.py b/Lib/py_compile.py
-index db52725..5fca65e 100644
---- a/Lib/py_compile.py
-+++ b/Lib/py_compile.py
-@@ -70,7 +70,9 @@ class PycInvalidationMode(enum.Enum):
- 
- 
- def _get_default_invalidation_mode():
-+    import _hashlib
-     if (os.environ.get('SOURCE_DATE_EPOCH') and not
-+            _hashlib.get_fips_mode() and not
-             os.environ.get('RPM_BUILD_ROOT')):
-         return PycInvalidationMode.CHECKED_HASH
-     else:
-diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
-index c33f90d..7d40540 100644
---- a/Lib/test/support/__init__.py
-+++ b/Lib/test/support/__init__.py
-@@ -2225,6 +2225,20 @@ def requires_venv_with_pip():
-     return unittest.skipUnless(ctypes, 'venv: pip requires ctypes')
- 
- 
-+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 4dadbc0..7dc7e51 100644
---- a/Lib/test/test_cmd_line_script.py
-+++ b/Lib/test/test_cmd_line_script.py
-@@ -286,6 +286,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 os_helper.temp_dir() as script_dir:
-             script_name = _make_test_script(script_dir, '__main__')
-@@ -296,6 +297,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 os_helper.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 05154c8..c678d4a 100644
---- a/Lib/test/test_compileall.py
-+++ b/Lib/test/test_compileall.py
-@@ -800,14 +800,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 4bb0390..ff62483 100644
---- a/Lib/test/test_imp.py
-+++ b/Lib/test/test_imp.py
-@@ -350,6 +350,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'\xfb\xd9G\x05\xaf$\x9b~')
-         self.assertEqual(_imp.source_hash(43, b'hi'), b'\xd0/\x87C\xccC\xff\xe2')
-@@ -369,6 +370,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 os_helper.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 378dcbe..7b223a1 100644
---- a/Lib/test/test_importlib/source/test_file_loader.py
-+++ b/Lib/test/test_importlib/source/test_file_loader.py
-@@ -16,6 +16,7 @@ import types
- import unittest
- import warnings
- 
-+from test import support
- from test.support.import_helper import make_legacy_pyc, unload
- 
- from test.test_py_compile import without_source_date_epoch
-@@ -238,6 +239,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']
-@@ -269,6 +271,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'):
-@@ -294,6 +297,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']
-@@ -324,6 +328,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'):
-@@ -433,6 +438,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 e53f5d9..7266212 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 59a5200..81fadb3 100644
---- a/Lib/test/test_zipimport.py
-+++ b/Lib/test/test_zipimport.py
-@@ -190,6 +190,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)
-@@ -204,6 +205,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 07a8b90..e97b47b 100644
---- a/Python/import.c
-+++ b/Python/import.c
-@@ -2437,6 +2437,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)];
+diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
+index e53f6ac..bdc491e 100644
+--- a/Lib/importlib/_bootstrap_external.py
++++ b/Lib/importlib/_bootstrap_external.py
+@@ -1077,7 +1077,8 @@ class SourceLoader(_LoaderBasics):
+                 source_mtime is not None):
+             if hash_based:
+                 if source_hash is None:
+-                    source_hash = _imp.source_hash(source_bytes)
++                    source_hash = _imp.source_hash(_RAW_MAGIC_NUMBER,
++                                                   source_bytes)
+                 data = _code_to_hash_pyc(code_object, source_hash, check_source)
+             else:
+                 data = _code_to_timestamp_pyc(code_object, source_mtime,
+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
+new file mode 100644
+index 0000000..82720d1
+--- /dev/null
++++ b/Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst
+@@ -0,0 +1,2 @@
++Fix ``TypeError`` in ``Lib/importlib/_bootstrap_external.py`` while calling
++``_imp.source_hash()``.
 -- 
-2.39.1
+2.45.0
 
diff --git a/SOURCES/00397-tarfile-filter.patch b/SOURCES/00397-tarfile-filter.patch
index 3c4ebf4..bae08fa 100644
--- a/SOURCES/00397-tarfile-filter.patch
+++ b/SOURCES/00397-tarfile-filter.patch
@@ -1,4 +1,4 @@
-From 8b70605b594b3831331a9340ba764ff751871612 Mon Sep 17 00:00:00 2001
+From 0181d677dd7fd11bc19a211b3eb735ac3ad3d7fb Mon Sep 17 00:00:00 2001
 From: Petr Viktorin <encukou@gmail.com>
 Date: Mon, 6 Mar 2023 17:24:24 +0100
 Subject: [PATCH] CVE-2007-4559, PEP-706: Add filters for tarfile extraction
@@ -9,11 +9,11 @@ variable and config file.
 ---
  Lib/tarfile.py           |  42 +++++++++++++
  Lib/test/test_shutil.py  |   3 +-
- Lib/test/test_tarfile.py | 128 ++++++++++++++++++++++++++++++++++++++-
- 3 files changed, 169 insertions(+), 4 deletions(-)
+ Lib/test/test_tarfile.py | 127 ++++++++++++++++++++++++++++++++++++++-
+ 3 files changed, 168 insertions(+), 4 deletions(-)
 
 diff --git a/Lib/tarfile.py b/Lib/tarfile.py
-index 130b5e0..3b7d8d5 100755
+index 612217b..dc59fc6 100755
 --- a/Lib/tarfile.py
 +++ b/Lib/tarfile.py
 @@ -72,6 +72,13 @@ __all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", "ReadError",
@@ -30,7 +30,7 @@ index 130b5e0..3b7d8d5 100755
  
  #---------------------------------------------------------
  # tar constants
-@@ -2211,6 +2218,41 @@ class TarFile(object):
+@@ -2219,6 +2226,41 @@ class TarFile(object):
          if filter is None:
              filter = self.extraction_filter
              if filter is None:
@@ -73,10 +73,10 @@ index 130b5e0..3b7d8d5 100755
              if isinstance(filter, str):
                  raise TypeError(
 diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
-index 9bf4145..f247b82 100644
+index 6728d30..2338b63 100644
 --- a/Lib/test/test_shutil.py
 +++ b/Lib/test/test_shutil.py
-@@ -1665,7 +1665,8 @@ class TestArchives(BaseTest, unittest.TestCase):
+@@ -1774,7 +1774,8 @@ class TestArchives(BaseTest, unittest.TestCase):
      def check_unpack_tarball(self, format):
          self.check_unpack_archive(format, filter='fully_trusted')
          self.check_unpack_archive(format, filter='data')
@@ -87,10 +87,10 @@ index 9bf4145..f247b82 100644
  
      def test_unpack_archive_tar(self):
 diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
-index cdea033..4724285 100644
+index 389da7b..5a43f9d 100644
 --- a/Lib/test/test_tarfile.py
 +++ b/Lib/test/test_tarfile.py
-@@ -2,7 +2,7 @@ import sys
+@@ -3,7 +3,7 @@ import sys
  import os
  import io
  from hashlib import sha256
@@ -99,7 +99,7 @@ index cdea033..4724285 100644
  from random import Random
  import pathlib
  import shutil
-@@ -2999,7 +2999,11 @@ class NoneInfoExtractTests(ReadTest):
+@@ -3049,7 +3049,11 @@ class NoneInfoExtractTests(ReadTest):
          tar = tarfile.open(tarname, mode='r', encoding="iso8859-1")
          cls.control_dir = pathlib.Path(TEMPDIR) / "extractall_ctrl"
          tar.errorlevel = 0
@@ -112,7 +112,7 @@ index cdea033..4724285 100644
          tar.close()
          cls.control_paths = set(
              p.relative_to(cls.control_dir)
-@@ -3674,7 +3678,8 @@ class TestExtractionFilters(unittest.TestCase):
+@@ -3868,7 +3872,8 @@ class TestExtractionFilters(unittest.TestCase):
          """Ensure the default filter does not warn (like in 3.12)"""
          with ArchiveMaker() as arc:
              arc.add('foo')
@@ -122,10 +122,10 @@ index cdea033..4724285 100644
              with self.check_context(arc.open(), None):
                  self.expect_file('foo')
  
-@@ -3844,6 +3849,123 @@ class TestExtractionFilters(unittest.TestCase):
+@@ -4037,6 +4042,122 @@ class TestExtractionFilters(unittest.TestCase):
+         with self.check_context(arc.open(errorlevel='boo!'), filtererror_filter):
              self.expect_exception(TypeError)  # errorlevel is not int
  
- 
 +    @contextmanager
 +    def rh_config_context(self, config_lines=None):
 +        """Set up for testing various ways of overriding the default filter
@@ -242,10 +242,9 @@ index cdea033..4724285 100644
 +            ):
 +                self.check_trusted_default(tar, tempdir)
 +
-+
- def setUpModule():
-     os_helper.unlink(TEMPDIR)
-     os.makedirs(TEMPDIR)
+ 
+ class OverwriteTests(archiver_tests.OverwriteTests, unittest.TestCase):
+     testdir = os.path.join(TEMPDIR, "testoverwrite")
 -- 
-2.41.0
+2.44.0
 
diff --git a/SOURCES/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch b/SOURCES/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch
new file mode 100644
index 0000000..b29388f
--- /dev/null
+++ b/SOURCES/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch
@@ -0,0 +1,748 @@
+From 642f28679e04c7b4ec7731f0c8872103f21a76f8 Mon Sep 17 00:00:00 2001
+From: Victor Stinner <vstinner@python.org>
+Date: Fri, 15 Dec 2023 16:10:40 +0100
+Subject: [PATCH 1/2] 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                            | 150 ++++++++++++-
+ Lib/test/test_email/test_email.py             | 204 +++++++++++++++++-
+ ...-10-20-15-28-08.gh-issue-102988.dStNO7.rst |   8 +
+ 4 files changed, 360 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 0e266b6..6723dc4 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 8993858..41bb3c9 100644
+--- a/Lib/email/utils.py
++++ b/Lib/email/utils.py
+@@ -106,12 +106,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 _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.
+ 
+-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
++    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 +320,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 785696e..ad60ed3 100644
+--- a/Lib/test/test_email/test_email.py
++++ b/Lib/test/test_email/test_email.py
+@@ -17,6 +17,7 @@ from unittest.mock import patch
+ 
+ import email
+ import email.policy
++import email.utils
+ 
+ from email.charset import Charset
+ from email.generator import Generator, DecodedGenerator, BytesGenerator
+@@ -3336,15 +3337,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"""
+@@ -3535,6 +3675,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 0000000..3d0e9e4
+--- /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.
+-- 
+2.44.0
+
+
+From d371679e7c485551c10380ac11e5039a9fb4515b Mon Sep 17 00:00:00 2001
+From: Lumir Balhar <lbalhar@redhat.com>
+Date: Wed, 10 Jan 2024 08:53:53 +0100
+Subject: [PATCH 2/2] Make it possible to disable strict parsing in email
+ module
+
+---
+ Doc/library/email.utils.rst       | 26 +++++++++++
+ Lib/email/utils.py                | 55 ++++++++++++++++++++++-
+ Lib/test/test_email/test_email.py | 74 ++++++++++++++++++++++++++++++-
+ 3 files changed, 151 insertions(+), 4 deletions(-)
+
+diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst
+index 6723dc4..c89602d 100644
+--- a/Doc/library/email.utils.rst
++++ b/Doc/library/email.utils.rst
+@@ -69,6 +69,19 @@ of the new API.
+ 
+    If *strict* is true, use a strict parser which rejects malformed inputs.
+ 
++   The default setting for *strict* is set to ``True``, but you can override
++   it by setting the environment variable ``PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING``
++   to non-empty string.
++
++   Additionally, you can permanently set the default value for *strict* to
++   ``False`` by creating the configuration file ``/etc/python/email.cfg``
++   with the following content:
++
++   .. code-block:: ini
++
++      [email_addr_parsing]
++      PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true
++
+    .. versionchanged:: 3.13
+       Add *strict* optional parameter and reject malformed inputs by default.
+ 
+@@ -97,6 +110,19 @@ of the new API.
+ 
+    If *strict* is true, use a strict parser which rejects malformed inputs.
+ 
++   The default setting for *strict* is set to ``True``, but you can override
++   it by setting the environment variable ``PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING``
++   to non-empty string.
++
++   Additionally, you can permanently set the default value for *strict* to
++   ``False`` by creating the configuration file ``/etc/python/email.cfg``
++   with the following content:
++
++   .. code-block:: ini
++
++      [email_addr_parsing]
++      PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true
++
+    Here's a simple example that gets all the recipients of a message::
+ 
+       from email.utils import getaddresses
+diff --git a/Lib/email/utils.py b/Lib/email/utils.py
+index 41bb3c9..09a414c 100644
+--- a/Lib/email/utils.py
++++ b/Lib/email/utils.py
+@@ -48,6 +48,47 @@ TICK = "'"
+ specialsre = re.compile(r'[][\\()<>@,:;".]')
+ escapesre = re.compile(r'[\\"]')
+ 
++_EMAIL_CONFIG_FILE = "/etc/python/email.cfg"
++_cached_strict_addr_parsing = None
++
++
++def _use_strict_email_parsing():
++    """"Cache implementation for _cached_strict_addr_parsing"""
++    global _cached_strict_addr_parsing
++    if _cached_strict_addr_parsing is None:
++        _cached_strict_addr_parsing = _use_strict_email_parsing_impl()
++    return _cached_strict_addr_parsing
++
++
++def _use_strict_email_parsing_impl():
++    """Returns True if strict email parsing is not disabled by
++    config file or env variable.
++    """
++    disabled = bool(os.environ.get("PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING"))
++    if disabled:
++        return False
++
++    try:
++        file = open(_EMAIL_CONFIG_FILE)
++    except FileNotFoundError:
++        pass
++    else:
++        with file:
++            import configparser
++            config = configparser.ConfigParser(
++                interpolation=None,
++                comment_prefixes=('#', ),
++
++            )
++            config.read_file(file)
++            disabled = config.getboolean('email_addr_parsing', "PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING", fallback=None)
++
++    if disabled:
++        return False
++
++    return True
++
++
+ 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
+@@ -148,7 +189,7 @@ def _strip_quoted_realnames(addr):
+ 
+ supports_strict_parsing = True
+ 
+-def getaddresses(fieldvalues, *, strict=True):
++def getaddresses(fieldvalues, *, strict=None):
+     """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue.
+ 
+     When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in
+@@ -157,6 +198,11 @@ def getaddresses(fieldvalues, *, strict=True):
+     If strict is true, use a strict parser which rejects malformed inputs.
+     """
+ 
++    # If default is used, it's True unless disabled
++    # by env variable or config file.
++    if strict == None:
++        strict = _use_strict_email_parsing()
++
+     # 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 [('',
+@@ -320,7 +366,7 @@ def parsedate_to_datetime(data):
+             tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
+ 
+ 
+-def parseaddr(addr, *, strict=True):
++def parseaddr(addr, *, strict=None):
+     """
+     Parse addr into its constituent realname and email address parts.
+ 
+@@ -329,6 +375,11 @@ def parseaddr(addr, *, strict=True):
+ 
+     If strict is True, use a strict parser which rejects malformed inputs.
+     """
++    # If default is used, it's True unless disabled
++    # by env variable or config file.
++    if strict == None:
++        strict = _use_strict_email_parsing()
++
+     if not strict:
+         addrs = _AddressList(addr).addresslist
+         if not addrs:
+diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
+index ad60ed3..f85da56 100644
+--- a/Lib/test/test_email/test_email.py
++++ b/Lib/test/test_email/test_email.py
+@@ -8,6 +8,9 @@ import base64
+ import unittest
+ import textwrap
+ import warnings
++import contextlib
++import tempfile
++import os
+ 
+ from io import StringIO, BytesIO
+ from itertools import chain
+@@ -41,8 +44,8 @@ from email import quoprimime
+ from email import utils
+ 
+ from test import support
+-from test.support import threading_helper
+-from test.support.os_helper import unlink
++from test.support import threading_helper, swap_attr
++from test.support.os_helper import unlink, EnvironmentVarGuard
+ from test.test_email import openfile, TestEmailBase
+ 
+ # These imports are documented to work, but we are testing them using a
+@@ -3442,6 +3445,73 @@ Foo
+         # Test email.utils.supports_strict_parsing attribute
+         self.assertEqual(email.utils.supports_strict_parsing, True)
+ 
++    def test_parsing_errors_strict_set_via_env_var(self):
++        address = 'alice@example.org )Alice('
++        empty = ('', '')
++
++        # Reset cached default value to make the function
++        # reload the config file provided below.
++        utils._cached_strict_addr_parsing = None
++
++        # Strict disabled via env variable, old behavior expected
++        with EnvironmentVarGuard() as environ:
++            environ["PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING"] = "1"
++
++            self.assertEqual(utils.getaddresses([address]),
++                             [('', 'alice@example.org'), ('', ''), ('', 'Alice')])
++            self.assertEqual(utils.parseaddr([address]), ('', address))
++
++        # Clear cache again
++        utils._cached_strict_addr_parsing = None
++
++        # Default strict=True, empty result expected
++        self.assertEqual(utils.getaddresses([address]), [empty])
++        self.assertEqual(utils.parseaddr([address]), empty)
++
++        # Clear cache again
++        utils._cached_strict_addr_parsing = None
++
++        # Empty string in env variable = strict parsing enabled (default)
++        with EnvironmentVarGuard() as environ:
++            environ["PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING"] = ""
++
++            # Default strict=True, empty result expected
++            self.assertEqual(utils.getaddresses([address]), [empty])
++            self.assertEqual(utils.parseaddr([address]), empty)
++
++    @contextlib.contextmanager
++    def _email_strict_parsing_conf(self):
++        """Context for the given email strict parsing configured in config file"""
++        with tempfile.TemporaryDirectory() as tmpdirname:
++            filename = os.path.join(tmpdirname, 'conf.cfg')
++            with swap_attr(utils, "_EMAIL_CONFIG_FILE", filename):
++                with open(filename, 'w') as file:
++                    file.write('[email_addr_parsing]\n')
++                    file.write('PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true')
++                utils._EMAIL_CONFIG_FILE = filename
++                yield
++
++    def test_parsing_errors_strict_disabled_via_config_file(self):
++        address = 'alice@example.org )Alice('
++        empty = ('', '')
++
++        # Reset cached default value to make the function
++        # reload the config file provided below.
++        utils._cached_strict_addr_parsing = None
++
++        # Strict disabled via config file, old results expected
++        with self._email_strict_parsing_conf():
++            self.assertEqual(utils.getaddresses([address]),
++                             [('', 'alice@example.org'), ('', ''), ('', 'Alice')])
++            self.assertEqual(utils.parseaddr([address]), ('', address))
++
++        # Clear cache again
++        utils._cached_strict_addr_parsing = None
++
++        # Default strict=True, empty result expected
++        self.assertEqual(utils.getaddresses([address]), [empty])
++        self.assertEqual(utils.parseaddr([address]), empty)
++
+     def test_getaddresses_nasty(self):
+         for addresses, expected in (
+             (['"Sürname, Firstname" <to@example.com>'],
+-- 
+2.44.0
+
diff --git a/SOURCES/00422-fix-expat-tests.patch b/SOURCES/00422-fix-expat-tests.patch
new file mode 100644
index 0000000..64c8449
--- /dev/null
+++ b/SOURCES/00422-fix-expat-tests.patch
@@ -0,0 +1,75 @@
+From 670984c96eea60488c5355b4cf535c1ee3cf081a Mon Sep 17 00:00:00 2001
+From: rpm-build <rpm-build>
+Date: Wed, 24 Apr 2024 04:24:16 +0200
+Subject: [PATCH] Fix xml tests
+
+---
+ Lib/test/test_pyexpat.py   | 3 +++
+ Lib/test/test_sax.py       | 2 ++
+ Lib/test/test_xml_etree.py | 6 ++++++
+ 3 files changed, 11 insertions(+)
+
+diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py
+index 44bd1de..5976fa0 100644
+--- a/Lib/test/test_pyexpat.py
++++ b/Lib/test/test_pyexpat.py
+@@ -3,6 +3,7 @@
+ 
+ import os
+ import platform
++import pyexpat
+ import sys
+ import sysconfig
+ import unittest
+@@ -793,6 +794,8 @@ class ReparseDeferralTest(unittest.TestCase):
+ 
+         self.assertEqual(started, ['doc'])
+ 
++    @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
++                     "Reparse deferral not defined for libexpat < 2.6.0")
+     def test_reparse_deferral_disabled(self):
+         started = []
+ 
+diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py
+index 9b3014a..5960de1 100644
+--- a/Lib/test/test_sax.py
++++ b/Lib/test/test_sax.py
+@@ -1240,6 +1240,8 @@ class ExpatReaderTest(XmlTestBase):
+ 
+         self.assertEqual(result.getvalue(), start + b"<doc></doc>")
+ 
++    @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
++                     "Reparse deferral not defined for libexpat < 2.6.0")
+     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 8becafb..5e9b6b5 100644
+--- a/Lib/test/test_xml_etree.py
++++ b/Lib/test/test_xml_etree.py
+@@ -1424,9 +1424,13 @@ class XMLPullParserTest(unittest.TestCase):
+         self.assert_event_tags(parser, [('end', 'root')])
+         self.assertIsNone(parser.close())
+ 
++    @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
++        "test not compatible with the latest expat security release")
+     def test_simple_xml_chunk_1(self):
+         self.test_simple_xml(chunk_size=1, flush=True)
+ 
++    @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
++        "test not compatible with the latest expat security release")
+     def test_simple_xml_chunk_5(self):
+         self.test_simple_xml(chunk_size=5, flush=True)
+ 
+@@ -1651,6 +1655,8 @@ class XMLPullParserTest(unittest.TestCase):
+ 
+         self.assert_event_tags(parser, [('end', 'doc')])
+ 
++    @unittest.skipIf(pyexpat.version_info < (2, 6, 0),
++                     "Reparse deferral not defined for libexpat < 2.6.0")
+     def test_flush_reparse_deferral_disabled(self):
+         parser = ET.XMLPullParser(events=('start', 'end'))
+ 
+-- 
+2.44.0
+
diff --git a/SOURCES/Python-3.11.9.tar.xz.asc b/SOURCES/Python-3.11.9.tar.xz.asc
new file mode 100644
index 0000000..c79d99b
--- /dev/null
+++ b/SOURCES/Python-3.11.9.tar.xz.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmYNMEcACgkQ/+h0BBaL
+2EeHhxAAuuIM9bl0dgAWOjbgRjCeXR8aFdfcI4dkO7bZrUy8eKbM+XCvPUUvloRJ
+vzGkxYyTmI4kcNPOHfscUwH7AVVij8nGv7WeaXBUZGIXNwfHwvqOxvYvSsNNNFnr
+70yJB7Df8/2s0XqFx3X1aWcnyMDerWKpfJ/VI/NPmCVxkYXGshuTTSFcCMTSFBQB
+sNrIb5NWAsBF4R85uRQDlCg1AoyaKOdJNQkPo1Nrjol1ExJ+MHE7+E+QL9pQkUWG
+SBISPUhJySBAegxolw6YR5dz1L4nukueQDJz3NizUeQGDvH7h1ImY8cypRi44U61
+SUUHhBfmUBiC2dS/tTQawySULWcgbkV4GJ6cJZfDd95uffd4S/GDJCa2wCE2UTlA
+XzQHwbcnIeoL064gX7ruBuFHJ6n/Oz7nZkFqbH2aqLTAWgLiUq31xH3HY734sL6X
+zIJQRbcK1EM7cnNjKMVPlnHpAeKbsbHbU6yzWwZ7reIoyWlZ7vEGrfXO7Kmul93K
+wVaWu0AiOY566ugekdDx4cKV+FQN6oppAN63yTfPJ2Ddcmxs4KNrtozw9OAgDTPE
+GTPFD6V1CMuyQj/jOpAmbj+4bRD4Mx3u2PSittvrIeopxrXPsGGSZ5kdl62Xa2+A
+DzKyYNXzcmxqS9lGdFb+OWCTyAIXxwZrdz1Q61g5xDvR9z/wZiI=
+=Br9/
+-----END PGP SIGNATURE-----
diff --git a/SOURCES/check-pyc-timestamps.py b/SOURCES/check-pyc-timestamps.py
index 5b4c809..d619dae 100644
--- a/SOURCES/check-pyc-timestamps.py
+++ b/SOURCES/check-pyc-timestamps.py
@@ -17,9 +17,9 @@ LEVELS = (None, 1, 2)
 not_compiled = [
     '/usr/bin/*',
     '/usr/lib/rpm/redhat/*',
-    '*/test/bad_coding.py',
-    '*/test/bad_coding2.py',
-    '*/test/badsyntax_*.py',
+    '*/test/*/bad_coding.py',
+    '*/test/*/bad_coding2.py',
+    '*/test/*/badsyntax_*.py',
     '*/lib2to3/tests/data/bom.py',
     '*/lib2to3/tests/data/crlf.py',
     '*/lib2to3/tests/data/different_encoding.py',
diff --git a/SPECS/python3.11.spec b/SPECS/python3.11.spec
index c9e8d45..357050d 100644
--- a/SPECS/python3.11.spec
+++ b/SPECS/python3.11.spec
@@ -16,11 +16,11 @@ URL: https://www.python.org/
 
 #  WARNING  When rebasing to a new Python version,
 #           remember to update the python3-docs package as well
-%global general_version %{pybasever}.5
+%global general_version %{pybasever}.9
 #global prerel ...
 %global upstream_version %{general_version}%{?prerel}
 Version: %{general_version}%{?prerel:~%{prerel}}
-Release: 1%{?dist}
+Release: 2%{?dist}
 License: Python
 
 
@@ -63,7 +63,7 @@ License: Python
 # If the rpmwheels condition is disabled, we use the bundled wheel packages
 # from Python with the versions below.
 # This needs to be manually updated when we update Python.
-%global pip_version 23.2.1
+%global pip_version 24.0
 %global setuptools_version 65.5.0
 
 # Expensive optimizations (mainly, profile-guided optimizations)
@@ -371,6 +371,26 @@ Patch378: 00378-support-expat-2-4-5.patch
 # - https://access.redhat.com/articles/7004769
 Patch397: 00397-tarfile-filter.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.
+#
+# Upstream PR: https://github.com/python/cpython/pull/111116
+#
+# Second patch implmenets the possibility to restore the old behavior via
+# config file or environment variable.
+Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch
+
+# 00422 #
+# Fix the test suite for releases of expat < 2.6.0
+# which backport the CVE-2023-52425 fix.
+# Downstream only.
+Patch422: 00422-fix-expat-tests.patch
+
 # (New patches go here ^^^)
 #
 # When adding new patches to "python" and "python3" in Fedora, EL, etc.,
@@ -389,10 +409,10 @@ Patch397: 00397-tarfile-filter.patch
 # Descriptions, and metadata for subpackages
 # ==========================================
 
-# Require alternatives version that implements the --keep-foreign flag
-Requires:         alternatives >= 1.19.1-1
-Requires(post):   alternatives >= 1.19.1-1
-Requires(postun): alternatives >= 1.19.1-1
+# Require alternatives version that implements the --keep-foreign flag and fixes rhbz#2203820
+Requires:         alternatives >= 1.19.2-1
+Requires(post):   alternatives >= 1.19.2-1
+Requires(postun): alternatives >= 1.19.2-1
 
 # When the user tries to `yum install python`, yum will list this package among
 # the possible alternatives
@@ -540,8 +560,8 @@ Requires: %{pkgname}-libs%{?_isa} = %{version}-%{release}
 Requires: (python-rpm-macros if rpm-build)
 Requires: (python3-rpm-macros if rpm-build)
 
-# Require alternatives version that implements the --keep-foreign flag
-Requires(postun): alternatives >= 1.19.1-1
+# Require alternatives version that implements the --keep-foreign flag and fixes rhbz#2203820
+Requires(postun): alternatives >= 1.19.2-1
 
 # python3.11 installs the alternatives master symlink to which we attach a slave
 Requires(post): %{pkgname}
@@ -594,8 +614,8 @@ Provides: idle = %{version}-%{release}
 Provides: %{pkgname}-tools = %{version}-%{release}
 Provides: %{pkgname}-tools%{?_isa} = %{version}-%{release}
 
-# Require alternatives version that implements the --keep-foreign flag
-Requires(postun): alternatives >= 1.19.1-1
+# Require alternatives version that implements the --keep-foreign flag and fixes rhbz#2203820
+Requires(postun): alternatives >= 1.19.2-1
 
 # python3.11 installs the alternatives master symlink to which we attach a slave
 Requires(post): %{pkgname}
@@ -660,8 +680,8 @@ Requires: %{pkgname}-idle%{?_isa} = %{version}-%{release}
 
 %unversioned_obsoletes_of_python3_X_if_main debug
 
-# Require alternatives version that implements the --keep-foreign flag
-Requires(postun): alternatives >= 1.19.1-1
+# Require alternatives version that implements the --keep-foreign flag and fixes rhbz#2203820
+Requires(postun): alternatives >= 1.19.2-1
 
 # python3.11 installs the alternatives master symlink to which we attach a slave
 Requires(post): %{pkgname}
@@ -1009,6 +1029,10 @@ for tool in pygettext msgfmt; do
   ln -s ${tool}%{pybasever}.py %{buildroot}%{_bindir}/${tool}3.py
 done
 
+# Install missing test data
+# Fixed upstream: https://github.com/python/cpython/pull/112784
+cp -rp Lib/test/regrtestdata/ %{buildroot}%{pylibdir}/test/
+
 # Switch all shebangs to refer to the specific Python version.
 # This currently only covers files matching ^[a-zA-Z0-9_]+\.py$,
 # so handle files named using other naming scheme separately.
@@ -1299,7 +1323,7 @@ if [ $1 -eq 0 ]; then
 fi
 
 %post idle
-alternatives --keep-foreign --add-slave python3 %{_bindir}/python3.11 \
+alternatives --add-slave python3 %{_bindir}/python3.11 \
      %{_bindir}/idle3 \
      idle3 \
      %{_bindir}/idle3.11
@@ -1307,7 +1331,7 @@ alternatives --keep-foreign --add-slave python3 %{_bindir}/python3.11 \
 %postun idle
 # Do this only during uninstall process (not during update)
 if [ $1 -eq 0 ]; then
-     alternatives --remove-slave python3 %{_bindir}/python3.11 \
+     alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.11 \
         idle3
 fi
 
@@ -1821,6 +1845,24 @@ fi
 # ======================================================
 
 %changelog
+* Tue Jun 11 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.11.9-2
+- Enable importing of hash-based .pyc files under FIPS mode
+Resolves: RHEL-40783
+
+* Mon Apr 22 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.11.9-1
+- Rebase to 3.11.9
+- Security fixes for CVE-2023-6597 and CVE-2024-0450
+- Fix expat tests for the latest expat security release
+Resolves: RHEL-33672, RHEL-33684
+
+* Mon Jan 22 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.11.7-1
+- Rebase to 3.11.7
+Resolves: RHEL-21915
+
+* Tue Jan 09 2024 Lumír Balhar <lbalhar@redhat.com> - 3.11.5-2
+- Security fix for CVE-2023-27043
+Resolves: RHEL-7842
+
 * Thu Sep 07 2023 Charalampos Stratakis <cstratak@redhat.com> - 3.11.5-1
 - Rebase to 3.11.5
 - Security fixes for CVE-2023-40217 and CVE-2023-41105
-- 
GitLab