import python38-3.8.6-3.module+el8.4.0+9579+e9717e18

c8-stream-3.8 imports/c8-stream-3.8/python38-3.8.6-3.module+el8.4.0+9579+e9717e18
MSVSphere Packaging Team 9 months ago
parent 86b2f9c6dc
commit 08cddf5419

2
.gitignore vendored

@ -1 +1 @@
SOURCES/Python-3.8.17-noexe.tar.xz
SOURCES/Python-3.8.6-noexe.tar.xz

@ -1 +1 @@
5ecdca78a141bb6d7e13732920886affd7338fca SOURCES/Python-3.8.17-noexe.tar.xz
e77d08894869ecf483e9f945663f75316ad68bf1 SOURCES/Python-3.8.6-noexe.tar.xz

@ -1,4 +1,4 @@
From ff07393a8c9c3d69090a775a8d4b89a3019f71a9 Mon Sep 17 00:00:00 2001
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
Date: Wed, 15 Aug 2018 15:36:29 +0200
Subject: [PATCH] 00189: Instead of bundled wheels, use our RPM packaged wheels
@ -8,11 +8,11 @@ We keep them in /usr/share/python-wheels
Downstream only: upstream bundles
We might eventually pursuit upstream support, but it's low prio
---
Lib/ensurepip/__init__.py | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
Lib/ensurepip/__init__.py | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
index b291e9a..798d0f4 100644
index 9415fd73b8..f58dab1800 100644
--- a/Lib/ensurepip/__init__.py
+++ b/Lib/ensurepip/__init__.py
@@ -1,6 +1,7 @@
@ -24,17 +24,16 @@ index b291e9a..798d0f4 100644
import sys
import runpy
import tempfile
@@ -9,8 +10,26 @@ import subprocess
@@ -8,10 +9,24 @@ import tempfile
__all__ = ["version", "bootstrap"]
_PACKAGE_NAMES = ('setuptools', 'pip')
-_SETUPTOOLS_VERSION = "56.0.0"
-_PIP_VERSION = "23.0.1"
+
+_WHEEL_DIR = "/usr/share/python38-wheels/"
+
-_SETUPTOOLS_VERSION = "49.2.1"
+_wheels = {}
+
-_PIP_VERSION = "20.2.1"
+def _get_most_recent_wheel_version(pkg):
+ prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg))
+ _wheels[pkg] = {}
@ -49,11 +48,10 @@ index b291e9a..798d0f4 100644
+_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools")
+
+_PIP_VERSION = _get_most_recent_wheel_version("pip")
+
_PROJECTS = [
("setuptools", _SETUPTOOLS_VERSION, "py3"),
("pip", _PIP_VERSION, "py3"),
@@ -99,13 +118,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
@@ -105,13 +120,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
# additional paths that need added to sys.path
additional_paths = []
for project, version, py_tag in _PROJECTS:
@ -71,6 +69,3 @@ index b291e9a..798d0f4 100644
additional_paths.append(os.path.join(tmpdir, wheel_name))
--
2.35.3

@ -1,4 +1,4 @@
From 31ae5d3189a0b8ec07c55df3785d27d769bc90a5 Mon Sep 17 00:00:00 2001
From 7b70e87ecc1a75f005bdffd644ceca6c9e9679fa Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 25 Jul 2019 16:19:52 +0200
Subject: [PATCH 01/36] Expose OpenSSL FIPS_mode() as hashlib.get_fips_mode()
@ -26,12 +26,12 @@ index 56873b7..63ae836 100644
for __func_name in __always_supported:
# try them all, some may not work due to the OpenSSL
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 93bf25f..2409522 100644
index edadbcb..9874b06 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -33,6 +33,9 @@
@@ -26,6 +26,9 @@
#include <openssl/objects.h>
#include <openssl/err.h>
#include "openssl/err.h"
+/* Expose FIPS_mode */
+#include <openssl/crypto.h>
@ -39,7 +39,7 @@ index 93bf25f..2409522 100644
#ifndef OPENSSL_THREADS
# error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL"
#endif
@@ -1079,12 +1082,46 @@ generate_hash_name_list(void)
@@ -1072,12 +1075,46 @@ generate_hash_name_list(void)
return state.set;
}
@ -123,10 +123,10 @@ index 9aaea47..30fd8a9 100644
-/*[clinic end generated code: output=38c2637f67e9bb79 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=5467006d93e7479e input=a9049054013a1b77]*/
--
2.38.1
2.25.4
From 9e12b2fdecca4fba5d777923f7742fffb7b6240d Mon Sep 17 00:00:00 2001
From 4e1fa0339c257987984caa278516d46c35463385 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Thu, 25 Jul 2019 17:04:06 +0200
Subject: [PATCH 02/36] Use python's fall backs for the crypto it implements
@ -410,10 +410,10 @@ index 63ae836..1bcfdf9 100644
+if not get_fips_mode():
+ del __py_new
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index e6cec4e..f40cc83 100644
index 8b53d23..e9abcbb 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -954,6 +954,7 @@ class KDFTests(unittest.TestCase):
@@ -945,6 +945,7 @@ class KDFTests(unittest.TestCase):
iterations=1, dklen=None)
self.assertEqual(out, self.pbkdf2_results['sha1'][0][0])
@ -422,10 +422,10 @@ index e6cec4e..f40cc83 100644
self._test_pbkdf2_hmac(py_hashlib.pbkdf2_hmac)
--
2.38.1
2.25.4
From a7711c418ea72f9ea18bbb70a2d52ab44489066b Mon Sep 17 00:00:00 2001
From 91b5c97d586a98cb95e215ecd2c02b18c8783e7a Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 25 Jul 2019 17:19:06 +0200
Subject: [PATCH 03/36] Disable Python's hash implementations in FIPS mode,
@ -438,8 +438,8 @@ Subject: [PATCH 03/36] Disable Python's hash implementations in FIPS mode,
Modules/_blake2/blake2s_impl.c | 5 +++
Modules/_hashopenssl.c | 37 +------------------
Modules/_sha3/sha3module.c | 5 +++
setup.py | 48 +++++++++++++------------
7 files changed, 111 insertions(+), 58 deletions(-)
setup.py | 47 ++++++++++++------------
7 files changed, 110 insertions(+), 58 deletions(-)
create mode 100644 Include/_hashopenssl.h
diff --git a/Include/_hashopenssl.h b/Include/_hashopenssl.h
@ -596,10 +596,10 @@ index ef2f7e1..389711a 100644
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 2409522..a51c502 100644
index 9874b06..d733a39 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -24,6 +24,7 @@
@@ -17,6 +17,7 @@
#include "structmember.h"
#include "hashlib.h"
#include "pystrhex.h"
@ -607,18 +607,18 @@ index 2409522..a51c502 100644
/* EVP is the preferred interface to hashing in OpenSSL */
@@ -31,10 +32,6 @@
@@ -24,10 +25,6 @@
#include <openssl/hmac.h>
/* We use the object interface to discover what hashes OpenSSL supports. */
#include <openssl/objects.h>
-#include <openssl/err.h>
-#include "openssl/err.h"
-
-/* Expose FIPS_mode */
-#include <openssl/crypto.h>
#ifndef OPENSSL_THREADS
# error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL"
@@ -76,38 +73,6 @@ class _hashlib.HASH "EVPobject *" "&EVPtype"
@@ -69,38 +66,6 @@ class _hashlib.HASH "EVPobject *" "&EVPtype"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a881a5092eecad28]*/
@ -695,10 +695,10 @@ index c1fb618..34d09b4 100644
return NULL;
}
diff --git a/setup.py b/setup.py
index 0b24dd6..f7c4be1 100644
index 84f7300..06d1ce6 100644
--- a/setup.py
+++ b/setup.py
@@ -1677,7 +1677,6 @@ class PyBuildExt(build_ext):
@@ -1688,7 +1688,6 @@ class PyBuildExt(build_ext):
def detect_modules(self):
self.configure_compiler()
self.init_inc_lib_dirs()
@ -706,7 +706,7 @@ index 0b24dd6..f7c4be1 100644
self.detect_simple_extensions()
if TEST_EXTENSIONS:
self.detect_test_extensions()
@@ -2165,7 +2164,7 @@ class PyBuildExt(build_ext):
@@ -2187,7 +2186,7 @@ class PyBuildExt(build_ext):
sources=sources,
depends=depends))
@ -715,7 +715,7 @@ index 0b24dd6..f7c4be1 100644
# Detect SSL support for the socket module (via _ssl)
config_vars = sysconfig.get_config_vars()
@@ -2186,7 +2185,7 @@ class PyBuildExt(build_ext):
@@ -2208,7 +2207,7 @@ class PyBuildExt(build_ext):
if not openssl_libs:
# libssl and libcrypto not found
self.missing.extend(['_ssl', '_hashlib'])
@ -724,7 +724,7 @@ index 0b24dd6..f7c4be1 100644
# Find OpenSSL includes
ssl_incs = find_file(
@@ -2194,7 +2193,7 @@ class PyBuildExt(build_ext):
@@ -2216,7 +2215,7 @@ class PyBuildExt(build_ext):
)
if ssl_incs is None:
self.missing.extend(['_ssl', '_hashlib'])
@ -733,7 +733,7 @@ index 0b24dd6..f7c4be1 100644
# OpenSSL 1.0.2 uses Kerberos for KRB5 ciphers
krb5_h = find_file(
@@ -2204,12 +2203,24 @@ class PyBuildExt(build_ext):
@@ -2226,12 +2225,23 @@ class PyBuildExt(build_ext):
if krb5_h:
ssl_incs.extend(krb5_h)
@ -749,7 +749,6 @@ index 0b24dd6..f7c4be1 100644
+ def detect_openssl_hashlib(self):
+
+ config_vars = sysconfig.get_config_vars()
+
+
if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"):
self.add(Extension(
@ -758,10 +757,10 @@ index 0b24dd6..f7c4be1 100644
- library_dirs=openssl_libdirs,
- libraries=openssl_libs,
+ **self.detect_openssl_args(),
depends=[
'socketmodule.h',
'_ssl/debughelpers.c',
@@ -2222,22 +2233,12 @@ class PyBuildExt(build_ext):
depends=['socketmodule.h', '_ssl/debughelpers.c'])
)
else:
@@ -2239,22 +2249,12 @@ class PyBuildExt(build_ext):
self.add(Extension('_hashlib', ['_hashopenssl.c'],
depends=['hashlib.h'],
@ -788,7 +787,7 @@ index 0b24dd6..f7c4be1 100644
blake2_deps = glob(os.path.join(escape(self.srcdir),
'Modules/_blake2/impl/*'))
@@ -2247,6 +2248,7 @@ class PyBuildExt(build_ext):
@@ -2264,6 +2264,7 @@ class PyBuildExt(build_ext):
['_blake2/blake2module.c',
'_blake2/blake2b_impl.c',
'_blake2/blake2s_impl.c'],
@ -796,7 +795,7 @@ index 0b24dd6..f7c4be1 100644
depends=blake2_deps))
sha3_deps = glob(os.path.join(escape(self.srcdir),
@@ -2254,7 +2256,9 @@ class PyBuildExt(build_ext):
@@ -2271,7 +2272,9 @@ class PyBuildExt(build_ext):
sha3_deps.append('hashlib.h')
self.add(Extension('_sha3',
['_sha3/sha3module.c'],
@ -808,10 +807,10 @@ index 0b24dd6..f7c4be1 100644
def detect_nis(self):
if MS_WINDOWS or CYGWIN or HOST_PLATFORM == 'qnx6':
--
2.38.1
2.25.4
From 5928affee1e8306877918efc417090512faea14d Mon Sep 17 00:00:00 2001
From d9b8f21a1b5feb177ece4c595ce8b639f02548c8 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Thu, 12 Dec 2019 16:58:31 +0100
Subject: [PATCH 04/36] Expose all hashes available to OpenSSL
@ -822,10 +821,10 @@ Subject: [PATCH 04/36] Expose all hashes available to OpenSSL
2 files changed, 447 insertions(+), 1 deletion(-)
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index a51c502..713e15a 100644
index d733a39..6982268 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -201,6 +201,12 @@ py_digest_by_name(const char *name)
@@ -194,6 +194,12 @@ py_digest_by_name(const char *name)
else if (!strcmp(name, "blake2b512")) {
digest = EVP_blake2b512();
}
@ -838,7 +837,7 @@ index a51c502..713e15a 100644
#endif
}
@@ -719,6 +725,142 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj)
@@ -712,6 +718,142 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj)
return EVP_fast_new(module, data_obj, EVP_sha512());
}
@ -981,7 +980,7 @@ index a51c502..713e15a 100644
/*[clinic input]
_hashlib.pbkdf2_hmac as pbkdf2_hmac
@@ -1094,6 +1236,14 @@ static struct PyMethodDef EVP_functions[] = {
@@ -1087,6 +1229,14 @@ static struct PyMethodDef EVP_functions[] = {
_HASHLIB_OPENSSL_SHA256_METHODDEF
_HASHLIB_OPENSSL_SHA384_METHODDEF
_HASHLIB_OPENSSL_SHA512_METHODDEF
@ -1310,10 +1309,10 @@ index 30fd8a9..e96a752 100644
-/*[clinic end generated code: output=5467006d93e7479e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=be8e21a10dff71e7 input=a9049054013a1b77]*/
--
2.38.1
2.25.4
From eb264b74a2a6fc820813edbfa611b782edcda088 Mon Sep 17 00:00:00 2001
From d4c78750ffb431fe34a18aab7cdf84d3a68d7fc1 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 25 Jul 2019 18:13:45 +0200
Subject: [PATCH 05/36] Fix tests
@ -1323,7 +1322,7 @@ Subject: [PATCH 05/36] Fix tests
1 file changed, 45 insertions(+), 13 deletions(-)
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index f40cc83..972eda1 100644
index e9abcbb..2a55fd4 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -190,7 +190,9 @@ class HashLibTestCase(unittest.TestCase):
@ -1442,10 +1441,10 @@ index f40cc83..972eda1 100644
@requires_sha3
def test_extra_sha3(self):
--
2.38.1
2.25.4
From 7c34172f398cdea804e8a4671f996e9b3706ec7d Mon Sep 17 00:00:00 2001
From 4ec7034d73e681041758fc80f75e061c0e506449 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Fri, 26 Jul 2019 11:27:57 +0200
Subject: [PATCH 06/36] Change FIPS exceptions from _blake2, _sha3 module init
@ -1584,10 +1583,10 @@ index 34d09b4..3079e1e 100644
if ((m = PyModule_Create(&_SHA3module)) == NULL) {
return NULL;
--
2.38.1
2.25.4
From c968e85d077123510f8b6441b169b46f6e0e9d26 Mon Sep 17 00:00:00 2001
From ed6f93218c2190d34ee0b0f4c7599d306708449f Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Fri, 26 Jul 2019 11:24:09 +0200
Subject: [PATCH 07/36] Make hashlib importable under FIPS mode
@ -1619,10 +1618,10 @@ index 1bcfdf9..898e6dc 100644
--
2.38.1
2.25.4
From ea2a87a494c36eae2cc2cf343074ff0214e37517 Mon Sep 17 00:00:00 2001
From 66c5862bb09586168caac4d6ba6142ed3198fe1d Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Fri, 26 Jul 2019 15:41:10 +0200
Subject: [PATCH 08/36] Implement hmac.new using new built-in module,
@ -2250,10 +2249,10 @@ index 0000000..b472a6e
+}
+/*[clinic end generated code: output=10b6e8cac6d7a2c9 input=a9049054013a1b77]*/
diff --git a/setup.py b/setup.py
index f7c4be1..6fc7e72 100644
index 06d1ce6..ca8bc2b 100644
--- a/setup.py
+++ b/setup.py
@@ -2235,6 +2235,10 @@ class PyBuildExt(build_ext):
@@ -2251,6 +2251,10 @@ class PyBuildExt(build_ext):
depends=['hashlib.h'],
**self.detect_openssl_args()) )
@ -2265,10 +2264,10 @@ index f7c4be1..6fc7e72 100644
# RHEL: Always force OpenSSL for md5, sha1, sha256, sha512;
# don't build Python's implementations.
--
2.38.1
2.25.4
From 03ac0fc03e418f73ed76b203f779a083312aaff9 Mon Sep 17 00:00:00 2001
From 6ec3a1afd87a3aa411a19727e212ebf81fee49cc Mon Sep 17 00:00:00 2001
From: Marcel Plch <mplch@redhat.com>
Date: Mon, 29 Jul 2019 12:45:11 +0200
Subject: [PATCH 09/36] FIPS review
@ -2480,10 +2479,10 @@ index ca95d72..216ed04 100644
+ return PyModuleDef_Init(&_hmacopenssl_def);
}
--
2.38.1
2.25.4
From f130424124b4cbd4b61f1dd84b69f1f4f9e766f6 Mon Sep 17 00:00:00 2001
From 8645a4cf6ee2ad10fac3d081da78eabb06099a9c Mon Sep 17 00:00:00 2001
From: Marcel Plch <mplch@redhat.com>
Date: Mon, 29 Jul 2019 13:05:04 +0200
Subject: [PATCH 10/36] revert cosmetic nitpick and remove trailing whitespace
@ -2529,10 +2528,10 @@ index 216ed04..221714c 100644
.m_methods = hmacopenssl_functions,
.m_slots = hmacopenssl_slots,
--
2.38.1
2.25.4
From 5259a4c7bd3d1ce2f0bdbcd0c95583c95d7d0d23 Mon Sep 17 00:00:00 2001
From d80ae6ac0abf1e0ca5a32ff80343e927587cf5a6 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Wed, 31 Jul 2019 15:43:43 +0200
Subject: [PATCH 11/36] Add initial tests for various hashes under FIPS mode
@ -2613,10 +2612,10 @@ index 0000000..bee911e
+if __name__ == "__main__":
+ unittest.main()
--
2.38.1
2.25.4
From c2b7ebc17d79f8c91572a790d425f9106d4dd70e Mon Sep 17 00:00:00 2001
From 414c04713ad89bdeeb7a074f953c0085d541eae6 Mon Sep 17 00:00:00 2001
From: Marcel Plch <mplch@redhat.com>
Date: Thu, 1 Aug 2019 16:39:37 +0200
Subject: [PATCH 12/36] Initialize HMAC type.
@ -2682,10 +2681,10 @@ index 221714c..239445a 100644
fail:
--
2.38.1
2.25.4
From 931784f1a431538486905ef1d3958dfefd5478e6 Mon Sep 17 00:00:00 2001
From 0157b52ac7f15610526497f9188eb84ed3846993 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 1 Aug 2019 17:57:05 +0200
Subject: [PATCH 13/36] Use a stronger hash in multiprocessing handshake
@ -2730,10 +2729,10 @@ index 8e2facf..bb4acb6 100644
response = connection.recv_bytes(256) # reject large message
if response != WELCOME:
--
2.38.1
2.25.4
From 8a68ba81a66181d9a4b6c7b1e77f944efada2aae Mon Sep 17 00:00:00 2001
From 3730b4186cf708bb8ea528c22734d4c1176fc9ad Mon Sep 17 00:00:00 2001
From: Marcel Plch <mplch@redhat.com>
Date: Fri, 2 Aug 2019 17:36:01 +0200
Subject: [PATCH 14/36] Fix refcounting
@ -2804,10 +2803,10 @@ index 239445a..9c28828 100644
--
2.38.1
2.25.4
From 5d835c236b392e8741fabe9b9d83d5482842dd87 Mon Sep 17 00:00:00 2001
From 1873bfe385a1b952ba11c2b2f15755353f2411df Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 13:37:05 +0200
Subject: [PATCH 15/36] hmac: Don't default to md5 in FIPS mode
@ -2830,10 +2829,10 @@ index daabc8c..0302364 100644
result = _hmacopenssl.new(key, digestmod=name)
if msg:
--
2.38.1
2.25.4
From d865fb5a73ffaf39de68b69c9ccb81ca17b6c430 Mon Sep 17 00:00:00 2001
From f77c854b9c5aab3e2bb517b6d0c08197a116efb1 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 14:20:58 +0200
Subject: [PATCH 16/36] Make _hmacopenssl.HMAC subclassable; subclass it as
@ -3121,10 +3120,10 @@ index b472a6e..861acc1 100644
-/*[clinic end generated code: output=10b6e8cac6d7a2c9 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d93ad460795d49b5 input=a9049054013a1b77]*/
--
2.38.1
2.25.4
From 0a75e9886721aa076542a40159b02fbd18bd8cd3 Mon Sep 17 00:00:00 2001
From b357a1f823b7b231d1a8bc149b5a950246350d3c Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 16:10:36 +0200
Subject: [PATCH 17/36] Fix _hmacopenssl.HMAC.block_size
@ -3147,10 +3146,10 @@ index 7d3d973..a24c8ba 100644
static PyMethodDef Hmac_methods[] = {
--
2.38.1
2.25.4
From 68b4ee700ed23422472c9b96cf0631cb5a5dab6e Mon Sep 17 00:00:00 2001
From ee03c8ff14206070a7e4e4d13c4b067bcf25193d Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 15:02:08 +0200
Subject: [PATCH 18/36] distutils upload: Skip md5 checksum in FIPS mode
@ -3229,10 +3228,10 @@ index c17d8e7..b4b64e9 100644
def test_upload_fails(self):
--
2.38.1
2.25.4
From 62c8250c2996af8e4d112676c0995616583fafa7 Mon Sep 17 00:00:00 2001
From fd0fd3310ff7c7dae0ea4377b71928ca3e242a21 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 15:32:25 +0200
Subject: [PATCH 19/36] Fix HMAC tests on FIPS mode
@ -3322,10 +3321,10 @@ index 23c108f..0a85981 100644
def test_equality(self):
# Testing if the copy has the same digests.
--
2.38.1
2.25.4
From a7f5d3a4712694e6b74295ba6980c554b56d7227 Mon Sep 17 00:00:00 2001
From e0c4dfcfc3070d0b3b25f77357509b9daa5f9891 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 16:37:12 +0200
Subject: [PATCH 20/36] test_tools: Skip md5sum tests in FIPS mode
@ -3355,10 +3354,10 @@ index fb565b7..7028a4d 100644
@classmethod
def setUpClass(cls):
--
2.38.1
2.25.4
From 9e6cfe34f7377761f758d57a1b68a62fd4d0f2d1 Mon Sep 17 00:00:00 2001
From 510915020bb7c7c91d297fb3330ee9be3ee16b6f Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 18:23:57 +0200
Subject: [PATCH 21/36] Make hashlib tests pass in FIPS mode
@ -3368,7 +3367,7 @@ Subject: [PATCH 21/36] Make hashlib tests pass in FIPS mode
1 file changed, 48 insertions(+), 19 deletions(-)
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 972eda1..19a9868 100644
index 2a55fd4..9ae5efc 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -28,6 +28,11 @@ COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
@ -3528,7 +3527,7 @@ index 972eda1..19a9868 100644
@unittest.skipIf(sys.maxsize < _4G - 1, 'test cannot run on 32-bit systems')
@bigmemtest(size=_4G - 1, memuse=1, dry_run=False)
def test_case_md5_uintmax(self, size):
@@ -851,14 +878,16 @@ class HashLibTestCase(unittest.TestCase):
@@ -842,14 +869,16 @@ class HashLibTestCase(unittest.TestCase):
m = cons(b'x' * gil_minsize)
m.update(b'1')
@ -3550,10 +3549,10 @@ index 972eda1..19a9868 100644
@support.reap_threads
def test_threaded_hashing(self):
--
2.38.1
2.25.4
From 22b6dcc045d87d130852bc4f017cfb6305b329dd Mon Sep 17 00:00:00 2001
From de9997db1f55fe4c70f0a5c4fe5b497e8c6839a2 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Wed, 14 Aug 2019 14:43:07 +0200
Subject: [PATCH 22/36] distutils upload: only add md5 if available, but
@ -3620,10 +3619,10 @@ index b4b64e9..f720a79 100644
def test_upload_fails(self):
--
2.38.1
2.25.4
From b02daa9dbf4fcd2d1e067d63873d276bd7bf132f Mon Sep 17 00:00:00 2001
From 30407ef6fd2fb0fcb950cab57d4bd23121ef9084 Mon Sep 17 00:00:00 2001
From: Christian Heimes <christian@python.org>
Date: Fri, 13 Sep 2019 02:30:00 +0200
Subject: [PATCH 23/36] bpo-9216: Add usedforsecurity to hashlib constructors
@ -3657,7 +3656,7 @@ Contributed and Signed-off-by: Christian Heimes christian@python.org
create mode 100644 Misc/NEWS.d/next/Library/2019-09-12-14-54-45.bpo-9216.W7QMpC.rst
diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst
index f5da6ec..86b9f65 100644
index a16c7cd..6eb3a7b 100644
--- a/Doc/library/hashlib.rst
+++ b/Doc/library/hashlib.rst
@@ -67,7 +67,7 @@ Constructors for hash algorithms that are always present in this module are
@ -3709,7 +3708,7 @@ index f5da6ec..86b9f65 100644
These functions return the corresponding hash objects for calculating
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 19a9868..3ee9b14 100644
index 9ae5efc..08bb91f 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -226,6 +226,15 @@ class HashLibTestCase(unittest.TestCase):
@ -3945,10 +3944,10 @@ index 560bd68..71c5706 100644
-/*[clinic end generated code: output=39af5a74c8805b36 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c80d8d06ce40a192 input=a9049054013a1b77]*/
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 713e15a..b4d05ab 100644
index 6982268..a1f81eb 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -528,7 +528,7 @@ static PyTypeObject EVPtype = {
@@ -521,7 +521,7 @@ static PyTypeObject EVPtype = {
\
static PyObject *
EVPnew(const EVP_MD *digest,
@ -3957,7 +3956,7 @@ index 713e15a..b4d05ab 100644
{
int result = 0;
EVPobject *self;
@@ -541,6 +541,12 @@ EVPnew(const EVP_MD *digest,
@@ -534,6 +534,12 @@ EVPnew(const EVP_MD *digest,
if ((self = newEVPobject()) == NULL)
return NULL;
@ -3970,7 +3969,7 @@ index 713e15a..b4d05ab 100644
if (!EVP_DigestInit_ex(self->ctx, digest, NULL)) {
_setException(PyExc_ValueError);
Py_DECREF(self);
@@ -572,6 +578,8 @@ _hashlib.new as EVP_new
@@ -565,6 +571,8 @@ _hashlib.new as EVP_new
name as name_obj: object
string as data_obj: object(c_default="NULL") = b''
@ -3979,7 +3978,7 @@ index 713e15a..b4d05ab 100644
Return a new hash object using the named algorithm.
@@ -582,8 +590,9 @@ The MD5 and SHA1 algorithms are always supported.
@@ -575,8 +583,9 @@ The MD5 and SHA1 algorithms are always supported.
[clinic start generated code]*/
static PyObject *
@ -3991,7 +3990,7 @@ index 713e15a..b4d05ab 100644
{
Py_buffer view = { 0 };
PyObject *ret_obj;
@@ -600,7 +609,9 @@ EVP_new_impl(PyObject *module, PyObject *name_obj, PyObject *data_obj)
@@ -593,7 +602,9 @@ EVP_new_impl(PyObject *module, PyObject *name_obj, PyObject *data_obj)
digest = py_digest_by_name(name);
@ -4002,7 +4001,7 @@ index 713e15a..b4d05ab 100644
if (data_obj)
PyBuffer_Release(&view);
@@ -608,7 +619,8 @@ EVP_new_impl(PyObject *module, PyObject *name_obj, PyObject *data_obj)
@@ -601,7 +612,8 @@ EVP_new_impl(PyObject *module, PyObject *name_obj, PyObject *data_obj)
}
static PyObject*
@ -4012,7 +4011,7 @@ index 713e15a..b4d05ab 100644
{
Py_buffer view = { 0 };
PyObject *ret_obj;
@@ -616,7 +628,8 @@ EVP_fast_new(PyObject *module, PyObject *data_obj, const EVP_MD *digest)
@@ -609,7 +621,8 @@ EVP_fast_new(PyObject *module, PyObject *data_obj, const EVP_MD *digest)
if (data_obj)
GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view);
@ -4022,7 +4021,7 @@ index 713e15a..b4d05ab 100644
if (data_obj)
PyBuffer_Release(&view);
@@ -628,16 +641,19 @@ EVP_fast_new(PyObject *module, PyObject *data_obj, const EVP_MD *digest)
@@ -621,16 +634,19 @@ EVP_fast_new(PyObject *module, PyObject *data_obj, const EVP_MD *digest)
_hashlib.openssl_md5
string as data_obj: object(py_default="b''") = NULL
@ -4045,7 +4044,7 @@ index 713e15a..b4d05ab 100644
}
@@ -645,16 +661,19 @@ _hashlib_openssl_md5_impl(PyObject *module, PyObject *data_obj)
@@ -638,16 +654,19 @@ _hashlib_openssl_md5_impl(PyObject *module, PyObject *data_obj)
_hashlib.openssl_sha1
string as data_obj: object(py_default="b''") = NULL
@ -4068,7 +4067,7 @@ index 713e15a..b4d05ab 100644
}
@@ -662,16 +681,19 @@ _hashlib_openssl_sha1_impl(PyObject *module, PyObject *data_obj)
@@ -655,16 +674,19 @@ _hashlib_openssl_sha1_impl(PyObject *module, PyObject *data_obj)
_hashlib.openssl_sha224
string as data_obj: object(py_default="b''") = NULL
@ -4091,7 +4090,7 @@ index 713e15a..b4d05ab 100644
}
@@ -679,16 +701,19 @@ _hashlib_openssl_sha224_impl(PyObject *module, PyObject *data_obj)
@@ -672,16 +694,19 @@ _hashlib_openssl_sha224_impl(PyObject *module, PyObject *data_obj)
_hashlib.openssl_sha256
string as data_obj: object(py_default="b''") = NULL
@ -4114,7 +4113,7 @@ index 713e15a..b4d05ab 100644
}
@@ -696,16 +721,19 @@ _hashlib_openssl_sha256_impl(PyObject *module, PyObject *data_obj)
@@ -689,16 +714,19 @@ _hashlib_openssl_sha256_impl(PyObject *module, PyObject *data_obj)
_hashlib.openssl_sha384
string as data_obj: object(py_default="b''") = NULL
@ -4137,7 +4136,7 @@ index 713e15a..b4d05ab 100644
}
@@ -713,152 +741,179 @@ _hashlib_openssl_sha384_impl(PyObject *module, PyObject *data_obj)
@@ -706,152 +734,179 @@ _hashlib_openssl_sha384_impl(PyObject *module, PyObject *data_obj)
_hashlib.openssl_sha512
string as data_obj: object(py_default="b''") = NULL
@ -5730,7 +5729,7 @@ index 459a934..b8185b6 100644
-/*[clinic end generated code: output=580df4b667084a7e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=bbfa72d8703c82b5 input=a9049054013a1b77]*/
diff --git a/Modules/md5module.c b/Modules/md5module.c
index 64fab80..ee4efe4 100644
index c2ebaaf..fdc4d7b 100644
--- a/Modules/md5module.c
+++ b/Modules/md5module.c
@@ -503,13 +503,15 @@ static PyTypeObject MD5type = {
@ -5752,7 +5751,7 @@ index 64fab80..ee4efe4 100644
MD5object *new;
Py_buffer buf;
diff --git a/Modules/sha1module.c b/Modules/sha1module.c
index 4a8dbd8..aec0bad 100644
index ce2ad26..4d191c3 100644
--- a/Modules/sha1module.c
+++ b/Modules/sha1module.c
@@ -480,13 +480,15 @@ static PyTypeObject SHA1type = {
@ -5774,7 +5773,7 @@ index 4a8dbd8..aec0bad 100644
SHA1object *new;
Py_buffer buf;
diff --git a/Modules/sha256module.c b/Modules/sha256module.c
index a1c8b1a..8777c59 100644
index b8d6c4c..245f4c0 100644
--- a/Modules/sha256module.c
+++ b/Modules/sha256module.c
@@ -601,13 +601,15 @@ static PyTypeObject SHA256type = {
@ -5814,7 +5813,7 @@ index a1c8b1a..8777c59 100644
SHAobject *new;
Py_buffer buf;
diff --git a/Modules/sha512module.c b/Modules/sha512module.c
index 4167fd3..504d40a 100644
index 98b9791..df4f9d2 100644
--- a/Modules/sha512module.c
+++ b/Modules/sha512module.c
@@ -666,13 +666,15 @@ static PyTypeObject SHA512type = {
@ -5854,10 +5853,10 @@ index 4167fd3..504d40a 100644
SHAobject *new;
Py_buffer buf;
--
2.38.1
2.25.4
From 0ee86826a425528d1f129788ad3e9e144e81da83 Mon Sep 17 00:00:00 2001
From 095d8ea318b20b5d42ada0367ca770c15e6f6fa2 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 26 Aug 2019 19:09:39 +0200
Subject: [PATCH 24/36] Test the usedforsecurity flag
@ -5867,7 +5866,7 @@ Subject: [PATCH 24/36] Test the usedforsecurity flag
1 file changed, 54 insertions(+), 34 deletions(-)
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 3ee9b14..a991f0a 100644
index 08bb91f..1368e91 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -21,6 +21,7 @@ from test import support
@ -6080,9 +6079,9 @@ index 3ee9b14..a991f0a 100644
- self.check('md5', b'A'*size, '28138d306ff1b8281f1a9067e1a1a2b3')
+ self.check('md5', b'A'*size, '28138d306ff1b8281f1a9067e1a1a2b3', usedforsecurity=False)
@unittest.skipIf(sys.maxsize < _4G - 1, 'test cannot run on 32-bit systems')
@bigmemtest(size=_4G - 1, memuse=1, dry_run=False)
@@ -934,6 +944,16 @@ class HashLibTestCase(unittest.TestCase):
# use the three examples from Federal Information Processing Standards
# Publication 180-1, Secure Hash Standard, 1995 April 17
@@ -925,6 +935,16 @@ class HashLibTestCase(unittest.TestCase):
self.assertEqual(expected_hash, hasher.hexdigest())
@ -6100,10 +6099,10 @@ index 3ee9b14..a991f0a 100644
class KDFTests(unittest.TestCase):
--
2.38.1
2.25.4
From 7602d70630fa0e736ad518b243079b658893d653 Mon Sep 17 00:00:00 2001
From 59b7e853d919380ca6c11655bbc7041ee395417d Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 29 Aug 2019 10:25:28 +0200
Subject: [PATCH 25/36] Skip error checking in _hashlib.get_fips_mode
@ -6114,10 +6113,10 @@ Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1745499
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index b4d05ab..a48b607 100644
index a1f81eb..eff331b 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -1260,20 +1260,22 @@ _hashlib_get_fips_mode_impl(PyObject *module)
@@ -1253,20 +1253,22 @@ _hashlib_get_fips_mode_impl(PyObject *module)
/*[clinic end generated code: output=ad8a7793310d3f98 input=f42a2135df2a5e11]*/
{
@ -6155,10 +6154,10 @@ index b4d05ab..a48b607 100644
--
2.38.1
2.25.4
From 07552e7758fdc9c9610d773b4100d4686b5353a3 Mon Sep 17 00:00:00 2001
From 7f5432d72546f60078989b6cadf26cd51de84ebd Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 10 Oct 2019 13:04:50 +0200
Subject: [PATCH 26/36] Skip error checking in _Py_hashlib_fips_error
@ -6193,10 +6192,10 @@ index 47ed003..d4cbdef 100644
}
PyErr_Format(exc, "%s is not available in FIPS mode", name);
--
2.38.1
2.25.4
From 8be15a9faf79a062be4b0f27605737bc34687e68 Mon Sep 17 00:00:00 2001
From 05f7188136bda8eeec06428aa4ddf9ab14a178a0 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 5 Aug 2019 19:12:38 +0200
Subject: [PATCH 27/36] Fixups
@ -6235,10 +6234,10 @@ index 0a85981..0b481ec 100644
h1 = hmac.HMAC(b"key", digestmod="sha1")
h2 = h1.copy()
--
2.38.1
2.25.4
From 112646fbf2af81df1750140f7486b32296518099 Mon Sep 17 00:00:00 2001
From 0f707443431d9dc22218be7208d940f4d42f122d Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 26 Aug 2019 19:39:48 +0200
Subject: [PATCH 28/36] Don't re-export get_fips_mode from hashlib
@ -6446,7 +6445,7 @@ index 34812e6..86e61e2 100644
self.compare_hashes(hashlib.shake_128(b'abc'), _hashlib.openssl_shake_128(b'abc'))
self.compare_hashes(hashlib.shake_256(b'abc'), _hashlib.openssl_shake_256(b'abc'))
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index a991f0a..b535059 100644
index 1368e91..a4b7840 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -29,7 +29,9 @@ COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
@ -6502,7 +6501,7 @@ index a991f0a..b535059 100644
self.check_blocksize_name('md5', 64, 16)
self.check_blocksize_name('sha1', 64, 20)
self.check_blocksize_name('sha224', 64, 28)
@@ -944,7 +946,7 @@ class HashLibTestCase(unittest.TestCase):
@@ -935,7 +937,7 @@ class HashLibTestCase(unittest.TestCase):
self.assertEqual(expected_hash, hasher.hexdigest())
@ -6551,7 +6550,7 @@ index 0b481ec..cc77928 100644
def test_realcopy(self):
# Testing if the copy method created a real copy.
diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py
index d14eb45..af53ec9 100644
index d0c9862..9a44c0d 100644
--- a/Lib/test/test_smtplib.py
+++ b/Lib/test/test_smtplib.py
@@ -17,6 +17,8 @@ import select
@ -6563,7 +6562,7 @@ index d14eb45..af53ec9 100644
import unittest
from test import support, mock_socket
@@ -1114,7 +1116,7 @@ class SMTPSimTests(unittest.TestCase):
@@ -1021,7 +1023,7 @@ class SMTPSimTests(unittest.TestCase):
def testAUTH_multiple(self):
# Test that multiple authentication methods are tried.
@ -6605,10 +6604,10 @@ index 1cb358f..6f5cb7f 100644
from test import support
--
2.38.1
2.25.4
From 69a0ddba408a9595aa0fc5b3fdfe7e59838acea2 Mon Sep 17 00:00:00 2001
From 9515f9be3409fdc59cf9c09dd200917483e1651a Mon Sep 17 00:00:00 2001
From: Christian Heimes <christian@python.org>
Date: Wed, 20 Nov 2019 10:59:25 +0100
Subject: [PATCH 29/36] Use FIPS compliant CSPRNG
@ -6625,7 +6624,7 @@ Signed-off-by: Christian Heimes <christian@python.org>
4 files changed, 89 insertions(+), 1 deletion(-)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 5302b1c..ed335ad 100644
index 2a4ae15..5ad5bd6 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -1546,6 +1546,11 @@ class GetRandomTests(unittest.TestCase):
@ -6641,7 +6640,7 @@ index 5302b1c..ed335ad 100644
def test_getrandom_type(self):
data = os.getrandom(16)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 381a8ab..e7778f4 100644
index 917303d..ddfbfd0 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -116,7 +116,7 @@ PY_STDMODULE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFOR
@ -6654,10 +6653,10 @@ index 381a8ab..e7778f4 100644
CFLAGS_ALIASING=@CFLAGS_ALIASING@
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index d7edabe..f825d5a 100644
index 726e372..9a1249a 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -389,6 +389,9 @@ extern char *ctermid_r(char *);
@@ -388,6 +388,9 @@ extern char *ctermid_r(char *);
#define MODNAME "posix"
#endif
@ -6667,7 +6666,7 @@ index d7edabe..f825d5a 100644
#if defined(__sun)
/* Something to implement in autoconf, not present in autoconf 2.69 */
#define HAVE_STRUCT_STAT_ST_FSTYPE 1
@@ -13650,6 +13653,11 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)
@@ -13558,6 +13561,11 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)
return posix_error();
}
@ -6773,10 +6772,10 @@ index eb2b6d0..cb38cfe 100644
return win32_urandom((unsigned char *)buffer, size, raise);
#else
--
2.38.1
2.25.4
From 72894033d2c1d29897204c5272d8f2878c17254c Mon Sep 17 00:00:00 2001
From ba95383d9b37f252bd153674404dc4055d49bf82 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Thu, 28 Nov 2019 17:26:02 +0100
Subject: [PATCH 30/36] Fixups for FIPS compliant CSPRNG
@ -6787,7 +6786,7 @@ Subject: [PATCH 30/36] Fixups for FIPS compliant CSPRNG
2 files changed, 5 insertions(+), 31 deletions(-)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index ed335ad..f306316 100644
index 5ad5bd6..ae53de9 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -28,6 +28,7 @@ import time
@ -6872,10 +6871,10 @@ index cb38cfe..08fa29a 100644
return 0;
}
--
2.38.1
2.25.4
From d8bc6ab755acf0e2feffda802aeed032a2319df8 Mon Sep 17 00:00:00 2001
From 496a58146aa42b97661c5ea1afeaa223e8fd4ceb Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Thu, 2 Apr 2020 16:50:37 +0200
Subject: [PATCH 31/36] Do not raise a ValueError if digestmod is missing in
@ -6902,10 +6901,10 @@ index 5055027..ee1ad76 100644
return digestmod.lower()
elif callable(digestmod):
--
2.38.1
2.25.4
From 9dd6dc8c11b96ac74abf220ed76d1176041c3711 Mon Sep 17 00:00:00 2001
From 3f346ea93c2504e169a2df21e2de206031a08600 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Thu, 2 Apr 2020 16:55:36 +0200
Subject: [PATCH 32/36] Regenerate the clinic files
@ -6989,10 +6988,10 @@ index 861acc1..527be83 100644
-/*[clinic end generated code: output=d93ad460795d49b5 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=9b75c31e1116bf6f input=a9049054013a1b77]*/
--
2.38.1
2.25.4
From f646402958e8d284519ef7f72d0225ace210ffa7 Mon Sep 17 00:00:00 2001
From f4465980ae75c0e56cd1edecf9a42fa38b9cd12a Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Tue, 7 Apr 2020 15:16:45 +0200
Subject: [PATCH 33/36] Pass kwargs (like usedforsecurity) through __hash_new
@ -7024,10 +7023,10 @@ index 2fc214e..785858f 100644
try:
--
2.38.1
2.25.4
From bd206ed69e7c19b62f0174a649b5c2d03f0d9f5b Mon Sep 17 00:00:00 2001
From 6c0ba219c01052f8b079ce67b89a75920b3aa867 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Tue, 7 Apr 2020 15:18:48 +0200
Subject: [PATCH 34/36] Adjust new upstream test for failing hashes with
@ -7038,7 +7037,7 @@ Subject: [PATCH 34/36] Adjust new upstream test for failing hashes with
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index b535059..6e846c4 100644
index a4b7840..a858bf4 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -239,15 +239,23 @@ class HashLibTestCase(unittest.TestCase):
@ -7070,10 +7069,10 @@ index b535059..6e846c4 100644
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
self.assertRaises(TypeError, hashlib.new, 1)
--
2.38.1
2.25.4
From d1ce20110573989382948a901896169eb637b265 Mon Sep 17 00:00:00 2001
From 041105f888785599e58213dfea55115a4e861d77 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Fri, 24 Apr 2020 19:57:16 +0200
Subject: [PATCH 35/36] Skip the test_with_digestmod_no_default under FIPS
@ -7116,10 +7115,10 @@ index cc77928..fd068e0 100644
class ConstructorTestCase(unittest.TestCase):
--
2.38.1
2.25.4
From bff06e176e60200582a42611d4fe3c240da314a3 Mon Sep 17 00:00:00 2001
From e20750200d560a549cbbf224ded74bb086ef3e66 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Tue, 31 Mar 2020 18:00:42 +0200
Subject: [PATCH 36/36] Add a sentinel value on the Hmac_members table of the
@ -7142,5 +7141,5 @@ index 9577cad..4bd7c15 100644
PyDoc_STRVAR(hmactype_doc,
--
2.38.1
2.25.4

@ -0,0 +1,186 @@
From ece5dfd403dac211f8d3c72701fe7ba7b7aa5b5f Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Mon, 18 Jan 2021 13:28:52 -0800
Subject: [PATCH] closes bpo-42938: Replace snprintf with Python unicode
formatting in ctypes param reprs. (GH-24248)
(cherry picked from commit 916610ef90a0d0761f08747f7b0905541f0977c7)
Co-authored-by: Benjamin Peterson <benjamin@python.org>
Co-authored-by: Benjamin Peterson <benjamin@python.org>
---
Lib/ctypes/test/test_parameters.py | 43 ++++++++++++++++
.../2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst | 2 +
Modules/_ctypes/callproc.c | 51 +++++++------------
3 files changed, 64 insertions(+), 32 deletions(-)
create mode 100644 Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py
index e4c25fd880cef..531894fdec838 100644
--- a/Lib/ctypes/test/test_parameters.py
+++ b/Lib/ctypes/test/test_parameters.py
@@ -201,6 +201,49 @@ def __dict__(self):
with self.assertRaises(ZeroDivisionError):
WorseStruct().__setstate__({}, b'foo')
+ def test_parameter_repr(self):
+ from ctypes import (
+ c_bool,
+ c_char,
+ c_wchar,
+ c_byte,
+ c_ubyte,
+ c_short,
+ c_ushort,
+ c_int,
+ c_uint,
+ c_long,
+ c_ulong,
+ c_longlong,
+ c_ulonglong,
+ c_float,
+ c_double,
+ c_longdouble,
+ c_char_p,
+ c_wchar_p,
+ c_void_p,
+ )
+ self.assertRegex(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
+ self.assertEqual(repr(c_char.from_param(97)), "<cparam 'c' ('a')>")
+ self.assertRegex(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
+ self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>")
+ self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>")
+ self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>")
+ self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>")
+ self.assertRegex(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+ self.assertRegex(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+ self.assertRegex(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+ self.assertRegex(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+ self.assertRegex(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$")
+ self.assertRegex(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
+ self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
+ self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
+ self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
+ self.assertRegex(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
+ self.assertRegex(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
+ self.assertRegex(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
+ self.assertRegex(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
+
################################################################
if __name__ == '__main__':
diff --git a/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
new file mode 100644
index 0000000000000..7df65a156feab
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
@@ -0,0 +1,2 @@
+Avoid static buffers when computing the repr of :class:`ctypes.c_double` and
+:class:`ctypes.c_longdouble` values.
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index a9b8675cd951b..de75918d49f37 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -484,58 +484,47 @@ is_literal_char(unsigned char c)
static PyObject *
PyCArg_repr(PyCArgObject *self)
{
- char buffer[256];
switch(self->tag) {
case 'b':
case 'B':
- sprintf(buffer, "<cparam '%c' (%d)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%d)>",
self->tag, self->value.b);
- break;
case 'h':
case 'H':
- sprintf(buffer, "<cparam '%c' (%d)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%d)>",
self->tag, self->value.h);
- break;
case 'i':
case 'I':
- sprintf(buffer, "<cparam '%c' (%d)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%d)>",
self->tag, self->value.i);
- break;
case 'l':
case 'L':
- sprintf(buffer, "<cparam '%c' (%ld)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%ld)>",
self->tag, self->value.l);
- break;
case 'q':
case 'Q':
- sprintf(buffer,
-#ifdef MS_WIN32
- "<cparam '%c' (%I64d)>",
-#else
- "<cparam '%c' (%lld)>",
-#endif
+ return PyUnicode_FromFormat("<cparam '%c' (%lld)>",
self->tag, self->value.q);
- break;
case 'd':
- sprintf(buffer, "<cparam '%c' (%f)>",
- self->tag, self->value.d);
- break;
- case 'f':
- sprintf(buffer, "<cparam '%c' (%f)>",
- self->tag, self->value.f);
- break;
-
+ case 'f': {
+ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d);
+ if (f == NULL) {
+ return NULL;
+ }
+ PyObject *result = PyUnicode_FromFormat("<cparam '%c' (%R)>", self->tag, f);
+ Py_DECREF(f);
+ return result;
+ }
case 'c':
if (is_literal_char((unsigned char)self->value.c)) {
- sprintf(buffer, "<cparam '%c' ('%c')>",
+ return PyUnicode_FromFormat("<cparam '%c' ('%c')>",
self->tag, self->value.c);
}
else {
- sprintf(buffer, "<cparam '%c' ('\\x%02x')>",
+ return PyUnicode_FromFormat("<cparam '%c' ('\\x%02x')>",
self->tag, (unsigned char)self->value.c);
}
- break;
/* Hm, are these 'z' and 'Z' codes useful at all?
Shouldn't they be replaced by the functionality of c_string
@@ -544,22 +533,20 @@ PyCArg_repr(PyCArgObject *self)
case 'z':
case 'Z':
case 'P':
- sprintf(buffer, "<cparam '%c' (%p)>",
+ return PyUnicode_FromFormat("<cparam '%c' (%p)>",
self->tag, self->value.p);
break;
default:
if (is_literal_char((unsigned char)self->tag)) {
- sprintf(buffer, "<cparam '%c' at %p>",
+ return PyUnicode_FromFormat("<cparam '%c' at %p>",
(unsigned char)self->tag, (void *)self);
}
else {
- sprintf(buffer, "<cparam 0x%02x at %p>",
+ return PyUnicode_FromFormat("<cparam 0x%02x at %p>",
(unsigned char)self->tag, (void *)self);
}
- break;
}
- return PyUnicode_FromString(buffer);
}
static PyMemberDef PyCArgType_members[] = {

@ -1,567 +0,0 @@
From 78da9e020385fe78e36c20f99a0910bbc4a0c100 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Thu, 1 Apr 2021 08:18:07 +0200
Subject: [PATCH] CVE-2021-23336: Add `separator` argument to parse_qs; warn
with default
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Partially backports https://bugs.python.org/issue42967 : [security] Address a web cache-poisoning issue reported in urllib.parse.parse_qsl().
However, this solution is different than the upstream solution in Python 3.6.13.
An optional argument seperator is added to specify the separator.
It is recommended to set it to '&' or ';' to match the application or proxy in use.
The default can be set with an env variable of a config file.
If neither the argument, env var or config file specifies a separator, "&" is used
but a warning is raised if parse_qs is used on input that contains ';'.
Co-authors of the upstream change (who do not necessarily agree with this):
Co-authored-by: Adam Goldschmidt <adamgold7@gmail.com>
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Co-authored-by: Éric Araujo <merwok@netwok.org>
---
Doc/library/cgi.rst | 2 +-
Doc/library/urllib.parse.rst | 12 +-
Lib/cgi.py | 4 +-
Lib/test/test_cgi.py | 29 +++++
Lib/test/test_urlparse.py | 232 ++++++++++++++++++++++++++++++++++-
Lib/urllib/parse.py | 78 +++++++++++-
6 files changed, 339 insertions(+), 18 deletions(-)
diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst
index 880074b..d8a6dc1 100644
--- a/Doc/library/cgi.rst
+++ b/Doc/library/cgi.rst
@@ -277,7 +277,7 @@ These are useful if you want more control, or if you want to employ some of the
algorithms implemented in this module in other circumstances.
-.. function:: parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator="&")
+.. function:: parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator=None)
Parse a query in the environment or from a file (the file defaults to
``sys.stdin``). The *keep_blank_values*, *strict_parsing* and *separator* parameters are
diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst
index a6cfc5d..85b2448 100644
--- a/Doc/library/urllib.parse.rst
+++ b/Doc/library/urllib.parse.rst
@@ -165,7 +165,7 @@ or on combining URL components into a URL string.
now raise :exc:`ValueError`.
-.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&')
+.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator=None)
Parse a query string given as a string argument (data of type
:mimetype:`application/x-www-form-urlencoded`). Data are returned as a
@@ -191,7 +191,13 @@ or on combining URL components into a URL string.
*max_num_fields* fields read.
The optional argument *separator* is the symbol to use for separating the
- query arguments. It defaults to ``&``.
+ query arguments. It is recommended to set it to ``'&'`` or ``';'``.
+ It defaults to ``'&'``; a warning is raised if this default is used.
+ This default may be changed with the following environment variable settings:
+
+ - ``PYTHON_URLLIB_QS_SEPARATOR='&'``: use only ``&`` as separator, without warning (as in Python 3.6.13+ or 3.10)
+ - ``PYTHON_URLLIB_QS_SEPARATOR=';'``: use only ``;`` as separator
+ - ``PYTHON_URLLIB_QS_SEPARATOR=legacy``: use both ``&`` and ``;`` (as in previous versions of Python)
Use the :func:`urllib.parse.urlencode` function (with the ``doseq``
parameter set to ``True``) to convert such dictionaries into query
@@ -236,7 +242,7 @@ or on combining URL components into a URL string.
*max_num_fields* fields read.
The optional argument *separator* is the symbol to use for separating the
- query arguments. It defaults to ``&``.
+ query arguments. It works as in :py:func:`parse_qs`.
Use the :func:`urllib.parse.urlencode` function to convert such lists of pairs into
query strings.
diff --git a/Lib/cgi.py b/Lib/cgi.py
index 1e880e5..d7b994b 100755
--- a/Lib/cgi.py
+++ b/Lib/cgi.py
@@ -116,7 +116,7 @@ log = initlog # The current logging function
maxlen = 0
def parse(fp=None, environ=os.environ, keep_blank_values=0,
- strict_parsing=0, separator='&'):
+ strict_parsing=0, separator=None):
"""Parse a query in the environment or from a file (default stdin)
Arguments, all optional:
@@ -319,7 +319,7 @@ class FieldStorage:
def __init__(self, fp=None, headers=None, outerboundary=b'',
environ=os.environ, keep_blank_values=0, strict_parsing=0,
limit=None, encoding='utf-8', errors='replace',
- max_num_fields=None, separator='&'):
+ max_num_fields=None, separator=None):
"""Constructor. Read multipart/* until last part.
Arguments, all optional:
diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py
index 4e1506a..49b6926 100644
--- a/Lib/test/test_cgi.py
+++ b/Lib/test/test_cgi.py
@@ -180,6 +180,35 @@ Content-Length: 3
env = {'QUERY_STRING': orig}
fs = cgi.FieldStorage(environ=env)
+ if isinstance(expect, dict):
+ # test dict interface
+ self.assertEqual(len(expect), len(fs))
+ self.assertCountEqual(expect.keys(), fs.keys())
+ self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
+ # test individual fields
+ for key in expect.keys():
+ expect_val = expect[key]
+ self.assertIn(key, fs)
+ if len(expect_val) > 1:
+ self.assertEqual(fs.getvalue(key), expect_val)
+ else:
+ self.assertEqual(fs.getvalue(key), expect_val[0])
+
+ def test_separator(self):
+ parse_semicolon = [
+ ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}),
+ ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
+ (";", ValueError("bad query field: ''")),
+ (";;", ValueError("bad query field: ''")),
+ ("=;a", ValueError("bad query field: 'a'")),
+ (";b=a", ValueError("bad query field: ''")),
+ ("b;=a", ValueError("bad query field: 'b'")),
+ ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
+ ("a=a+b;a=b+a", {'a': ['a b', 'b a']}),
+ ]
+ for orig, expect in parse_semicolon:
+ env = {'QUERY_STRING': orig}
+ fs = cgi.FieldStorage(separator=';', environ=env)
if isinstance(expect, dict):
# test dict interface
self.assertEqual(len(expect), len(fs))
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
index 0f99130..4e0d7e5 100644
--- a/Lib/test/test_urlparse.py
+++ b/Lib/test/test_urlparse.py
@@ -2,6 +2,11 @@ import sys
import unicodedata
import unittest
import urllib.parse
+from test.support import EnvironmentVarGuard
+from warnings import catch_warnings
+import tempfile
+import contextlib
+import os.path
RFC1808_BASE = "http://a/b/c/d;p?q#f"
RFC2396_BASE = "http://a/b/c/d;p?q"
@@ -32,10 +37,34 @@ parse_qsl_test_cases = [
(b"&a=b", [(b'a', b'b')]),
(b"a=a+b&b=b+c", [(b'a', b'a b'), (b'b', b'b c')]),
(b"a=1&a=2", [(b'a', b'1'), (b'a', b'2')]),
+]
+
+parse_qsl_test_cases_semicolon = [
+ (";", []),
+ (";;", []),
+ (";a=b", [('a', 'b')]),
+ ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]),
+ ("a=1;a=2", [('a', '1'), ('a', '2')]),
+ (b";", []),
+ (b";;", []),
+ (b";a=b", [(b'a', b'b')]),
+ (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]),
+ (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]),
+]
+
+parse_qsl_test_cases_legacy = [
+ (b"a=1;a=2&a=3", [(b'a', b'1'), (b'a', b'2'), (b'a', b'3')]),
+ (b"a=1;b=2&c=3", [(b'a', b'1'), (b'b', b'2'), (b'c', b'3')]),
+ (b"a=1&b=2&c=3;", [(b'a', b'1'), (b'b', b'2'), (b'c', b'3')]),
+]
+
+parse_qsl_test_cases_warn = [
(";a=b", [(';a', 'b')]),
("a=a+b;b=b+c", [('a', 'a b;b=b c')]),
(b";a=b", [(b';a', b'b')]),
(b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]),
+ ("a=1;a=2&a=3", [('a', '1;a=2'), ('a', '3')]),
+ (b"a=1;a=2&a=3", [(b'a', b'1;a=2'), (b'a', b'3')]),
]
# Each parse_qs testcase is a two-tuple that contains
@@ -62,10 +91,37 @@ parse_qs_test_cases = [
(b"&a=b", {b'a': [b'b']}),
(b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
(b"a=1&a=2", {b'a': [b'1', b'2']}),
+]
+
+parse_qs_test_cases_semicolon = [
+ (";", {}),
+ (";;", {}),
+ (";a=b", {'a': ['b']}),
+ ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
+ ("a=1;a=2", {'a': ['1', '2']}),
+ (b";", {}),
+ (b";;", {}),
+ (b";a=b", {b'a': [b'b']}),
+ (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
+ (b"a=1;a=2", {b'a': [b'1', b'2']}),
+]
+
+parse_qs_test_cases_legacy = [
+ ("a=1;a=2&a=3", {'a': ['1', '2', '3']}),
+ ("a=1;b=2&c=3", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
+ ("a=1&b=2&c=3;", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
+ (b"a=1;a=2&a=3", {b'a': [b'1', b'2', b'3']}),
+ (b"a=1;b=2&c=3", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
+ (b"a=1&b=2&c=3;", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
+]
+
+parse_qs_test_cases_warn = [
(";a=b", {';a': ['b']}),
("a=a+b;b=b+c", {'a': ['a b;b=b c']}),
(b";a=b", {b';a': [b'b']}),
(b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}),
+ ("a=1;a=2&a=3", {'a': ['1;a=2', '3']}),
+ (b"a=1;a=2&a=3", {b'a': [b'1;a=2', b'3']}),
]
class UrlParseTestCase(unittest.TestCase):
@@ -123,23 +179,57 @@ class UrlParseTestCase(unittest.TestCase):
def test_qsl(self):
for orig, expect in parse_qsl_test_cases:
- result = urllib.parse.parse_qsl(orig, keep_blank_values=True)
+ result = urllib.parse.parse_qsl(orig, keep_blank_values=True, separator="&")
self.assertEqual(result, expect, "Error parsing %r" % orig)
expect_without_blanks = [v for v in expect if len(v[1])]
- result = urllib.parse.parse_qsl(orig, keep_blank_values=False)
+ result = urllib.parse.parse_qsl(orig, keep_blank_values=False, separator="&")
self.assertEqual(result, expect_without_blanks,
"Error parsing %r" % orig)
def test_qs(self):
for orig, expect in parse_qs_test_cases:
- result = urllib.parse.parse_qs(orig, keep_blank_values=True)
+ result = urllib.parse.parse_qs(orig, keep_blank_values=True, separator="&")
self.assertEqual(result, expect, "Error parsing %r" % orig)
expect_without_blanks = {v: expect[v]
for v in expect if len(expect[v][0])}
- result = urllib.parse.parse_qs(orig, keep_blank_values=False)
+ result = urllib.parse.parse_qs(orig, keep_blank_values=False, separator="&")
self.assertEqual(result, expect_without_blanks,
"Error parsing %r" % orig)
+ def test_qs_default_warn(self):
+ for orig, expect in parse_qs_test_cases_warn:
+ with self.subTest(orig=orig, expect=expect):
+ with catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qs(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 1)
+ self.assertEqual(w[0].category, urllib.parse._QueryStringSeparatorWarning)
+
+ def test_qsl_default_warn(self):
+ for orig, expect in parse_qsl_test_cases_warn:
+ with self.subTest(orig=orig, expect=expect):
+ with catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qsl(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 1)
+ self.assertEqual(w[0].category, urllib.parse._QueryStringSeparatorWarning)
+
+ def test_default_qs_no_warnings(self):
+ for orig, expect in parse_qs_test_cases:
+ with self.subTest(orig=orig, expect=expect):
+ with catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qs(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_default_qsl_no_warnings(self):
+ for orig, expect in parse_qsl_test_cases:
+ with self.subTest(orig=orig, expect=expect):
+ with catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qsl(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
def test_roundtrips(self):
str_cases = [
('file:///tmp/junk.txt',
@@ -919,8 +1009,8 @@ class UrlParseTestCase(unittest.TestCase):
def test_parse_qsl_max_num_fields(self):
with self.assertRaises(ValueError):
- urllib.parse.parse_qs('&'.join(['a=a']*11), max_num_fields=10)
- urllib.parse.parse_qs('&'.join(['a=a']*10), max_num_fields=10)
+ urllib.parse.parse_qs('&'.join(['a=a']*11), max_num_fields=10, separator='&')
+ urllib.parse.parse_qs('&'.join(['a=a']*10), max_num_fields=10, separator='&')
def test_parse_qs_separator(self):
parse_qs_semicolon_cases = [
@@ -964,6 +1054,136 @@ class UrlParseTestCase(unittest.TestCase):
self.assertEqual(result_bytes, expect, "Error parsing %r" % orig)
+ @contextlib.contextmanager
+ def _qsl_sep_config(self, sep):
+ """Context for the given parse_qsl default separator configured in config file"""
+ old_filename = urllib.parse._QS_SEPARATOR_CONFIG_FILENAME
+ urllib.parse._default_qs_separator = None
+ try:
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ filename = os.path.join(tmpdirname, 'conf.cfg')
+ with open(filename, 'w') as file:
+ file.write(f'[parse_qs]\n')
+ file.write(f'PYTHON_URLLIB_QS_SEPARATOR = {sep}')
+ urllib.parse._QS_SEPARATOR_CONFIG_FILENAME = filename
+ yield
+ finally:
+ urllib.parse._QS_SEPARATOR_CONFIG_FILENAME = old_filename
+ urllib.parse._default_qs_separator = None
+
+ def test_parse_qs_separator_semicolon(self):
+ for orig, expect in parse_qs_test_cases_semicolon:
+ with self.subTest(orig=orig, expect=expect, method='arg'):
+ result = urllib.parse.parse_qs(orig, separator=';')
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ with self.subTest(orig=orig, expect=expect, method='env'):
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
+ result = urllib.parse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self.subTest(orig=orig, expect=expect, method='conf'):
+ with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qsl_separator_semicolon(self):
+ for orig, expect in parse_qsl_test_cases_semicolon:
+ with self.subTest(orig=orig, expect=expect, method='arg'):
+ result = urllib.parse.parse_qsl(orig, separator=';')
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ with self.subTest(orig=orig, expect=expect, method='env'):
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
+ result = urllib.parse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self.subTest(orig=orig, expect=expect, method='conf'):
+ with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qs_separator_legacy(self):
+ for orig, expect in parse_qs_test_cases_legacy:
+ with self.subTest(orig=orig, expect=expect, method='env'):
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
+ result = urllib.parse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self.subTest(orig=orig, expect=expect, method='conf'):
+ with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qsl_separator_legacy(self):
+ for orig, expect in parse_qsl_test_cases_legacy:
+ with self.subTest(orig=orig, expect=expect, method='env'):
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
+ result = urllib.parse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self.subTest(orig=orig, expect=expect, method='conf'):
+ with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
+ result = urllib.parse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qs_separator_bad_value_env_or_config(self):
+ for bad_sep in '', 'abc', 'safe', '&;', 'SEP':
+ with self.subTest(bad_sep, method='env'):
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = bad_sep
+ with self.assertRaises(ValueError):
+ urllib.parse.parse_qsl('a=1;b=2')
+ with self.subTest(bad_sep, method='conf'):
+ with self._qsl_sep_config('bad_sep'), catch_warnings(record=True) as w:
+ with self.assertRaises(ValueError):
+ urllib.parse.parse_qsl('a=1;b=2')
+
+ def test_parse_qs_separator_bad_value_arg(self):
+ for bad_sep in True, {}, '':
+ with self.subTest(bad_sep):
+ with self.assertRaises(ValueError):
+ urllib.parse.parse_qsl('a=1;b=2', separator=bad_sep)
+
+ def test_parse_qs_separator_num_fields(self):
+ for qs, sep in (
+ ('a&b&c', '&'),
+ ('a;b;c', ';'),
+ ('a&b;c', 'legacy'),
+ ):
+ with self.subTest(qs=qs, sep=sep):
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ if sep != 'legacy':
+ with self.assertRaises(ValueError):
+ urllib.parse.parse_qsl(qs, separator=sep, max_num_fields=2)
+ if sep:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = sep
+ with self.assertRaises(ValueError):
+ urllib.parse.parse_qsl(qs, max_num_fields=2)
+
+ def test_parse_qs_separator_priority(self):
+ # env variable trumps config file
+ with self._qsl_sep_config('~'), EnvironmentVarGuard() as environ:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = '!'
+ result = urllib.parse.parse_qs('a=1!b=2~c=3')
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+ # argument trumps config file
+ with self._qsl_sep_config('~'):
+ result = urllib.parse.parse_qs('a=1$b=2~c=3', separator='$')
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+ # argument trumps env variable
+ with EnvironmentVarGuard() as environ:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = '~'
+ result = urllib.parse.parse_qs('a=1$b=2~c=3', separator='$')
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+
+
def test_urlencode_sequences(self):
# Other tests incidentally urlencode things; test non-covered cases:
# Sequence and object values.
diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py
index f0d9d4d..70fc268 100644
--- a/Lib/urllib/parse.py
+++ b/Lib/urllib/parse.py
@@ -28,6 +28,7 @@ test_urlparse.py provides a good indicator of parsing behavior.
"""
import re
+import os
import sys
import collections
import warnings
@@ -660,7 +661,7 @@ def unquote(string, encoding='utf-8', errors='replace'):
def parse_qs(qs, keep_blank_values=False, strict_parsing=False,
- encoding='utf-8', errors='replace', max_num_fields=None, separator='&'):
+ encoding='utf-8', errors='replace', max_num_fields=None, separator=None):
"""Parse a query given as a string argument.
Arguments:
@@ -700,9 +701,16 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False,
parsed_result[name] = [value]
return parsed_result
+class _QueryStringSeparatorWarning(RuntimeWarning):
+ """Warning for using default `separator` in parse_qs or parse_qsl"""
+
+# The default "separator" for parse_qsl can be specified in a config file.
+# It's cached after first read.
+_QS_SEPARATOR_CONFIG_FILENAME = '/etc/python/urllib.cfg'
+_default_qs_separator = None
def parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
- encoding='utf-8', errors='replace', max_num_fields=None, separator='&'):
+ encoding='utf-8', errors='replace', max_num_fields=None, separator=None):
"""Parse a query given as a string argument.
Arguments:
@@ -731,20 +739,78 @@ def parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
Returns a list, as G-d intended.
"""
qs, _coerce_result = _coerce_args(qs)
- separator, _ = _coerce_args(separator)
- if not separator or (not isinstance(separator, (str, bytes))):
+ if isinstance(separator, bytes):
+ separator = separator.decode('ascii')
+
+ if (not separator or (not isinstance(separator, (str, bytes)))) and separator is not None:
raise ValueError("Separator must be of type string or bytes.")
+ # Used when both "&" and ";" act as separators. (Need a non-string value.)
+ _legacy = object()
+
+ if separator is None:
+ global _default_qs_separator
+ separator = _default_qs_separator
+ envvar_name = 'PYTHON_URLLIB_QS_SEPARATOR'
+ if separator is None:
+ # Set default separator from environment variable
+ separator = os.environ.get(envvar_name)
+ config_source = 'environment variable'
+ if separator is None:
+ # Set default separator from the configuration file
+ try:
+ file = open(_QS_SEPARATOR_CONFIG_FILENAME)
+ except FileNotFoundError:
+ pass
+ else:
+ with file:
+ import configparser
+ config = configparser.ConfigParser(
+ interpolation=None,
+ comment_prefixes=('#', ),
+ )
+ config.read_file(file)
+ separator = config.get('parse_qs', envvar_name, fallback=None)
+ _default_qs_separator = separator
+ config_source = _QS_SEPARATOR_CONFIG_FILENAME
+ if separator is None:
+ # The default is '&', but warn if not specified explicitly
+ if ';' in qs:
+ from warnings import warn
+ warn("The default separator of urllib.parse.parse_qsl and "
+ + "parse_qs was changed to '&' to avoid a web cache "
+ + "poisoning issue (CVE-2021-23336). "
+ + "By default, semicolons no longer act as query field "
+ + "separators. "
+ + "See https://access.redhat.com/articles/5860431 for "
+ + "more details.",
+ _QueryStringSeparatorWarning, stacklevel=2)
+ separator = '&'
+ elif separator == 'legacy':
+ separator = _legacy
+ elif len(separator) != 1:
+ raise ValueError(
+ f'{envvar_name} (from {config_source}) must contain '
+ + '1 character, or "legacy". See '
+ + 'https://access.redhat.com/articles/5860431 for more details.'
+ )
+
# If max_num_fields is defined then check that the number of fields
# is less than max_num_fields. This prevents a memory exhaustion DOS
# attack via post bodies with many fields.
if max_num_fields is not None:
- num_fields = 1 + qs.count(separator)
+ if separator is _legacy:
+ num_fields = 1 + qs.count('&') + qs.count(';')
+ else:
+ num_fields = 1 + qs.count(separator)
if max_num_fields < num_fields:
raise ValueError('Max number of fields exceeded')
- pairs = [s1 for s1 in qs.split(separator)]
+ if separator is _legacy:
+ pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
+ else:
+ pairs = [s1 for s1 in qs.split(separator)]
r = []
for name_value in pairs:
if not name_value and not strict_parsing:
--
2.31.1

@ -1,36 +0,0 @@
diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py
index 06c9107..6c3f5a8 100644
--- a/Lib/test/test_minidom.py
+++ b/Lib/test/test_minidom.py
@@ -1149,14 +1149,11 @@ class MinidomTest(unittest.TestCase):
# Verify that character decoding errors raise exceptions instead
# of crashing
- if pyexpat.version_info >= (2, 4, 5):
- self.assertRaises(ExpatError, parseString,
- b'<fran\xe7ais></fran\xe7ais>')
- self.assertRaises(ExpatError, parseString,
- b'<franais>Comment \xe7a va ? Tr\xe8s bien ?</franais>')
- else:
- self.assertRaises(UnicodeDecodeError, parseString,
- b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
+
+ self.assertRaises(ExpatError, parseString,
+ b'<fran\xe7ais></fran\xe7ais>')
+ self.assertRaises(ExpatError, parseString,
+ b'<franais>Comment \xe7a va ? Tr\xe8s bien ?</franais>')
doc.unlink()
@@ -1601,10 +1598,7 @@ class MinidomTest(unittest.TestCase):
self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
def testExceptionOnSpacesInXMLNSValue(self):
- if pyexpat.version_info >= (2, 4, 5):
- context = self.assertRaisesRegex(ExpatError, 'syntax error')
- else:
- context = self.assertRaisesRegex(ValueError, 'Unsupported syntax')
+ context = self.assertRaisesRegex(ExpatError, 'syntax error')
with context:
parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')

@ -1,357 +0,0 @@
From f36519078bde3cce4328c03fffccb846121fb5bc Mon Sep 17 00:00:00 2001
From: Petr Viktorin <encukou@gmail.com>
Date: Wed, 9 Aug 2023 20:23:03 +0200
Subject: [PATCH] Fix symlink handling for tarfile.data_filter
---
Doc/library/tarfile.rst | 5 +++++
Lib/tarfile.py | 9 ++++++++-
Lib/test/test_tarfile.py | 26 ++++++++++++++++++++++++--
3 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst
index 00f3070324e..e0511bfeb64 100644
--- a/Doc/library/tarfile.rst
+++ b/Doc/library/tarfile.rst
@@ -740,6 +740,11 @@ A ``TarInfo`` object has the following public data attributes:
Name of the target file name, which is only present in :class:`TarInfo` objects
of type :const:`LNKTYPE` and :const:`SYMTYPE`.
+ For symbolic links (``SYMTYPE``), the linkname is relative to the directory
+ that contains the link.
+ For hard links (``LNKTYPE``), the linkname is relative to the root of
+ the archive.
+
.. attribute:: TarInfo.uid
:type: int
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index df4e41f7a0d..d62323715b4 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -802,7 +802,14 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
if member.islnk() or member.issym():
if os.path.isabs(member.linkname):
raise AbsoluteLinkError(member)
- target_path = os.path.realpath(os.path.join(dest_path, member.linkname))
+ if member.issym():
+ target_path = os.path.join(dest_path,
+ os.path.dirname(name),
+ member.linkname)
+ else:
+ target_path = os.path.join(dest_path,
+ member.linkname)
+ target_path = os.path.realpath(target_path)
if os.path.commonpath([target_path, dest_path]) != dest_path:
raise LinkOutsideDestinationError(member, target_path)
return new_attrs
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 2eda7fc4cea..79fc35c2895 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -3337,10 +3337,12 @@ def __exit__(self, *exc):
self.bio = None
def add(self, name, *, type=None, symlink_to=None, hardlink_to=None,
- mode=None, **kwargs):
+ mode=None, size=None, **kwargs):
"""Add a member to the test archive. Call within `with`."""
name = str(name)
tarinfo = tarfile.TarInfo(name).replace(**kwargs)
+ if size is not None:
+ tarinfo.size = size
if mode:
tarinfo.mode = _filemode_to_int(mode)
if symlink_to is not None:
@@ -3416,7 +3418,8 @@ def check_context(self, tar, filter):
raise self.raised_exception
self.assertEqual(self.expected_paths, set())
- def expect_file(self, name, type=None, symlink_to=None, mode=None):
+ def expect_file(self, name, type=None, symlink_to=None, mode=None,
+ size=None):
"""Check a single file. See check_context."""
if self.raised_exception:
raise self.raised_exception
@@ -3445,6 +3448,8 @@ def expect_file(self, name, type=None, symlink_to=None, mode=None):
self.assertTrue(path.is_fifo())
else:
raise NotImplementedError(type)
+ if size is not None:
+ self.assertEqual(path.stat().st_size, size)
for parent in path.parents:
self.expected_paths.discard(parent)
@@ -3649,6 +3654,22 @@ def test_sly_relative2(self):
+ """['"].*moo['"], which is outside the """
+ "destination")
+ def test_deep_symlink(self):
+ with ArchiveMaker() as arc:
+ arc.add('targetdir/target', size=3)
+ arc.add('linkdir/hardlink', hardlink_to='targetdir/target')
+ arc.add('linkdir/symlink', symlink_to='../targetdir/target')
+
+ for filter in 'tar', 'data', 'fully_trusted':
+ with self.check_context(arc.open(), filter):
+ self.expect_file('targetdir/target', size=3)
+ self.expect_file('linkdir/hardlink', size=3)
+ if support.can_symlink():
+ self.expect_file('linkdir/symlink', size=3,
+ symlink_to='../targetdir/target')
+ else:
+ self.expect_file('linkdir/symlink', size=3)
+
def test_modes(self):
# Test how file modes are extracted
# (Note that the modes are ignored on platforms without working chmod)
--
2.41.0
From dc84087083c5ad99a5016e8349c96d9654a08f46 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <encukou@gmail.com>
Date: Mon, 6 Mar 2023 17:24:24 +0100
Subject: [PATCH 2/2] CVE-2007-4559, PEP-706: Add filters for tarfile
extraction (downstream)
Add and test RHEL-specific ways of configuring the default behavior: environment
variable and config file.
---
Lib/tarfile.py | 42 +++++++++++++
Lib/test/test_shutil.py | 3 +-
Lib/test/test_tarfile.py | 124 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 165 insertions(+), 4 deletions(-)
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 5291622ab8e..12ab00d748a 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -72,6 +72,13 @@ __all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", "ReadError",
"ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT",
"DEFAULT_FORMAT", "open"]
+# If true, use the safer (but backwards-incompatible) 'tar' extraction filter,
+# rather than 'fully_trusted', by default.
+# The emitted warning is changed to match.
+_RH_SAFER_DEFAULT = True
+
+# System-wide configuration file
+_CONFIG_FILENAME = '/etc/python/tarfile.cfg'
#---------------------------------------------------------
# tar constants
@@ -2188,6 +2195,41 @@ class TarFile(object):
if filter is None:
filter = self.extraction_filter
if filter is None:
+ name = os.environ.get('PYTHON_TARFILE_EXTRACTION_FILTER')
+ if name is None:
+ try:
+ file = bltn_open(_CONFIG_FILENAME)
+ except FileNotFoundError:
+ pass
+ else:
+ import configparser
+ conf = configparser.ConfigParser(
+ interpolation=None,
+ comment_prefixes=('#', ),
+ )
+ with file:
+ conf.read_file(file)
+ name = conf.get('tarfile',
+ 'PYTHON_TARFILE_EXTRACTION_FILTER',
+ fallback='')
+ if name:
+ try:
+ filter = _NAMED_FILTERS[name]
+ except KeyError:
+ raise ValueError(f"filter {filter!r} not found") from None
+ self.extraction_filter = filter
+ return filter
+ if _RH_SAFER_DEFAULT:
+ warnings.warn(
+ 'The default behavior of tarfile extraction has been '
+ + 'changed to disallow common exploits '
+ + '(including CVE-2007-4559). '
+ + 'By default, absolute/parent paths are disallowed '
+ + 'and some mode bits are cleared. '
+ + 'See https://access.redhat.com/articles/7004769 '
+ + 'for more details.',
+ RuntimeWarning)
+ return tar_filter
return fully_trusted_filter
if isinstance(filter, str):
raise TypeError(
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 5cef59ea9c6..73fffe0fd33 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -1494,7 +1494,8 @@ class TestShutil(unittest.TestCase):
def check_unpack_tarball(self, format):
self.check_unpack_archive(format, filter='fully_trusted')
self.check_unpack_archive(format, filter='data')
- with support.check_no_warnings(self):
+ with support.check_warnings(
+ ('.*CVE-2007-4559', RuntimeWarning)):
self.check_unpack_archive(format)
def test_unpack_archive_tar(self):
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 03be10b1fee..15df6a9ced6 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -2,7 +2,7 @@
import os
import io
from hashlib import sha256
-from contextlib import contextmanager
+from contextlib import contextmanager, ExitStack
from random import Random
import pathlib
import shutil
@@ -2744,7 +2744,11 @@ class NoneInfoExtractTests(ReadTest):
tar = tarfile.open(tarname, mode='r', encoding="iso8859-1")
cls.control_dir = pathlib.Path(TEMPDIR) / "extractall_ctrl"
tar.errorlevel = 0
- tar.extractall(cls.control_dir, filter=cls.extraction_filter)
+ with ExitStack() as cm:
+ if cls.extraction_filter is None:
+ cm.enter_context(warnings.catch_warnings())
+ warnings.simplefilter(action="ignore", category=RuntimeWarning)
+ tar.extractall(cls.control_dir, filter=cls.extraction_filter)
tar.close()
cls.control_paths = set(
p.relative_to(cls.control_dir)
@@ -3407,7 +3411,8 @@ class TestExtractionFilters(unittest.TestCase):
"""Ensure the default filter does not warn (like in 3.12)"""
with ArchiveMaker() as arc:
arc.add('foo')
- with support.check_no_warnings(self):
+ with support.check_warnings(
+ ('.*CVE-2007-4559', RuntimeWarning)):
with self.check_context(arc.open(), None):
self.expect_file('foo')
@@ -3577,6 +3582,119 @@ class TestExtractionFilters(unittest.TestCase):
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
+
+ return a triple with:
+ - temporary directory
+ - EnvironmentVarGuard()
+ - a test archive for use with check_* methods below
+
+ If config_lines is given, write them to the config file. Otherwise
+ the config file is missing.
+ """
+ tempdir = pathlib.Path(TEMPDIR) / 'tmp'
+ configfile = tempdir / 'tarfile.cfg'
+ with ArchiveMaker() as arc:
+ arc.add('good')
+ arc.add('ugly', symlink_to='/etc/passwd')
+ arc.add('../bad')
+ with ExitStack() as cm:
+ cm.enter_context(support.temp_dir(tempdir))
+ cm.enter_context(support.swap_attr(tarfile, '_CONFIG_FILENAME', str(configfile)))
+ env = cm.enter_context(support.EnvironmentVarGuard())
+ tar = cm.enter_context(arc.open())
+ if config_lines is not None:
+ with configfile.open('w') as f:
+ for line in config_lines:
+ print(line, file=f)
+ yield tempdir, env, tar
+
+ def check_rh_default_behavior(self, tar, tempdir):
+ """Check RH default: warn and refuse to extract dangerous files."""
+ with ExitStack() as cm:
+ cm.enter_context(support.check_warnings(
+ ('.*CVE-2007-4559', RuntimeWarning)))
+ cm.enter_context(self.assertRaises(tarfile.OutsideDestinationError))
+ tar.extractall(tempdir / 'outdir')
+
+ def check_trusted_default(self, tar, tempdir):
+ """Check 'fully_trusted' is configured as the default filter."""
+ with support.check_no_warnings(self):
+ tar.extractall(tempdir / 'outdir')
+ self.assertTrue((tempdir / 'outdir/good').exists())
+ self.assertEqual(os.readlink(str(tempdir / 'outdir/ugly')),
+ '/etc/passwd')
+ self.assertTrue((tempdir / 'bad').exists())
+
+ def test_rh_default_no_conf(self):
+ with self.rh_config_context() as (tempdir, env, tar):
+ self.check_rh_default_behavior(tar, tempdir)
+
+ def test_rh_default_from_file(self):
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=fully_trusted']
+ with self.rh_config_context(lines) as (tempdir, env, tar):
+ self.check_trusted_default(tar, tempdir)
+
+ def test_rh_empty_config_file(self):
+ """Empty config file -> default behavior"""
+ lines = []
+ with self.rh_config_context(lines) as (tempdir, env, tar):
+ self.check_rh_default_behavior(tar, tempdir)
+
+ def test_empty_config_section(self):
+ """Empty section in config file -> default behavior"""
+ lines = ['[tarfile]']
+ with self.rh_config_context(lines) as (tempdir, env, tar):
+ self.check_rh_default_behavior(tar, tempdir)
+
+ def test_rh_default_empty_config_option(self):
+ """Empty option value in config file -> default behavior"""
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=']
+ with self.rh_config_context(lines) as (tempdir, env, tar):
+ self.check_rh_default_behavior(tar, tempdir)
+
+ def test_bad_config_option(self):
+ """Bad option value in config file -> ValueError"""
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=unknown!']
+ with self.rh_config_context(lines) as (tempdir, env, tar):
+ with self.assertRaises(ValueError):
+ tar.extractall(tempdir / 'outdir')
+
+ def test_default_from_envvar(self):
+ with self.rh_config_context() as (tempdir, env, tar):
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'fully_trusted'
+ self.check_trusted_default(tar, tempdir)
+
+ def test_empty_envvar(self):
+ """Empty env variable -> default behavior"""
+ with self.rh_config_context() as (tempdir, env, tar):
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = ''
+ self.check_rh_default_behavior(tar, tempdir)
+
+ def test_bad_envvar(self):
+ with self.rh_config_context() as (tempdir, env, tar):
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'unknown!'
+ with self.assertRaises(ValueError):
+ tar.extractall(tempdir / 'outdir')
+
+ def test_envvar_overrides_file(self):
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=data']
+ with self.rh_config_context(lines) as (tempdir, env, tar):
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'fully_trusted'
+ self.check_trusted_default(tar, tempdir)
+
+ def test_monkeypatch_overrides_envvar(self):
+ with self.rh_config_context(None) as (tempdir, env, tar):
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'data'
+ with support.swap_attr(
+ tarfile.TarFile, 'extraction_filter',
+ staticmethod(tarfile.fully_trusted_filter)
+ ):
+ self.check_trusted_default(tar, tempdir)
+
+
def setUpModule():
support.unlink(TEMPDIR)
os.makedirs(TEMPDIR)
--
2.41.0

@ -13,11 +13,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}.17
%global general_version %{pybasever}.6
#global prerel ...
%global upstream_version %{general_version}%{?prerel}
Version: %{general_version}%{?prerel:~%{prerel}}
Release: 2%{?dist}
Release: 3%{?dist}
License: Python
# Exclude i686 arch. Due to a modularity issue it's being added to the
@ -350,47 +350,11 @@ Patch329: 00329-fips.patch
# a nightmare because it's basically a binary file.
Patch353: 00353-architecture-names-upstream-downstream.patch
# 00359 #
# CVE-2021-23336 python: Web Cache Poisoning via urllib.parse.parse_qsl and
# urllib.parse.parse_qs by using a semicolon in query parameters
# Upstream: https://bugs.python.org/issue42967
# Main BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1928904
Patch359: 00359-CVE-2021-23336.patch
# 00378 #
# Support expat 2.4.5
#
# Curly brackets were never allowed in namespace URIs
# according to RFC 3986, and so-called namespace-validating
# XML parsers have the right to reject them a invalid URIs.
#
# libexpat >=2.4.5 has become strcter in that regard due to
# related security issues; with ET.XML instantiating a
# namespace-aware parser under the hood, this test has no
# future in CPython.
#
# References:
# - https://datatracker.ietf.org/doc/html/rfc3968
# - https://www.w3.org/TR/xml-names/
#
# Also, test_minidom.py: Support Expat >=2.4.5
#
# The patch has diverged from upstream as the python test
# suite was relying on checking the expat version, whereas
# in RHEL fixes get backported instead of rebasing packages.
#
# Upstream: https://bugs.python.org/issue46811
Patch378: 00378-support-expat-2-4-5.patch
# 00397 #
# Add filters for tarfile extraction (CVE-2007-4559, PEP-706)
# First patch fixes determination of symlink targets, which were treated
# as relative to the root of the archive,
# rather than the directory containing the symlink.
# Not yet upstream as of this writing.
# The second patch is Red Hat configuration, see KB for documentation:
# - https://access.redhat.com/articles/7004769
Patch397: 00397-tarfile-filter.patch
# 00357 #
# Security fix for CVE-2021-3177
# Stack-based buffer overflow in PyCArg_repr in _ctypes/callproc.c
# Resolves upstream: https://bugs.python.org/issue42938
Patch357: 00357-CVE-2021-3177.patch
# (New patches go here ^^^)
#
@ -418,10 +382,10 @@ Provides: python%{pybasever} = %{version}-%{release}
# the possible alternatives
Provides: alternative-for(python)
# 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
# Runtime require alternatives
Requires: %{_sbindir}/alternatives
Requires(post): %{_sbindir}/alternatives
Requires(postun): %{_sbindir}/alternatives
%if %{without flatpackage}
@ -515,8 +479,8 @@ Summary: Python runtime libraries
Requires: python38-setuptools-wheel
Requires: python38-pip-wheel
%else
Provides: bundled(python38-pip) = 23.0.1
Provides: bundled(python38-setuptools) = 56.0.0
Provides: bundled(python38-pip) = 19.2.3
Provides: bundled(python38-setuptools) = 41.2.0
%endif
%{?python_provide:%python_provide python38-libs}
@ -548,9 +512,6 @@ BuildRequires: python-rpm-macros
# But we want them when packages BuildRequire python3-devel
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
# python38 installs the alternatives master symlink to which we attach a slave
Requires(post): python38
Requires(postun): python38
@ -589,8 +550,6 @@ Requires: %{name}-tkinter = %{version}-%{release}
%{?python_provide:%python_provide python38-idle}
# Require alternatives version that implements the --keep-foreign flag
Requires(postun): alternatives >= 1.19.1-1
# python38 installs the alternatives master symlink to which we attach a slave
Requires(post): python38
Requires(postun): python38
@ -647,9 +606,6 @@ Requires: %{name}-devel%{?_isa} = %{version}-%{release}
Requires: %{name}-test%{?_isa} = %{version}-%{release}
Requires: %{name}-tkinter%{?_isa} = %{version}-%{release}
Requires: %{name}-idle%{?_isa} = %{version}-%{release}
# Require alternatives version that implements the --keep-foreign flag
Requires(postun): alternatives >= 1.19.1-1
# python38 installs the alternatives master symlink to which we attach a slave
Requires(post): python38
Requires(postun): python38
@ -684,8 +640,8 @@ The debug runtime additionally supports debug builds of C-API extensions
Requires: python38-setuptools-wheel
Requires: python38-pip-wheel
%else
Provides: bundled(python38-pip) = 23.0.1
Provides: bundled(python38-setuptools) = 56.0.0
Provides: bundled(python38-pip) = 19.2.3
Provides: bundled(python38-setuptools) = 41.2.0
%endif
# The description for the flat package
@ -747,9 +703,8 @@ rm Lib/ensurepip/_bundled/*.whl
%patch328 -p1
%patch329 -p1
%patch353 -p1
%patch359 -p1
%patch378 -p1
%patch397 -p1
%patch357 -p1
# Remove files that should be generated by the build
# (This is after patching, so that we can use patches directly from upstream)
@ -1132,11 +1087,6 @@ touch %{buildroot}%{_bindir}/python3-config
touch %{buildroot}%{_bindir}/python3-debug
touch %{buildroot}%{_bindir}/python3-debug-config
# Strip the LTO bytecode from python.o
# Based on the fedora brp-strip-lto scriptlet
# https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/9dd5528cf9805ebfe31cff04fe7828ad06a6023f/f/brp-strip-lto
find %{buildroot} -type f -name 'python.o' -print0 | xargs -0 \
bash -c "strip -p -R .gnu.lto_* -R .gnu.debuglto_* -N __gnu_lto_v1 \"\$@\"" ARG0
# ======================================================
# Checks for packaging issues
@ -1274,15 +1224,15 @@ fi
%postun
# Do this only during uninstall process (not during update)
if [ $1 -eq 0 ]; then
alternatives --keep-foreign --remove python \
alternatives --remove python \
%{_bindir}/python3.8
alternatives --keep-foreign --remove python3 \
alternatives --remove python3 \
%{_bindir}/python3.8
# Remove link python → python3 if no other python3.* exists
if ! alternatives --display python3 > /dev/null; then
alternatives --keep-foreign --remove python \
alternatives --remove python \
%{_bindir}/python3
fi
fi
@ -1297,7 +1247,7 @@ alternatives --add-slave python3 %{_bindir}/python3.8 \
%postun devel
# Do this only during uninstall process (not during update)
if [ $1 -eq 0 ]; then
alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.8 \
alternatives --remove-slave python3 %{_bindir}/python3.8 \
python3-config
fi
@ -1315,9 +1265,9 @@ alternatives --add-slave python3 %{_bindir}/python3.8 \
%postun debug
# Do this only during uninstall process (not during update)
if [ $1 -eq 0 ]; then
alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.8 \
alternatives --remove-slave python3 %{_bindir}/python3.8 \
python3-debug
alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.8 \
alternatives --remove-slave python3 %{_bindir}/python3.8 \
python3-debug-config
fi
@ -1331,7 +1281,7 @@ alternatives --add-slave python3 %{_bindir}/python3.8 \
%postun idle
# Do this only during uninstall process (not during update)
if [ $1 -eq 0 ]; then
alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.8 \
alternatives --remove-slave python3 %{_bindir}/python3.8 \
idle3
fi
@ -1843,61 +1793,6 @@ fi
# ======================================================
%changelog
* Wed Aug 09 2023 Petr Viktorin <pviktori@redhat.com> - 3.8.17-2
- Fix symlink handling in the fix for CVE-2023-24329
Resolves: rhbz#263261
* Mon Aug 07 2023 Charalampos Stratakis <cstratak@redhat.com> - 3.8.17-1
- Update to 3.8.17
- Security fix for CVE-2023-24329
- Add filters for tarfile extraction (CVE-2007-4559, PEP-706)
Resolves: rhbz#2173917, rhbz#263261
* Tue Jul 18 2023 Charalampos Stratakis <cstratak@redhat.com> - 3.8.16-2
- Strip the LTO bytecode from python.o
Resolves: rhbz#2213526
* Tue Dec 13 2022 Charalampos Stratakis <cstratak@redhat.com> - 3.8.16-1
- Update to 3.8.16
- Security fix for CVE-2022-45061
Resolves: rhbz#2144072
* Mon Sep 12 2022 Charalampos Stratakis <cstratak@redhat.com> - 3.8.14-1
- Rebase to 3.8.14
- Security fixes for CVE-2020-10735 and CVE-2021-28861
Resolves: rhbz#1834423, rhbz#2120642
* Tue Jun 14 2022 Charalampos Stratakis <cstratak@redhat.com> - 3.8.13-1
- Rebase to 3.8.13
- Security fix for CVE-2015-20107
- Fix the test suite support for Expat >= 2.4.5
Resolves: rhbz#2075390
* Wed Sep 15 2021 Charalampos Stratakis <cstratak@redhat.com> - 3.8.12-1
- Update to 3.8.12
Resolves: rhbz#2004587
* Tue Sep 07 2021 Charalampos Stratakis <cstratak@redhat.com> - 3.8.11-1
- Update to 3.8.11
- Fix for CVE-2021-3733 and CVE-2021-3737
Resolves: rhbz#1995234, rhbz#1995162
* Mon Aug 02 2021 Tomas Orsava <torsava@redhat.com> - 3.8.8-4
- Adjusted the postun scriptlets to enable upgrading to RHEL 9
- Resolves: rhbz#1933055
* Tue Jul 27 2021 Charalampos Stratakis <cstratak@redhat.com> - 3.8.8-3
- Security fix for CVE-2021-29921: Leading zeros in IPv4 addresses are no longer tolerated
Resolves: rhbz#1957458
* Fri Apr 30 2021 Charalampos Stratakis <cstratak@redhat.com> - 3.8.8-2
- Security fix for CVE-2021-3426: information disclosure via pydoc
Resolves: rhbz#1935913
* Mon Mar 15 2021 Lumír Balhar <lbalhar@redhat.com> - 3.8.8-1
- Update to 3.8.8 and fix CVE-2021-23336
Resolves: rhbz#1928904
* Fri Jan 22 2021 Charalampos Stratakis <cstratak@redhat.com> - 3.8.6-3
- Security fix for CVE-2021-3177
Resolves: rhbz#1919161

Loading…
Cancel
Save