From ff2416b8488ec6261efa5a193827e8b61ce61da5da6863f00efbb7944ffe8507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Fri, 21 Nov 2025 17:36:09 +0100 Subject: [PATCH 01/23] Init of the SLE repo. --- .gitattributes | 42 ++++++++++++++++++++++++++++++++++++++++++ .gitignore | 3 +++ 2 files changed, 45 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5ffafbd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,42 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*.changes merge=merge-changes +*.gitignore filter=lfs diff=lfs merge=lfs -text +*.patch filter=lfs diff=lfs merge=lfs -text +/PACKAGING-NOTES filter=lfs diff=lfs merge=lfs -text +*.asc filter=lfs diff=lfs merge=lfs -text +*.SUSE filter=lfs diff=lfs merge=lfs -text +/_multibuild filter=lfs diff=lfs merge=lfs -text +*.conf filter=lfs diff=lfs merge=lfs -text +*.xml filter=lfs diff=lfs merge=lfs -text +*.desktop filter=lfs diff=lfs merge=lfs -text +*.map filter=lfs diff=lfs merge=lfs -text +*.py filter=lfs diff=lfs merge=lfs -text +*.python3 filter=lfs diff=lfs merge=lfs -text +*.sh filter=lfs diff=lfs merge=lfs -text +*.keyring filter=lfs diff=lfs merge=lfs -text +*.changes filter=lfs diff=lfs merge=lfs -text +*.spec filter=lfs diff=lfs merge=lfs -text +/python310-rpmlintrc filter=lfs diff=lfs merge=lfs -text +*.sigstore filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..15e3165 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f058df83f155413282cab291d4865b00bbb3a69d1e9b6e5f247a28211587fe4a +size 51 From 239187173a1e729356219c9048ad14a015f0384ef9dda6786f7aec4481f6054c Mon Sep 17 00:00:00 2001 From: Wolfgang Engel Date: Wed, 6 Jul 2022 11:04:35 +0000 Subject: [PATCH 02/23] Set link to python310.24645 via maintenance_release request --- .gitattributes | 18 - .gitignore | 8 +- CVE-2015-20107-mailcap-unsafe-filenames.patch | 136 + F00251-change-user-install-location.patch | 58 + PACKAGING-NOTES | 26 + Python-3.10.5.tar.xz | 3 + Python-3.10.5.tar.xz.asc | 16 + README.SUSE | 43 + _multibuild | 4 + baselibs.conf | 3 + bluez-devel-vendor.tar.xz | 3 + bpo-31046_ensurepip_honours_prefix.patch | 163 + distutils-reproducible-compile.patch | 15 + fix_configure_rst.patch | 40 + idle3.appdata.xml | 35 + idle3.desktop | 12 + import_failed.map | 7 + import_failed.py | 23 + macros.python3 | 28 + no-skipif-doctests.patch | 660 ++++ pre_checkin.sh | 78 + python-3.3.0b1-fix_date_time_compiler.patch | 25 + python-3.3.0b1-localpath.patch | 11 + python-3.3.0b1-test-posix_fadvise.patch | 15 + python.keyring | 109 + python310-rpmlintrc | 1 + python310.changes | 3243 +++++++++++++++++ python310.spec | 1022 ++++++ skip-test_pyobject_freed_is_freed.patch | 14 + skipped_tests.py | 69 + subprocess-raise-timeout.patch | 16 + support-expat-CVE-2022-25236-patched.patch | 75 + 32 files changed, 5958 insertions(+), 21 deletions(-) create mode 100644 CVE-2015-20107-mailcap-unsafe-filenames.patch create mode 100644 F00251-change-user-install-location.patch create mode 100644 PACKAGING-NOTES create mode 100644 Python-3.10.5.tar.xz create mode 100644 Python-3.10.5.tar.xz.asc create mode 100644 README.SUSE create mode 100644 _multibuild create mode 100644 baselibs.conf create mode 100644 bluez-devel-vendor.tar.xz create mode 100644 bpo-31046_ensurepip_honours_prefix.patch create mode 100644 distutils-reproducible-compile.patch create mode 100644 fix_configure_rst.patch create mode 100644 idle3.appdata.xml create mode 100644 idle3.desktop create mode 100644 import_failed.map create mode 100644 import_failed.py create mode 100644 macros.python3 create mode 100644 no-skipif-doctests.patch create mode 100644 pre_checkin.sh create mode 100644 python-3.3.0b1-fix_date_time_compiler.patch create mode 100644 python-3.3.0b1-localpath.patch create mode 100644 python-3.3.0b1-test-posix_fadvise.patch create mode 100644 python.keyring create mode 100644 python310-rpmlintrc create mode 100644 python310.changes create mode 100644 python310.spec create mode 100644 skip-test_pyobject_freed_is_freed.patch create mode 100644 skipped_tests.py create mode 100644 subprocess-raise-timeout.patch create mode 100644 support-expat-CVE-2022-25236-patched.patch diff --git a/.gitattributes b/.gitattributes index 5ffafbd..a9bfeea 100644 --- a/.gitattributes +++ b/.gitattributes @@ -22,21 +22,3 @@ *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *.changes merge=merge-changes -*.gitignore filter=lfs diff=lfs merge=lfs -text -*.patch filter=lfs diff=lfs merge=lfs -text -/PACKAGING-NOTES filter=lfs diff=lfs merge=lfs -text -*.asc filter=lfs diff=lfs merge=lfs -text -*.SUSE filter=lfs diff=lfs merge=lfs -text -/_multibuild filter=lfs diff=lfs merge=lfs -text -*.conf filter=lfs diff=lfs merge=lfs -text -*.xml filter=lfs diff=lfs merge=lfs -text -*.desktop filter=lfs diff=lfs merge=lfs -text -*.map filter=lfs diff=lfs merge=lfs -text -*.py filter=lfs diff=lfs merge=lfs -text -*.python3 filter=lfs diff=lfs merge=lfs -text -*.sh filter=lfs diff=lfs merge=lfs -text -*.keyring filter=lfs diff=lfs merge=lfs -text -*.changes filter=lfs diff=lfs merge=lfs -text -*.spec filter=lfs diff=lfs merge=lfs -text -/python310-rpmlintrc filter=lfs diff=lfs merge=lfs -text -*.sigstore filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 15e3165..3b0de5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f058df83f155413282cab291d4865b00bbb3a69d1e9b6e5f247a28211587fe4a -size 51 +.osc +*.obscpio +_build.* +.pbuild +python310-*-build/ diff --git a/CVE-2015-20107-mailcap-unsafe-filenames.patch b/CVE-2015-20107-mailcap-unsafe-filenames.patch new file mode 100644 index 0000000..5ab5999 --- /dev/null +++ b/CVE-2015-20107-mailcap-unsafe-filenames.patch @@ -0,0 +1,136 @@ +From c3e7f139b440d7424986204e9f3fc2275aea3377 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Wed, 27 Apr 2022 18:17:33 +0200 +Subject: [PATCH 1/4] gh-68966: Make mailcap refuse to match unsafe + filenames/types/params + +--- + Doc/library/mailcap.rst | 12 ++++ + Lib/mailcap.py | 26 +++++++++- + Lib/test/test_mailcap.py | 8 ++- + Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst | 4 + + 4 files changed, 46 insertions(+), 4 deletions(-) + +--- a/Doc/library/mailcap.rst ++++ b/Doc/library/mailcap.rst +@@ -60,6 +60,18 @@ standard. However, mailcap files are su + use) to determine whether or not the mailcap line applies. :func:`findmatch` + will automatically check such conditions and skip the entry if the check fails. + ++ .. versionchanged:: 3.11 ++ ++ To prevent security issues with shell metacharacters (symbols that have ++ special effects in a shell command line), ``findmatch`` will refuse ++ to inject ASCII characters other than alphanumerics and ``@+=:,./-_`` ++ into the returned command line. ++ ++ If a disallowed character appears in *filename*, ``findmatch`` will always ++ return ``(None, None)`` as if no entry was found. ++ If such a character appears elsewhere (a value in *plist* or in *MIMEtype*), ++ ``findmatch`` will ignore all mailcap entries which use that value. ++ A :mod:`warning ` will be raised in either case. + + .. function:: getcaps() + +--- a/Lib/mailcap.py ++++ b/Lib/mailcap.py +@@ -2,6 +2,7 @@ + + import os + import warnings ++import re + + __all__ = ["getcaps","findmatch"] + +@@ -13,6 +14,11 @@ def lineno_sort_key(entry): + else: + return 1, 0 + ++_find_unsafe = re.compile(r'[^\xa1-\U0010FFFF\w@+=:,./-]').search ++ ++class UnsafeMailcapInput(Warning): ++ """Warning raised when refusing unsafe input""" ++ + + # Part 1: top-level interface. + +@@ -165,15 +171,22 @@ def findmatch(caps, MIMEtype, key='view' + entry to use. + + """ ++ if _find_unsafe(filename): ++ msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (filename,) ++ warnings.warn(msg, UnsafeMailcapInput) ++ return None, None + entries = lookup(caps, MIMEtype, key) + # XXX This code should somehow check for the needsterminal flag. + for e in entries: + if 'test' in e: + test = subst(e['test'], filename, plist) ++ if test is None: ++ continue + if test and os.system(test) != 0: + continue + command = subst(e[key], MIMEtype, filename, plist) +- return command, e ++ if command is not None: ++ return command, e + return None, None + + def lookup(caps, MIMEtype, key=None): +@@ -206,6 +219,10 @@ def subst(field, MIMEtype, filename, pli + elif c == 's': + res = res + filename + elif c == 't': ++ if _find_unsafe(MIMEtype): ++ msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,) ++ warnings.warn(msg, UnsafeMailcapInput) ++ return None + res = res + MIMEtype + elif c == '{': + start = i +@@ -213,7 +230,12 @@ def subst(field, MIMEtype, filename, pli + i = i+1 + name = field[start:i] + i = i+1 +- res = res + findparam(name, plist) ++ param = findparam(name, plist) ++ if _find_unsafe(param): ++ msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name) ++ warnings.warn(msg, UnsafeMailcapInput) ++ return None ++ res = res + param + # XXX To do: + # %n == number of parts if type is multipart/* + # %F == list of alternating type and filename for parts +--- a/Lib/test/test_mailcap.py ++++ b/Lib/test/test_mailcap.py +@@ -123,7 +123,8 @@ class HelperFunctionTest(unittest.TestCa + (["", "audio/*", "foo.txt"], ""), + (["echo foo", "audio/*", "foo.txt"], "echo foo"), + (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"), +- (["echo %t", "audio/*", "foo.txt"], "echo audio/*"), ++ (["echo %t", "audio/*", "foo.txt"], None), ++ (["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"), + (["echo \\%t", "audio/*", "foo.txt"], "echo %t"), + (["echo foo", "audio/*", "foo.txt", plist], "echo foo"), + (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3") +@@ -207,7 +208,10 @@ class FindmatchTest(unittest.TestCase): + ('"An audio fragment"', audio_basic_entry)), + ([c, "audio/*"], + {"filename": fname}, +- ("/usr/local/bin/showaudio audio/*", audio_entry)), ++ (None, None)), ++ ([c, "audio/wav"], ++ {"filename": fname}, ++ ("/usr/local/bin/showaudio audio/wav", audio_entry)), + ([c, "message/external-body"], + {"plist": plist}, + ("showexternal /dev/null default john python.org /tmp foo bar", message_entry)) +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst +@@ -0,0 +1,4 @@ ++The deprecated mailcap module now refuses to inject unsafe text (filenames, ++MIME types, parameters) into shell commands. Instead of using such text, it ++will warn and act as if a match was not found (or for test commands, as if ++the test failed). diff --git a/F00251-change-user-install-location.patch b/F00251-change-user-install-location.patch new file mode 100644 index 0000000..e69c5a9 --- /dev/null +++ b/F00251-change-user-install-location.patch @@ -0,0 +1,58 @@ +From 910f38d9768d39d4d31426743ae4081ed1ab66b6 Mon Sep 17 00:00:00 2001 +From: Michal Cyprian +Date: Mon, 26 Jun 2017 16:32:56 +0200 +Subject: [PATCH] 00251: Change user install location + +Set values of prefix and exec_prefix in distutils install command +to /usr/local if executable is /usr/bin/python* and RPM build +is not detected to make pip and distutils install into separate location. + +Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe +--- + Lib/distutils/command/install.py | 15 +++++++++++++-- + Lib/site.py | 9 ++++++++- + 2 files changed, 21 insertions(+), 3 deletions(-) + +--- a/Lib/distutils/command/install.py ++++ b/Lib/distutils/command/install.py +@@ -441,8 +441,19 @@ class install(Command): + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") + +- self.prefix = os.path.normpath(sys.prefix) +- self.exec_prefix = os.path.normpath(sys.exec_prefix) ++ # self.prefix is set to sys.prefix + /local/ ++ # if neither RPM build nor virtual environment is ++ # detected to make pip and distutils install packages ++ # into the separate location. ++ if (not (hasattr(sys, 'real_prefix') or ++ sys.prefix != sys.base_prefix) and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ addition = "/local" ++ else: ++ addition = "" ++ ++ self.prefix = os.path.normpath(sys.prefix) + addition ++ self.exec_prefix = os.path.normpath(sys.exec_prefix) + addition + + else: + if self.exec_prefix is None: +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -380,8 +380,15 @@ def getsitepackages(prefixes=None): + return sitepackages + + def addsitepackages(known_paths, prefixes=None): +- """Add site-packages to sys.path""" ++ """Add site-packages to sys.path ++ ++ '/usr/local' is included in PREFIXES if RPM build is not detected ++ to make packages installed into this location visible. ++ ++ """ + _trace("Processing global site-packages") ++ if ENABLE_USER_SITE and 'RPM_BUILD_ROOT' not in os.environ: ++ PREFIXES.insert(0, "/usr/local") + for sitedir in getsitepackages(prefixes): + if os.path.isdir(sitedir): + addsitedir(sitedir, known_paths) diff --git a/PACKAGING-NOTES b/PACKAGING-NOTES new file mode 100644 index 0000000..e28c88c --- /dev/null +++ b/PACKAGING-NOTES @@ -0,0 +1,26 @@ +Notes for packagers of Python3 +============================== + +0. Faster build turnaround +-------------------------- + +By default, python builds with profile-guided optimization. This needs +an additional run of the test suite and it is generally slow. +PGO build takes around 50 minutes. + +For development, use "--without profileopt" option to disable PGO. This +shortens the build time to ~5 minutes including test suite. + +1. import_failed.map +---------------------- + +This is a mechanism installed as part of python3-base, that places shim modules +on python's path (through a generated zzzz-import-failed-hooks.pth file, so that +it is imported as much at the end as makes sense; and an _import_failed subdir +of /usr/lib/pythonX.Y). Then when the user tries to import a module that is part +of a subpackage, the ImportError will contain a helpful message telling them +which missing subpackage to install. + +This can sometimes cause problems on non-standard configurations, if the pth +gets included too early (for instance if you are using a script to include all +pths by hand in some strange order). Just something to look out for. diff --git a/Python-3.10.5.tar.xz b/Python-3.10.5.tar.xz new file mode 100644 index 0000000..6f6bbaf --- /dev/null +++ b/Python-3.10.5.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8437efd5b106ef0a75aabfbf23d880625120a73a86a22ade4d2e2e68d7b74486 +size 19361320 diff --git a/Python-3.10.5.tar.xz.asc b/Python-3.10.5.tar.xz.asc new file mode 100644 index 0000000..4f10f1d --- /dev/null +++ b/Python-3.10.5.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmKd7cEACgkQ/+h0BBaL +2EfE/g/+MM3/BRFoUwEFRF+s0WYh1IxjalHXx+9IjKFYH9xYgz4hkegcU2A6XftC +mNHI9WRZ0tXPCOe/HSL3cmGretOW59Fh1outhzL3xumLAhODMJ5JBQM3/pQ2q/CV +/zvX5nVWjwg4XhlOg6AtIRRdmqjbNesGj4a0laG5l45AzxggAVe/2l/YMvo5aq4s +uTZ8s0EdNkPugVOZBe3bQ6MxkWymUmB0VC86mCuhcNx2uzB2ulyjUHBKUwqRo55N +C7BQUvL+dUNf27aFUBs42D3bjlUijvwf0Nc3BQM95d6WgmIsEOUQj/Tevsdb34DL +zt/slvwiwxJYlIlJP9jmxd6/CuqCdt07ML24/EMv1UUadwyvi5zVhmatuKpACULX +RNZSHy8ksgclc1KszxQfJMOqdbjy4K4Wa9jmh8/URCSOoagkF1opr7n9NXjPARXa +NoZCAbwoBiV9E1F4Fs8AmubI9tLyL9tMYayqF4vQgnSKlYD/Y5bxV7bmYTV6ELXE +m6UurUeCx0kzAvGt9qNx2B2TBoeyMdy12nmiiOAF1CCK76UUXwFFnG+vOlxC1d4U +GSKISTJkNY8dn40RPBpYjhCgbEPJiJbpvh4ryE3EVUQ6sPOBdrt2/xKJq/UprpFf +/rf5gk0BoNLtTp23k+Hh9UeRkji+0PMR0DgVS4DxzV9RUaSMyDY= +=FV1Y +-----END PGP SIGNATURE----- diff --git a/README.SUSE b/README.SUSE new file mode 100644 index 0000000..0053bcf --- /dev/null +++ b/README.SUSE @@ -0,0 +1,43 @@ +Python 3 in SUSE +============== + +* Subpackages * + +Python 3 is split into several subpackages, based on external dependencies. +The main package 'python3' has soft dependencies on all subpackages needed to +assemble the standard library; however, these might not all be installed by default. + +If you attempt to import a module that is currently not installed, an ImportError is thrown, +with instructions to install the missing subpackage. Installing the subpackage might result +in installing libraries that the subpackage requires to function. + + +* ensurepip * + +The 'ensurepip' module from Python 3 standard library (PEP 453) is supposed to deploy +a bundled copy of the pip installer. This makes no sense in a managed distribution like SUSE. +Instead, you need to install package 'python3-pip'. Usually this will be installed automatically +with 'python3'. + +Using 'ensurepip' when pip is not installed will result in an ImportError with instructions +to install 'python3-pip'. + + +* Documentation * + +You can find documentation in seprarate packages: python3-doc and +python3-doc-pdf. These contan following documents: + + Tutorial, What's New in Python, Global Module Index, Library Reference, + Macintosh Module Reference, Installing Python Modules, Distributing Python + Modules, Language Reference, Extending and Embedding, Python/C API, + Documenting Python + +The python3-doc package constains many text files from source tarball. + + +* Interactive mode * + +Interactive mode is by default enhanced with of history and command completion. +If you don't like these features, you can unset the PYTHONSTARTUP variable +in your .profile or disable it system wide in /etc/profile.d/python.sh. diff --git a/_multibuild b/_multibuild new file mode 100644 index 0000000..1d50bc4 --- /dev/null +++ b/_multibuild @@ -0,0 +1,4 @@ + + base + doc + diff --git a/baselibs.conf b/baselibs.conf new file mode 100644 index 0000000..67e73d0 --- /dev/null +++ b/baselibs.conf @@ -0,0 +1,3 @@ +python310-base +python310 +libpython3_10-1_0 diff --git a/bluez-devel-vendor.tar.xz b/bluez-devel-vendor.tar.xz new file mode 100644 index 0000000..57fb3e5 --- /dev/null +++ b/bluez-devel-vendor.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:868fbfba2ddaed62f2c45cf869fe2c648527ac3bac738f4f41f3c2872f5b0211 +size 25040 diff --git a/bpo-31046_ensurepip_honours_prefix.patch b/bpo-31046_ensurepip_honours_prefix.patch new file mode 100644 index 0000000..e3bb24d --- /dev/null +++ b/bpo-31046_ensurepip_honours_prefix.patch @@ -0,0 +1,163 @@ +From 5754521af1d51aa8e445cba07a093bbc0c88596d Mon Sep 17 00:00:00 2001 +From: Zackery Spytz +Date: Mon, 16 Dec 2019 18:24:08 -0700 +Subject: [PATCH] bpo-31046: ensurepip does not honour the value of $(prefix) + +Co-Authored-By: Xavier de Gaye +--- + Doc/library/ensurepip.rst | 9 +++-- + Lib/ensurepip/__init__.py | 18 +++++++--- + Lib/test/test_ensurepip.py | 11 ++++++ + Makefile.pre.in | 4 +- + Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst | 1 + 5 files changed, 34 insertions(+), 9 deletions(-) + create mode 100644 Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst + +--- a/Doc/library/ensurepip.rst ++++ b/Doc/library/ensurepip.rst +@@ -56,8 +56,9 @@ is at least as recent as the one availab + By default, ``pip`` is installed into the current virtual environment + (if one is active) or into the system site packages (if there is no + active virtual environment). The installation location can be controlled +-through two additional command line options: ++through some additional command line options: + ++* ``--prefix ``: Installs ``pip`` using the given directory prefix. + * ``--root ``: Installs ``pip`` relative to the given root directory + rather than the root of the currently active virtual environment (if any) + or the default root for the current Python installation. +@@ -89,7 +90,7 @@ Module API + Returns a string specifying the available version of pip that will be + installed when bootstrapping an environment. + +-.. function:: bootstrap(root=None, upgrade=False, user=False, \ ++.. function:: bootstrap(root=None, prefix=None, upgrade=False, user=False, \ + altinstall=False, default_pip=False, \ + verbosity=0) + +@@ -99,6 +100,8 @@ Module API + If *root* is ``None``, then installation uses the default install location + for the current environment. + ++ *prefix* specifies the directory prefix to use when installing. ++ + *upgrade* indicates whether or not to upgrade an existing installation + of an earlier version of ``pip`` to the available version. + +@@ -119,6 +122,8 @@ Module API + *verbosity* controls the level of output to :data:`sys.stdout` from the + bootstrapping operation. + ++ .. versionchanged:: 3.9 the *prefix* parameter was added. ++ + .. audit-event:: ensurepip.bootstrap root ensurepip.bootstrap + + .. note:: +--- a/Lib/ensurepip/__init__.py ++++ b/Lib/ensurepip/__init__.py +@@ -113,27 +113,27 @@ def _disable_pip_configuration_settings( + os.environ['PIP_CONFIG_FILE'] = os.devnull + + +-def bootstrap(*, root=None, upgrade=False, user=False, ++def bootstrap(*, root=None, prefix=None, upgrade=False, user=False, + altinstall=False, default_pip=False, + verbosity=0): + """ + Bootstrap pip into the current Python installation (or the given root +- directory). ++ and directory prefix). + + Note that calling this function will alter both sys.path and os.environ. + """ + # Discard the return value +- _bootstrap(root=root, upgrade=upgrade, user=user, ++ _bootstrap(root=root, prefix=prefix, upgrade=upgrade, user=user, + altinstall=altinstall, default_pip=default_pip, + verbosity=verbosity) + + +-def _bootstrap(*, root=None, upgrade=False, user=False, ++def _bootstrap(*, root=None, prefix=None, upgrade=False, user=False, + altinstall=False, default_pip=False, + verbosity=0): + """ + Bootstrap pip into the current Python installation (or the given root +- directory). Returns pip command status code. ++ and directory prefix). Returns pip command status code. + + Note that calling this function will alter both sys.path and os.environ. + """ +@@ -183,6 +183,8 @@ def _bootstrap(*, root=None, upgrade=Fal + args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir] + if root: + args += ["--root", root] ++ if prefix: ++ args += ["--prefix", prefix] + if upgrade: + args += ["--upgrade"] + if user: +@@ -258,6 +260,11 @@ def _main(argv=None): + help="Install everything relative to this alternate root directory.", + ) + parser.add_argument( ++ "--prefix", ++ default=None, ++ help="Install everything using this prefix.", ++ ) ++ parser.add_argument( + "--altinstall", + action="store_true", + default=False, +@@ -276,6 +283,7 @@ def _main(argv=None): + + return _bootstrap( + root=args.root, ++ prefix=args.prefix, + upgrade=args.upgrade, + user=args.user, + verbosity=args.verbosity, +--- a/Lib/test/test_ensurepip.py ++++ b/Lib/test/test_ensurepip.py +@@ -112,6 +112,17 @@ class TestBootstrap(EnsurepipMixin, unit + unittest.mock.ANY, + ) + ++ def test_bootstrapping_with_prefix(self): ++ ensurepip.bootstrap(prefix="/foo/bar/") ++ self.run_pip.assert_called_once_with( ++ [ ++ "install", "--no-cache-dir", "--no-index", "--find-links", ++ unittest.mock.ANY, "--prefix", "/foo/bar/", ++ "setuptools", "pip", ++ ], ++ unittest.mock.ANY, ++ ) ++ + def test_bootstrapping_with_user(self): + ensurepip.bootstrap(user=True) + +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -1278,7 +1278,7 @@ install: @FRAMEWORKINSTALLFIRST@ commoni + install|*) ensurepip="" ;; \ + esac; \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \ +- $$ensurepip --root=$(DESTDIR)/ ; \ ++ $$ensurepip --root=$(DESTDIR)/ --prefix=$(prefix) ; \ + fi + + altinstall: commoninstall +@@ -1288,7 +1288,7 @@ altinstall: commoninstall + install|*) ensurepip="--altinstall" ;; \ + esac; \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) -m ensurepip \ +- $$ensurepip --root=$(DESTDIR)/ ; \ ++ $$ensurepip --root=$(DESTDIR)/ --prefix=$(prefix) ; \ + fi + + commoninstall: check-clean-src @FRAMEWORKALTINSTALLFIRST@ \ +--- /dev/null ++++ b/Misc/NEWS.d/next/Build/2019-12-16-17-50-42.bpo-31046.XA-Qfr.rst +@@ -0,0 +1 @@ ++A directory prefix can now be specified when using :mod:`ensurepip`. diff --git a/distutils-reproducible-compile.patch b/distutils-reproducible-compile.patch new file mode 100644 index 0000000..fd98baa --- /dev/null +++ b/distutils-reproducible-compile.patch @@ -0,0 +1,15 @@ +--- + Lib/distutils/util.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Lib/distutils/util.py ++++ b/Lib/distutils/util.py +@@ -436,7 +436,7 @@ byte_compile(files, optimize=%r, force=% + else: + from py_compile import compile + +- for file in py_files: ++ for file in sorted(py_files): + if file[-3:] != ".py": + # This lets us be lazy and not filter filenames in + # the "install_lib" command. diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch new file mode 100644 index 0000000..85be030 --- /dev/null +++ b/fix_configure_rst.patch @@ -0,0 +1,40 @@ +--- + Doc/using/configure.rst | 3 --- + Misc/NEWS | 2 +- + 2 files changed, 1 insertion(+), 4 deletions(-) + +--- a/Doc/using/configure.rst ++++ b/Doc/using/configure.rst +@@ -42,7 +42,6 @@ General Options + + See :data:`sys.int_info.bits_per_digit `. + +-.. cmdoption:: --with-cxx-main + .. cmdoption:: --with-cxx-main=COMPILER + + Compile the Python ``main()`` function and link Python executable with C++ +@@ -457,13 +456,11 @@ macOS Options + + See ``Mac/README.rst``. + +-.. cmdoption:: --enable-universalsdk + .. cmdoption:: --enable-universalsdk=SDKDIR + + Create a universal binary build. *SDKDIR* specifies which macOS SDK should + be used to perform the build (default is no). + +-.. cmdoption:: --enable-framework + .. cmdoption:: --enable-framework=INSTALLDIR + + Create a Python.framework rather than a traditional Unix install. Optional +--- a/Misc/NEWS ++++ b/Misc/NEWS +@@ -2370,7 +2370,7 @@ C API + ----- + + - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name +- :c:struct:`PyFrameObject` rather than ``_frame``. The non-existing entry ++ :c:type:`PyFrameObject` rather than ``_frame``. The non-existing entry + ``_node`` no longer appears in the list. + + - bpo-44378: :c:func:`Py_IS_TYPE` no longer uses :c:func:`Py_TYPE` to avoid diff --git a/idle3.appdata.xml b/idle3.appdata.xml new file mode 100644 index 0000000..554b7c4 --- /dev/null +++ b/idle3.appdata.xml @@ -0,0 +1,35 @@ + + + + + idle3.desktop + IDLE3 + CC0 + Python-2.0 + Python 3 Integrated Development and Learning Environment + +

+ IDLE is Python’s Integrated Development and Learning Environment. + The GUI is uniform between Windows, Unix, and Mac OS X. + IDLE provides an easy way to start writing, running, and debugging + Python code. +

+

+ IDLE is written in pure Python, and uses the tkinter GUI toolkit. + It provides: +

+
    +
  • a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,
  • +
  • a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,
  • +
  • search within any window, replace within editor windows, and search through multiple files (grep),
  • +
  • a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.
  • +
+
+ https://docs.python.org/3/library/idle.html + + http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png + http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png + http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png + + zbyszek@in.waw.pl +
diff --git a/idle3.desktop b/idle3.desktop new file mode 100644 index 0000000..43f5a4c --- /dev/null +++ b/idle3.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Version=1.0 +Name=IDLE 3 +GenericName=Python 3 IDE +Comment=Python 3 Integrated Development and Learning Environment +Exec=idle3 %F +TryExec=idle3 +Terminal=false +Type=Application +Icon=idle3 +Categories=Development;IDE; +MimeType=text/x-python; diff --git a/import_failed.map b/import_failed.map new file mode 100644 index 0000000..9f01f41 --- /dev/null +++ b/import_failed.map @@ -0,0 +1,7 @@ +python39-curses: curses _curses _curses_panel +python39-dbm: dbm _dbm _gdbm +python39-idle: idlelib +python39-testsuite: test _ctypes_test _testbuffer _testcapi _testinternalcapi _testimportmultiple _testmultiphase xxlimited +python39-tk: tkinter _tkinter +python39-tools: turtledemo +python39: sqlite3 readline _sqlite3 nis diff --git a/import_failed.py b/import_failed.py new file mode 100644 index 0000000..258b5a5 --- /dev/null +++ b/import_failed.py @@ -0,0 +1,23 @@ +import sys, os +from sysconfig import get_path + +failed_map_path = os.path.join(get_path('stdlib'), '_import_failed', 'import_failed.map') + +if __spec__: + failed_name = __spec__.name +else: + failed_name = __name__ + +with open(failed_map_path) as fd: + for line in fd: + package = line.split(':')[0] + imports = line.split(':')[1] + if failed_name in imports: + raise ImportError(f"""Module '{failed_name}' is not installed. +Use: + sudo zypper install {package} +to install it.""") + +raise ImportError(f"""Module '{failed_name}' is not installed. +It is supposed to be part of python3 distribution, but missing from failed import map. +Please file a bug on the SUSE Bugzilla.""") diff --git a/macros.python3 b/macros.python3 new file mode 100644 index 0000000..2bd193b --- /dev/null +++ b/macros.python3 @@ -0,0 +1,28 @@ +%have_python3 1 + +# commented out legacy macro definitions +#py3_prefix /usr +#py3_incdir /usr/include/python3.5m +#py3_ver 3.5 + +# these should now be provided by macros.python_all +#python3_sitearch /usr/lib64/python3.5/site-packages +#python3_sitelib /usr/lib/python3.5/site-packages +#python3_version 3.5 + +# hard to say if anyone ever used these? +#py3_soflags cpython-35m-x86_64-linux-gnu +#py3_abiflags m +%cpython3_soabi %(python3 -c "import sysconfig; print(sysconfig.get_config_var('SOABI'))") +%py3_soflags %cpython3_soabi + +# compilation macros that might be in use somewhere +%py3_compile(O) \ +find %1 -name '*.pyc' -exec rm -f {} ";"\ +python3 -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1\ +%{-O:\ +find %1 -name '*.pyo' -exec rm -f {} ";"\ +python3 -O -c "import sys, os, compileall; br='%{buildroot}'; compileall.compile_dir(sys.argv[1], ddir=br and (sys.argv[1][len(os.path.abspath(br)):]+'/') or None)" %1\ +} + + diff --git a/no-skipif-doctests.patch b/no-skipif-doctests.patch new file mode 100644 index 0000000..62758d3 --- /dev/null +++ b/no-skipif-doctests.patch @@ -0,0 +1,660 @@ +only in patch2: +unchanged: +--- a/Doc/conf.py ++++ b/Doc/conf.py +@@ -46,7 +46,7 @@ today_fmt = '%B %d, %Y' + highlight_language = 'python3' + + # Minimum version of sphinx required +-needs_sphinx = '1.8' ++needs_sphinx = '1.7.6' + + # Ignore any .rst files in the venv/ directory. + exclude_patterns = ['venv/*', 'README.rst'] +--- a/Doc/library/turtle.rst ++++ b/Doc/library/turtle.rst +@@ -250,7 +250,6 @@ Turtle motion + turtle is headed. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,0.00) +@@ -277,7 +276,6 @@ Turtle motion + >>> turtle.goto(0, 0) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,0.00) +@@ -296,13 +294,11 @@ Turtle motion + orientation depends on the turtle mode, see :func:`mode`. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.setheading(22) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.heading() + 22.0 +@@ -321,13 +317,11 @@ Turtle motion + orientation depends on the turtle mode, see :func:`mode`. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.setheading(22) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.heading() + 22.0 +@@ -350,13 +344,11 @@ Turtle motion + not change the turtle's orientation. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.goto(0, 0) + + .. doctest:: +- :skipif: _tkinter is None + + >>> tp = turtle.pos() + >>> tp +@@ -380,13 +372,11 @@ Turtle motion + unchanged. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.goto(0, 240) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,240.00) +@@ -402,13 +392,11 @@ Turtle motion + Set the turtle's second coordinate to *y*, leave first coordinate unchanged. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.goto(0, 40) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (0.00,40.00) +@@ -435,7 +423,6 @@ Turtle motion + =================== ==================== + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.setheading(90) + >>> turtle.heading() +@@ -448,14 +435,12 @@ Turtle motion + its start-orientation (which depends on the mode, see :func:`mode`). + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.setheading(90) + >>> turtle.goto(0, -10) + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.heading() + 90.0 +@@ -487,7 +472,6 @@ Turtle motion + calculated automatically. May be used to draw regular polygons. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.position() +@@ -516,7 +500,6 @@ Turtle motion + + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.dot() +@@ -534,7 +517,6 @@ Turtle motion + it by calling ``clearstamp(stamp_id)``. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.color("blue") + >>> turtle.stamp() +@@ -550,7 +532,6 @@ Turtle motion + Delete stamp with given *stampid*. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.position() + (150.00,-0.00) +@@ -595,7 +576,6 @@ Turtle motion + undo actions is determined by the size of the undobuffer. + + .. doctest:: +- :skipif: _tkinter is None + + >>> for i in range(4): + ... turtle.fd(50); turtle.lt(80) +@@ -628,7 +608,6 @@ Turtle motion + turtle turn instantly. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.speed() + 3 +@@ -649,7 +628,6 @@ Tell Turtle's state + Return the turtle's current location (x,y) (as a :class:`Vec2D` vector). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.pos() + (440.00,-0.00) +@@ -665,7 +643,6 @@ Tell Turtle's state + orientation which depends on the mode - "standard"/"world" or "logo". + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.goto(10, 10) + >>> turtle.towards(0,0) +@@ -677,7 +654,6 @@ Tell Turtle's state + Return the turtle's x coordinate. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(50) +@@ -693,7 +669,6 @@ Tell Turtle's state + Return the turtle's y coordinate. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(60) +@@ -710,7 +685,6 @@ Tell Turtle's state + :func:`mode`). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(67) +@@ -727,7 +701,6 @@ Tell Turtle's state + other turtle, in turtle step units. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.distance(30,40) +@@ -751,7 +724,6 @@ Settings for measurement + Default value is 360 degrees. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(90) +@@ -774,7 +746,6 @@ Settings for measurement + ``degrees(2*math.pi)``. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.left(90) +@@ -785,7 +756,6 @@ Settings for measurement + 1.5707963267948966 + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.degrees(360) +@@ -821,7 +791,6 @@ Drawing state + thickness. If no argument is given, the current pensize is returned. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.pensize() + 1 +@@ -853,7 +822,6 @@ Drawing state + attributes in one statement. + + .. doctest:: +- :skipif: _tkinter is None + :options: +NORMALIZE_WHITESPACE + + >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10) +@@ -876,7 +844,6 @@ Drawing state + Return ``True`` if pen is down, ``False`` if it's up. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.penup() + >>> turtle.isdown() +@@ -917,7 +884,6 @@ Color control + newly set pencolor. + + .. doctest:: +- :skipif: _tkinter is None + + >>> colormode() + 1.0 +@@ -966,7 +932,6 @@ Color control + with the newly set fillcolor. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.fillcolor("violet") + >>> turtle.fillcolor() +@@ -1005,7 +970,6 @@ Color control + with the newly set colors. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.color("red", "green") + >>> turtle.color() +@@ -1022,7 +986,6 @@ Filling + ~~~~~~~ + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> turtle.home() +@@ -1032,7 +995,6 @@ Filling + Return fillstate (``True`` if filling, ``False`` else). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.begin_fill() + >>> if turtle.filling(): +@@ -1057,7 +1019,6 @@ Filling + above may be either all yellow or have some white regions. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.color("black", "red") + >>> turtle.begin_fill() +@@ -1075,7 +1036,6 @@ More drawing control + variables to the default values. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.goto(0,-22) + >>> turtle.left(100) +@@ -1127,7 +1087,6 @@ Visibility + drawing observably. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.hideturtle() + +@@ -1138,7 +1097,6 @@ Visibility + Make the turtle visible. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.showturtle() + +@@ -1169,7 +1127,6 @@ Appearance + deal with shapes see Screen method :func:`register_shape`. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shape() + 'classic' +@@ -1195,7 +1152,6 @@ Appearance + ``resizemode("user")`` is called by :func:`shapesize` when used with arguments. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.resizemode() + 'noresize' +@@ -1219,7 +1175,6 @@ Appearance + of the shapes's outline. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shapesize() + (1.0, 1.0, 1) +@@ -1244,7 +1199,6 @@ Appearance + heading of the turtle are sheared. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) +@@ -1261,7 +1215,6 @@ Appearance + change the turtle's heading (direction of movement). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.reset() + >>> turtle.shape("circle") +@@ -1281,7 +1234,6 @@ Appearance + (direction of movement). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.reset() + >>> turtle.shape("circle") +@@ -1307,7 +1259,6 @@ Appearance + turtle (its direction of movement). + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.reset() + >>> turtle.shape("circle") +@@ -1336,7 +1287,6 @@ Appearance + given matrix. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle = Turtle() + >>> turtle.shape("square") +@@ -1352,7 +1302,6 @@ Appearance + can be used to define a new shape or components of a compound shape. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.shape("square") + >>> turtle.shapetransform(4, -1, 0, 2) +@@ -1377,7 +1326,6 @@ Using events + procedural way: + + .. doctest:: +- :skipif: _tkinter is None + + >>> def turn(x, y): + ... left(180) +@@ -1398,7 +1346,6 @@ Using events + ``None``, existing bindings are removed. + + .. doctest:: +- :skipif: _tkinter is None + + >>> class MyTurtle(Turtle): + ... def glow(self,x,y): +@@ -1426,7 +1373,6 @@ Using events + mouse-click event on that turtle. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.ondrag(turtle.goto) + +@@ -1454,7 +1400,6 @@ Special Turtle methods + Return the last recorded polygon. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.home() + >>> turtle.begin_poly() +@@ -1474,7 +1419,6 @@ Special Turtle methods + turtle properties. + + .. doctest:: +- :skipif: _tkinter is None + + >>> mick = Turtle() + >>> joe = mick.clone() +@@ -1487,7 +1431,6 @@ Special Turtle methods + return the "anonymous turtle": + + .. doctest:: +- :skipif: _tkinter is None + + >>> pet = getturtle() + >>> pet.fd(50) +@@ -1501,7 +1444,6 @@ Special Turtle methods + TurtleScreen methods can then be called for that object. + + .. doctest:: +- :skipif: _tkinter is None + + >>> ts = turtle.getscreen() + >>> ts +@@ -1519,7 +1461,6 @@ Special Turtle methods + ``None``, the undobuffer is disabled. + + .. doctest:: +- :skipif: _tkinter is None + + >>> turtle.setundobuffer(42) + +@@ -1529,7 +1470,6 @@ Special Turtle methods + Return number of entries in the undobuffer. + + .. doctest:: +- :skipif: _tkinter is None + + >>> while undobufferentries(): + ... undo() +@@ -1552,7 +1492,6 @@ below: + For example: + + .. doctest:: +- :skipif: _tkinter is None + + >>> s = Shape("compound") + >>> poly1 = ((0,0),(10,-5),(0,10),(-10,-5)) +@@ -1563,7 +1502,6 @@ below: + 3. Now add the Shape to the Screen's shapelist and use it: + + .. doctest:: +- :skipif: _tkinter is None + + >>> register_shape("myshape", s) + >>> shape("myshape") +@@ -1583,7 +1521,6 @@ Most of the examples in this section ref + ``screen``. + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> screen = Screen() +@@ -1600,7 +1537,6 @@ Window control + Set or return background color of the TurtleScreen. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.bgcolor("orange") + >>> screen.bgcolor() +@@ -1686,7 +1622,6 @@ Window control + distorted. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.reset() + >>> screen.setworldcoordinates(-50,-7.5,50,7.5) +@@ -1697,7 +1632,6 @@ Window control + ... left(45); fd(2) # a regular octagon + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> screen.reset() +@@ -1719,7 +1653,6 @@ Animation control + Optional argument: + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.delay() + 10 +@@ -1741,7 +1674,6 @@ Animation control + :func:`delay`). + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.tracer(8, 25) + >>> dist = 2 +@@ -1778,7 +1710,6 @@ Using screen events + must have the focus. (See method :func:`listen`.) + + .. doctest:: +- :skipif: _tkinter is None + + >>> def f(): + ... fd(50) +@@ -1799,7 +1730,6 @@ Using screen events + must have focus. (See method :func:`listen`.) + + .. doctest:: +- :skipif: _tkinter is None + + >>> def f(): + ... fd(50) +@@ -1824,7 +1754,6 @@ Using screen events + named ``turtle``: + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.onclick(turtle.goto) # Subsequently clicking into the TurtleScreen will + >>> # make the turtle move to the clicked point. +@@ -1844,7 +1773,6 @@ Using screen events + Install a timer that calls *fun* after *t* milliseconds. + + .. doctest:: +- :skipif: _tkinter is None + + >>> running = True + >>> def f(): +@@ -1926,7 +1854,6 @@ Settings and special methods + ============ ========================= =================== + + .. doctest:: +- :skipif: _tkinter is None + + >>> mode("logo") # resets turtle heading to north + >>> mode() +@@ -1941,7 +1868,6 @@ Settings and special methods + values of color triples have to be in the range 0..\ *cmode*. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.colormode(1) + >>> turtle.pencolor(240, 160, 80) +@@ -1962,7 +1888,6 @@ Settings and special methods + do with a Tkinter Canvas. + + .. doctest:: +- :skipif: _tkinter is None + + >>> cv = screen.getcanvas() + >>> cv +@@ -1974,7 +1899,6 @@ Settings and special methods + Return a list of names of all currently available turtle shapes. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.getshapes() + ['arrow', 'blank', 'circle', ..., 'turtle'] +@@ -1998,7 +1922,6 @@ Settings and special methods + coordinates: Install the corresponding polygon shape. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.register_shape("triangle", ((5,-3), (0,5), (-5,-3))) + +@@ -2014,7 +1937,6 @@ Settings and special methods + Return the list of turtles on the screen. + + .. doctest:: +- :skipif: _tkinter is None + + >>> for turtle in screen.turtles(): + ... turtle.color("red") +@@ -2076,7 +1998,6 @@ Methods specific to Screen, not inherite + center window vertically + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.setup (width=200, height=200, startx=0, starty=0) + >>> # sets window to 200x200 pixels, in upper left of screen +@@ -2092,7 +2013,6 @@ Methods specific to Screen, not inherite + Set title of turtle window to *titlestring*. + + .. doctest:: +- :skipif: _tkinter is None + + >>> screen.title("Welcome to the turtle zoo!") + +@@ -2163,7 +2083,6 @@ Public classes + Example: + + .. doctest:: +- :skipif: _tkinter is None + + >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) + >>> s = Shape("compound") +@@ -2510,7 +2429,6 @@ Changes since Python 3.0 + + + .. doctest:: +- :skipif: _tkinter is None + :hide: + + >>> for turtle in turtles(): diff --git a/pre_checkin.sh b/pre_checkin.sh new file mode 100644 index 0000000..a2cf992 --- /dev/null +++ b/pre_checkin.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +export LC_ALL=C + +master=python*.spec + +# create import_failed.map from package definitions +pkgname=$(grep python_pkg_name $master |grep define |awk -F' ' '{print $3}') +MAPFILE=import_failed.map +function new_map_line () { + package=$1 + package=$(echo $1 |sed -e "s:%{python_pkg_name}:$pkgname:") + modules=$2 + if [ -z "$package" -o -z "$modules" ]; then + return + fi + if [[ "$package" =~ "-base" ]]; then + return + fi + echo "$package:$modules" >> $MAPFILE.tmp +} + +for spec in *.spec; do + basename=${spec%.spec} + package= + modules= + while read line; do + case $line in + "%files -n "*) + new_map_line $package "$modules" + package=${line#"%files -n "} + modules= + ;; + "%files "*) + new_map_line $package "$modules" + package=$basename-${line#"%files "} + modules= + ;; + "%files") + new_map_line $package "$modules" + package=$basename + modules= + ;; + "%{sitedir}/config-"*) + # ignore + ;; + "%{sitedir}/"*) + word=${line#"%{sitedir}/"} + if ! echo $word | grep -q /; then + modules="$modules $word" + fi + ;; + "%{dynlib "*"}") + word=${line#"%{dynlib "} + word=${word%"}"} + modules="$modules $word" + ;; + esac + done < $spec + new_map_line $package "$modules" +done + +cat $MAPFILE.tmp |sort -u > $MAPFILE +rm $MAPFILE.tmp + +# run test inclusion check +tar xJf Python-*.xz +python3 skipped_tests.py + +# generate baselibs.conf +VERSION=$(grep ^Version $master|awk -F':' '{print $2}' |sed -e 's/ //g') +python_version=${VERSION:0:3} # 3.3 +python_version_abitag=${python_version//./} # 33 +python_version_soname=${python_version//./_} # 3_3 +echo "$pkgname-base" > baselibs.conf +echo "$pkgname" >> baselibs.conf +echo "libpython$python_version_soname-1_0" >> baselibs.conf + diff --git a/python-3.3.0b1-fix_date_time_compiler.patch b/python-3.3.0b1-fix_date_time_compiler.patch new file mode 100644 index 0000000..f69846f --- /dev/null +++ b/python-3.3.0b1-fix_date_time_compiler.patch @@ -0,0 +1,25 @@ +--- + Makefile.pre.in | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -784,11 +784,18 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \ + $(DTRACE_OBJS) \ + $(srcdir)/Modules/getbuildinfo.c + $(CC) -c $(PY_CORE_CFLAGS) \ ++ -DDATE="\"`date -u -r Makefile.pre.in +"%b %d %Y"`\"" \ ++ -DTIME="\"`date -u -r Makefile.pre.in +"%T"`\"" \ + -DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \ + -DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \ + -DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \ + -o $@ $(srcdir)/Modules/getbuildinfo.c + ++Python/getcompiler.o: $(srcdir)/Python/getcompiler.c Makefile ++ $(CC) -c $(PY_CORE_CFLAGS) \ ++ -DCOMPILER='"[GCC]"' \ ++ -o $@ $(srcdir)/Python/getcompiler.c ++ + Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile + $(CC) -c $(PY_CORE_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \ + -DPREFIX='"$(prefix)"' \ diff --git a/python-3.3.0b1-localpath.patch b/python-3.3.0b1-localpath.patch new file mode 100644 index 0000000..ff9a376 --- /dev/null +++ b/python-3.3.0b1-localpath.patch @@ -0,0 +1,11 @@ +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -76,7 +76,7 @@ import _sitebuiltins + import io + + # Prefixes for site-packages; add additional prefixes like /usr/local here +-PREFIXES = [sys.prefix, sys.exec_prefix] ++PREFIXES = [sys.prefix, sys.exec_prefix, '/usr/local'] + # Enable per user site-packages directory + # set it to False to disable the feature or True to force the feature + ENABLE_USER_SITE = None diff --git a/python-3.3.0b1-test-posix_fadvise.patch b/python-3.3.0b1-test-posix_fadvise.patch new file mode 100644 index 0000000..763441e --- /dev/null +++ b/python-3.3.0b1-test-posix_fadvise.patch @@ -0,0 +1,15 @@ +--- + Lib/test/test_posix.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Lib/test/test_posix.py ++++ b/Lib/test/test_posix.py +@@ -425,7 +425,7 @@ class PosixTester(unittest.TestCase): + def test_posix_fadvise(self): + fd = os.open(os_helper.TESTFN, os.O_RDONLY) + try: +- posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED) ++ posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_RANDOM) + finally: + os.close(fd) + diff --git a/python.keyring b/python.keyring new file mode 100644 index 0000000..747bc27 --- /dev/null +++ b/python.keyring @@ -0,0 +1,109 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFq+ToQBEADRYvIVtbK6owynD3j3nxwpW2KEk/p+aDvtXmc2SR2dBcZ8sFW2 +R5vEsG8d3/D3wgv5pcL3KfNNXQYUnXVbobrFUUWQYc79qIsE3MgiPf5NVOtwKPUR +i5g9YJgKvpBxkQfqp3LYGm9ZBtwo3DVLA3yn7KsazCmAgTNFJYw7ku1XxgmIzY6K +5J30DfbJiqDqj4f9GslCCCCH3qiPnuLG/HUyVLHMpbWlaiy9NI0GcaLxjJewHj9w +W2D2lydkxe5JGo7egUkV3ILcuLVSVKA35SKY27dYqfuyqp9tAzaRbjDYjsYdHA6G +BqrNrKBn/GwlFDPrVdcvN3ZSY2wMLTxWE3Axc/FweuHxFnou/80FwX7F3JD+oEQ6 +rofmcxOBCC7J98I7HZAhP9jBn88XIS2hztbLq8d6rZJZRtcz0k61VR0ddO+TrFmf +9rMYCPgCckRtVxeFIVIabrN1IzKynLFeo040h8hSGswd6YKDOVwjJY6Oa6EmVefZ +a8QSt4+M65RSzH6SEPY008F3nJUAK6MEkzTak+tFltZNrVWu8p2xd1j9nmxAwEhZ +/lgbxLqzYgaUWmfyHeZ8yVA0MhHzdiAL8nVUEdG3KecIq0RWCJLGLWWIjd6KAJl1 +yAmhRYKK/sjPDsL3elHsFACfZbyx3o5GGQNlas1FYoPLWbaNGaJtgFTF2QARAQAB +tCtQYWJsbyBHYWxpbmRvIFNhbGdhZG8gPHBhYmxvZ3NhbEBnbWFpbC5jb20+iQJO +BBMBCgA4FiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+ToQCGwMFCwkIBwMFFQoJ +CAsFFgIDAQACHgECF4AACgkQZOYo+NaEaW2bmA/+PXIap2udLoUVOHxnsIBdqYwp +sv1Aj5lfIJmNhmxPbHShwp1Jg+w4urxe+2Dj5ofKVlIo1i83bQkvnKJMDXDVuc/K +P6zqhBJ3rT4Q3qx2mzX8bIfQoJ2JHuH4lkP+I7doDcHHRyeNASyk72VdQmU4twNw +Ibn8nSNV6ThKHdoPYzVnO2rZUFcGIqH5HNsvR+B7cc1MBCHsgURYwSVhSePIFGlZ +iasdBD6QQkDSe4QWi7AcJFWFElw4kbOKJWxAWsrEk+tMXJVGRjnmL289EmPCx/vx +BqKy7Mse0yWCSRR3vB+O6TB1S5SgEyEgqlYsfGNv1qf/rfRD4KkyCbNU3LhY1Aim +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 +=6Zb4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/python310-rpmlintrc b/python310-rpmlintrc new file mode 100644 index 0000000..92241ae --- /dev/null +++ b/python310-rpmlintrc @@ -0,0 +1 @@ +addFilter("pem-certificate.*/usr/lib.*/python.*/test/*.pem") diff --git a/python310.changes b/python310.changes new file mode 100644 index 0000000..57e429c --- /dev/null +++ b/python310.changes @@ -0,0 +1,3243 @@ +------------------------------------------------------------------- +Thu Jun 9 16:43:30 UTC 2022 - Matej Cepl + +- Add CVE-2015-20107-mailcap-unsafe-filenames.patch to avoid + CVE-2015-20107 (bsc#1198511, gh#python/cpython#68966), the + command injection in the mailcap module. +- Fix building of documentation and the universal configuration of the + %primary_interpreter. + +------------------------------------------------------------------- +Mon Jun 6 22:29:23 UTC 2022 - Matej Cepl + +- Update to 3.10.5: + - Core and Builtins + - gh-93418: Fixed an assert where an f-string has an equal + sign ‘=’ following an expression, but there’s no trailing + brace. For example, f”{i=”. + - gh-91924: Fix __ltrace__ debug feature if the stdout + encoding is not UTF-8. Patch by Victor Stinner. + - gh-93061: Backward jumps after async for loops are no + longer given dubious line numbers. + - gh-93065: Fix contextvars HAMT implementation to handle + iteration over deep trees. + - The bug was discovered and fixed by Eli Libman. See + MagicStack/immutables#84 for more details. + - gh-92311: Fixed a bug where setting frame.f_lineno to jump + over a list comprehension could misbehave or crash. + - gh-92112: Fix crash triggered by an evil custom mro() on + a metaclass. + - gh-92036: Fix a crash in subinterpreters related to the + garbage collector. When a subinterpreter is deleted, + untrack all objects tracked by its GC. To prevent a crash + in deallocator functions expecting objects to be tracked by + the GC, leak a strong reference to these objects on + purpose, so they are never deleted and their deallocator + functions are not called. Patch by Victor Stinner. + - gh-91421: Fix a potential integer overflow in + _Py_DecodeUTF8Ex. + - bpo-47212: Raise IndentationError instead of SyntaxError + for a bare except with no following indent. Improve + SyntaxError locations for an un-parenthesized generator + used as arguments. Patch by Matthieu Dartiailh. + - bpo-47182: Fix a crash when using a named unicode character + like "\N{digit nine}" after the main interpreter has been + initialized a second time. + - bpo-47117: Fix a crash if we fail to decode characters in + interactive mode if the tokenizer buffers are + uninitialized. Patch by Pablo Galindo. + - bpo-39829: Removed the __len__() call when initializing + a list and moved initializing to list_extend. Patch by + Jeremiah Pascual. + - bpo-46962: Classes and functions that unconditionally + declared their docstrings ignoring the + --without-doc-strings compilation flag no longer do so. + - The classes affected are ctypes.UnionType, + pickle.PickleBuffer, testcapi.RecursingInfinitelyError, and + types.GenericAlias. + - The functions affected are 24 methods in ctypes. + - Patch by Oleg Iarygin. + - bpo-36819: Fix crashes in built-in encoders with error + handlers that return position less or equal than the + starting position of non-encodable characters. + - Library + - gh-93156: Accessing the pathlib.PurePath.parents sequence + of an absolute path using negative index values produced + incorrect results. + - gh-89973: Fix re.error raised in fnmatch if the pattern + contains a character range with upper bound lower than + lower bound (e.g. [c-a]). Now such ranges are interpreted + as empty ranges. + - gh-93010: In a very special case, the email package tried + to append the nonexistent InvalidHeaderError to the defect + list. It should have been InvalidHeaderDefect. + - gh-92839: Fixed crash resulting from calling + bisect.insort() or bisect.insort_left() with the key + argument not equal to None. + - gh-91581: utcfromtimestamp() no longer attempts to resolve + fold in the pure Python implementation, since the fold is + never 1 in UTC. In addition to being slightly faster in the + common case, this also prevents some errors when the + timestamp is close to datetime.min. Patch by Paul Ganssle. + - gh-92530: Fix an issue that occurred after interrupting + threading.Condition.notify(). + - gh-92049: Forbid pickling constants re._constants.SUCCESS + etc. Previously, pickling did not fail, but the result + could not be unpickled. + - bpo-47029: Always close the read end of the pipe used by + multiprocessing.Queue after the last write of buffered data + to the write end of the pipe to avoid BrokenPipeError at + garbage collection and at multiprocessing.Queue.close() + calls. Patch by Géry Ogam. + - gh-91401: Provide a fail-safe way to disable subprocess use + of vfork() via a private subprocess._USE_VFORK attribute. + While there is currently no known need for this, if you + find a need please only set it to False. File a CPython + issue as to why you needed it and link to that from + a comment in your code. This attribute is documented as + a footnote in 3.11. + - gh-91910: Add missing f prefix to f-strings in error + messages from the multiprocessing and asyncio modules. + - gh-91810: ElementTree method write() and function + tostring() now use the text file’s encoding (“UTF-8” if not + available) instead of locale encoding in XML declaration + when encoding="unicode" is specified. + - gh-91832: Add required attribute to argparse.Action repr + output. + - gh-91700: Compilation of regular expression containing + a conditional expression (?(group)...) now raises an + appropriate re.error if the group number refers to not + defined group. Previously an internal RuntimeError was + raised. + - gh-91676: Fix unittest.IsolatedAsyncioTestCase to shutdown + the per test event loop executor before returning from its + run method so that a not yet stopped or garbage collected + executor state does not persist beyond the test. + - gh-90568: Parsing \N escapes of Unicode Named Character + Sequences in a regular expression raises now re.error + instead of TypeError. + - gh-91595: Fix the comparison of character and integer + inside Tools.gdb.libpython.write_repr(). Patch by Yu Liu. + - gh-90622: Worker processes for + concurrent.futures.ProcessPoolExecutor are no longer + spawned on demand (a feature added in 3.9) when the + multiprocessing context start method is "fork" as that can + lead to deadlocks in the child processes due to a fork + happening while threads are running. + - gh-91575: Update case-insensitive matching in the re module + to the latest Unicode version. + - gh-91581: Remove an unhandled error case in the + C implementation of calls to datetime.fromtimestamp with no + time zone (i.e. getting a local time from an epoch + timestamp). This should have no user-facing effect other + than giving a possibly more accurate error message when + called with timestamps that fall on 10000-01-01 in the + local time. Patch by Paul Ganssle. + - bpo-47260: Fix os.closerange() potentially being a no-op in + a Linux seccomp sandbox. + - bpo-39064: zipfile.ZipFile now raises zipfile.BadZipFile + instead of ValueError when reading a corrupt zip file in + which the central directory offset is negative. + - bpo-47151: When subprocess tries to use vfork, it now falls + back to fork if vfork returns an error. This allows use in + situations where vfork isn’t allowed by the OS kernel. + - bpo-27929: Fix asyncio.loop.sock_connect() to only resolve + names for socket.AF_INET or socket.AF_INET6 families. + Resolution may not make sense for other families, like + socket.AF_BLUETOOTH and socket.AF_UNIX. + - bpo-43323: Fix errors in the email module if the charset + itself contains undecodable/unencodable characters. + - bpo-47101: hashlib.algorithms_available now lists only + algorithms that are provided by activated crypto providers + on OpenSSL 3.0. Legacy algorithms are not listed unless the + legacy provider has been loaded into the default OSSL + context. + - bpo-46787: Fix concurrent.futures.ProcessPoolExecutor + exception memory leak + - bpo-45393: Fix the formatting for await x and not x in the + operator precedence table when using the help() system. + - bpo-46415: Fix ipaddress.ip_{address,interface,network} + raising TypeError instead of ValueError if given invalid + tuple as address parameter. + - bpo-28249: Set doctest.DocTest.lineno to None when object + does not have __doc__. + - bpo-45138: Fix a regression in the sqlite3 trace callback + where bound parameters were not expanded in the passed + statement string. The regression was introduced in Python + 3.10 by bpo-40318. Patch by Erlend E. Aasland. + - bpo-44493: Add missing terminated NUL in sockaddr_un’s + length + - This was potentially observable when using non-abstract + AF_UNIX datagram sockets to processes written in another + programming language. + - bpo-42627: Fix incorrect parsing of Windows registry proxy + settings + - bpo-36073: Raise ProgrammingError instead of segfaulting on + recursive usage of cursors in sqlite3 converters. Patch by + Sergey Fedoseev. + - Documentation + - gh-86438: Clarify that -W and PYTHONWARNINGS are matched + literally and case-insensitively, rather than as regular + expressions, in warnings. + - gh-92240: Added release dates for “What’s New in Python + 3.X” for 3.0, 3.1, 3.2, 3.8 and 3.10 + - gh-91888: Add a new gh role to the documentation to link to + GitHub issues. + - gh-91783: Document security issues concerning the use of + the function shutil.unpack_archive() + - gh-91547: Remove “Undocumented modules” page. + - bpo-44347: Clarify the meaning of dirs_exist_ok, a kwarg of + shutil.copytree(). + - bpo-38668: Update the introduction to documentation for + os.path to remove warnings that became irrelevant after the + implementations of PEP 383 and PEP 529. + - bpo-47138: Pin Jinja to a version compatible with Sphinx + version 3.2.1. + - bpo-46962: All docstrings in code snippets are now wrapped + into PyDoc_STR() to follow the guideline of PEP 7’s + Documentation Strings paragraph. Patch by Oleg Iarygin. + - bpo-26792: Improve the docstrings of runpy.run_module() and + runpy.run_path(). Original patch by Andrew Brezovsky. + - bpo-40838: Document that inspect.getdoc(), + inspect.getmodule(), and inspect.getsourcefile() might + return None. + - bpo-45790: Adjust inaccurate phrasing in Defining Extension + Types: Tutorial about the ob_base field and the macros used + to access its contents. + - bpo-42340: Document that in some circumstances + KeyboardInterrupt may cause the code to enter an + inconsistent state. Provided a sample workaround to avoid + it if needed. + - bpo-41233: Link the errnos referenced in + Doc/library/exceptions.rst to their respective section in + Doc/library/errno.rst, and vice versa. Previously this was + only done for EINTR and InterruptedError. Patch by Yan + “yyyyyyyan” Orestes. + - bpo-38056: Overhaul the Error Handlers documentation in + codecs. + - bpo-13553: Document tkinter.Tk args. + - Tests + - gh-92886: Fixing tests that fail when running with + optimizations (-O) in test_imaplib.py. + - gh-92670: Skip + test_shutil.TestCopy.test_copyfile_nonexistent_dir test on + AIX as the test uses a trailing slash to force the OS + consider the path as a directory, but on AIX the trailing + slash has no effect and is considered as a file. + - gh-91904: Fix initialization of + PYTHONREGRTEST_UNICODE_GUARD which prevented running + regression tests on non-UTF-8 locale. + - gh-91607: Fix test_concurrent_futures to test the correct + multiprocessing start method context in several cases where + the test logic mixed this up. + - bpo-47205: Skip test for sched_getaffinity() and + sched_setaffinity() error case on FreeBSD. + - bpo-47104: Rewrite asyncio.to_thread() tests to use + unittest.IsolatedAsyncioTestCase. + - bpo-29890: Add tests for ipaddress.IPv4Interface and + ipaddress.IPv6Interface construction with tuple arguments. + Original patch and tests by louisom. + - Tools/Demos + - gh-91583: Fix regression in the code generated by Argument + Clinic for functions with the defining_class parameter. + +------------------------------------------------------------------- +Tue May 10 14:35:52 UTC 2022 - Matej Cepl + +- Refresh bluez-devel-vendor.tar.xz + +------------------------------------------------------------------- +Thu May 5 14:35:56 UTC 2022 - Matej Cepl + +- Switch primary_interpreter from python38 to python310 for + Factory (only) + +------------------------------------------------------------------- +Sat Mar 26 22:52:45 UTC 2022 - Matej Cepl + +- Update to 3.10.4: + - bpo-46968: Check for the existence of the “sys/auxv.h” header + in faulthandler to avoid compilation problems in systems + where this header doesn’t exist. Patch by Pablo Galindo + - bpo-23691: Protect the re.finditer() iterator from + re-entering. + - bpo-42369: Fix thread safety of zipfile._SharedFile.tell() to + avoid a “zipfile.BadZipFile: Bad CRC-32 for file” exception + when reading a ZipFile from multiple threads. + - bpo-38256: Fix binascii.crc32() when it is compiled to use + zlib’c crc32 to work properly on inputs 4+GiB in length + instead of returning the wrong result. The workaround prior + to this was to always feed the function data in increments + smaller than 4GiB or to just call the zlib module function. + - bpo-39394: A warning about inline flags not at the start of + the regular expression now contains the position of the flag. + - bpo-47061: Deprecate the various modules listed by PEP 594: + - aifc, asynchat, asyncore, audioop, cgi, cgitb, chunk, crypt, + imghdr, msilib, nntplib, nis, ossaudiodev, pipes, smtpd, + sndhdr, spwd, sunau, telnetlib, uu, xdrlib + - bpo-2604: Fix bug where doctests using globals would fail + when run multiple times. + - bpo-45997: Fix asyncio.Semaphore re-aquiring FIFO order. + - bpo-47022: The asynchat, asyncore and smtpd modules have been + deprecated since at least Python 3.6. Their documentation and + deprecation warnings and have now been updated to note they + will removed in Python 3.12 (PEP 594). + - bpo-46421: Fix a unittest issue where if the command was + invoked as python -m unittest and the filename(s) began with + a dot (.), a ValueError is returned. + - bpo-40296: Fix supporting generic aliases in pydoc. + +- Update to 3.10.3: + - bpo-46940: Avoid overriding AttributeError metadata + information for nested attribute access calls. Patch by Pablo + Galindo. + - bpo-46852: Rename the private undocumented + float.__set_format__() method to float.__setformat__() to fix + a typo introduced in Python 3.7. The method is only used by + test_float. Patch by Victor Stinner. + - bpo-46794: Bump up the libexpat version into 2.4.6 + - bpo-46820: Fix parsing a numeric literal immediately (without + spaces) followed by “not in” keywords, like in 1not in x. Now + the parser only emits a warning, not a syntax error. + - bpo-46762: Fix an assert failure in debug builds when a ‘<’, + ‘>’, or ‘=’ is the last character in an f-string that’s + missing a closing right brace. + - bpo-46724: Make sure that all backwards jumps use the + JUMP_ABSOLUTE instruction, rather than JUMP_FORWARD with an + argument of (2**32)+offset. + - bpo-46732: Correct the docstring for the __bool__() method. + Patch by Jelle Zijlstra. + - bpo-46707: Avoid potential exponential backtracking when + producing some syntax errors involving lots of brackets. + Patch by Pablo Galindo. + - bpo-40479: Add a missing call to va_end() in + Modules/_hashopenssl.c. + - bpo-46615: When iterating over sets internally in + setobject.c, acquire strong references to the resulting items + from the set. This prevents crashes in corner-cases of + various set operations where the set gets mutated. + - bpo-45773: Remove two invalid “peephole” optimizations from + the bytecode compiler. + - bpo-43721: Fix docstrings of getter, setter, and deleter to + clarify that they create a new copy of the property. + - bpo-46503: Fix an assert when parsing some invalid N escape + sequences in f-strings. + - bpo-46417: Fix a race condition on setting a type __bases__ + attribute: the internal function add_subclass() now gets the + PyTypeObject.tp_subclasses member after calling + PyWeakref_NewRef() which can trigger a garbage collection + which can indirectly modify PyTypeObject.tp_subclasses. Patch + by Victor Stinner. + - bpo-46383: Fix invalid signature of _zoneinfo’s module_free + function to resolve a crash on wasm32-emscripten platform. + - bpo-46070: Py_EndInterpreter() now explicitly untracks all + objects currently tracked by the GC. Previously, if an object + was used later by another interpreter, calling + PyObject_GC_UnTrack() on the object crashed if the previous + or the next object of the PyGC_Head structure became + a dangling pointer. Patch by Victor Stinner. + - bpo-46339: Fix a crash in the parser when retrieving the + error text for multi-line f-strings expressions that do not + start in the first line of the string. Patch by Pablo Galindo + - bpo-46240: Correct the error message for unclosed parentheses + when the tokenizer doesn’t reach the end of the source when + the error is reported. Patch by Pablo Galindo + - bpo-46091: Correctly calculate indentation levels for lines + with whitespace character that are ended by line continuation + characters. Patch by Pablo Galindo + - bpo-43253: Fix a crash when closing transports where the + underlying socket handle is already invalid on the Proactor + event loop. + - bpo-47004: Apply bugfixes from importlib_metadata 4.11.3, + including bugfix for EntryPoint.extras, which was returning + match objects and not the extras strings. + - bpo-46985: Upgrade pip wheel bundled with ensurepip (pip + 22.0.4) + - bpo-46968: faulthandler: On Linux 5.14 and newer, dynamically + determine size of signal handler stack size CPython allocates + using getauxval(AT_MINSIGSTKSZ). This changes allows for + Python extension’s request to Linux kernel to use AMX_TILE + instruction set on Sapphire Rapids Xeon processor to succeed, + unblocking use of the ISA in frameworks. + - bpo-46955: Expose asyncio.base_events.Server as + asyncio.Server. Patch by Stefan Zabka. + - bpo-23325: The signal module no longer assumes that SIG_IGN + and SIG_DFL are small int singletons. + - bpo-46932: Update bundled libexpat to 2.4.7 + - bpo-25707: Fixed a file leak in + xml.etree.ElementTree.iterparse() when the iterator is not + exhausted. Patch by Jacob Walls. + - bpo-44886: Inherit asyncio proactor datagram transport from + asyncio.DatagramTransport. + - bpo-46827: Support UDP sockets in asyncio.loop.sock_connect() + for selector-based event loops. Patch by Thomas Grainger. + - bpo-46811: Make test suite support Expat >=2.4.5 + - bpo-46252: Raise TypeError if ssl.SSLSocket is passed to + transport-based APIs. + - bpo-46784: Fix libexpat symbols collisions with user + dynamically loaded or statically linked libexpat in embedded + Python. + - bpo-39327: shutil.rmtree() can now work with VirtualBox + shared folders when running from the guest operating-system. + - bpo-46756: Fix a bug in + urllib.request.HTTPPasswordMgr.find_user_password() and + urllib.request.HTTPPasswordMgrWithPriorAuth.is_authenticated() + which allowed to bypass authorization. For example, access to + URI example.org/foobar was allowed if the user was authorized + for URI example.org/foo. + - bpo-46643: In typing.get_type_hints(), support evaluating + stringified ParamSpecArgs and ParamSpecKwargs annotations. + Patch by Gregory Beauregard. + - bpo-45863: When the tarfile module creates a pax format + archive, it will put an integer representation of timestamps + in the ustar header (if possible) for the benefit of older + unarchivers, in addition to the existing full-precision + timestamps in the pax extended header. + - bpo-46676: Make typing.ParamSpec args and kwargs equal to + themselves. Patch by Gregory Beauregard. + - bpo-46672: Fix NameError in asyncio.gather() when initial + type check fails. + - bpo-46655: In typing.get_type_hints(), support evaluating + bare stringified TypeAlias annotations. Patch by Gregory + Beauregard. + - bpo-45948: Fixed a discrepancy in the C implementation of the + xml.etree.ElementTree module. Now, instantiating an + xml.etree.ElementTree.XMLParser with a target=None keyword + provides a default xml.etree.ElementTree.TreeBuilder target + as the Python implementation does. + - bpo-46521: Fix a bug in the codeop module that was + incorrectly identifying invalid code involving string quotes + as valid code. + - bpo-46581: Brings ParamSpec propagation for GenericAlias in + line with Concatenate (and others). + - bpo-46591: Make the IDLE doc URL on the About IDLE dialog + clickable. + - bpo-46400: expat: Update libexpat from 2.4.1 to 2.4.4 + - bpo-46487: Add the get_write_buffer_limits method to + asyncio.transports.WriteTransport and to the SSL transport. + - bpo-45173: Note the configparser deprecations will be removed + in Python 3.12. + - bpo-46539: In typing.get_type_hints(), support evaluating + stringified ClassVar and Final annotations inside Annotated. + Patch by Gregory Beauregard. + - bpo-46491: Allow typing.Annotated to wrap typing.Final and + typing.ClassVar. Patch by Gregory Beauregard. + - bpo-46436: Fix command-line option -d/--directory in module + http.server which is ignored when combined with command-line + option --cgi. Patch by Géry Ogam. + - bpo-41403: Make mock.patch() raise a TypeError with + a relevant error message on invalid arg. Previously it + allowed a cryptic AttributeError to escape. + - bpo-46474: In importlib.metadata.EntryPoint.pattern, avoid + potential REDoS by limiting ambiguity in consecutive + whitespace. + - bpo-46469: asyncio generic classes now return + types.GenericAlias in __class_getitem__ instead of the same + class. + - bpo-46434: pdb now gracefully handles help when __doc__ is + missing, for example when run with pregenerated optimized + .pyc files. + - bpo-46333: The __eq__() and __hash__() methods of + typing.ForwardRef now honor the module parameter of + typing.ForwardRef. Forward references from different modules + are now differentiated. + - bpo-46246: Add missing __slots__ to + importlib.metadata.DeprecatedList. Patch by Arie Bovenberg. + - bpo-46266: Improve day constants in calendar. + - Now all constants (MONDAY … SUNDAY) are documented, tested, + and added to __all__. + - bpo-46232: The ssl module now handles certificates with bit + strings in DN correctly. + - bpo-43118: Fix a bug in inspect.signature() that was causing + it to fail on some subclasses of classes with + a __text_signature__ referencing module globals. Patch by + Weipeng Hong. + - bpo-26552: Fixed case where failing asyncio.ensure_future() + did not close the coroutine. Patch by Kumar Aditya. + - bpo-21987: Fix an issue with tarfile.TarFile.getmember() + getting a directory name with a trailing slash. + - bpo-20392: Fix inconsistency with uppercase file extensions + in MimeTypes.guess_type(). Patch by Kumar Aditya. + - bpo-46080: Fix exception in argparse help text generation if + a argparse.BooleanOptionalAction argument’s default is + argparse.SUPPRESS and it has help specified. Patch by Felix + Fontein. + - bpo-44439: Fix .write() method of a member file in ZipFile, + when the input data is an object that supports the buffer + protocol, the file length may be wrong. + - bpo-45703: When a namespace package is imported before + another module from the same namespace is created/installed + in a different sys.path location while the program is + running, calling the importlib.invalidate_caches() function + will now also guarantee the new module is noticed. + - bpo-24959: Fix bug where unittest sometimes drops frames from + tracebacks of exceptions raised in tests. + - bpo-44791: Fix substitution of ParamSpec in Concatenate with + different parameter expressions. Substitution with a list of + types returns now a tuple of types. Substitution with + Concatenate returns now a Concatenate with concatenated lists + of arguments. + - bpo-14156: argparse.FileType now supports an argument of ‘-’ + in binary mode, returning the .buffer attribute of + sys.stdin/sys.stdout as appropriate. Modes including ‘x’ and + ‘a’ are treated equivalently to ‘w’ when argument is ‘-’. + Patch contributed by Josh Rosenberg + - bpo-46463: Fixes escape4chm.py script used when building the + CHM documentation file + - bpo-46913: Fix test_faulthandler.test_sigfpe() if Python is + built with undefined behavior sanitizer (UBSAN): disable + UBSAN on the faulthandler_sigfpe() function. Patch by Victor + Stinner. + - bpo-46708: Prevent default asyncio event loop policy + modification warning after test_asyncio execution. + - bpo-46678: The function make_legacy_pyc in + Lib/test/support/import_helper.py no longer fails when + PYTHONPYCACHEPREFIX is set to a directory on a different + device from where tempfiles are stored. + - bpo-46616: Ensures test_importlib.test_windows cleans up + registry keys after completion. + - bpo-44359: test_ftplib now silently ignores socket errors to + prevent logging unhandled threading exceptions. Patch by + Victor Stinner. + - bpo-46542: Fix a Python crash in test_lib2to3 when using + Python built in debug mode: limit the recursion limit. Patch + by Victor Stinner. + - bpo-46576: test_peg_generator now disables compiler + optimization when testing compilation of its own C extensions + to significantly speed up the testing on non-debug builds of + CPython. + - bpo-46542: Fix test_json tests checking for RecursionError: + modify these tests to use support.infinite_recursion(). Patch + by Victor Stinner. + - bpo-13886: Skip test_builtin PTY tests on non-ASCII + characters if the readline module is loaded. The readline + module changes input() behavior, but test_builtin is not + intented to test the readline module. Patch by Victor + Stinner. + - bpo-38472: Fix GCC detection in setup.py when + cross-compiling. The C compiler is now run with LC_ALL=C. + Previously, the detection failed with a German locale. + - bpo-46513: configure no longer uses AC_C_CHAR_UNSIGNED macro + and pyconfig.h no longer defines reserved symbol + __CHAR_UNSIGNED__. + - bpo-45296: Clarify close, quit, and exit in IDLE. In the File + menu, ‘Close’ and ‘Exit’ are now ‘Close Window’ (the current + one) and ‘Exit’ is now ‘Exit IDLE’ (by closing all windows). + In Shell, ‘quit()’ and ‘exit()’ mean ‘close Shell’. If there + are no other windows, this also exits IDLE. + - bpo-45447: Apply IDLE syntax highlighting to pyi files. Patch + by Alex Waygood and Terry Jan Reedy. + - bpo-46433: The internal function _PyType_GetModuleByDef now + correctly handles inheritance patterns involving static + types. + - bpo-14916: Fixed bug in the tokenizer that prevented + PyRun_InteractiveOne from parsing from the provided FD. + +------------------------------------------------------------------- +Thu Mar 24 18:55:46 UTC 2022 - David Anes + +- (bsc#1196784, CVE-2022-25236) Rename patch: + support-expat-245.patch to support-expat-CVE-2022-25236-patched.patch + and update the patch to detect expat >= 2.4.4 instead of >= 2.4.5 + as it was fully patched against CVE-2022-25236. + +------------------------------------------------------------------- +Tue Feb 22 05:53:06 UTC 2022 - Steve Kowalik + +- Add patch support-expat-245.patch: + * Support Expat >= 2.4.5 + +------------------------------------------------------------------- +Tue Feb 15 23:05:55 UTC 2022 - Matej Cepl + +- bsc#1195831 Obsolete older "most modern" versions of python + packages (python39 for python310 and so forth). For next + versions it is necessary just to edit the macro. + +------------------------------------------------------------------- +Tue Jan 25 16:09:25 UTC 2022 - Matej Cepl + +- Remove second superfluous BR rpm-build-python + +------------------------------------------------------------------- +Tue Jan 25 16:09:25 UTC 2022 - Matej Cepl + +- Remove second superfluous BR rpm-build-python +- Add fix_configure_rst.patch, which removes duplicate link + targets and make documentation with old Sphinx in SLE +- Skip test_capi (bsc#1195140 and bpo#37169) + +------------------------------------------------------------------- +Wed Jan 19 22:01:51 UTC 2022 - Matej Cepl + +- Update to 3.10.2: + Bugfix only + - bpo#46347 memory leak in PyEval_EvalCodeEx (especially + visible with Cython code) + - and many others + +------------------------------------------------------------------- +Wed Dec 8 13:07:25 UTC 2021 - Matej Cepl + +- Upgrade to 3.10.1 (jsc#SLE-18038): + - PEP 623 – Deprecate and prepare for the removal of the wstr + member in PyUnicodeObject. + - PEP 604 – Allow writing union types as X | Y + - PEP 612 – Parameter Specification Variables + - PEP 626 – Precise line numbers for debugging and other tools. + - PEP 618 – Add Optional Length-Checking To zip. + - bpo-12782: Parenthesized context managers are now officially + allowed. + - PEP 632 – Deprecate distutils module. + - PEP 613 – Explicit Type Aliases + - PEP 634 – Structural Pattern Matching: Specification + - PEP 635 – Structural Pattern Matching: Motivation and + Rationale + - PEP 636 – Structural Pattern Matching: Tutorial + - PEP 644 – Require OpenSSL 1.1.1 or newer + - PEP 624 – Remove Py_UNICODE encoder APIs + - PEP 597 – Add optional EncodingWarning +- Patches readjusted: + - bpo-31046_ensurepip_honours_prefix.patch + - python-3.3.0b1-fix_date_time_compiler.patch + +------------------------------------------------------------------- +Sat Dec 4 18:40:28 UTC 2021 - Matej Cepl + +- Remove pdb_adjust_breakpoints.patch and instead just adjust location + of the test breakpoint in Lib/test/test_pdb.py via sed, because we + have shortened Lib/pdb.py by removing the shebang (bpo#45964). + +------------------------------------------------------------------- +Thu Dec 2 13:51:57 UTC 2021 - Matej Cepl + +- Add pdb_adjust_breakpoints.patch fixing expectd results in + test_pdb_breakpoints_preserved_across_interactive_sessions + (bpo#45964). + +------------------------------------------------------------------- +Mon Nov 29 00:17:07 UTC 2021 - Matej Cepl + +- Remove shebangs from from python-base libraries in _libdir + (bsc#1193179). +- Readjust patches: + - bpo-31046_ensurepip_honours_prefix.patch + - decimal.patch + - python-3.3.0b1-fix_date_time_compiler.patch + +------------------------------------------------------------------- +Tue Nov 16 16:03:43 UTC 2021 - Matej Cepl + +- Move rpm-build-python construct to correct place. + +------------------------------------------------------------------- +Wed Oct 13 08:52:47 UTC 2021 - Dominique Leuenberger + +- BuildRequire rpm-build-python: The provider to inject python(abi) + has been moved there. rpm-build pulls rpm-build-python + automatically in when building anything against python3-base, but + this implies that the initial build of python3-base does not + trigger the automatic installation. + +------------------------------------------------------------------- +Tue Oct 5 22:36:51 UTC 2021 - Matej Cepl + +- Final release of 3.10.0: + Complete list on https://www.python.org/downloads/release/python-3100/, + but highlights are: + - PEP 623 – Deprecate and prepare for the removal of the wstr + member in PyUnicodeObject. + - PEP 604 – Allow writing union types as X | Y + - PEP 612 – Parameter Specification Variables + - PEP 626 – Precise line numbers for debugging and other + tools. + - PEP 618 – Add Optional Length-Checking To zip. + - PEP 632 – Deprecate distutils module. + - PEP 613 – Explicit Type Aliases + - PEP 634 – Structural Pattern Matching: Specification + - PEP 635 – Structural Pattern Matching: Motivation and + Rationale + - PEP 636 – Structural Pattern Matching: Tutorial + - PEP 644 – Require OpenSSL 1.1.1 or newer + - PEP 624 – Remove Py_UNICODE encoder APIs + - PEP 597 – Add optional EncodingWarning + - bpo-12782: Parenthesized context managers are now officially + allowed. + +------------------------------------------------------------------- +Mon Aug 30 12:48:25 UTC 2021 - Matej Cepl + +- Switch on option --with-system-libmpdec (bsc#1189356). + +------------------------------------------------------------------- +Fri Aug 27 13:15:03 UTC 2021 - Andreas Schwab + +- Reenable profileopt with qemu emulation, test_faulthandler is no longer + run during profiling + +------------------------------------------------------------------- +Thu Aug 12 15:11:39 UTC 2021 - Andreas Schwab + +- test_faulthandler is still problematic under qemu linux-user emulation, + disable it there + +------------------------------------------------------------------- +Wed Aug 11 05:57:11 UTC 2021 - Matej Cepl + +- Update to 3.10.0rc1 (the penultimate prerelease), which contains + plenty of small bugfixes among others: + - bpo#38605: from __future__ import annotations (PEP 563) used to be + on this list in previous pre-releases but it has been postponed to + Python 3.11 due to some compatibility concerns. + - bpo-44600: Fix incorrect line numbers while tracing some failed + patterns in match statements. Patch by Charles Burkland. + - plenty of modifications in types.Union + +------------------------------------------------------------------- +Wed Jul 21 13:44:48 UTC 2021 - Matej Cepl + +- Update to 3.10.0b4: + https://docs.python.org/3.10/whatsnew/changelog.html#python-3-10-0-beta-4 +- Remove python3-imp-returntype.patch which has been upstreamed. + +------------------------------------------------------------------- +Mon Jun 7 15:52:44 UTC 2021 - Matej Cepl + +- Update to 3.10.0b2: + - PEP 623 -- Deprecate and prepare for the removal of the wstr + member in PyUnicodeObject. + - PEP 604 -- Allow writing union types as X | Y + - PEP 612 -- Parameter Specification Variables + - PEP 626 -- Precise line numbers for debugging and other + tools. + - PEP 618 -- Add Optional Length-Checking To zip. + - bpo-12782: Parenthesized context managers are now officially + allowed. + - PEP 632 -- Deprecate distutils module. + - PEP 613 -- Explicit Type Aliases + - PEP 634 -- Structural Pattern Matching: Specification + - PEP 635 -- Structural Pattern Matching: Motivation and + Rationale + - PEP 636 -- Structural Pattern Matching: Tutorial + - PEP 644 -- Require OpenSSL 1.1.1 or newer + - PEP 624 -- Remove Py_UNICODE encoder APIs + - PEP 597 -- Add optional EncodingWarning +- Removed patches (assumed upstream): + - sphinx-update-removed-function.patch + +------------------------------------------------------------------- +Sat Jun 5 21:21:38 UTC 2021 - Matej Cepl + +- Revert previous skip over test_capi +- Add skip-test_pyobject_freed_is_freed.patch to skip failing + test on SLE-15. + +------------------------------------------------------------------- +Fri Jun 4 21:36:30 UTC 2021 - Dirk Müller + +- allow build with Sphinx >= 3.x + +------------------------------------------------------------------- +Wed Jun 2 13:12:04 UTC 2021 - Dan Čermák + +- Exclude test_capi on Leap (test fails there) + +------------------------------------------------------------------- +Fri May 21 15:13:59 UTC 2021 - Matej Cepl + +- Stop providing "python" symbol (bsc#1185588), which means + python2 currently. + +------------------------------------------------------------------- +Wed May 5 15:16:58 UTC 2021 - Matej Cepl + +- Update to 3.9.5: + * Security + - bpo-43434: Creating a sqlite3.Connection object now also + produces a sqlite3.connect auditing event. Previously this + event was only produced by sqlite3.connect() calls. Patch + by Erlend E. Aasland. + - bpo-43882: The presence of newline or tab characters in + parts of a URL could allow some forms of attacks. + - Following the controlling specification for URLs defined by + WHATWG urllib.parse() now removes ASCII newlines and tabs + from URLs, preventing such attacks. + - bpo-43472: Ensures interpreter-level audit hooks receive + the cpython.PyInterpreterState_New event when called + through the _xxsubinterpreters module. + - bpo-36384: ipaddress module no longer accepts any leading + zeros in IPv4 address strings. Leading zeros are ambiguous + and interpreted as octal notation by some libraries. For + example the legacy function socket.inet_aton() treats + leading zeros as octal notatation. glibc implementation of + modern inet_pton() does not accept any leading zeros. For + a while the ipaddress module used to accept ambiguous + leading zeros. + - bpo-43075: Fix Regular Expression Denial of Service (ReDoS) + vulnerability in urllib.request.AbstractBasicAuthHandler. + The ReDoS-vulnerable regex has quadratic worst-case + complexity and it allows cause a denial of service when + identifying crafted invalid RFCs. This ReDoS issue is on + the client side and needs remote attackers to control the + HTTP server. + - bpo-42800: Audit hooks are now fired for frame.f_code, + traceback.tb_frame, and generator code/frame attribute + access. + * Core and Builtins + - bpo-43105: Importlib now resolves relative paths when + creating module spec objects from file locations. + - bpo-42924: Fix bytearray repetition incorrectly copying + data from the start of the buffer, even if the data is + offset within the buffer (e.g. after reassigning a slice at + the start of the bytearray to a shorter byte string). + * Library + - bpo-43993: Update bundled pip to 21.1.1. + - bpo-43937: Fixed the turtle module working with non-default + root window. + - bpo-43930: Update bundled pip to 21.1 and setuptools to + 56.0.0 + - bpo-43920: OpenSSL 3.0.0: load_verify_locations() now + returns a consistent error message when cadata contains no + valid certificate. + - bpo-43607: urllib can now convert Windows paths with \\?\ + prefixes into URL paths. + - bpo-43284: platform.win32_ver derives the windows version + from sys.getwindowsversion().platform_version which in turn + derives the version from kernel32.dll (which can be of + a different version than Windows itself). Therefore change + the platform.win32_ver to determine the version using the + platform module’s _syscmd_ver private function to return an + accurate version. + - bpo-42248: [Enum] ensure exceptions raised in _missing__ + are released + - bpo-43799: OpenSSL 3.0.0: define OPENSSL_API_COMPAT 1.1.1 + to suppress deprecation warnings. Python requires OpenSSL + 1.1.1 APIs. + - bpo-43794: Add ssl.OP_IGNORE_UNEXPECTED_EOF constants + (OpenSSL 3.0.0) + - bpo-43789: OpenSSL 3.0.0: Don’t call the password callback + function a second time when first call has signaled an + error condition. + - bpo-43788: The header files for ssl error codes are now + OpenSSL version-specific. Exceptions will now show correct + reason and library codes. The make_ssl_data.py script has + been rewritten to use OpenSSL’s text file with error codes. + - bpo-43655: tkinter dialog windows are now recognized as + dialogs by window managers on macOS and X Window. + - bpo-43534: turtle.textinput() and turtle.numinput() create + now a transient window working on behalf of the canvas + window. + - bpo-43522: Fix problem with hostname_checks_common_name. + OpenSSL does not copy hostflags from struct SSL_CTX to + struct SSL. + - bpo-42967: Allow bytes separator argument in + urllib.parse.parse_qs and urllib.parse.parse_qsl when + parsing str query strings. Previously, this raised + a TypeError. + - bpo-43176: Fixed processing of a dataclass that inherits + from a frozen dataclass with no fields. It is now correctly + detected as an error. + - bpo-41735: Fix thread locks in zlib module may go wrong in + rare case. Patch by Ma Lin. + - bpo-36470: Fix dataclasses with InitVars and replace(). + Patch by Claudiu Popa. + - bpo-32745: Fix a regression in the handling of ctypes’ + ctypes.c_wchar_p type: embedded null characters would cause + a ValueError to be raised. Patch by Zackery Spytz. + * Documentation + - bpo-43959: The documentation on the PyContextVar C-API was + clarified. + - bpo-43938: Update dataclasses documentation to express that + FrozenInstanceError is derived from AttributeError. + - bpo-43755: Update documentation to reflect that + unparenthesized lambda expressions can no longer be the + expression part in an if clause in comprehensions and + generator expressions since Python 3.9. + - bpo-43739: Fixing the example code in + Doc/extending/extending.rst to declare and initialize the + pmodule variable to be of the right type. + * Tests + - bpo-43961: Fix + test_logging.test_namer_rotator_inheritance() on Windows: + use os.replace() rather than os.rename(). Patch by Victor + Stinner. + - bpo-43842: Fix a race condition in the SMTP test of + test_logging. Don’t close a file descriptor (socket) from + a different thread while asyncore.loop() is polling the + file descriptor. Patch by Victor Stinner. + - bpo-43811: Tests multiple OpenSSL versions on GitHub + Actions. Use ccache to speed up testing. + - bpo-43791: OpenSSL 3.0.0: Disable testing of legacy + protocols TLS 1.0 and 1.1. Tests are failing with + TLSV1_ALERT_INTERNAL_ERROR. +- Refreshed patches: + - bpo-31046_ensurepip_honours_prefix.patch + - python-3.3.0b1-fix_date_time_compiler.patch +- Add vendorized files from bluez-devel to enable building support for + Bluetooth. + +------------------------------------------------------------------- +Sun May 2 09:20:06 UTC 2021 - Ben Greiner + +- Make sure to close the import_failed.map file after the exception + has been raised in order to avoid ResourceWarnings when the + failing import is part of a try...except block. + +------------------------------------------------------------------- +Wed Apr 28 16:39:54 UTC 2021 - Matej Cepl + +- Update to 3.9.4: + - bpo#43710: Reverted the fix for https://bugs.python.org/issue42500 + as it changed the PyThreadState struct size and broke the 3.9.x ABI + in the 3.9.3 release (visible on 32-bit platforms using binaries + compiled using an earlier version of Python 3.9.x headers). + - bpo#26053: Fixed bug where the pdb interactive run command echoed + the args from the shell command line, even if those have been + overridden at the pdb prompt. + - bpo#42988 (bsc#1183374) CVE-2021-3426: Remove the getfile + feature of the pydoc module which could be abused to read + arbitrary files on the disk (directory traversal + vulnerability). Moreover, even source code of Python modules + can contain sensitive data like passwords. Vulnerability + reported by David Schwörer. + - bpo#43285: ftplib no longer trusts the IP address value + returned from the server in response to the PASV command by + default. This prevents a malicious FTP server from using the + response to probe IPv4 address and port combinations on the + client network. Code that requires the former vulnerable + behavior may set a trust_server_pasv_ipv4_address attribute + on their ftplib.FTP instances to True to re-enable it. + - bpo#43439: Add audit hooks for gc.get_objects(), + gc.get_referrers() and gc.get_referents(). Patch by Pablo + Galindo. + - bpo#43660: Fix crash that happens when replacing sys.stderr + with a callable that can remove the object while an exception + is being printed. Patch by Pablo Galindo. + - bpo#43555: Report the column offset for SyntaxError for + invalid line continuation characters. Patch by Pablo Galindo. + - bpo#43517: Fix misdetection of circular imports when using + from pkg.mod import attr, which caused false positives in + non-trivial multi-threaded code. + - bpo#35883: Python no longer fails at startup with a fatal + error if a command line argument contains an invalid Unicode + character. The Py_DecodeLocale() function now escapes byte + sequences which would be decoded as Unicode characters + outside the [U+0000; U+10ffff] range. + - bpo#43406: Fix a possible race condition where + PyErr_CheckSignals tries to execute a non-Python signal + handler. + - bpo#42500: Improve handling of exceptions near recursion + limit. Converts a number of Fatal Errors in RecursionErrors. + - bpo#43433: xmlrpc.client.ServerProxy no longer ignores query + and fragment in the URL of the server. + - bpo#35930: Raising an exception raised in a “future” instance + will create reference cycles. + - bpo#43577: Fix deadlock when using ssl.SSLContext debug + callback with ssl.SSLContext.sni_callback(). + - bpo#43521: ast.unparse can now render NaNs and empty sets. + - bpo#43423: subprocess.communicate() no longer raises an + IndexError when there is an empty stdout or stderr IO buffer + during a timeout on Windows. + - bpo#27820: Fixed long-standing bug of smtplib.SMTP where + doing AUTH LOGIN with initial_response_ok=False will fail. + The cause is that SMTP.auth_login _always_ returns a password + if provided with a challenge string, thus non-compliant with + the standard for AUTH LOGIN. Also fixes bug with the test for + smtpd. + - bpo#43332: Improves the networking efficiency of http.client + when using a proxy via set_tunnel(). Fewer small send calls + are made during connection setup. + - bpo#43399: Fix ElementTree.extend not working on iterators + when using the Python implementation + - bpo#43316: The python -m gzip command line application now + properly fails when detecting an unsupported extension. It + exits with a non-zero exit code and prints an error message + to stderr. + - bpo#43260: Fix TextIOWrapper can not flush internal buffer + forever after very large text is written. + - bpo#42782: Fail fast in shutil.move() to avoid creating + destination directories on failure. + - bpo#37193: Fixed memory leak in socketserver.ThreadingMixIn + introduced in Python 3.7. + - bpo#43199: Answer “Why is there no goto?” in the Design and + History FAQ. + - bpo#43407: Clarified that a result from time.monotonic(), + time.perf_counter(), time.process_time(), or + time.thread_time() can be compared with the result from any + following call to the same function - not just the next + immediate call. + - bpo#27646: Clarify that ‘yield from ’ works with any + iterable, not just iterators. + - bpo#36346: Update some deprecated unicode APIs which are + documented as “will be removed in 4.0” to “3.12”. See PEP 623 + for detail. + - bpo#37945: Fix test_getsetlocale_issue1813() of test_locale: + skip the test if setlocale() fails. Patch by Victor Stinner. + - bpo#41561: Add workaround for Ubuntu’s custom OpenSSL + security level policy. + - bpo#43288: Fix test_importlib to correctly skip Unicode file + tests if the fileystem does not support them. + - bpo#43617: Improve configure.ac: Check for presence of + autoconf-archive package and remove our copies of M4 macros. + - bpo#42225: Document that IDLE can fail on Unix either from + misconfigured IP masquerage rules or failure displaying + complex colored (non-ascii) characters. + - bpo#43283: Document why printing to IDLE’s Shell is often + slower than printing to a system terminal and that it can be + made faster by pre-formatting a single string before + printing. + +------------------------------------------------------------------- +Fri Feb 19 16:58:38 UTC 2021 - Matej Cepl + +- Update to 3.9.2: + - bpo#42938 (bsc#1181126): Avoid static buffers when computing + the repr of ctypes.c_double and ctypes.c_longdouble + values. This issue was assigned CVE-2021-3177. + - bpo#42967 (bsc#1182379): Fix web cache poisoning + vulnerability by defaulting the query args separator to &, + and allowing the user to choose a custom separator. This + issue was assigned CVE-2021-23336. +- Upstreamed patches were removed: + - CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch + - bsc1167501-invalid-alignment.patch + - skip_random_failing_tests.patch + - CVE-2019-5010-null-defer-x509-cert-DOS.patch + +------------------------------------------------------------------- +Tue Feb 9 01:37:59 UTC 2021 - Steve Kowalik + +- Add Obsoletes for python3-base when primary interpreter is set to + properly replace it during upgrades. (bsc#1181324) + +------------------------------------------------------------------- +Mon Feb 8 22:02:03 UTC 2021 - Matej Cepl + +- Update to 3.9.1: + Security bugs: + - Prevented potential DoS attack via CPU and RAM exhaustion + when processing malformed Apple Property List files in binary + format. + - The plistlib module no longer accepts entity declarations in + XML plist files to avoid XML vulnerabilities. This should not + affect users as entity declarations are not used in regular + plist files. + - Add volatile to the accumulator variable in + hmac.compare_digest, making constant-time-defeating + optimizations less likely. + Core and Builtins + - Allow assignment expressions in set literals and set + comprehensions as per PEP 572. Patch by Pablo Galindo. + - Fix a regression introduced by the new parser, where an + unparenthesized walrus operator was not allowed within + generator expressions. + - types.GenericAlias objects can now be the targets of + weakrefs. + - Fixed a bug in the PEG parser that was causing crashes in + debug mode. Now errors are checked in left-recursive rules to + avoid cases where such errors do not get handled in time and + appear as long-distance crashes in other places. + - Fixed a possible crash in the PEG parser when checking for + the ‘!=’ token in the barry_as_flufl rule. Patch by Pablo + Galindo. + - Fix handling of errors during creation of PyFunctionObject, + which resulted in operations on uninitialized memory. Patch + by Yonatan Goldschmidt. + - Fix a bug in the parser, where a curly brace following + a primary didn’t fail immediately. This led to invalid + expressions like a {b} to throw a SyntaxError with a wrong + offset, or invalid expressions ending with a curly brace like + a { to not fail immediately in the REPL. + - Fix possible buffer overflow in the new parser when checking + for continuation lines. Patch by Pablo Galindo. + - Run the parser two times. On the first run, disable all the + rules that only generate better error messages to gain + performance. If there’s a parse failure, run the parser + a second time with those enabled. + - Document the default implementation of object.__eq__. + - Fix peephole optimizer misoptimize conditional jump + + JUMP_IF_NOT_EXC_MATCH pair. + - The garbage collector now tracks all user-defined classes. + Patch by Brandt Bucher. + - Fixed potential issues with removing not completely + initialized module from sys.modules when import fails. + - Star-unpacking is now allowed for with item’s targets in the + PEG parser. + - Fixed stack overflow in issubclass() and isinstance() when + getting the __bases__ attribute leads to infinite recursion. + - When loading a native module and a load failure occurs, + prevent a possible UnicodeDecodeError when not running in + a UTF-8 locale by decoding the load error message using the + current locale’s encoding. + - Correctly count control blocks in ‘except’ in compiler. + Ensures that a syntax error, rather a fatal error, occurs for + deeply nested, named exception handlers. + Library + - types.GenericAlias will now raise a TypeError when attempting + to initialize with a keyword argument. Previously, this would + cause the interpreter to crash if the interpreter was + compiled with debug symbols. This does not affect + interpreters compiled for release. Patch by Ken Jin. + - CGIHTTPRequestHandler.run_cgi() HTTP_ACCEPT improperly + parsed. Replace the special purpose getallmatchingheaders + with generic get_all method and add relevant tests. + - inspect.findsource() now raises OSError instead of IndexError + when co_lineno of a code object is greater than the file + length. This can happen, for example, when a file is edited + after it was imported. PR by Irit Katriel. + - Fix handling of trailing comments by inspect.getsource(). + - ChainMap.__iter__ no longer calls __getitem__ on underlying + maps + - TracebackException no longer holds a reference to the + exception’s traceback object. Consequently, instances of + TracebackException for equivalent but non-equal exceptions + now compare as equal. + - We fixed an issue in pickle.whichmodule in which importing + multiprocessing could change the how pickle identifies which + module an object belongs to, potentially breaking the + unpickling of those objects. + - Clarify the error message for asyncio.IncompleteReadError + when expected is None. + - Extracting a symlink from a tarball should succeed and + overwrite the symlink if it already exists. The fix is to + remove the existing file or symlink before extraction. Based + on patch by Chris AtLee, Jeffrey Kintscher, and Senthil + Kumaran. + - Fixed tkinter.ttk.Style.map(). The function accepts now the + representation of the default state as empty sequence (as + returned by Style.map()). The structure of the result is now + the same on all platform and does not depend on the value of + wantobjects. + - Fix various issues with typing.Literal parameter handling + (flatten, deduplicate, use type to cache key). Patch provided + by Yurii Karabas. + - Fix the threading.Thread class at fork: do nothing if the + thread is already stopped (ex: fork called at Python exit). + Previously, an error was logged in the child process. + - The onerror callback from shutil.rmtree now receives correct + function when os.open fails. + - Fix os.sendfile() on illumos. + - Fixed writing binary Plist files larger than 4 GiB. + - The repr() of typing types containing Generic Alias Types + previously did not show the parameterized types in the + GenericAlias. They have now been changed to do so. + - webbrowser: Ignore NotADirectoryError when calling + xdg-settings. + - binhex.binhex() consisently writes macOS 9 line endings. + - Fix a stack overflow error for asyncio Task or Future repr(). + - The overflow occurs under some circumstances when a Task or + Future recursively returns itself. + - Fix memory leak in subprocess.Popen() in case an uid (gid) + specified in user (group, extra_groups) overflows uid_t + (gid_t). + - Improve asyncio.wait function to create the futures set just + one time. + - InvalidFileException and RecursionError are now the only + errors caused by loading malformed binary Plist file + (previously ValueError and TypeError could be raised in some + specific cases). + - Pickling heap types implemented in C with protocols 0 and + 1 raises now an error instead of producing incorrect data. + - plistlib: fix parsing XML plists with hexadecimal integer + values + - Fix an incorrectly formatted error from + _codecs.charmap_decode() when called with a mapped value + outside the range of valid Unicode code points. PR by Max + Bernstein. + - Fix pickling pure Python datetime.time subclasses. Patch by + Dean Inwood. + - Fixed a bug that was causing ctypes.util.find_library() to + return None when triying to locate a library in an + environment when gcc>=9 is available and ldconfig is not. + Patch by Pablo Galindo + - C14N 2.0 serialisation in xml.etree.ElementTree failed for + unprefixed attributes when a default namespace was defined. + - Fix a bug in the symtable module that was causing + module-scope global variables to not be reported as both + local and global. Patch by Pablo Galindo. + - str() for the type attribute of the tkinter.Event object + always returns now the numeric code returned by Tk instead of + the name of the event type. + - fix tkinter.EventType Enum so all members are strings, and + none are tuples + - Fix SQLite3 segfault when backing up closed database. Patch + contributed by Peter David McCormick. + - Fix the tarfile module to write only basename of TAR file to + GZIP compression header. + - Allow ctypes.wintypes to be imported on non-Windows systems. + - shutil.which() now ignores empty entries in PATHEXT instead + of treating them as a match. + - Fix time-of-check/time-of-action issue in + subprocess.Popen.send_signal. + - Fix --outfile for cProfile / profile not writing the output + file in the original directory when the program being + profiled changes the working directory. PR by Anthony + Sottile. + - ZipFile truncates files to avoid corruption when a shorter + comment is provided in append (“a”) mode. Patch by Jan Mazur. + - Fixed KeyError exception when flattening an email to a string + attempts to replace a non-existent Content-Transfer-Encoding + header. + Documentation + - Fix the URL for the IMAP protocol documents. + - Document __format__ functionality for IP addresses. + - Clarify that subscription expressions are also valid for + certain classes and types in the standard library, and for + user-defined classes and types if the classmethod + __class_getitem__() is provided. + - Documented generic alias type and types.GenericAlias. Also + added an entry in glossary for generic types. + - In Programming FAQ “Sequences (Tuples/Lists)” section, add + “How do you remove multiple items from a list”. + - Fix RemovedInSphinx40Warning when building the documentation. + Patch by Dong-hee Na. + - Update the refcounts info of PyType_FromModuleAndSpec. + - Fix tarfile’s extractfile documentation + - Document some restrictions on the default string + representations of numeric classes. + Tests + - Reenable test_gdb on gdb 9.2 and newer: + https://bugzilla.redhat.com/show_bug.cgi?id=1866884 bug is + fixed in gdb 10.1. + - Fix test_asyncio.test_call_later() race condition: don’t + measure asyncio performance in the call_later() unit test. + The test failed randomly on the CI. + - Include _testinternalcapi module in Windows installer for + test suite + - Fix test_logging.test_race_between_set_target_and_flush(): + the test now waits until all threads complete to avoid + leaking running threads. + - Avoid a test failure in test_lib2to3 if the module has + already imported at the time the test executes. Patch by + Pablo Galindo. + - Tests for CJK codecs no longer call eval() on content + received via HTTP. + - Fix test_site.test_license_exists_at_url(): call + urllib.request.urlcleanup() to reset the global + urllib.request._opener. Patch by Victor Stinner. + - test_ssl: skip test_min_max_version_mismatch when TLS 1.0 is + not available + - Add tests for SIGINT handling in the runpy module. + - Fixed a failure in test_tk.test_widgets.ScaleTest happening + when executing the test with Tk 8.6.10. + Build + - Fix a race condition in “make regen-all” when make -jN option + is used to run jobs in parallel. The clinic.py script now + only use atomic write to write files. Moveover, generated + files are now left unchanged if the content does not change, + to not change the file modification time. + - Update Py_UNREACHABLE to use __builtin_unreachable() if only + the compiler is able to use it. Patch by Dong-hee Na. + - Addressed three compiler warnings found by undefined behavior + sanitizer (ubsan). + IDLE + - Fix reporting offset of the RE error in searchengine. + - Get docstrings for IDLE calltips more often by using + inspect.getdoc. + - Mostly finish using ttk widgets, mainly for editor, settings, + and searches. Some patches by Mark Roseman. + - Use ‘IDLE Shell’ as shell title + - Rewrite the Calltips doc section. + - In calltips, stop reminding that ‘/’ marks the end of + positional-only arguments. + - Typing opening and closing parentheses inside the parentheses + of a function call will no longer cause unnecessary + “flashing” off and on of an existing open call-tip, e.g. when + typed in a string literal. + C API + - Fix potential crash in deallocating method objects when + dynamically allocated PyMethodDef’s lifetime is managed + through the self argument of a PyCFunction. + - Py_FileSystemDefaultEncodeErrors and Py_UTF8Mode are + available again in limited API. +- Readjustet and reapplied patches: + - CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch + - bpo-31046_ensurepip_honours_prefix.patch + - python-3.3.0b1-fix_date_time_compiler.patch + - skip_random_failing_tests.patch + - sphinx-update-removed-function.patch + +------------------------------------------------------------------- +Fri Jan 29 17:22:48 UTC 2021 - Matej Cepl + +- Add CVE-2021-3177-buf_ovrfl_PyCArg_repr.patch fixing + bsc#1181126 (CVE-2021-3177) buffer overflow in PyCArg_repr in + _ctypes/callproc.c, which may lead to remote code execution. + +------------------------------------------------------------------- +Tue Jan 5 09:15:36 UTC 2021 - Matej Cepl + +- (bsc#1180125) We really don't Require python-rpm-macros package. + Unnecessary dependency. + +------------------------------------------------------------------- +Wed Dec 16 16:08:42 UTC 2020 - Matej Cepl + +- Make python39-doc building again +- Add no-skipif-doctests.patch, because SLE-15 version of Sphinx + doesn't know about skipif directive in doctests. + +------------------------------------------------------------------- +Sat Dec 12 14:29:33 UTC 2020 - Matej Cepl + +- Update sphinx-update-removed-function.patch patch to the latest + version in python36. + +------------------------------------------------------------------- +Thu Dec 10 00:26:51 UTC 2020 - Benjamin Greiner + +- Last try before this results in an editwar: + * remove importlib_resources and importlib-metadata + provides/obsoletes + * import importlib_resources is not the same as + import importlib.resources, same for metadata + * The backport packages from PyPI needed for older flavors are + specified as such for setuptools or in pyproject.toml. If a + package requires them they typically add them with a python + version qualifier and the packages have their own version + numbers. + +------------------------------------------------------------------- +Sat Dec 5 16:55:12 UTC 2020 - Matej Cepl + +- Add patch sphinx-update-removed-function.patch to no longer call + a now removed function and to make documentation build independent of + the Sphinx version (bsc#1179630, gh#python/cpython#13236). + +------------------------------------------------------------------- +Fri Nov 13 17:20:08 UTC 2020 - Matej Cepl + +- Don't require packages which break build on SLE-15 although we really + don't need them (python3-python-docs-theme and + python3-sphinxcontrib-qthelp). + +------------------------------------------------------------------- +Fri Oct 9 16:05:50 UTC 2020 - Dominique Leuenberger + +- Fix build with RPM 4.16: error: bare words are no longer + supported, please use "...": x86 == ppc. + +------------------------------------------------------------------- +Tue Oct 6 07:30:56 UTC 2020 - Matej Cepl + +- Update to the final version 3.9.0: + Complete changelog with all (many) + changes from previous version is on + https://docs.python.org/release/3.9.0/whatsnew/3.9.html + Changes from the previous RC versions (not that many) are on + https://docs.python.org/release/3.9.0/whatsnew/changelog.html#changelog + +------------------------------------------------------------------- +Fri Sep 25 06:58:03 UTC 2020 - Dominique Leuenberger + +- Buildrequire timezone only for general flavor. It's used in this + flavor for the test suite. + +------------------------------------------------------------------- +Wed Sep 2 14:39:44 UTC 2020 - Matej Cepl + +- Update to 3.9.0rc1: + * Core and Builtins + - bpo-38156: Handle interrupts that come after EOF + correctly in PyOS_StdioReadline. + * Library + - bpo-41497: Fix potential UnicodeDecodeError in dis + module. + - bpo-41490: Update ensurepip to install pip 20.2.1 and + setuptools 49.2.1. + - bpo-41467: On Windows, fix asyncio recv_into() return + value when the socket/pipe is closed (BrokenPipeError): + return 0 rather than an empty byte string (b''). + - bpo-41425: Make tkinter doc example runnable. + - bpo-41384: Raise TclError instead of TypeError when an + unknown option is passed to tkinter.OptionMenu. + - bpo-38731: Fix NameError in command-line interface of + py_compile. + - bpo-41317: Use add_done_callback() in + asyncio.loop.sock_accept() to unsubscribe reader early on + cancellation. + - bpo-41364: Reduce import overhead of uuid. + - bpo-41341: Recursive evaluation of typing.ForwardRef in + get_type_hints. + - bpo-41182: selector: use DefaultSelector based upon + implementation + - bpo-40726: Handle cases where the end_lineno is None on + ast.increment_lineno(). + * Documentation + - bpo-41045: Add documentation for debug feature of + f-strings. + - bpo-41314: Changed the release when from __future__ + import annotations becomes the default from 4.0 to 3.10 + (following a change in PEP 563). + * Windows + - bpo-41492: Fixes the description that appears in UAC + prompts. + - bpo-40948: Improve post-install message to direct people + to the “py” command. + - bpo-41412: The installer will now fail to install on + Windows 7 and Windows 8. Further, the UCRT dependency is + now always downloaded on demand. + - bpo-40741: Update Windows release to include SQLite + 3.32.3. + * IDLE + - bpo-41468: Improve IDLE run crash error message (which + users should never see). + - bpo-41373: Save files loaded with no line ending, as when + blank, or different line endings, by setting its line + ending to the system default. Fix regression in 3.8.4 and + 3.9.0b4. + +------------------------------------------------------------------- +Tue Sep 1 10:15:06 UTC 2020 - Matej Cepl + +- Synchronize formatting and fixes with python38. + +------------------------------------------------------------------- +Thu Aug 20 15:41:28 UTC 2020 - Andreas Schwab + +- Increase testsuite timeout to account for super long running + test_peg_generator + +------------------------------------------------------------------- +Tue Jul 21 09:53:06 UTC 2020 - Callum Farmer + +- Removed CVE-2019-20907_tarfile-inf-loop.patch: fixed in upstream +- Removed recursion.tar: contained in upstream +- Update to 3.9.0b5: + - bpo-41304: Fixes python3x._pth being ignored on Windows, caused + by the fix for bpo-29778 (CVE-2020-15801). + - bpo-41162: Audit hooks are now cleared later during + finalization to avoid missing events. + - bpo-29778: Ensure python3.dll is loaded from correct locations + when Python is embedded (CVE-2020-15523). + - bpo-39603: Prevent http header injection by rejecting control + characters in http.client.putrequest(…). + - bpo-41295: Resolve a regression in CPython 3.8.4 where defining + “__setattr__” in a multi-inheritance setup and + calling up the hierarchy chain could fail if builtins/extension + types were involved in the base types. + - bpo-41247: Always cache the running loop holder when running + asyncio.set_running_loop. + - bpo-41252: Fix incorrect refcounting in + _ssl.c’s _servername_callback(). + - bpo-41215: Use non-NULL default values in the PEG parser + keyword list to overcome a bug that was ' + preventing Python from being properly compiled when using the + XLC compiler. Patch by Pablo Galindo. + - bpo-41218: Python 3.8.3 had a regression where compiling with + ast.PyCF_ALLOW_TOP_LEVEL_AWAIT would + aggressively mark list comprehension with CO_COROUTINE. Now only + list comprehension making use of async/await will tagged as so. + - bpo-41175: Guard against a NULL pointer dereference within + bytearrayobject triggered by the bytearray() + bytearray() operation. + - bpo-39960: The “hackcheck” that prevents sneaking around a type’s + __setattr__() by calling the superclass method was + rewritten to allow C implemented heap types. + - bpo-41288: Unpickling invalid NEWOBJ_EX opcode with the + C implementation raises now UnpicklingError instead of crashing. + - bpo-39017: Avoid infinite loop when reading specially crafted + TAR files using the tarfile module (CVE-2019-20907, bsc#1174091). + - bpo-41235: Fix the error handling in ssl.SSLContext.load_dh_params(). + - bpo-41207: In distutils.spawn, restore expectation that + DistutilsExecError is raised when the command is not found. + - bpo-39168: Remove the __new__ method of typing.Generic. + - bpo-41194: Fix a crash in the _ast module: it can no longer be + loaded more than once. It now uses a global state rather than a module state. + - bpo-39384: Fixed email.contentmanager to allow set_content() to set a + null string. + - bpo-41300: Save files with non-ascii chars. + Fix regression released in 3.9.0b4 and 3.8.4. + - bpo-37765: Add keywords to module name completion list. + Rewrite Completions section of IDLE doc. + - bpo-40170: Revert PyType_HasFeature() change: it reads + again directly the PyTypeObject.tp_flags + member when the limited C API is not used, rather than always calling + PyType_GetFlags() which hides implementation details. + +------------------------------------------------------------------- +Mon Jul 20 12:06:41 UTC 2020 - Matej Cepl + +- Add CVE-2019-20907_tarfile-inf-loop.patch fixing bsc#1174091 + (CVE-2019-20907, bpo#39017) avoiding possible infinite loop + in specifically crafted tarball. + Add recursion.tar as a testing tarball for the patch. + +------------------------------------------------------------------- +Fri Jul 17 07:07:19 UTC 2020 - Callum Farmer + +- Changed bpo-31046_ensurepip_honours_prefix.patch to include fix from py3.8 + +------------------------------------------------------------------- +Thu Jul 16 21:45:50 UTC 2020 - Callum Farmer + +- Spec file fixes +- Re-added subprocess-raise-timeout.patch: now compatible +- Removed bpo34022-stop_hash-based_invalidation_w_SOURCE_DATE_EPOCH.patch: contained in upstream + +------------------------------------------------------------------- +Wed Jul 15 09:10:42 UTC 2020 - Tomáš Chvátal + +- Fix minor issues found in the staging. + +------------------------------------------------------------------- +Wed Jul 15 06:13:33 UTC 2020 - Tomáš Chvátal + +- Do not set ourselves as primary interpreter + +------------------------------------------------------------------- +Tue Jul 14 20:45:11 UTC 2020 - Matej Cepl + +- Update to 3.9.0b4: + - PEP 584, Union Operators in dict + - PEP 585, Type Hinting Generics In Standard Collections + - PEP 593, Flexible function and variable annotations + - PEP 602, Python adopts a stable annual release cadence + - PEP 615, Support for the IANA Time Zone Database in the + Standard Library + - PEP 616, String methods to remove prefixes and suffixes + - PEP 617, New PEG parser for CPython + - bpo#38379, garbage collection does not block on resurrected + objects; + - bpo#38692, os.pidfd_open added that allows process + management without races and signals; + - bpo#39926, Unicode support updated to version 13.0.0; + - bpo#1635741, when Python is initialized multiple times in + the same process, it does not leak memory anymore; + - A number of Python builtins (range, tuple, set, frozenset, + list, dict) are now sped up using PEP 590 vectorcall; + - A number of Python modules (_abc, audioop, _bz2, _codecs, + _contextvars, _crypt, _functools, _json, _locale, operator, + resource, time, _weakref) now use multiphase initialization + as defined by PEP 489; + - A number of standard library modules (audioop, ast, grp, + _hashlib, pwd, _posixsubprocess, random, select, struct, + termios, zlib) are now using the stable ABI defined by + PEP 384. +- Remove upstreamed patches: + - F00102-lib64.patch + - SUSE-FEDORA-multilib.patch + - OBS_dev-shm.patch + - subprocess-raise-timeout.patch + - bpo36302-sort-module-sources.patch + - bpo40784-Fix-sqlite3-deterministic-test.patch + +------------------------------------------------------------------- +Fri Jul 10 10:55:15 UTC 2020 - Tomáš Chvátal + +- Update pre_checkin.sh and regenerate + +------------------------------------------------------------------- +Fri Jul 10 10:11:39 UTC 2020 - Tomáš Chvátal + +- Convert few dependencies to their pkgconfig counterparts + +------------------------------------------------------------------- +Fri Jul 10 10:08:48 UTC 2020 - Tomáš Chvátal + +- Remove release requirement on libpython, it is not really needed + to be equal as the abi changes with versions + +------------------------------------------------------------------- +Fri Jul 10 10:07:50 UTC 2020 - Tomáš Chvátal + +- Add provides python3-bla on all the subpkgs in case we are + primary provider of the functionality + +------------------------------------------------------------------- +Fri Jul 10 10:02:01 UTC 2020 - Tomáš Chvátal + +- Remove unversioned files from devel subpkg too +- Remove main python3 files from -base based whether we are + primary interpreter or not +- Fix idle to be co-installable +- Add condition to be primary to provide/obsolete python3-* +- Fix doc to build in versioned folder so the pythons can be + installed next to each other + +------------------------------------------------------------------- +Fri Jul 10 07:57:10 UTC 2020 - Tomáš Chvátal + +- Revert the full versioning of calls on the macros. These + are generic so they should really just call python3 X + +------------------------------------------------------------------- +Fri Jul 10 07:56:11 UTC 2020 - Tomáš Chvátal + +- For the doc package we can build with generic flavor, we don't + need the our-interpreter based one + +------------------------------------------------------------------- +Fri Jul 10 07:18:53 UTC 2020 - Tomáš Chvátal + +- Add provides for pytohn3X-typing/etc to allow BR on those still + to work when needed + +------------------------------------------------------------------- +Fri Jul 10 07:14:33 UTC 2020 - Tomáš Chvátal + +- Change macros.python3 to use full versioned 3.8 instead of just 3 + for python interpreter + +------------------------------------------------------------------- +Wed Jul 1 11:50:19 UTC 2020 - Tomáš Chvátal + +- Reduce some now unused conditionals + +------------------------------------------------------------------- +Wed Jul 1 11:00:40 UTC 2020 - Tomáš Chvátal + +- Redux the -base dependencies to match up pre-merge layout + +------------------------------------------------------------------- +Wed Jul 1 09:24:39 UTC 2020 - Tomáš Chvátal + +- Generate baselibs in pre-checkin too + +------------------------------------------------------------------- +Wed Jul 1 09:14:33 UTC 2020 - Tomáš Chvátal + +- Generate the importlib-failed using pre_checking again +- Add back the information about skipped tests on the pre_checkin + output + +------------------------------------------------------------------- +Tue Jun 30 07:11:19 UTC 2020 - Tomáš Chvátal + +- Use %python_pkg_name instead of hardcoding python3 where + applicable +- Sort out preamble with spec-cleaner + +------------------------------------------------------------------- +Mon Jun 29 14:36:10 UTC 2020 - Matej Cepl + +- Calculate required variables instead of relying on their continuous manual update + +------------------------------------------------------------------- +Thu Jun 25 10:44:08 UTC 2020 - Tomáš Chvátal + +- Fix the -base module build again to generate only the deps + we need + +------------------------------------------------------------------- +Wed Jun 17 18:42:51 UTC 2020 - Matej Cepl + +- Replace OBS_dev-shm.patch with the upstream PR#20944 + +------------------------------------------------------------------- +Thu Jun 10 14:30:15 UTC 2020 - Tomáš Chvátal + +- Use the %{python_pkg_name} on more places to allow easier + multiversioning +- Switch to _multibuild approach for easier maintenance of this + package. All is now in one spec file with 3 conditionals: + * bcond_with base + * bcond_with doc + * bcond_with general + +------------------------------------------------------------------- +Mon Jun 8 14:26:00 UTC 2020 - Matej Cepl + +- add requires python3-base on libpython subpackage (bsc#1167008) + +------------------------------------------------------------------- +Fri Jun 5 06:08:12 UTC 2020 - Dirk Mueller + +- build against Sphinx 2.x until python is compatible with + Sphinx 3.x (see gh#python/cpython#19397, bpo#40204) + +------------------------------------------------------------------- +Fri May 29 19:59:01 UTC 2020 - Andreas Stieger + +- Fix build with SQLite 3.32 (bpo#40783) + add bpo40784-Fix-sqlite3-deterministic-test.patch + +------------------------------------------------------------------- +Sun May 17 15:37:35 UTC 2020 - Callum Farmer + +- Update to version 3.8.3: + - Complete list of changes is available at + https://docs.python.org/release/3.8.3/whatsnew/changelog.html#python-3-8-3-final, + but most of them are just bugfixes. + - Removed patch CVE-2020-8492-urllib-ReDoS.patch: contained in upstream + +------------------------------------------------------------------- +Thu Apr 16 12:06:01 UTC 2020 - Matej Cepl + +- Add #!BuildIgnore: gdk-pixbuf-loader-rsvg to python3 SPEC + +------------------------------------------------------------------- +Thu Mar 26 15:36:55 UTC 2020 - Matej Cepl + +- Add patch bsc1167501-invalid-alignment.patch + (bsc#1167501, bpo#40052) to fix alignment in abstract.h header file. + +------------------------------------------------------------------- +Wed Mar 11 11:09:41 UTC 2020 - Andreas Schwab + +- Update list of skipped tests for qemu linux-user build, test_setegid + (test.test_os.PosixUidGidTests) is confusing it + +------------------------------------------------------------------- +Thu Mar 5 18:40:29 UTC 2020 - Matej Cepl + +- Update to 3.8.2: + - Complete list of changes is available at + https://docs.python.org/release/3.8.2/whatsnew/changelog.html#python-3-8-2-final, + but most of them are just bugfixes. + - Updated patches: + - F00102-lib64.patch + - OBS_dev-shm.patch + - SUSE-FEDORA-multilib.patch + - subprocess-raise-timeout.patch + +------------------------------------------------------------------- +Sun Feb 9 00:14:24 CET 2020 - Matej Cepl + +- Add CVE-2020-8492-urllib-ReDoS.patch fixing the security bug + "Python urrlib allowed an HTTP server to conduct Regular + Expression Denial of Service (ReDoS)" (bsc#1162367) + +------------------------------------------------------------------- +Sat Feb 8 22:21:10 CET 2020 - Matej Cepl + +- Add Requires: libpython%{so_version} == %{version}-%{release} + to python3-base to keep both packages always synchronized + (bsc#1162224). + +------------------------------------------------------------------- +Mon Feb 3 20:27:54 UTC 2020 - Tomáš Chvátal + +- Do not pull in bluez in base again, explain the cycle, + it needs to be solved by bluez maintainer for us by providing + just the headers separately + +------------------------------------------------------------------- +Mon Feb 3 19:54:25 UTC 2020 - Tomáš Chvátal + +- Reame idle icons to idle3 in order to not conflict with python2 + variant of the package + * renamed the icons + * renamed icon load in desktop file + +------------------------------------------------------------------- +Thu Jan 16 09:50:03 UTC 2020 - Tomáš Chvátal + +- Add importlib_resources provide/obsolete as it is integral + part of the lang since 3.7 release + +------------------------------------------------------------------- +Mon Jan 13 11:10:47 UTC 2020 - Martin Liška + +- Add -fno-semantic-interposition as it brings speed up: + https://fedoraproject.org/wiki/Changes/PythonNoSemanticInterpositionSpeedup + +------------------------------------------------------------------- +Thu Dec 19 16:25:26 CET 2019 - Matej Cepl + +- Update to 3.8.1: + - This is mainly bugfix release and no significant changes to + API are expected. The full changelog is available on + https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-1 + - Remove bpo-38688_shutil.copytree_prevent-infinite-recursion.patch, + which is included in the upstream tarball. + +------------------------------------------------------------------- +Thu Dec 19 14:57:32 CET 2019 - Matej Cepl + +- Add bpo-31046_ensurepip_honours_prefix.patch which makes + ensurepip to honour the value of $(prefix). Proposed fix for + bpo#31046.. + +------------------------------------------------------------------- +Tue Dec 10 11:07:16 UTC 2019 - Tomáš Chvátal + +- Move bluez-devel dependency to base as it is needed for + socket.AF_BLUETOOTH and otherwise does not work + +------------------------------------------------------------------- +Mon Dec 2 16:52:32 CET 2019 - Matej Cepl + +- Reintroduce QtHelp with the help of the new BR + python-sphinxcontrib-qthelp. + +------------------------------------------------------------------- +Mon Oct 21 18:51:00 UTC 2019 - Stefan Brüns + +- Fix SUSE-FEDORA-multilib.patch, the platform agnostic infix for + library installation is "lib", not "dir". + +------------------------------------------------------------------- +Thu Oct 17 14:19:20 UTC 2019 - Stefan Brüns + +- Move idle subpackage build from python3-base to python3. + appstream-glib required for packaging introduces considerable + extra dependencies and a build loop via rust/librsvg. +- Correct installation of idle IDE icons: + + idle.png is not the target directory + + non-GNOME-specific icons belong into icons/hicolor +- Add required Name key to idle3 desktop file + +------------------------------------------------------------------- +Tue Oct 15 16:39:12 CEST 2019 - Matej Cepl + +- Update to the final release 3.8.0. . + - New Features: + - Assignment expressions + - Positional-only parameters + - Parallel filesystem cache for compiled bytecode files + - Debug build uses the same ABI as release build + - f-strings support = for self-documenting expressions and + debugging + - PEP 578: Python Runtime Audit Hooks + - PEP 587: Python Initialization Configuration + - Vectorcall: a fast calling protocol for CPython + - Pickle protocol 5 with out-of-band data buffers + - New modules: + - importlib.metadata + - Improved modules: + - ast asyncio, builtins, collections, curses, ctypes, + datetime, functools, gc, gettext, gzip, idelib and IDLE, + inspect, io, json.tool, math, mmap, multiprocessing, os, + os.path, pathlib, pickle, plistlib, py_compile, shlex, + shutil, socket, ssl, statistics, sys, tarfile, threading, + tokenize, tkinter, time, typing, unicodedata, unittest, + venv, weakref, xml + - C API improvements + - bdist_winnst command has been deprecated (use bdist_wheel) +- https://docs.python.org/3.8/whatsnew/3.8.html remains rest of + changes including documentation on how to port your programs to + the current version of Python. + +------------------------------------------------------------------- +Mon Oct 14 15:02:08 CEST 2019 - Matej Cepl + +- Add idle3.appdata.xml and idle3.desktop (originally from + Fedora) to make Idle3 full GUI desktop application. + (bsc#1153830) + +------------------------------------------------------------------- +Wed Oct 9 19:09:16 UTC 2019 - Michael Gorse + +- Drop intltool from BuildRequires. Doesn't appear to be used. + +------------------------------------------------------------------- +Wed Oct 9 10:37:59 UTC 2019 - Tomáš Chvátal + +- Add folder version to allow tarball downloads even for beta/rc + releases + +------------------------------------------------------------------- +Tue Oct 8 14:53:54 CEST 2019 - Matej Cepl + +- Revert patches from Fedora (F00102-lib64.patch and + F00251-change-user-install-location.patch) into their original + prisitine Fedora versions, SUSE-FEDORA-multilib.patch refreshed + accordingly. + +------------------------------------------------------------------- +Mon Oct 7 14:33:30 UTC 2019 - Matej Cepl + +- Correct quotation of platsubdir in Lib/distutils/command/install.py + +------------------------------------------------------------------- +Thu Oct 3 13:59:57 CEST 2019 - Matej Cepl + +- Replace python-3.6.0-multilib.patch with two patches from + Fedora (F00102-lib64.patch and + F00251-change-user-install-location.patch), and our own + SUSE-FEDORA-multilib.patch to allow better cooperation with + Fedora and better upstreaming. +- Add OBS_dev-shm.patch fixing bpo#38377 + +------------------------------------------------------------------- +Thu Oct 3 08:39:18 UTC 2019 - Tomáš Chvátal + +- Pull in just gettext and let solver to sort out between: + gettext-runtime-mini and gettext-runtime + +------------------------------------------------------------------- +Wed Oct 2 15:00:09 CEST 2019 - Matej Cepl + +- Update to 3.8.0rc1. Overall changes from 3.7: + - PEP 572, Assignment expressions + - PEP 570, Positional-only arguments + - PEP 587, Python Initialization Configuration (improved + embedding) + - PEP 590, Vectorcall: a fast calling protocol for CPython + - PEP 578, Runtime audit hooks + - PEP 574, Pickle protocol 5 with out-of-band data + - Typing-related: PEP 591 (Final qualifier), PEP 586 (Literal + types), and PEP 589 (TypedDict) + - Parallel filesystem cache for compiled bytecode + - Debug builds share ABI as release builds, also the 'm' ABI + tag was removed (irrelevant since 3.4), bpo#36707 + - f-strings support a handy = specifier for debugging + - continue is now legal in finally: blocks + - on Windows, the default asyncio event loop is now + ProactorEventLoop + - on macOS, the spawn start method is now used by default in + multiprocessing + - multiprocessing can now use shared memory segments to avoid + pickling costs between processes + - typed_ast is merged back to CPython + - LOAD_GLOBAL is now 40% faster + - pickle now uses Protocol 4 by default, improving performance +- Refreshed patches: + - CVE-2019-5010-null-defer-x509-cert-DOS.patch + - python-3.3.0b1-fix_date_time_compiler.patch + - python-3.6.0-multilib.patch + - subprocess-raise-timeout.patch + +------------------------------------------------------------------- +Wed Sep 25 09:46:41 UTC 2019 - Bernhard Wiedemann + +- Add bpo36302-sort-module-sources.patch (boo#1041090) + +------------------------------------------------------------------- +Tue Sep 10 13:43:18 UTC 2019 - Tomáš Chvátal + +- Try harder obsoleting importlib-metadata + +------------------------------------------------------------------- +Sat Aug 31 00:16:47 CEST 2019 - Matej Cepl + +- Update to 3.8.0b4: + Many bugfixes, full list on + https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-0-beta-4 + +------------------------------------------------------------------- +Thu Aug 29 06:28:15 UTC 2019 - Guillaume GARDET + +- Re-enable test_threading on aarch64 + +------------------------------------------------------------------- +Sat Aug 17 13:21:15 UTC 2019 - John Vandenberg + +- Remove xrpm from subpackage tk description + +------------------------------------------------------------------- +Tue Aug 6 14:24:55 CEST 2019 - Matej Cepl + +- Update to 3.8.0b3: + Many bugfixes, full list on + https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-0-beta-3 +- Patches reapplied: + - python-3.3.0b1-fix_date_time_compiler.patch + - python-3.3.0b1-test-posix_fadvise.patch + - python-3.6.0-multilib.patch + - subprocess-raise-timeout.patch + +------------------------------------------------------------------- +Tue Jul 23 13:20:49 UTC 2019 - Matej Cepl + +- Add Provides: python3-importlib-metadata + +------------------------------------------------------------------- +Sun Jul 7 19:08:48 CEST 2019 - Matej Cepl + +- Update to 3.8.0b2: + Many bugfixes, full list on + https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-0-beta-2 +- Patches included in upstream: + - bpo-37169_PyObject_IsFreed.patch +- Patches reapplied: + - 00251-change-user-install-location.patch + - distutils-reproducible-compile.patch + - python-3.3.0b1-localpath.patch + - python-3.6.0-multilib.patch + +------------------------------------------------------------------- +Tue Jul 2 09:03:04 UTC 2019 - Andreas Schwab + +- Update list of skipped tests for qemu linux-user build +- Don't do profiling in qemu linux-user build + +------------------------------------------------------------------- +Wed Jun 5 12:19:09 CEST 2019 - Matej Cepl + +- Update to 3.8.0b1 (changes since 3.7.*): + - PEP 572, Assignment expressions + - PEP 570, Positional-only arguments + - PEP 587, Python Initialization Configuration (improved embedding) + - PEP 590, Vectorcall: a fast calling protocol for CPython + - PEP 578, Runtime audit hooks + - PEP 574, Pickle protocol 5 with out-of-band data + - Typing-related: PEP 591 (Final qualifier), PEP 586 (Literal + types), and PEP 589 (TypedDict) + - Parallel filesystem cache for compiled bytecode + - Debug builds share ABI as release builds + - f-strings support a handy = specifier for debugging + - continue is now legal in finally: blocks + - multiprocessing can now use shared memory segments to avoid + pickling costs between processes + - typed_ast is merged back to CPython + - LOAD_GLOBAL is now 40% faster + - pickle now uses Protocol 4 by default, improving performance +- Remove patches which were included in the upstream: + - 00251-change-user-install-location.patch + - 00316-mark-bdist_wininst-unsupported.patch + - CVE-2019-9947-no-ctrl-char-http.patch + - raise_SIGING_not_handled.patch + +------------------------------------------------------------------- +Wed May 22 10:53:03 UTC 2019 - Martin Liška + +- Set _lto_cflags to nil as the package is using LTO via --enable-lto. + That will prevent to propage LTO for Python modules that are + built in a separate package. + +------------------------------------------------------------------- +Sat May 4 21:29:20 CEST 2019 - Matej Cepl + +- Update to 3.8.0.a3: + - PEP 572: Assignment Expressions. + - Other (mostly small) changes are on + https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-0-alpha-3 + +------------------------------------------------------------------- +Mon Apr 29 15:40:34 CEST 2019 - Matej Cepl + +- bsc#1130840 (CVE-2019-9947): add CVE-2019-9947-no-ctrl-char-http.patch + Address the issue by disallowing URL paths with embedded + whitespace or control characters through into the underlying + http client request. Such potentially malicious header + injection URLs now cause a ValueError to be raised. + +------------------------------------------------------------------- +Wed Apr 10 10:22:58 CEST 2019 - Matej Cepl + +- Fix metadata of patches. +- Rename boo1071941-make-install-in-sep-loc.patch to + 00251-change-user-install-location.patch which is the original + name, so it can be looked up in the Fedora VCS. + +------------------------------------------------------------------- +Tue Apr 9 04:55:24 UTC 2019 - John Vandenberg + +- Mark distutils bdist_wininst command unsupported + with 00316-mark-bdist_wininst-unsupported.patch +- Remove Windows bdist_wininst executables from runtime package + +------------------------------------------------------------------- +Tue Apr 9 01:21:45 CEST 2019 - Matej Cepl + +- Update to 3.7.3, which is the maintenance release without any + significant changes in API. + - Updated patches: + - CVE-2019-5010-null-defer-x509-cert-DOS.patch + - distutils-reproducible-compile.patch + - python-3.3.0b1-fix_date_time_compiler.patch + - python-3.6.0-multilib.patch + - raise_SIGING_not_handled.patch + +------------------------------------------------------------------ +Wed Mar 20 14:59:58 UTC 2019 - Matěj Cepl + +- Remove building of Qt Develop help files. + +------------------------------------------------------------------- +Fri Mar 15 15:10:30 CET 2019 - Matej Cepl + +- Return distutils-reproducible-compile.patch which is still + missing (still unfinished bpo#29708). + +------------------------------------------------------------------- +Mon Feb 25 23:30:56 CET 2019 - Matej Cepl + +- Update to 3.8.0a2: + * List of all (mostly small) changes are on + https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-0-alpha-2 + +------------------------------------------------------------------- +Tue Feb 12 10:25:52 CET 2019 - Matej Cepl + +- Build nis module again. + +------------------------------------------------------------------- +Tue Feb 12 10:06:17 CET 2019 - Matej Cepl + +- Update to 3.8.0a1: + * The most visible change so far is probably the + implementation of PEP 572: Assignment Expressions. For + a detailed list of changes, see: + https://docs.python.org/3.8/whatsnew/changelog.html + * Recover building of nis module properly in python3 package +- Update patches: + * CVE-2019-5010-null-defer-x509-cert-DOS.patch + * python-3.3.0b1-fix_date_time_compiler.patch + * python-3.3.0b1-test-posix_fadvise.patch + * python-3.6.0-multilib.patch + * raise_SIGING_not_handled.patch + +------------------------------------------------------------------- +Wed Jan 30 18:07:49 CET 2019 - mcepl@suse.com + +- Put LICENSE file where it belongs (bsc#1121852) + +------------------------------------------------------------------- +Sat Jan 19 16:19:38 CET 2019 - mcepl@suse.com + +- bsc#1122191: add CVE-2019-5010-null-defer-x509-cert-DOS.patch + fixing bpo-35746. + An exploitable denial-of-service vulnerability exists in the + X509 certificate parser of Python.org Python 2.7.11 / 3.7.2. + A specially crafted X509 certificate can cause a NULL pointer + dereference, resulting in a denial of service. An attacker can + initiate or accept TLS connections using crafted certificates + to trigger this vulnerability. + +------------------------------------------------------------------- +Tue Jan 8 12:51:01 UTC 2019 - Tomáš Chvátal + +- Do not require full gettext in order to avoid pulling in the + glib2 as a dependency + +------------------------------------------------------------------- +Tue Jan 8 12:25:27 UTC 2019 - Tomáš Chvátal + +- Update to 3.7.2: + * bugfix release: + https://docs.python.org/3.7/whatsnew/changelog.html#changelog + +------------------------------------------------------------------- +Wed Jan 2 12:51:48 CET 2019 - mcepl@suse.com + +- Stop applying python-3.6.0-multilib-new.patch (which is still + WIP), and apply the old proven python-3.6.0-multilib.patch + instead. + +------------------------------------------------------------------- +Wed Dec 19 19:29:44 UTC 2018 - Todd R + +- Use upstream-recommended %{_rpmconfigdir}/macros.d directory + for the rpm macros. + +------------------------------------------------------------------- +Mon Dec 17 17:24:49 CET 2018 - mcepl@suse.com + +- Upgrade to 3.7.2rc1: + * bugfix release, for the full list of all changes see + https://docs.python.org/3.7/whatsnew/changelog.html#changelog +- Make run of the test suite more verbose + +------------------------------------------------------------------- +Tue Dec 11 01:52:45 UTC 2018 - Jan Engelhardt + +- Write summaries without em dashes. + +------------------------------------------------------------------- +Mon Dec 3 13:27:54 UTC 2018 - Matěj Cepl + +- Remove python-3.3.0b1-curses-panel.patch it is unnecessary anymore. +- Add boo1071941-make-install-in-sep-loc.patch to make pip and + distutils in user environment install into separate location + (boo#1071941) + + Set values of prefix and exec_prefix in distutils install + command to /usr/local if executable is /usr/bin/python* and RPM + build is not detected to make pip and distutils install into + separate location +- Remove finally python-3.3.3-skip-distutils-test_sysconfig_module.patch +- Remove distutils-reproducible-compile.patch which doesn't make + really much difference in reproducibility (see + gh#python/cpython#8057 and discussion there). + +------------------------------------------------------------------- +Sat Dec 1 00:14:28 CET 2018 - mcepl@suse.com + +- Rename Stop_hash-based_invalidation_w_SOURCE_DATE_EPOCH.patch + to bpo34022-stop_hash-based_invalidation_w_SOURCE_DATE_EPOCH.patch + +------------------------------------------------------------------- +Wed Nov 7 12:10:41 CET 2018 - mcepl@suse.com + +- Add dependency on bluez-devel to build support for Bluetooth + (boo#1109998) + +------------------------------------------------------------------- +Tue Nov 6 13:52:45 CET 2018 - mcepl@suse.com + +- Add devhelp subpackage and split qthelp into another + subpackage. + +------------------------------------------------------------------- +Wed Oct 24 12:38:00 UTC 2018 - Matěj Cepl + +- Remove python-3.0b1-record-rpm.patch and + Python-3.0b1-record-rpm.patch, as they are not needed anymore + +------------------------------------------------------------------- +Tue Oct 23 14:14:16 UTC 2018 - Matej Cepl + +- Switch off test_threading for optimization builds. + +------------------------------------------------------------------- +Mon Oct 22 14:41:59 CEST 2018 - mcepl@suse.com + +- Update to python-3.7.1. This is just a brief overview, complete + changelog available at + https://docs.python.org/3.7/whatsnew/changelog.html#python-3-7-1-final: + Library + bpo-34970: Protect tasks weak set manipulation in asyncio.all_tasks() +- Patches already accepted upstream are removed: + * 00307-allow-to-call-Py_Main-after-Py_Initialize.patch + * 00308-tls-1.3.patch +- New patches added: + * Stop_hash-based_invalidation_w_SOURCE_DATE_EPOCH.patch + * raise_SIGING_not_handled.patch +- All other patches refreshed via quilt. + +------------------------------------------------------------------- +Mon Oct 22 12:22:19 UTC 2018 - Matej Cepl + +- Add raise_SIGING_not_handled.patch to fix bsc#1094814 + +------------------------------------------------------------------- +Wed Oct 17 14:04:35 UTC 2018 - Tomáš Chvátal + +- Add patch to fix importlib return types: + * python3-imp-returntype.patch + +------------------------------------------------------------------- +Mon Oct 15 13:46:32 CEST 2018 - mcepl@suse.com + +- bpo-34022 still not completely fixed, so we have to keep + excluding test_cmd_line_script, + test_multiprocessing_main_handling, and test_runpy from the + test suite. + +------------------------------------------------------------------- +Sun Oct 14 15:57:24 UTC 2018 - Matej Cepl + +- Update to python 3.7.1~rc2: + Core and Builtins + bpo-34879: Fix a possible null pointer dereference in + bytesobject.c. Patch by Zackery Spytz. + bpo-34854: Fixed a crash in compiling string annotations + containing a lambda with a keyword-only argument that + doesn’t have a default value. + bpo-34320: Fix dict(od) didn’t copy iteration order of + OrderedDict. + Library + bpo-34769: Fix for async generators not finalizing when event + loop is in debug mode and garbage collector runs in another + thread. + bpo-34922: Fixed integer overflow in the digest() and + hexdigest() methods for the SHAKE algorithm in the hashlib + module. + bpo-34900: Fixed unittest.TestCase.debug() when used to call + test methods with subtests. Patch by Bruno Oliveira. + bpo-34871: Fix inspect module polluted sys.modules when parsing + __text_signature__ of callable. + bpo-34872: Fix self-cancellation in C implementation of + asyncio.Task + bpo-34819: Use a monotonic clock to compute timeouts in + Executor.map() and as_completed(), in order to prevent + timeouts from deviating when the system clock is adjusted. + bpo-34334: In QueueHandler, clear exc_text from LogRecord to + prevent traceback from being written twice. + bpo-6721: Acquire the logging module’s commonly used internal + locks while fork()ing to avoid deadlocks in the child + process. + bpo-34172: Fix a reference issue inside multiprocessing.Pool + that caused the pool to remain alive if it was deleted + without being closed or terminated explicitly. + Documentation + bpo-32174: chm document displays non-ASCII charaters properly on + some MBCS Windows systems. + Tests + bpo-32962: Fixed test_gdb when Python is compiled with flags + -mcet -fcf-protection -O0. + C API + bpo-34910: Ensure that PyObject_Print() always returns -1 on + error. Patch by Zackery Spytz. + +------------------------------------------------------------------- +Fri Oct 12 20:46:58 CEST 2018 - mcepl@suse.com + +- Add Stop_hash-based_invalidation_w_SOURCE_DATE_EPOCH.patch to + fix problems with SOURCE_DATE_EPOCH variable (bpo-34022) + +------------------------------------------------------------------- +Mon Sep 17 09:44:02 UTC 2018 - Tomáš Chvátal + +- Add patch to fix build with tls1.3 supported openssl + * 00308-tls-1.3.patch +- Add patch to fix Py_Main calls after Py_initialize + * 00307-allow-to-call-Py_Main-after-Py_Initialize.patch + +------------------------------------------------------------------- +Mon Sep 3 15:22:42 UTC 2018 - Matěj Cepl + +- Add -fwrapv to OPTS, which is default for python3 anyway + See for example https://github.com/zopefoundation/persistent/issues/86 + for bugs which are caused by avoiding it. + +------------------------------------------------------------------- +Tue Jul 10 11:12:32 UTC 2018 - mcepl@suse.com + +- Fix ownership of _contextvars, _queue, and _xxtestfuzz + +------------------------------------------------------------------- +Tue Jul 3 15:04:48 UTC 2018 - mcepl@suse.com + +- Switch off LTO for distros with older GCC +- Fix %files + +------------------------------------------------------------------- +Fri Jun 29 14:20:03 UTC 2018 - tchvatal@suse.com + +- Add dependency over libuuid-devel + +------------------------------------------------------------------- +Thu Jun 28 10:42:15 UTC 2018 - mimi.vx@gmail.com + +- update to python 3.7.0 + Complete overview of changes is available on + https://docs.python.org/3/whatsnew/3.7.html, these are just + highlights: + * PEP 563, postponed evaluation of type annotations. + * async and await are now reserved keywords. + * New library modules: + contextvars: PEP 567 – Context Variables + dataclasses: PEP 557 – Data Classes + importlib.resources + * New built-in features: + PEP 553, the new breakpoint() function. + * Python data model improvements: + PEP 562, customization of access to module attributes. + PEP 560, core support for typing module and generic types. + the insertion-order preservation nature of dict objects + has been declared to be an official part of the Python + language spec. + * Significant improvements in the standard library: + The asyncio module has received new features, significant + usability and performance improvements. + The time module gained support for functions with + nanosecond resolution. + * CPython implementation improvements: + Avoiding the use of ASCII as a default text encoding: + PEP 538, legacy C locale coercion + PEP 540, forced UTF-8 runtime mode + PEP 552, deterministic .pycs + the new development runtime mode + PEP 565, improved DeprecationWarning handling + * C API improvements: + PEP 539, new C API for thread-local storage + * Documentation improvements: + PEP 545, Python documentation translations + New documentation translations: Japanese, French, and Korean. +- drop python3-sorted_tar.patch +- drop 0001-allow-for-reproducible-builds-of-python-packages.patch +- refresh python-3.6.0-multilib-new.patch +- refresh subprocess-raise-timeout.patch + * new C API for thread-local storage + * Deterministic pyc files + * Built-in breakpoint() + * Data Classes + * Core support for typing module and generic types + * Customization of access to module attributes + * Postponed evaluation of annotations + * Time functions with nanosecond resolution + * Improved DeprecationWarning handling + * Context Variables + * Avoiding the use of ASCII as a default text encoding + (PEP 538, legacy C locale coercion and PEP 540, forced UTF-8 runtime mode) + * The insertion-order preservation nature of dict objects is now + an official part of the Python language spec. + * Notable performance improvements in many areas. + +------------------------------------------------------------------- +Thu May 17 18:26:42 UTC 2018 - hpj@urpla.net + +- disable lto with gcc versions below 7 (results in link failures) + +------------------------------------------------------------------- +Mon Apr 30 15:23:24 UTC 2018 - jengelh@inai.de + +- Use faster find subcommand execution strategies. + +------------------------------------------------------------------- +Fri Apr 20 16:17:29 UTC 2018 - tchvatal@suse.com + +- Do not mention the testsuite disabling in opts as it was moved to + main pkg so base is test-free + +------------------------------------------------------------------- +Tue Apr 17 08:36:08 UTC 2018 - tchvatal@suse.com + +- As we run in main python package do not generate the pre_checkin + from both now + +------------------------------------------------------------------- +Mon Apr 16 14:11:56 UTC 2018 - tchvatal@suse.com + +- Move the tests from base to generic package wrt bsc#1088573 + * We still fail the whole distro if python3 is not build + * The other archs than x86_64 took couple of hours to unblock + build of other software, this way we work around the issue +- Some tests are still run in -base for the LTO tweaking, but at + least it is not run twice + +------------------------------------------------------------------- +Sat Mar 31 19:41:12 UTC 2018 - mimi.vx@gmail.com + +- update to 3.6.5 + * bugfix release + * see Misc/NEWS for details +- drop ctypes-pass-by-value.patch +- drop fix-localeconv-encoding-for-LC_NUMERIC.patch +- refresh python-3.6.0-multilib-new.patch + +------------------------------------------------------------------ +Wed Mar 7 09:16:39 UTC 2018 - adam@mizerski.pl + +- Created %so_major and %so_minor macros +- Put Tools/gdb/libpython.py script into proper place and ship it with devel + subpackage. + +------------------------------------------------------------------- +Tue Feb 20 15:04:56 UTC 2018 - schwab@suse.de + +- ctypes-pass-by-value.patch: Fix pass by value for structs on aarch64 + +------------------------------------------------------------------- +Tue Feb 20 14:28:00 UTC 2018 - bwiedemann@suse.com + +- Add python3-sorted_tar.patch (boo#1081750) + +------------------------------------------------------------------- +Tue Feb 20 14:08:57 UTC 2018 - tchvatal@suse.com + +- Drop python3-tk and python3-idle recommends to reduce python3 + always pulling X stack bsc#1081751 + +------------------------------------------------------------------- +Wed Feb 7 09:10:03 UTC 2018 - tchvatal@suse.com + +- Add patch to fix glibc 2.27 fail bsc#1079761: + * fix-localeconv-encoding-for-LC_NUMERIC.patch + +------------------------------------------------------------------- +Mon Feb 5 17:14:43 UTC 2018 - normand@linux.vnet.ibm.com + +- Update skip_random_failing_tests.patch (for PowerPC) + to avoid test_call_later failure + +------------------------------------------------------------------- +Wed Jan 24 14:35:58 UTC 2018 - jmatejek@suse.com + +- move XML modules and python3-xml provide to python3-base + (fixes bsc#1077230) +- move ensurepip to base + +------------------------------------------------------------------- +Thu Jan 18 12:31:47 UTC 2018 - normand@linux.vnet.ibm.com + +- Add skip_random_failing_tests.patch only for PowerPC + +------------------------------------------------------------------- +Wed Jan 3 12:18:51 UTC 2018 - jmatejek@suse.com + +- update to 3.6.4 + * bugfix release, over a hundred bugs fixed + * see Misc/NEWS for details +- drop upstreamed python3-ncurses-6.0-accessors.patch +- drop PYTHONSTARTUP hooks that cause spurious startup errors + * fixes bsc#1070738 + * the relevant feature (REPL history) is now built into Python itself + +------------------------------------------------------------------- +Sat Dec 2 11:11:46 UTC 2017 - dimstar@opensuse.org + +- Install 2to3-%{python_version} executable (override defattr of + the -tools package). 2to3 (unversioned) is a symlink and does not + carry permissions (bsc#1070853). + +------------------------------------------------------------------- +Thu Nov 16 11:02:18 UTC 2017 - mimi.vx@gmail.com + +- move 2to3 to python3-tools package + +------------------------------------------------------------------- +Wed Oct 11 13:15:23 UTC 2017 - jmatejek@suse.com + +- update to 3.6.3 + * bugfix release, over a hundred bugs fixed + * see Misc/NEWS for details +- drop upstreamed 0001-3.6-bpo-30714-ALPN-changes-for-OpenSSL-1.1.0f-3093.patch + +------------------------------------------------------------------- +Wed Sep 20 09:54:05 UTC 2017 - dmueller@suse.com + +- drop python-2.7-libffi-aarch64.patch: this patches the intree + copy of libffi which is unused/deleted in the line afterwards +- fix build against system libffi: include flags weren't set + so it actually used the in-tree libffi headers. + +------------------------------------------------------------------- +Thu Sep 14 13:23:10 UTC 2017 - vcizek@suse.com + +- Fix test broken with OpenSSL 1.1 (bsc#1042670) + * add 0001-3.6-bpo-30714-ALPN-changes-for-OpenSSL-1.1.0f-3093.patch + +------------------------------------------------------------------- +Tue Sep 5 11:47:05 UTC 2017 - jengelh@inai.de + +- Update RPM group for python documentation. + +------------------------------------------------------------------- +Thu Aug 31 08:39:31 UTC 2017 - schwab@suse.de + +- fix missing %{?armsuffix} + +------------------------------------------------------------------- +Wed Aug 30 13:41:38 UTC 2017 - jmatejek@suse.com + +- distutils-reproducible-compile.patch: ensure distutils order files + before compiling, which works around bsc#1049186 + +------------------------------------------------------------------- +Thu Aug 17 08:59:05 CEST 2017 - kukuk@suse.de + +- Add libnsl-devel build requires for glibc obsoleting libnsl + +------------------------------------------------------------------- +Thu Aug 3 16:09:26 UTC 2017 - jmatejek@suse.com + +- update to 3.6.2 + * bugfix release, over a hundred bugs fixed + * see Misc/NEWS for details +- drop upstreamed test-socket-aead-kernel49.patch +- add Provides: python3-typing (fixes bsc#1050653) +- drop duplicate Provides: python3 + +------------------------------------------------------------------- +Mon Jun 26 12:10:07 UTC 2017 - jmatejek@suse.com + +- drop db-devel from requirements + +------------------------------------------------------------------- +Tue Jun 20 09:26:52 UTC 2017 - asn@cryptomilk.org + +- Add missing link to python library in config dir (bsc#1040164) + +------------------------------------------------------------------- +Thu Mar 23 12:42:59 UTC 2017 - jmatejek@suse.com + +- update to 3.6.1 + * bugfix release, over a hundred bugs fixed + * never add import location's parent directory to sys.path + * switch to git for version control, build changes related to that + * fix "failed to get random numbers" on old kernels (bsc#1029902) + * several crashes and memory leaks corrected + * f-string are no longer accepted as docstrings + +------------------------------------------------------------------- +Mon Mar 13 14:04:22 UTC 2017 - jmatejek@suse.com + +- prevent regenerating AST at build-time more robustly +- add "--without profileopt" and "--without testsuite" options to python3-base + to allow short circuiting when working on the package + +------------------------------------------------------------------- +Sat Feb 25 20:55:57 UTC 2017 - bwiedemann@suse.com + +- Add 0001-allow-for-reproducible-builds-of-python-packages.patch + upstream https://github.com/python/cpython/pull/296 + +------------------------------------------------------------------- +Wed Feb 8 12:30:20 UTC 2017 - jmatejek@suse.com + +- reenable test_socket with AEAD patch (test-socket-aead-kernel49.patch) +- reintroduce %py3_soflags macro (and better named %cpython3_soabi equivalent) + +------------------------------------------------------------------- +Wed Jan 11 14:57:07 UTC 2017 - jmatejek@suse.com + +- update to 3.6.0 + * PEP 498 Formated string literals + * PEP 515 Underscores in numeric literals + * PEP 526 Syntax for variable annotations + * PEP 525 Asynchronous generators + * PEP 530 Asynchronous comprehensions + * PEP 506 New "secrets" module for safe key generation + * less memory consumed by dicts + * dtrace and systemtap support + * improved asyncio module + * better defaults for ssl + * new hashing algorithms in hashlib + * bytecode format changed to allow more optimizations + * "async" and "await" are on track to be reserved words + * StopIteration from generators is deprecated + * support for openssl < 1.0.2 is deprecated + * os.urandom now blocks when getrandom() blocks + * huge number of new features, bugfixes and optimizations + * see https://docs.python.org/3.6/whatsnew/3.6.html for details +- rework multilib patch: drop Python-3.5.0-multilib.patch, implement + upstreamable python-3.6.0-multilib-new.patch +- refresh python-3.3.0b1-localpath.patch, subprocess-raise-timeout.patch +- drop upstreamed Python-3.5.1-fix_lru_cache_copying.patch +- finally drop python-2.6b1-canonicalize2.patch that was not applied in source + and only kept around in case we needed it in the future. (which we don't, as it seems) +- update import_failed map and baselibs +- build ctypes against system libffi + (buildrequire libffi-devel in python3-base) +- add new key to keyring (signed by keys already in keyring) +- introduced common configure section between python3 and python3-base +- moved pyconfig.h and Makefile to devel subpackage as distutils no longer + need it at runtime +- added python-rpm-macros dependency, regenerated macros file, drop macros.python3.py + because it is not used now +- improve summaries and descriptions (fixes bsc#917607) +- enabled Link-Time Optimization, see what happens +- including skipped_tests.py in pre_checkin.sh run +- run specs through spec-cleaner, rearrange sections + +------------------------------------------------------------------- +Fri Apr 22 17:20:29 UTC 2016 - jmatejek@suse.com + +- move _hashlib and _ssl modules and tests to python3-base +- recommend python3 + +------------------------------------------------------------------- +Tue Mar 15 15:05:23 UTC 2016 - schwab@suse.de + +- Skip test_asyncio under qemu_user_space_build + +------------------------------------------------------------------- +Mon Mar 7 20:38:11 UTC 2016 - toddrme2178@gmail.com + +- Add Python-3.5.1-fix_lru_cache_copying.patch + Fix copying the lru_cache() wrapper object. + Fixes deep-copying lru_cache regression, which worked on + previous versions of python but fails on python 3.5. + This fixes a bunch of packages in devel:languages:python3. + See: https://bugs.python.org/issue25447 + +------------------------------------------------------------------- +Sun Jan 24 00:44:08 UTC 2016 - arichardson.kde@gmail.com + +- Build the docs in .qch format as well + +------------------------------------------------------------------- +Wed Dec 9 07:35:20 UTC 2015 - toddrme2178@gmail.com + +- update to 3.5.1 + * bugfix-only release, dozens of bugs fixed +- Drop upstreamed Python-3.5.0-_Py_atomic_xxx-symbols.patch +- "Python3" to "Python 3" in summary + * This seems cleaner and fixes and rpmlint warning + +------------------------------------------------------------------- +Wed Oct 14 20:21:52 UTC 2015 - toddrme2178@gmail.com + +- Add Python-3.5.0-_Py_atomic_xxx-symbols.patch + This fixes a build error for many packages that use the Python, + C-API. + This patch is already accepted upstream and is slated to appear in + python 3.5.1. + +------------------------------------------------------------------- +Tue Sep 29 15:53:24 UTC 2015 - jmatejek@suse.com + +- update to 3.5.0 + * coroutines with async/await syntax + * matrix multiplication operator `@` + * unpacking generalizations + * new modules `typing` and `zipapp` + * type annotations + * .pyo files replaced by custom suffixes for optimization levels in __pycache__ + * support for memory BIO in ssl module + * performance improvements in several modules + * and many more +- removals and behavior changes + * deprecated `__version__` is removed + * support for .pyo files was removed + * system calls are auto-retried on EINTR + * bare generator expressions in function calls now cause SyntaxError + (change "f(x for x in i)" to "f((x for x in i))" to fix) + * removed undocumented `format` member of private `PyMemoryViewObject` struct + * renamed `PyMemAllocator` to `PyMemAllocatorEx` +- redefine %dynlib macro to reflect that modules now have arch+os as part of name +- module `time` is now built-in +- dropped upstreamed patches: + python-3.4.1-fix-faulthandler.patch + python-3.4.3-test-conditional-ssl.patch + python-fix-short-dh.patch (also dropped dh2048.pem required for this patch) +- updated patch Python-3.3.0b2-multilib.patch to Python-3.5.0-multilib.patch +- python-ncurses-6.0-accessors.patch taken from python 2 to fix build failure + with new gcc + ncurses + +------------------------------------------------------------------- +Wed Sep 9 11:51:22 UTC 2015 - dimstar@opensuse.org + +- Add python3-ncurses-6.0-accessors.patch: Fix build with + NCurses 6.0 and OPAQUE_WINDOW set to 1. + +------------------------------------------------------------------- +Mon Aug 24 17:02:08 UTC 2015 - jmatejek@suse.com + +- improve import_failed hook to do the right thing when invoking + missing modules with "python3 -m modulename" (boo#942751) + +------------------------------------------------------------------- +Thu Jul 23 22:08:10 UTC 2015 - fisiu@opensuse.org + +- Build with --enable-loadable-sqlite-extensions to make it works + as geospatial database. + +------------------------------------------------------------------- +Wed Jul 1 07:07:26 UTC 2015 - dimstar@opensuse.org + +- Fix source list for previous change (add dh2048.pem). + +------------------------------------------------------------------- +Wed Jun 24 06:54:30 UTC 2015 - meissner@suse.com + +- dh2048.pem: added generated 2048 dh parameter set to fix + ssl test (bsc#935856) +- python-fix-short-dh.patch: replace the 512 bits dh parameter set + by 2048 bits to fix build with new openssl 1.0.2c (bsc#935856) + +------------------------------------------------------------------- +Tue May 19 14:59:30 UTC 2015 - schwab@suse.de + +- ctypes-libffi-aarch64.patch: remove upstreamed patch +- python-2.7-libffi-aarch64.patch: Fix argument passing in libffi for + aarch64 + +------------------------------------------------------------------- +Thu May 14 10:58:36 UTC 2015 - jmatejek@suse.com + +- drop the PDF subpackage + (removes the massive texlive dependency, and most likely nobody is + using the PDFs anyway) + +------------------------------------------------------------------- +Thu May 14 09:53:29 UTC 2015 - jmatejek@suse.com + +- python-3.4.3-test-conditional-ssl.patch - restore tests failing because + test_urllib was unconditionally importing ssl (without really needing it) +- restore functionality of multilib patch +- drop libffi-ppc64le.diff because upstream completely changed everything + yet again (sorry ppc64 folks :| ) + + +------------------------------------------------------------------- +Fri May 1 15:11:21 UTC 2015 - mailaender@opensuse.org + +- Update to version 3.4.3 +- Drop upstreamed CVE-2014-4650-CGIHTTPServer-traversal.patch + (bpo#21766) + +------------------------------------------------------------------- +Wed Mar 25 10:57:28 UTC 2015 - rguenther@suse.com + +- Add python-3.4.1-fix-faulthandler.patch, upstream patch for bogus + faulthandler which fails with GCC 5. + +------------------------------------------------------------------- +Sun Jan 11 13:01:30 UTC 2015 - p.drouand@gmail.com + +- asyncio has been merged in python3 main package; provide and + obsolete it +- Remove obsolete AUTHORS section +- Remove redundant %clean section + +------------------------------------------------------------------- +Sat Oct 18 20:14:54 UTC 2014 - crrodriguez@opensuse.org + +- Only pkgconfig(x11) is required for build, not the whole + set of packages provided by xorg-x11-devel metapackage. + +------------------------------------------------------------------- +Mon Oct 13 13:38:20 UTC 2014 - jmatejek@suse.com + +- add %python3_version rpm macro for Fedora compatibility +- add missing argument in import_failed, rename Novell Bugzilla + to SUSE Bugzilla + +------------------------------------------------------------------- +Thu Jul 31 17:24:59 UTC 2014 - dimstar@opensuse.org + +- Rename rpmlintrc to %{name}-rpmlintrc. + Follow the packaging guidelines. + +------------------------------------------------------------------- +Wed Jul 23 16:31:02 UTC 2014 - jmatejek@suse.com + +- CVE-2014-4650-CGIHTTPServer-traversal.patch: CGIHTTPServer file + disclosure and directory traversal through URL-encoded characters + (CVE-2014-4650, bnc#885882) + +------------------------------------------------------------------- +Tue Jul 22 13:55:57 UTC 2014 - jmatejek@suse.com + +- drop python-3.4.1-SUSE-ensurepip.patch for compatibility reasons, + reinstate bundled copies of pip and setuptools + (fixes bnc#885662) +- add more files as sources to silence the validator + +------------------------------------------------------------------- +Wed May 21 11:01:56 UTC 2014 - jmatejek@suse.com + +- update to 3.4.1 + * bugfix-only release, over 300 bugs fixed +- drop upstreamed python-3.4.0rc2-sqlite-3.8.4-tests.patch +- drop upstreamed CVE-2014-2667-mkdir.patch +- include Python release manager keyring and signature file + for the source archive (thus renumbering of source files) + (see https://www.python.org/download/#openpgp-public-keys ) +- move ensurepip to python3, because it transitively requires ssl + +------------------------------------------------------------------- +Fri Apr 4 16:21:40 UTC 2014 - jmatejek@suse.com + +- CVE-2014-2667-mkdir.patch: race condition with reseting umask + in os.makedirs + (CVE-2014-2667, bnc#871152) +- updated multilib patch to include ~/.local/lib64 (bnc#637176) + +------------------------------------------------------------------- +Wed Mar 26 15:24:46 UTC 2014 - jmatejek@suse.com + +- raise timeout value for test_subprocess to 10s (might fix + intermittent build failures in OBS) + +------------------------------------------------------------------- +Mon Mar 24 17:29:31 UTC 2014 - dmueller@suse.com + +- remove blacklisting of test_posix on aarch64: qemu bug is fixed + +------------------------------------------------------------------- +Mon Mar 17 18:26:58 UTC 2014 - jmatejek@suse.com + +- update to 3.4.0 final +- drop upstreamed python-3.4rc2-importlib.patch + +------------------------------------------------------------------- +Sun Mar 16 16:33:25 UTC 2014 - schwab@suse.de + +- Only build with profile-opt if profiling is enabled +- Update test exclusion lists: + * test_ctypes no longer fails on arm + * test_io no longer fails on ppc* + * test_multiprocessing has been split in multiple tests + * test_posix and test_signal fail due to qemu bugs + +------------------------------------------------------------------- +Fri Mar 14 20:26:03 UTC 2014 - andreas.stieger@gmx.de + +- Fix build with SQLite 3.8.4 [bnc#867887], fixing SQLite tests, + adding python-2.7.6-sqlite-3.8.4-tests.patch + +------------------------------------------------------------------- +Thu Feb 27 14:08:40 UTC 2014 - jmatejek@suse.com + +- update to 3.4.0 rc2 + * pre-release bugfixes + * improvements to asyncio library +- drop upstreamed tracemalloc_gcov.patch +- python-3.4rc2-importlib.patch fixes backwards-incompatibility + in the reworked importlib module that blocks build of vim + +------------------------------------------------------------------- +Fri Jan 17 18:45:27 UTC 2014 - jmatejek@suse.com + +- initial commit of 3.4.0 beta 3 + * new stdlib modules: pathlib, enum, statistics, tracemalloc + * asynchronous IO with new asyncio module + * introspection data for builtins + * subprocesses no longer inherit open file descriptors + * standardized metadata for packages + * internal hashing changed to SipHash + * new pickle protocol + * improved handling of codecs + * TLS 1.2 support + * major speed improvements for internal unicode handling + * many bugfixes and optimizations +- see porting guide at: + http://docs.python.org/3.4/whatsnew/3.4.html#porting-to-python-3-4 +- moved several modules to -testsuite subpackage +- updated list of binary extensions, refreshed patches +- tracemalloc_gcov.patch fixes profile-based optimization build +- updated packages and pre_checkin.sh to use ~-version notation + for prereleases +- fix-shebangs part of build process moved to common %prep +- drop python-3.3.2-no-REUSEPORT.patch (upstreamed) +- update baselibs for new soname + +- TODOs: + * require python-pip, make ensurepip work with zypper + +------------------------------------------------------------------- +Wed Dec 4 13:21:26 UTC 2013 - matz@suse.de + +- add ppc64le (ELFv2) support for libffi copy for ctypes module +- Adjust Python-3.3.0b2-multilib.patch for ppc64le (make sys.lib be + "lib64"). +- added patches: + * libffi-ppc64le.diff +------------------------------------------------------------------- +Tue Dec 3 09:51:43 UTC 2013 - adrian@suse.de + +- add ppc64le rules + +------------------------------------------------------------------- +Fri Nov 22 13:17:23 UTC 2013 - speilicke@suse.com + +- Add python-3.3.3-skip-distutils-test_sysconfig_module.patch: + + Disable global and distutils sysconfig comparison test, we deviate + from the default depending on optflags + +------------------------------------------------------------------- +Tue Nov 19 14:28:41 UTC 2013 - jmatejek@suse.com + +- update to 3.3.3 + * bugfix-only release + * many SSL-related fixes + * upstream fix for CVE-2013-4238 + * upstream fixes for CVE-2013-1752 +- move example module xxlimited to python3-testsuite +- drop CVE-2013-4238_py33.patch - it is upstreamed +- remove --with-wide-unicode config option, it is now the default + (and only) choice +- don't touch anything between make and makeinstall +- drop python-3.2b2-buildtime-generate.patch - the issue was caused + by touching things between make and makeinstall +- link pycache entries for import_failed hooks properly + +------------------------------------------------------------------- +Tue Oct 15 17:44:08 UTC 2013 - crrodriguez@opensuse.org + +- build with -DOPENSSL_LOAD_CONF for the same reasons + described in the python2 package. + +------------------------------------------------------------------- +Fri Aug 16 11:35:15 UTC 2013 - jmatejek@suse.com + +- handle NULL bytes in certain fields of SSL certificates + (CVE-2013-4238, bnc#834601) + +------------------------------------------------------------------- +Thu Aug 8 14:54:49 UTC 2013 - dvaleev@suse.com + +- Exclue test_faulthandler from tests on powerpc due to bnc#831629 + +------------------------------------------------------------------- +Thu Jun 13 15:05:34 UTC 2013 - jmatejek@suse.com + +- update to 3.3.2 + * bugfix-only release + * fixes several regressions introduced in 3.3.1 +- switch to xz compression +- move _lzma module to python3-base +- python-3.3.2-no-REUSEPORT.patch to fix build on kernels without SO_REUSEPORT + +------------------------------------------------------------------- +Mon Apr 29 22:32:43 UTC 2013 - schwab@suse.de + +- Readd missing bits from ctypes-libffi-aarch64.patch + +------------------------------------------------------------------- +Sat Apr 13 07:56:51 UTC 2013 - idonmez@suse.com + +- Update to version 3.3.1 + * Fix the –enable-profiling configure switch. + * In IDLE, close the replace dialog after it is used. +- Too many bugfixes to list here, + see See http://hg.python.org/cpython/file/v3.3.0/Misc/NEWS +- Refresh Python-3.3.0b2-multilib.patch +- Refresh python-3.2b2-buildtime-generate.patch +- Drop upstream patches: ctypes-libffi-aarch64.patch, + python-3.2.3rc2-pypirc-secure.patch, python-3.3.0-getdents64.patch + +------------------------------------------------------------------- +Mon Apr 8 11:25:30 UTC 2013 - speilicke@suse.com + +- Exclude sqlite/test and tk/test directories from the respective + sub-packages. These are owned by the testsuite sub-package already + +------------------------------------------------------------------- +Fri Apr 5 12:59:20 UTC 2013 - idonmez@suse.com + +- Add Source URL, see https://en.opensuse.org/title=SourceUrls + +------------------------------------------------------------------- +Wed Apr 3 15:36:04 UTC 2013 - jmatejek@suse.com + +- remove spurious modification of python-3.3.0b1-localpath.patch + that would force installation into /usr/local. + this fixes bnc#809831 + +------------------------------------------------------------------- +Thu Mar 28 18:38:51 UTC 2013 - jmatejek@suse.com + +- replace broken movetogetdents64.diff patch with a correct one + from upstream repo (python-3.3.0-getdents64.patch) + +------------------------------------------------------------------- +Fri Mar 1 07:42:21 UTC 2013 - dmueller@suse.com + +- add ctypes-libffi-aarch64.patch: + * import aarch64 support for libffi in _ctypes module +- add aarch64 to the list of lib64 based archs +- add movetogetdents64.diff: + * port to getdents64, as SYS_getdents is not implemented everywhere + +------------------------------------------------------------------- +Tue Feb 26 08:57:55 UTC 2013 - saschpe@suse.de + +- /etc/rpm/macros.python3 is no %config, it is not meant to be changed + by users. +- Add rpmlintrc with some obvious filters + +------------------------------------------------------------------- +Mon Jan 28 18:14:39 UTC 2013 - jmatejek@suse.com + +- update baselibs for new version of libpython3 + +------------------------------------------------------------------- +Thu Nov 29 17:02:37 UTC 2012 - jmatejek@suse.com + +- fix include path in macros (bnc#787526) +- implement failed import handlers for modules that live in + subpackages - e.g. "import ssl" will now throw a sensible error + message telling you to install "python3" + +------------------------------------------------------------------- +Wed Nov 28 17:02:07 UTC 2012 - jmatejek@suse.com + +- merge python3-xml into python3 +- merge python3-2to3 library into python3-base + and the 2to3 binary into python3-devel + (python3-devel is now in conflict with python-2to3, which + will be dropped) +- enable --with-system-expat for python3, making the xml modules + (and thus python3) depend on expat +- reconfigure tests to disable network and GUI resources, which + the upstream apparently thought is a good idea to enable by default. + this fixes build failures in Factory +- add lzma-devel to build the _lzma module +- moved %dynlib macro definition to common section + +------------------------------------------------------------------- +Mon Nov 5 20:01:46 UTC 2012 - coolo@suse.com + +- buildrequire timezone for the test suite + +------------------------------------------------------------------- +Mon Oct 29 18:21:45 UTC 2012 - dmueller@suse.com + +- disable more checks for qemu builds as they use syscalls not + implemented yet + +------------------------------------------------------------------- +Thu Oct 25 08:14:36 UTC 2012 - Rene.vanPaassen@gmail.com + +- exclude test_math for SLE 11; math library fails on negative + gamma function values close to integers and 0, probably + due to imprecision in -lm on SLE_11_SP2. + +------------------------------------------------------------------- +Tue Oct 16 12:15:34 UTC 2012 - coolo@suse.com + +- buildrequire libbz2-devel explicitly + +------------------------------------------------------------------- +Mon Oct 8 14:33:08 UTC 2012 - jmatejek@suse.com + +- remove distutils.cfg (bnc#658604) + * this changes default prefix for distutils to /usr + * see ML for details: +http://lists.opensuse.org/opensuse-packaging/2012-09/msg00254.html + +------------------------------------------------------------------- +Mon Oct 1 08:53:03 UTC 2012 - idonmez@suse.com + +- Update to final 3.3.0 release + * See http://hg.python.org/cpython/file/v3.3.0/Misc/NEWS + +------------------------------------------------------------------- +Thu Sep 27 12:35:01 UTC 2012 - idonmez@suse.com + +- Correct dependency for python3-testsuite, + python3-tkinter -> python3-tk + +------------------------------------------------------------------- +Thu Aug 23 13:08:11 UTC 2012 - jmatejek@suse.com + +- update to 3.3.0 RC1 + +------------------------------------------------------------------- +Fri Aug 3 12:09:34 UTC 2012 - jmatejek@suse.com + +- update to 3.3.0 beta 1 + * flexible string representation, no longer distinguishing + between wide and narrow Unicode builds + * importlib-based import system + * virtualenv support in core + * namespace packages + * explicit Unicode literals for easier porting + * key-sharing dict implementation reduces memory footprint + of OO code + * hash randomization on by default + * many other new bugfixes and features, check NEWS for details + +- pre_checkin.sh now autofills various version strings in specs +- ship hashlib's fallback modules - those uselessly take up space + when real _hashlib.so from python3 is present, but the space wasted + is only 114kB and it provides python3-base with a working hashlib + module. + (also, this fixes bnc#743787) + +------------------------------------------------------------------- +Fri Jul 27 09:02:41 UTC 2012 - dvaleev@suse.com + +- skip test_io on ppc +- drop test_io ppc patch + +------------------------------------------------------------------- +Thu Jun 28 07:57:58 UTC 2012 - saschpe@suse.de + +- Satisfy source_validator by uncommenting an otherwise unused "Patch" + line + +------------------------------------------------------------------- +Tue Jun 12 15:39:08 UTC 2012 - adrian@suse.de + +- fix logic of checks exclusion + +------------------------------------------------------------------- +Fri May 18 11:50:27 UTC 2012 - idonmez@suse.com + +- update to 3.2.3 + * No changes since rc2 + +------------------------------------------------------------------- +Thu Mar 29 15:44:33 UTC 2012 - jmatejek@suse.com + +- update to 3.2.3rc2 + * fixes several security issues: + * CVE-2012-0845, bnc#747125 + * CVE-2012-1150, bnc#751718 + * CVE-2011-4944, bnc#754447 + * CVE-2011-3389, bnc#754677 +- fix for insecure .pypirc (CVE-2011-4944, bnc#754447) +- disable test_gdb because it is broken by our gdb + +------------------------------------------------------------------- +Thu Feb 16 12:33:12 UTC 2012 - dvaleev@suse.com + +- skip broken test_io test on ppc + +------------------------------------------------------------------- +Wed Jan 18 15:49:47 UTC 2012 - jmatejek@suse.com + +- update to 3.2.2 + * bugfix-only release + * reports "linux2" as sys.platform regardless of Linux kernel +- added pre_checkin.sh to copy common spec sections to python3.spec +- added PACKAGING-NOTES with some helpful info for packagers + +------------------------------------------------------------------- +Sun Dec 25 13:25:01 UTC 2011 - idonmez@suse.com + +- Use system ffi, included one is broken see + http://bugs.python.org/issue11729 and + http://bugs.python.org/issue12081 + +------------------------------------------------------------------- +Fri Dec 9 17:19:55 UTC 2011 - jmatejek@suse.com + +- license.opensuse.org-compatible license headers + +------------------------------------------------------------------- +Fri Dec 2 16:46:44 UTC 2011 - coolo@suse.com + +- add automake as buildrequire to avoid implicit dependency + +------------------------------------------------------------------- +Thu Nov 24 12:42:25 UTC 2011 - agraf@suse.com + +- fix ARM build (exclude some test cases which break for us) + +------------------------------------------------------------------- +Tue Aug 16 17:02:22 UTC 2011 - termim@gmail.com + +- use sysconfig module to get py3_incdir, py3_abiflags, + py3_soflags, python3_sitelib and python3_sitearch + +------------------------------------------------------------------- +Mon Jul 18 16:22:31 UTC 2011 - jmatejek@novell.com + +- update to 3.2.1 + * bugfix-only release, no major changes +- fix build on linux3 platform +- remove upstreamed pybench patch +- install /usr/lib directories in all cases to prevent spurious + "directory not owned" in dependent packages + +------------------------------------------------------------------- +Wed Jun 15 14:16:38 UTC 2011 - jmatejek@novell.com + +- replaced dynamic so version with manual so version, because + autobuild does not support autogeneration + +------------------------------------------------------------------- +Tue May 24 13:39:06 UTC 2011 - jmatejek@novell.com + +- generate macros.python3 at compile-time with fixed values +- don't include bogus values in pyconfig.h, as they can break + third-party packages (bnc#673071) + +------------------------------------------------------------------- +Tue May 17 12:52:51 UTC 2011 - jmatejek@novell.com + +- added Obsoletes: python3 < 3.1 so that the transition from + non-split to split packages goes smoothly + +------------------------------------------------------------------- +Fri May 13 12:38:19 UTC 2011 - jmatejek@novell.com + +- fixed RPM macros to use python3 instead of python +- updated to build --with-wide-unicode (for compatibility with + fedora and our own python 2.x series) + +------------------------------------------------------------------- +Thu Apr 21 03:39:25 UTC 2011 - termim@gmail.com + +- fix python3-base build failure due to pybench.py crash by + python-3.2-pybench.patch +- move pyconfig.h from python3-devel to python3-base package to + make python3-base functional again + +------------------------------------------------------------------- +Wed Mar 23 04:26:28 UTC 2011 - termim@gmail.com + +- update to python 3.2 + * stable ABI, ABI-tagged .so files + * concurrent.futures and many other new or upgraded modules + * PYC repository directories ( __pycache__ ) + * python WSGI 1.0.1 + * Unicode 6.0.0 support + * a great number of bugfixes and assorted improvements + +------------------------------------------------------------------- +Tue Feb 8 19:42:17 CET 2011 - matejcik@suse.cz + +- update to python 3.2 RC2 +- renamed python3-demo to python3-tools, because the demo part + became much smaller than the tools part +- added rpm macros + +------------------------------------------------------------------- +Tue Jan 18 14:13:04 UTC 2011 - jmatejek@novell.com + +- update to python 3.2 beta 2, see NEWS for details +- split off -base package with less dependencies, and a shlib-policy + compliant libpython3 package +- mostly rewritten the spec file with more detailed comments +- cleaned up lists of patches + diff --git a/python310.spec b/python310.spec new file mode 100644 index 0000000..7f7da63 --- /dev/null +++ b/python310.spec @@ -0,0 +1,1022 @@ +# +# spec file +# +# Copyright (c) 2022 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + + +%global flavor @BUILD_FLAVOR@%{nil} +%if "%{flavor}" == "doc" +%define psuffix -documentation +%bcond_without doc +%bcond_with base +%bcond_with general +%endif +%if "%{flavor}" == "base" +%define psuffix -core +%bcond_with doc +%bcond_without base +%bcond_with general +%endif +%if "%{flavor}" == "" +%define psuffix %{nil} +%bcond_with doc +%bcond_with base +%bcond_without general +%endif + +%if 0%{?sle_version} && 0%{?suse_version} < 1550 +# Obsoleting previous "latest" Python versions +# Next versions will get more lines like for older versions +%define obsolete_python_versioned() \ +Obsoletes: python39%{?1:-%{1}} +%else +%define obsolete_python_versioned() %{nil} +%endif + +# Setting up variables +%define _version %(c=%{version}; echo ${c/[a-z]*/}) +%define tar_suffix %(c=%{_version}; echo ${c#%{_version}}) +%define python_version %(echo %{_version}|cut -d. -f1-2) +# based on the current source tarball +%define python_version_abitag %(c=%{python_version}; echo ${c//./}) +# FIXME %%define python_version_soname %%(c=%%{python_version}; echo ${c//./_}) +%define python_version_soname 3_10 +%if 0%(test -n "%{tar_suffix}" && echo 1) +%define _version %(echo "%{_version}~%{tar_suffix}") +%define tarversion %{version} +%else +%define tarversion %{version} +%endif +%define python_pkg_name python310 +# Will provide the python3-* provides +# Will do the /usr/bin/python3 and all the core links +%if 0%{?sle_version} || 0%{?suse_version} < 1550 +%define primary_interpreter 0 +%else +%define primary_interpreter 1 +%endif +# We don't process beta signs well +%define folderversion 3.10.5 +%define tarname Python-%{tarversion} +%define sitedir %{_libdir}/python%{python_version} +# three possible ABI kinds: m - pymalloc, d - debug build; see PEP 3149 +%define abi_kind %{nil} +# python ABI version - used in some file names +%define python_abi %{python_version}%{abi_kind} +# soname ABI tag defined in PEP 3149 +%define abi_tag %{python_version_abitag}%{abi_kind} +# version part of "libpython" package +%define so_major 1 +%define so_minor 0 +%define so_version %{python_version_soname}%{abi_kind}-%{so_major}_%{so_minor} +# rpm and python have different ideas about what is an arch-dependent name, so: +%if "%{__isa_name}" == "ppc" +%define archname %(echo %{_arch} | sed s/ppc/powerpc/) +%else +%define archname %{_arch} +%endif +# our arm has Hardware-Floatingpoint +%if "%{_arch}" == "arm" +%define armsuffix hf +%endif +# Decide whether we want to use mpdecimal +%if 0%{?suse_version} >= 1550 +%bcond_without mpdecimal +%else +%bcond_with mpdecimal +%endif +# pyexpat.cpython-35m-x86_64-linux-gnu +# pyexpat.cpython-35m-powerpc64le-linux-gnu +# pyexpat.cpython-35m-armv7-linux-gnueabihf +# _md5.cpython-38m-x86_64-linux-gnu.so +%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so +%bcond_without profileopt +Name: %{python_pkg_name}%{psuffix} +Version: 3.10.5 +Release: 0 +Summary: Python 3 Interpreter +License: Python-2.0 +URL: https://www.python.org/ +Source0: http://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz +Source1: http://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.asc +Source2: baselibs.conf +Source3: README.SUSE +Source7: macros.python3 +Source8: import_failed.py +Source9: import_failed.map +Source10: pre_checkin.sh +Source11: skipped_tests.py +Source19: idle3.desktop +Source20: idle3.appdata.xml +# content of bluez-devel: +# 1. sudo zypper --pkg-cache-dir /tmp install -f -d --no-recommends bluez-devel +# 2. rpm2cpio /tmp/*/*/bluez-devel-*.rpm|cpio -idu +# 3. mkdir Vendor && mv usr/include/* Vendor/ +# 4. tar cJf bluez-devel-vendor.tar.xz Vendor/ +Source21: bluez-devel-vendor.tar.xz +Source98: python310-rpmlintrc +# Tarball is signed by the GPG key of Pablo Galindo Salgado (0x64E628F8D684696D) +# https://keybase.io/pablogsal/pgp_keys.asc?fingerprint=a035c8c19219ba821ecea86b64e628f8d684696d +Source99: python.keyring +# The following files are not used in the build. +# They are listed here to work around missing functionality in rpmbuild, +# which would otherwise exclude them from distributed src.rpm files. +Source100: PACKAGING-NOTES +# PATCH-FEATURE-UPSTREAM F00251-change-user-install-location.patch bsc#[0-9]+ mcepl@suse.com +# Fix installation in /usr/local (boo#1071941), originally from Fedora +# https://src.fedoraproject.org/rpms/python3/blob/master/f/00251-change-user-install-location.patch +# Set values of prefix and exec_prefix in distutils install command +# to /usr/local if executable is /usr/bin/python* and RPM build +# is not detected to make pip and distutils install into separate location +Patch02: F00251-change-user-install-location.patch +# PATCH-FEATURE-UPSTREAM distutils-reproducible-compile.patch gh#python/cpython#8057 mcepl@suse.com +# Improve reproduceability +Patch06: distutils-reproducible-compile.patch +# support finding packages in /usr/local, install to /usr/local by default +Patch07: python-3.3.0b1-localpath.patch +# replace DATE, TIME and COMPILER by fixed definitions to aid reproducible builds +Patch08: python-3.3.0b1-fix_date_time_compiler.patch +# POSIX_FADV_WILLNEED throws EINVAL. Use a different constant in test +Patch09: python-3.3.0b1-test-posix_fadvise.patch +# Raise timeout value for test_subprocess +Patch15: subprocess-raise-timeout.patch +# PATCH-FEATURE-UPSTREAM bpo-31046_ensurepip_honours_prefix.patch bpo#31046 mcepl@suse.com +# ensurepip should honour the value of $(prefix) +Patch29: bpo-31046_ensurepip_honours_prefix.patch +# PATCH-FIX-SLE no-skipif-doctests.patch jsc#SLE-13738 mcepl@suse.com +# SLE-15 version of Sphinx doesn't know about skipif directive in doctests. +Patch33: no-skipif-doctests.patch +# PATCH-FIX-SLE skip-test_pyobject_freed_is_freed.patch mcepl@suse.com +# skip a test failing on SLE-15 +Patch34: skip-test_pyobject_freed_is_freed.patch +# PATCH-FIX-SLE fix_configure_rst.patch bpo#43774 mcepl@suse.com +# remove duplicate link targets and make documentation with old Sphinx in SLE +Patch35: fix_configure_rst.patch +# PATCH-FIX-UPSTREAM bpo-46811 gh#python/cpython#7da97f61816f mcepl@suse.com +# NOTE: SUSE version of expat 2.4.4 is patched in SUSE for CVE-2022-25236 +Patch36: support-expat-CVE-2022-25236-patched.patch +# PATCH-FIX-UPSTREAM CVE-2015-20107-mailcap-unsafe-filenames.patch bsc#1198511 mcepl@suse.com +# avoid the command injection in the mailcap module. +Patch37: CVE-2015-20107-mailcap-unsafe-filenames.patch +BuildRequires: autoconf-archive +BuildRequires: automake +BuildRequires: fdupes +BuildRequires: gmp-devel +BuildRequires: lzma-devel +BuildRequires: netcfg +BuildRequires: openssl-devel +BuildRequires: pkgconfig +BuildRequires: xz +BuildRequires: pkgconfig(bzip2) +BuildRequires: pkgconfig(expat) +BuildRequires: pkgconfig(libffi) +BuildRequires: pkgconfig(uuid) +BuildRequires: pkgconfig(zlib) +#!BuildIgnore: gdk-pixbuf-loader-rsvg +%if 0%{?suse_version} >= 1550 +# The provider for python(abi) is in rpm-build-python +BuildRequires: rpm-build-python +%endif +%if 0%{?suse_version} >= 1500 +BuildRequires: pkgconfig(libnsl) +BuildRequires: pkgconfig(libtirpc) +%endif +%if %{with mpdecimal} +BuildRequires: mpdecimal-devel +%endif +%if %{with doc} +%if 0%{?sle_version} && 0%{?sle_version} <= 150300 +BuildRequires: python3-Sphinx +%else +BuildRequires: python3-Sphinx >= 3.2.0 +%endif +%if 0%{?suse_version} >= 1500 +BuildRequires: python3-python-docs-theme >= 2022.1 +%endif +%endif +%if %{with general} +# required for idle3 (.desktop and .appdata.xml files) +BuildRequires: appstream-glib +BuildRequires: gcc-c++ +BuildRequires: gdbm-devel +BuildRequires: gettext +BuildRequires: readline-devel +BuildRequires: sqlite-devel +BuildRequires: timezone +BuildRequires: update-desktop-files +BuildRequires: pkgconfig(ncurses) +BuildRequires: pkgconfig(tk) +BuildRequires: pkgconfig(x11) +Requires: %{python_pkg_name}-base = %{version} +Recommends: %{python_pkg_name}-curses +Recommends: %{python_pkg_name}-dbm +Recommends: %{python_pkg_name}-pip +%obsolete_python_versioned +%if %{primary_interpreter} +Provides: python3 = %{python_version} +%endif +%endif + +%description +Python 3 is modern interpreted, object-oriented programming language, +often compared to Tcl, Perl, Scheme, or Java. You can find an overview +of Python in the documentation and tutorials included in the python3-doc +package. + +This package supplies rich command line features provided by readline, +and sqlite3 support for the interpreter core, thus forming a so called +"extended" runtime. +Installing "python3" is sufficient for the vast majority of usecases. +In addition, recommended packages provide UI toolkit support (python3-curses, +python3-tk), legacy UNIX database bindings (python3-dbm), and the IDLE +development environment (python3-idle). + +%package -n %{python_pkg_name}-tk +Summary: TkInter, a Python Tk Interface +Requires: %{python_pkg_name} = %{version} +%obsolete_python_versioned tk +%if %{primary_interpreter} +Provides: python3-tk = %{version} +%endif + +%description -n %{python_pkg_name}-tk +Python interface to Tk. Tk is the GUI toolkit that comes with Tcl. + +%package -n %{python_pkg_name}-curses +Summary: Python Interface to the (N)Curses Library +Requires: %{python_pkg_name} = %{version} +%obsolete_python_versioned curses +%if %{primary_interpreter} +Provides: python3-curses +%endif + +%description -n %{python_pkg_name}-curses +An easy to use interface to the (n)curses CUI library. CUI stands for +Console User Interface. + +%package -n %{python_pkg_name}-dbm +Summary: Python Interface to the GDBM Library +Requires: %{python_pkg_name} = %{version} +%obsolete_python_versioned dbm +%if %{primary_interpreter} +Provides: python3-dbm +%endif + +%description -n %{python_pkg_name}-dbm +An easy to use interface for Unix DBM databases, and more specifically, +the GNU implementation GDBM. + +%package -n %{python_pkg_name}-idle +Summary: An Integrated Development Environment for Python +Requires: %{python_pkg_name} = %{version} +Requires: %{python_pkg_name}-tk +%obsolete_python_versioned idle +%if %{primary_interpreter} +Provides: python3-idle = %{version} +%endif + +%description -n %{python_pkg_name}-idle +IDLE is a Tkinter based integrated development environment for Python. +It features a multi-window text editor with multiple undo, Python +colorizing, and many other things, as well as a Python shell window and +a debugger. + +%package -n %{python_pkg_name}-doc +Summary: Package Documentation for Python 3 +Enhances: %{python_pkg_name} = %{python_version} +%obsolete_python_versioned doc +%if %{primary_interpreter} +Provides: python3-doc = %{version} +%endif + +%description -n %{python_pkg_name}-doc +Tutorial, Global Module Index, Language Reference, Library Reference, +Extending and Embedding Reference, Python/C API Reference, Documenting +Python, and Macintosh Module Reference in HTML format. + +%package -n %{python_pkg_name}-doc-devhelp +Summary: Additional Package Documentation for Python 3 in devhelp format +%obsolete_python_versioned doc-devhelp +%if %{primary_interpreter} +Provides: python3-doc-devhelp = %{version} +%endif + +%description -n %{python_pkg_name}-doc-devhelp +Tutorial, Global Module Index, Language Reference, Library Reference, +Extending and Embedding Reference, Python/C API Reference, Documenting +Python, and Macintosh Module Reference in format for devhelp. + +%package -n %{python_pkg_name}-base +Summary: Python 3 Interpreter and Stdlib Core +Requires: libpython%{so_version} = %{version} +Recommends: %{python_pkg_name} = %{version} +%obsolete_python_versioned base +#Recommends: python3-ensurepip +# python 3.1 didn't have a separate python-base, so it is wrongly +# not a conflict to have python3-3.1 and python3-base > 3.1 +Obsoletes: python3 < 3.2 +# no Provides, because python3 is obviously provided by package python3 +# python 3.4 provides asyncio +Provides: %{python_pkg_name}-asyncio = %{version} +%obsolete_python_versioned asyncio +# python 3.6 provides typing +Provides: %{python_pkg_name}-typing = %{version} +%obsolete_python_versioned typing +# python3-xml was merged into python3, now moved into -base +Provides: %{python_pkg_name}-xml = %{version} +%if %{primary_interpreter} +Provides: python3-asyncio = %{version} +Obsoletes: python3-asyncio < %{version} +Provides: python3-base = %{version} +Obsoletes: python3-base < %{version} +Provides: python3-typing = %{version} +Obsoletes: python3-typing < %{version} +Provides: python3-xml = %{version} +Obsoletes: python3-xml < %{version} +%endif + +%description -n %{python_pkg_name}-base +Python is an interpreted, object-oriented programming language, and is +often compared to Tcl, Perl, Scheme, or Java. You can find an overview +of Python in the documentation and tutorials included in the python-doc +package. + +This package contains the interpreter core and most commonly used modules +from the standard library. This is sufficient for many usecases, but it +excludes components that depend on external libraries, most notably XML, +database and UI toolkits support. + +%package -n %{python_pkg_name}-tools +Summary: Python Utility and Demonstration Scripts +Requires: %{python_pkg_name}-base = %{version} +Provides: %{python_pkg_name}-2to3 = %{version} +Provides: %{python_pkg_name}-demo = %{version} +%obsolete_python_versioned tools +%if %{primary_interpreter} +Provides: python3-2to3 = %{version} +Provides: python3-demo = %{version} +Provides: python3-tools = %{version} +Obsoletes: python3-2to3 < %{version} +Obsoletes: python3-demo < %{version} +%endif + +%description -n %{python_pkg_name}-tools +A number of scripts that are useful for building, testing or extending Python, +and a set of demonstration programs. + +%package -n %{python_pkg_name}-devel +Summary: Include Files and Libraries Mandatory for Building Python Modules +Requires: %{python_pkg_name}-base = %{version} +%obsolete_python_versioned devel +%if %{primary_interpreter} +Provides: python3-devel = %{version} +%endif + +%description -n %{python_pkg_name}-devel +The Python programming language's interpreter can be extended with +dynamically loaded extensions and can be embedded in other programs. + +This package contains header files, a static library, and development +tools for building Python modules, extending the Python interpreter or +embedding Python in applications. + +This also includes the Python distutils, which were in the Python +package up to version 2.2.2. + +%package -n %{python_pkg_name}-testsuite +Summary: Unit tests for Python and its standard library +Requires: %{python_pkg_name} = %{version} +Requires: %{python_pkg_name}-tk = %{version} +%obsolete_python_versioned testsuite +%if %{primary_interpreter} +Provides: python3-testsuite = %{version} +%endif + +%description -n %{python_pkg_name}-testsuite +Unit tests that are useful for verifying integrity and functionality +of the installed Python interpreter and standard library. +They are a documented part of stdlib, as a module 'test'. + +%package -n libpython%{so_version} +Summary: Python Interpreter shared library +Requires: %{python_pkg_name}-base >= %{version} + +%description -n libpython%{so_version} +Python is an interpreted, object-oriented programming language, and is +often compared to Tcl, Perl, Scheme, or Java. You can find an overview +of Python in the documentation and tutorials included in the python-doc +(HTML) or python-doc-pdf (PDF) packages. + +This package contains libpython3.2 shared library for embedding in +other applications. + +%prep +%setup -q -n %{tarname} +%patch02 -p1 + +%patch06 -p1 +%patch07 -p1 +%patch08 -p1 +%patch09 -p1 +%patch15 -p1 +%patch29 -p1 +%if 0%{?sle_version} && 0%{?sle_version} <= 150300 +%patch33 -p1 +%patch34 -p1 +%endif +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 + +# drop Autoconf version requirement +sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac + +%if %{primary_interpreter} +# fix shebangs - convert /usr/local/bin/python and /usr/bin/env/python to /usr/bin/python3 +for dir in Lib Tools; do + # find *.py, filter to files that contain bad shebangs + # break up "/""usr" like this to prevent replacing with %%{_prefix} + find $dir -name '*.py' -type f -print0 \ + | xargs -0 grep -lE '^#! *(/''usr/.*bin/(env +)?)?python' \ + | xargs sed -r -i -e '1s@^#![[:space:]]*(/''usr/(local/)?bin/(env +)?)?python([0-9]+(\.[0-9]+)?)?@#!%{_bindir}/python3@' +done +%else +# For non-primary Python, just don't bother (bsc#1193179) and remove all +# those shebangs +for dir in Lib Tools; do + find $dir -name '*.py' -type f -exec sed -i '1{/^#!.*python/ d}' '{}' \; +done +# We shortened the file Lib/pdb.py so we have to move the test breakpoint location +sed -i -e '/Breakpoint 3 at ...pdb.py:94/s/94/93/' Lib/test/test_pdb.py +%endif + +# drop in-tree libffi and expat +rm -r Modules/_ctypes/libffi* Modules/_ctypes/darwin +rm -r Modules/expat + +# drop duplicate README from site-packages +rm Lib/site-packages/README.txt + +# Add vendored bluez-devel files +tar xvf %{SOURCE21} + +%build +%if %{with doc} +TODAY_DATE=`date -r %{SOURCE0} "+%%B %%d, %%Y"` +# TODO use not date of tarball but date of latest patch + +cd Doc +sed -i "s/^today = .*/today = '$TODAY_DATE'/" conf.py +%make_build -j1 html + +# Build also devhelp files +sphinx-build -a -b devhelp . build/devhelp +rm -rfv build/devhelp/.doctrees +%else +%define _lto_cflags %{nil} +# use rpm_opt_flags +export OPT="%{optflags} -DOPENSSL_LOAD_CONF -fwrapv $(pkg-config --cflags-only-I libffi) -fno-semantic-interposition" + +touch -r %{SOURCE0} Makefile.pre.in + +autoreconf -fvi + +%if 0%{?sles_version} +sed -e 's/-fprofile-correction//' -i Makefile.pre.in +%endif + +export CFLAGS="%{optflags} -IVendor/" + +%configure \ + --with-platlibdir=%{_lib} \ + --docdir=%{_docdir}/python \ + --enable-ipv6 \ + --enable-shared \ + --with-ensurepip=no \ + --with-system-ffi \ + --with-system-expat \ + --with-lto \ +%if %{with profileopt} + --enable-optimizations \ +%endif +%if %{with mpdecimal} + --with-system-libmpdec \ +%endif + --enable-loadable-sqlite-extensions + +# prevent make from trying to rebuild PYTHON_FOR_GEN stuff +# %%make_build -t Python/Python-ast.c \ + # Include/Python-ast.h \ + # Objects/typeslots.inc \ + # Python/opcode_targets.h \ + # Include/opcode.h +%make_build + +%if %{with general} +%make_build +%endif +%if %{with base} +%if %{with profileopt} + target=profile-opt +%else + target=all +%endif +LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH \ + %make_build $target +%endif +%endif + +%check +%if %{with general} +# exclude test_gdb -- it doesn't run in buildservice anyway, and fails on missing debuginfos +# when you install gdb into your test env +EXCLUDE="test_gdb" +# we patch out the message to recommend zypper in and thus this would fail +EXCLUDE="$EXCLUDE test_pydoc" + +%ifarch %{arm} s390x +# test_multiprocessing_forkserver is racy +EXCLUDE="$EXCLUDE test_multiprocessing_forkserver" +%endif +%ifarch ppc ppc64 ppc64le +# exclue test_faulthandler due to bnc#831629 +EXCLUDE="$EXCLUDE test_faulthandler" +%endif +# some tests break in QEMU +%if 0%{?qemu_user_space_build} +EXCLUDE="$EXCLUDE test_faulthandler test_multiprocessing_forkserver test_multiprocessing_spawn test_os test_posix test_signal test_socket test_subprocess" +%endif + +# This test (part of test_uuid) requires real network interfaces +# so that ifconfig output has "HWaddr ". Some kvm instances +# done have any such interface breaking the uuid module. +EXCLUDE="$EXCLUDE test_uuid" + +# bsc#1195140 and bpo#37169 - test_capi is failing on openSUSE, and not sure why +EXCLUDE="$EXCLUDE test_capi" + +# Limit virtual memory to avoid spurious failures +if test $(ulimit -v) = unlimited || test $(ulimit -v) -gt 10000000; then + ulimit -v 10000000 || : +fi + +export PYTHONPATH="$(pwd -P)/Lib" +# Use timeout, like make target buildbottest +# We cannot run tests parallel, because osc build environment doesn’t +# have /dev/shm +%make_build -j1 test TESTOPTS="-u curses -v -x $EXCLUDE --timeout=3000" +# use network, be verbose: +#make test TESTOPTS="-l -u network -v" +%endif + +%install +%if %{with doc} +export PDOCS=%{buildroot}%{_docdir}/python%{python_version} +mkdir -p $PDOCS +# generated docs +rm Doc/build/*/.buildinfo +cp -r Doc/build/html $PDOCS +# misc +install -d -m 755 $PDOCS/Misc +rm Misc/README.AIX +for i in Misc/* ; do + [ -f $i ] && install -c -m 644 $i $PDOCS/Misc/ +done +# devhelp +mkdir -p %{buildroot}%{_datadir}/gtk-doc/html +cp -r Doc/build/devhelp %{buildroot}%{_datadir}/gtk-doc/html/Python%{python_version} +rm -rf %{buildroot}%{_datadir}/gtk-doc/html/Python%{python_version}/.doctrees +%endif +%if %{with general} +%make_install + +# clean out stuff that is in python-base and subpackages + +find %{buildroot}%{_bindir} -mindepth 1 -not -name "*idle3*" -print -delete +rm %{buildroot}%{_libdir}/lib* +rm -r %{buildroot}%{_libdir}/pkgconfig +rm -r %{buildroot}%{_mandir}/* +rm -r %{buildroot}%{_includedir}/* + +rm -r %{buildroot}%{sitedir}/config* +find %{buildroot}%{sitedir} -name "*.egg-info" -delete +rm -r %{buildroot}%{sitedir}/__pycache__ +rm -r %{buildroot}%{sitedir}/site-packages +rm %{buildroot}%{sitedir}/*.* + +for module in \ + asyncio ctypes collections concurrent distutils email encodings \ + ensurepip html http \ + importlib json logging multiprocessing pydoc_data unittest \ + urllib venv wsgiref lib2to3 test turtledemo \ + xml xmlrpc zoneinfo +do + rm -r %{buildroot}%{sitedir}/$module +done + +for library in \ + array _asyncio audioop binascii _bisect _bz2 cmath _codecs_* \ + _contextvars _crypt _csv _ctypes _datetime _decimal fcntl grp \ + _hashlib _heapq _json _lsprof _lzma math mmap _multibytecodec \ + _multiprocessing _opcode ossaudiodev _pickle _posixshmem \ + _posixsubprocess _queue _random resource select _ssl _socket spwd \ + _statistics _struct syslog termios _testbuffer _testimportmultiple \ + _testmultiphase unicodedata zlib _ctypes_test _testinternalcapi _testcapi \ + xxlimited xxlimited_35 \ + _xxtestfuzz _xxsubinterpreters _elementtree pyexpat _md5 _sha1 \ + _sha256 _sha512 _blake2 _sha3 _uuid _zoneinfo +do + eval rm "%{buildroot}%{sitedir}/lib-dynload/$library.*" +done + +# Idle is not packaged in base due to the appstream-glib dependency +# move idle config into /etc +install -d -m 755 %{buildroot}%{_sysconfdir}/idle%{python_version} +( + cd %{buildroot}/%{sitedir}/idlelib/ + for file in *.def ; do + mv $file %{buildroot}%{_sysconfdir}/idle%{python_version}/ + ln -sf %{_sysconfdir}/idle%{python_version}/$file %{buildroot}/%{sitedir}/idlelib/ + done +) + +# keep just idle3.X +rm %{buildroot}%{_bindir}/idle3 + +# install idle icons +for size in 16 32 48 ; do + install -m 644 -D Lib/idlelib/Icons/idle_${size}.png \ + %{buildroot}%{_datadir}/icons/hicolor/${size}x${size}/apps/idle%{python_version}.png +done + +# install idle desktop file +cp %{SOURCE19} idle%{python_version}.desktop +sed -i -e 's:idle3:idle%{python_version}:g' idle%{python_version}.desktop +install -m 644 -D -t %{buildroot}%{_datadir}/applications idle%{python_version}.desktop +%suse_update_desktop_file idle%{python_version} + +cp %{SOURCE20} idle%{python_version}.appdata.xml +sed -i -e 's:idle3.desktop:idle%{python_version}.desktop:g' idle%{python_version}.appdata.xml +install -m 644 -D -t %{buildroot}%{_datadir}/metainfo idle%{python_version}.appdata.xml +appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/idle%{python_version}.appdata.xml + +%fdupes %{buildroot}/%{_libdir}/python%{python_version} +%endif +%if %{with base} +%make_install + +# remove .a +find %{buildroot} -name "*.a" -delete + +# install "site-packages" and __pycache__ for third parties +install -d -m 755 %{buildroot}%{sitedir}/site-packages +install -d -m 755 %{buildroot}%{sitedir}/site-packages/__pycache__ +# and their 32bit counterparts explicitly +mkdir -p %{buildroot}%{_prefix}/lib/python%{python_version}/site-packages/__pycache__ + +# cleanup parts that don't belong +for dir in curses dbm sqlite3 tkinter idlelib; do + find "%{buildroot}/%{sitedir}/$dir"/* -maxdepth 0 -name "test" -o -exec rm -rf {} + +done +rm -fv %{buildroot}%{dynlib nis} + +# overwrite the copied binary with a link +ln -sf python%{python_version} %{buildroot}%{_bindir}/python3 + +# decide to ship python3 or just python3.X +%if !%{primary_interpreter} +# base +rm %{buildroot}%{_bindir}/python3 +rm %{buildroot}%{_bindir}/pydoc3 +rm %{buildroot}%{_mandir}/man1/python3.1 +# devel +rm %{buildroot}%{_bindir}/python3-config +rm %{buildroot}%{_libdir}/libpython3.so +rm %{buildroot}%{_libdir}/pkgconfig/{python3,python3-embed}.pc +%endif + +# link shared library instead of static library that tools expect +ln -s ../../libpython%{python_abi}.so %{buildroot}%{_libdir}/python%{python_version}/config-%{python_abi}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}/libpython%{python_abi}.so + +# delete idle3, which has to many packaging dependencies for base +rm %{buildroot}%{_bindir}/idle3* + +# delete the generic 2to3 binary if we are not primary +%if !%{primary_interpreter} +rm %{buildroot}%{_bindir}/2to3 +%endif + +# replace duplicate .pyo/.pyc with hardlinks +%fdupes %{buildroot}/%{sitedir} + +# documentation +export PDOCS=%{buildroot}%{_docdir}/%{name} +install -d -m 755 $PDOCS +install -c -m 644 %{SOURCE3} $PDOCS/ +install -c -m 644 README.rst $PDOCS/ + +# tools +for x in `find Tools/ \( -not -name Makefile \) -print | sort` ; do + test -d $x && ( install -c -m 755 -d $PDOCS/$x ) \ + || ( install -c -m 644 $x $PDOCS/$x ) +done +# gdb script is shipped with devel subpackage +rm -r $PDOCS/Tools/gdb +# clean up the bat files +find "$PDOCS" -name "*.bat" -delete + +# put gdb helper script into place +install -m 755 -D Tools/gdb/libpython.py %{buildroot}%{_datadir}/gdb/auto-load/%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}-gdb.py + +# install devel files to /config +#cp Makefile Makefile.pre.in Makefile.pre $RPM_BUILD_ROOT%{sitedir}/config-%{python_abi}/ + +# RPM macros +%if %{primary_interpreter} +mkdir -p %{buildroot}%{_rpmconfigdir}/macros.d/ +install -m 644 %{SOURCE7} %{buildroot}%{_rpmconfigdir}/macros.d/ # macros.python3 +%endif + +# import_failed hooks +FAILDIR=%{buildroot}/%{sitedir}/_import_failed +mkdir $FAILDIR +install -m 644 %{SOURCE8} %{SOURCE9} $FAILDIR # import_failed.* +LD_LIBRARY_PATH=. ./python -c "from py_compile import compile; compile('$FAILDIR/import_failed.py', dfile='%{sitedir}/_import_failed/import_failed.py')" +LD_LIBRARY_PATH=. ./python -O -c "from py_compile import compile; compile('$FAILDIR/import_failed.py', dfile='%{sitedir}/_import_failed/import_failed.py')" +( + cd $FAILDIR + while read package modules; do + for module in $modules; do + ln import_failed.py $module.py + pushd __pycache__ + for i in import_failed*; do + ln $i "$module${i#import_failed}" + done + popd + done + done < %{SOURCE9} +) +echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-import-failed-hooks.pth +%endif + +%if %{with general} +%files -n %{python_pkg_name}-tk +%defattr(644, root, root, 755) +%{sitedir}/tkinter +%exclude %{sitedir}/tkinter/test +%{dynlib _tkinter} + +%files -n %{python_pkg_name}-curses +%defattr(644, root, root, 755) +%{sitedir}/curses +%{dynlib _curses} +%{dynlib _curses_panel} + +%files -n %{python_pkg_name}-dbm +%defattr(644, root, root, 755) +%{sitedir}/dbm +%{dynlib _dbm} +%{dynlib _gdbm} + +%files -n %{python_pkg_name} +%defattr(644, root, root, 755) +%dir %{sitedir} +%dir %{sitedir}/lib-dynload +%{sitedir}/sqlite3 +%exclude %{sitedir}/sqlite3/test +%{dynlib readline} +%{dynlib _sqlite3} +%{dynlib nis} + +%files -n %{python_pkg_name}-idle +%defattr(644, root, root, 755) +%{sitedir}/idlelib +%dir %{_sysconfdir}/idle%{python_version} +%config %{_sysconfdir}/idle%{python_version}/* +%doc Lib/idlelib/NEWS.txt +%doc Lib/idlelib/README.txt +%doc Lib/idlelib/TODO.txt +%doc Lib/idlelib/extend.txt +%doc Lib/idlelib/ChangeLog +%{_bindir}/idle%{python_version} +%{_datadir}/applications/idle%{python_version}.desktop +%{_datadir}/metainfo/idle%{python_version}.appdata.xml +%{_datadir}/icons/hicolor/*/apps/idle%{python_version}.png +%dir %{_datadir}/icons/hicolor +%dir %{_datadir}/icons/hicolor/16x16 +%dir %{_datadir}/icons/hicolor/32x32 +%dir %{_datadir}/icons/hicolor/48x48 +%dir %{_datadir}/icons/hicolor/*/apps +%attr(755, root, root) %{_bindir}/idle%{python_version} +# endif for if general +%endif + +%if %{with doc} +%files -n %{python_pkg_name}-doc +%dir %{_docdir}/python%{python_version} +%doc %{_docdir}/python%{python_version}/Misc +%doc %{_docdir}/python%{python_version}/html + +%files -n %{python_pkg_name}-doc-devhelp +%dir %{_datadir}/gtk-doc +%dir %{_datadir}/gtk-doc/html +%doc %{_datadir}/gtk-doc/html/Python%{python_version} +%endif + +%if %{with base} +%post -n libpython%{so_version} -p /sbin/ldconfig +%postun -n libpython%{so_version} -p /sbin/ldconfig + +%files -n libpython%{so_version} +%defattr(644, root,root) +%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor} + +%files -n %{python_pkg_name}-tools +%defattr(644, root, root, 755) +%{sitedir}/turtledemo +%if %{primary_interpreter} +%{_bindir}/2to3 +%endif +%attr(755, root, root)%{_bindir}/2to3-%{python_version} +%doc %{_docdir}/%{name}/Tools + +%files -n %{python_pkg_name}-devel +%defattr(644, root, root, 755) +%{_libdir}/libpython%{python_abi}.so +%if %{primary_interpreter} +%{_libdir}/libpython3.so +%endif +%{_libdir}/pkgconfig/* +%{_includedir}/python%{python_abi} +%{sitedir}/config-%{python_abi}-* +%defattr(755, root, root) +%{_bindir}/python%{python_abi}-config +%if %{primary_interpreter} +%{_bindir}/python3-config +%endif +# Own these directories to not depend on gdb +%dir %{_datadir}/gdb +%dir %{_datadir}/gdb/auto-load +%dir %{_datadir}/gdb/auto-load%{_prefix} +%dir %{_datadir}/gdb/auto-load%{_libdir} +%{_datadir}/gdb/auto-load/%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}-gdb.py + +%files -n %{python_pkg_name}-testsuite +%defattr(644, root, root, 755) +%{sitedir}/test +%{sitedir}/*/test +%{sitedir}/*/tests +%{dynlib _ctypes_test} +%{dynlib _testbuffer} +%{dynlib _testcapi} +%{dynlib _testinternalcapi} +%{dynlib _testimportmultiple} +%{dynlib _testmultiphase} +%{dynlib xxlimited} +# workaround for missing packages +%dir %{sitedir}/sqlite3 +%dir %{sitedir}/tkinter + +%files -n %{python_pkg_name}-base +%defattr(644, root, root, 755) +# docs +%dir %{_docdir}/%{name} +%doc %{_docdir}/%{name}/README.rst +%license LICENSE +%doc %{_docdir}/%{name}/README.SUSE +%if %{primary_interpreter} +%{_mandir}/man1/python3.1%{?ext_man} +%endif +%{_mandir}/man1/python%{python_version}.1%{?ext_man} +# license text, not a doc because the code can use it at run-time +%{sitedir}/LICENSE.txt +# RPM macros +%if %{primary_interpreter} +%{_rpmconfigdir}/macros.d/macros.python3 +%endif +# binary parts +%dir %{sitedir}/lib-dynload +%{dynlib array} +%{dynlib _asyncio} +%{dynlib audioop} +%{dynlib binascii} +%{dynlib _bisect} +%{dynlib _bz2} +%{dynlib cmath} +%{dynlib _codecs_cn} +%{dynlib _codecs_hk} +%{dynlib _codecs_iso2022} +%{dynlib _codecs_jp} +%{dynlib _codecs_kr} +%{dynlib _codecs_tw} +%{dynlib _contextvars} +%{dynlib _crypt} +%{dynlib _csv} +%{dynlib _ctypes} +%{dynlib _datetime} +%{dynlib _decimal} +%{dynlib _elementtree} +%{dynlib fcntl} +%{dynlib grp} +%{dynlib _hashlib} +%{dynlib _heapq} +%{dynlib _json} +%{dynlib _lsprof} +%{dynlib _lzma} +%{dynlib math} +%{dynlib mmap} +%{dynlib _multibytecodec} +%{dynlib _multiprocessing} +%{dynlib _opcode} +%{dynlib ossaudiodev} +%{dynlib _pickle} +%{dynlib _posixshmem} +%{dynlib _posixsubprocess} +%{dynlib pyexpat} +%{dynlib _queue} +%{dynlib _random} +%{dynlib resource} +%{dynlib select} +%{dynlib _socket} +%{dynlib spwd} +%{dynlib _ssl} +%{dynlib _statistics} +%{dynlib _struct} +%{dynlib syslog} +%{dynlib termios} +%{dynlib unicodedata} +%{dynlib _uuid} +%{dynlib _xxsubinterpreters} +%{dynlib _xxtestfuzz} +%{dynlib xxlimited_35} +%{dynlib zlib} +%{dynlib _zoneinfo} +# hashlib fallback modules +%{dynlib _blake2} +%{dynlib _md5} +%{dynlib _sha1} +%{dynlib _sha256} +%{dynlib _sha512} +%{dynlib _sha3} +# python parts +%dir %{_prefix}/lib/python%{python_version} +%dir %{_prefix}/lib/python%{python_version}/site-packages +%dir %{_prefix}/lib/python%{python_version}/site-packages/__pycache__ +%dir %{sitedir} +%dir %{sitedir}/site-packages +%dir %{sitedir}/site-packages/__pycache__ +%exclude %{sitedir}/*/test +%exclude %{sitedir}/*/tests +%{sitedir}/*.py +%{sitedir}/asyncio +%{sitedir}/ctypes +%{sitedir}/collections +%{sitedir}/concurrent +%{sitedir}/distutils +%{sitedir}/email +%{sitedir}/encodings +%{sitedir}/ensurepip +%{sitedir}/html +%{sitedir}/http +%{sitedir}/importlib +%{sitedir}/json +%{sitedir}/lib2to3 +%{sitedir}/logging +%{sitedir}/multiprocessing +%{sitedir}/pydoc_data +%{sitedir}/unittest +%{sitedir}/urllib +%{sitedir}/venv +%{sitedir}/wsgiref +%{sitedir}/xml +%{sitedir}/xmlrpc +%{sitedir}/zoneinfo +%{sitedir}/__pycache__ +# import-failed hooks +%{sitedir}/_import_failed +%{sitedir}/site-packages/zzzz-import-failed-hooks.pth +# symlinks +%if %{primary_interpreter} +%{_bindir}/python3 +%{_bindir}/pydoc3 +%endif +# executables +%attr(755, root, root) %{_bindir}/pydoc%{python_version} +# %%attr(755, root, root) %%{_bindir}/python%%{python_abi} +%attr(755, root, root) %{_bindir}/python%{python_version} +# endif for if base +%endif + +%changelog diff --git a/skip-test_pyobject_freed_is_freed.patch b/skip-test_pyobject_freed_is_freed.patch new file mode 100644 index 0000000..a13a2da --- /dev/null +++ b/skip-test_pyobject_freed_is_freed.patch @@ -0,0 +1,14 @@ +--- + Lib/test/test_capi.py | 1 + + 1 file changed, 1 insertion(+) + +--- a/Lib/test/test_capi.py ++++ b/Lib/test/test_capi.py +@@ -794,6 +794,7 @@ class PyMemDebugTests(unittest.TestCase) + def test_pyobject_forbidden_bytes_is_freed(self): + self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed') + ++ @unittest.skip('Failing on Leap 15.*') + def test_pyobject_freed_is_freed(self): + self.check_pyobject_is_freed('check_pyobject_freed_is_freed') + diff --git a/skipped_tests.py b/skipped_tests.py new file mode 100644 index 0000000..47002e6 --- /dev/null +++ b/skipped_tests.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +Simple regexp-based skipped test checker. +It lists tests that are mentioned (presumably for exclusion) +in BASE, and in MAIN (presumably for inclusion) +and reports discrepancies. + +This will have a number of +""" + +MAIN = "python39.spec" + +import glob +import re +from os.path import basename + +alltests = set() +qemu_exclusions = set() + +for item in glob.glob("Python-*/Lib/test/test_*"): + testname = basename(item) + if testname.endswith(".py"): + testname = testname[:-3] + alltests.add(testname) + +testre = re.compile(r'[\s"](test_\w+)\b') + +def find_tests_in_spec(specname): + global qemu_exclusions + + found_tests = set() + with open(specname) as spec: + in_qemu = False + for line in spec: + line = line.strip() + if "#" in line: + line = line[:line.index("#")] + tests = set(testre.findall(line)) + found_tests |= tests + if line == "%if 0%{?qemu_user_space_build} > 0": + in_qemu = True + if in_qemu: + if line == "%endif": + in_qemu = False + qemu_exclusions |= tests + return found_tests + +excluded = find_tests_in_spec(MAIN) + +#print("--- excluded tests:", " ".join(sorted(excluded))) +#print("--- included tests:", " ".join(sorted(included))) + +mentioned = excluded +nonexistent = mentioned - alltests +missing = excluded - qemu_exclusions + +print("--- the following tests are excluded for QEMU and not tested in python") +print("--- (that probably means we don't need to worry about them)") +for test in sorted(qemu_exclusions - excluded): + print(test) + +print("--- the following tests might be excluded in python:") +for test in sorted(missing): + print(test) + +if nonexistent: + print("--- the following tests don't exist:") + for test in sorted(nonexistent): + print(test) diff --git a/subprocess-raise-timeout.patch b/subprocess-raise-timeout.patch new file mode 100644 index 0000000..cb39e1a --- /dev/null +++ b/subprocess-raise-timeout.patch @@ -0,0 +1,16 @@ +--- + Lib/test/test_subprocess.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/Lib/test/test_subprocess.py ++++ b/Lib/test/test_subprocess.py +@@ -261,7 +261,8 @@ class ProcessTestCase(BaseTestCase): + "time.sleep(3600)"], + # Some heavily loaded buildbots (sparc Debian 3.x) require + # this much time to start and print. +- timeout=3) ++ # OBS might require even more ++ timeout=10) + self.fail("Expected TimeoutExpired.") + self.assertEqual(c.exception.output, b'BDFL') + diff --git a/support-expat-CVE-2022-25236-patched.patch b/support-expat-CVE-2022-25236-patched.patch new file mode 100644 index 0000000..e7b3acb --- /dev/null +++ b/support-expat-CVE-2022-25236-patched.patch @@ -0,0 +1,75 @@ +From 7da97f61816f3cadaa6788804b22a2434b40e8c5 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Mon, 21 Feb 2022 08:16:09 -0800 +Subject: [PATCH] bpo-46811: Make test suite support Expat >=2.4.5 (GH-31453) + (GH-31472) + +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 +(cherry picked from commit 2cae93832f46b245847bdc252456ddf7742ef45e) + +Co-authored-by: Sebastian Pipping +--- + Lib/test/test_minidom.py | 25 ++++++++++--------------- + 1 file changed, 10 insertions(+), 15 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst + +--- a/Lib/test/test_minidom.py ++++ b/Lib/test/test_minidom.py +@@ -6,7 +6,6 @@ import io + from test import support + import unittest + +-import pyexpat + import xml.dom.minidom + + from xml.dom.minidom import parse, Node, Document, parseString +@@ -1149,13 +1148,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'') +- self.assertRaises(ExpatError, parseString, +- b'Comment \xe7a va ? Tr\xe8s bien ?') +- else: +- self.assertRaises(UnicodeDecodeError, parseString, ++ # It doesn’t make any sense to insist on the exact text of the ++ # error message, or even the exact Exception … it is enough that ++ # the error has been discovered. ++ with self.assertRaises((UnicodeDecodeError, ExpatError)): ++ parseString( + b'Comment \xe7a va ? Tr\xe8s bien ?') + + doc.unlink() +@@ -1617,12 +1614,10 @@ 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') +- +- with context: ++ # It doesn’t make any sense to insist on the exact text of the ++ # error message, or even the exact Exception … it is enough that ++ # the error has been discovered. ++ with self.assertRaises((ExpatError, ValueError)): + parseString('') + + def testDocRemoveChild(self): From 0b0d7c7ce2a8d8101608a022f65308790ce3dd948af5c77c3ce6fee0257f89bd Mon Sep 17 00:00:00 2001 From: Marcus Rueckert Date: Fri, 30 Sep 2022 08:33:53 +0000 Subject: [PATCH 03/23] Set link to python310.26164 via maintenance_release request --- Python-3.10.5.tar.xz.asc | 16 -- Python-3.10.5.tar.xz => Python-3.10.7.tar.xz | 0 Python-3.10.7.tar.xz.asc | 16 ++ bpo-31046_ensurepip_honours_prefix.patch | 8 +- distutils-reproducible-compile.patch | 27 +- fix_configure_rst.patch | 2 +- import_failed.map | 14 +- no-skipif-doctests.patch | 101 ++++--- python310.changes | 277 +++++++++++++++++++ python310.spec | 22 +- skip-test_pyobject_freed_is_freed.patch | 2 +- support-expat-CVE-2022-25236-patched.patch | 10 +- test-int-timing.patch | 78 ++++++ 13 files changed, 469 insertions(+), 104 deletions(-) delete mode 100644 Python-3.10.5.tar.xz.asc rename Python-3.10.5.tar.xz => Python-3.10.7.tar.xz (100%) create mode 100644 Python-3.10.7.tar.xz.asc create mode 100644 test-int-timing.patch diff --git a/Python-3.10.5.tar.xz.asc b/Python-3.10.5.tar.xz.asc deleted file mode 100644 index 4f10f1d..0000000 --- a/Python-3.10.5.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmKd7cEACgkQ/+h0BBaL -2EfE/g/+MM3/BRFoUwEFRF+s0WYh1IxjalHXx+9IjKFYH9xYgz4hkegcU2A6XftC -mNHI9WRZ0tXPCOe/HSL3cmGretOW59Fh1outhzL3xumLAhODMJ5JBQM3/pQ2q/CV -/zvX5nVWjwg4XhlOg6AtIRRdmqjbNesGj4a0laG5l45AzxggAVe/2l/YMvo5aq4s -uTZ8s0EdNkPugVOZBe3bQ6MxkWymUmB0VC86mCuhcNx2uzB2ulyjUHBKUwqRo55N -C7BQUvL+dUNf27aFUBs42D3bjlUijvwf0Nc3BQM95d6WgmIsEOUQj/Tevsdb34DL -zt/slvwiwxJYlIlJP9jmxd6/CuqCdt07ML24/EMv1UUadwyvi5zVhmatuKpACULX -RNZSHy8ksgclc1KszxQfJMOqdbjy4K4Wa9jmh8/URCSOoagkF1opr7n9NXjPARXa -NoZCAbwoBiV9E1F4Fs8AmubI9tLyL9tMYayqF4vQgnSKlYD/Y5bxV7bmYTV6ELXE -m6UurUeCx0kzAvGt9qNx2B2TBoeyMdy12nmiiOAF1CCK76UUXwFFnG+vOlxC1d4U -GSKISTJkNY8dn40RPBpYjhCgbEPJiJbpvh4ryE3EVUQ6sPOBdrt2/xKJq/UprpFf -/rf5gk0BoNLtTp23k+Hh9UeRkji+0PMR0DgVS4DxzV9RUaSMyDY= -=FV1Y ------END PGP SIGNATURE----- diff --git a/Python-3.10.5.tar.xz b/Python-3.10.7.tar.xz similarity index 100% rename from Python-3.10.5.tar.xz rename to Python-3.10.7.tar.xz diff --git a/Python-3.10.7.tar.xz.asc b/Python-3.10.7.tar.xz.asc new file mode 100644 index 0000000..1fc8949 --- /dev/null +++ b/Python-3.10.7.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmMV9eMACgkQ/+h0BBaL +2Ec6FQ//eXrKOb5NoocNPIs9o5Jcbk5jtWxnOp3mqjO8D/LBYc8gwovZMPd+903w +In2QWtPSK22ZWdaR4DqizK9GjBRi+Y/ZEFfh3uiPM0I2/jGkr5BXizRZNdTo3GyB +/OuPiBKBVqMgTSGrpO4M24yUekqGdPfzg614GroWSr/16UVVUQADE8DP+BJCTIbk +v+t+AIYsALR0cUO+uqp2QbWf7N2aF+r08g59Vyb09+Nr2ZfjjINIbHQgRtHv7ZoI +7BsWiGW3qIeY8XxYt1/kWv4yMqaTyABdmdEHeM0vCzeEUpurj5072isGvOpI92N4 +LZ6nK8GR4pBS+OfOB7bgzUTC+tQ48wPQwb9lZTuWfSXGYotVdTXs1zW6o1T+vply +MrMZcsc6Y9o8fX3Mkkv4zT9S6JkXtq/MUTIZ27cZr492DzJNaKBG+NqF22FKR35M +ojLK24YpGyw2PCIlUSiFfAqkGNu53U5rP3N71mh7Ao00nx8WhKj4YAci0tBkfHyw +NYoX4tz7ybiR3zV5kyrmJv4G2x89cgosfHuEL2Lr+Irf3PV5vgjXHteAwu8Egdej +myokqzWEwoqNtrK9JsSYE3bcWmrVU9R/siQnNJXKWj+AkHKG0jMsrIh6iRvQGDhp +Q3Avu3ZQ/K9rreZ4Jk1DHX3BoDvRIFdEjBDAB+b6UUQEGE32nj8= +=O8kG +-----END PGP SIGNATURE----- diff --git a/bpo-31046_ensurepip_honours_prefix.patch b/bpo-31046_ensurepip_honours_prefix.patch index e3bb24d..1c1f3a7 100644 --- a/bpo-31046_ensurepip_honours_prefix.patch +++ b/bpo-31046_ensurepip_honours_prefix.patch @@ -55,7 +55,7 @@ Co-Authored-By: Xavier de Gaye .. note:: --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py -@@ -113,27 +113,27 @@ def _disable_pip_configuration_settings( +@@ -123,27 +123,27 @@ def _disable_pip_configuration_settings( os.environ['PIP_CONFIG_FILE'] = os.devnull @@ -88,7 +88,7 @@ Co-Authored-By: Xavier de Gaye Note that calling this function will alter both sys.path and os.environ. """ -@@ -183,6 +183,8 @@ def _bootstrap(*, root=None, upgrade=Fal +@@ -193,6 +193,8 @@ def _bootstrap(*, root=None, upgrade=Fal args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir] if root: args += ["--root", root] @@ -97,7 +97,7 @@ Co-Authored-By: Xavier de Gaye if upgrade: args += ["--upgrade"] if user: -@@ -258,6 +260,11 @@ def _main(argv=None): +@@ -268,6 +270,11 @@ def _main(argv=None): help="Install everything relative to this alternate root directory.", ) parser.add_argument( @@ -109,7 +109,7 @@ Co-Authored-By: Xavier de Gaye "--altinstall", action="store_true", default=False, -@@ -276,6 +283,7 @@ def _main(argv=None): +@@ -286,6 +293,7 @@ def _main(argv=None): return _bootstrap( root=args.root, diff --git a/distutils-reproducible-compile.patch b/distutils-reproducible-compile.patch index fd98baa..02f6b90 100644 --- a/distutils-reproducible-compile.patch +++ b/distutils-reproducible-compile.patch @@ -1,9 +1,24 @@ ---- - Lib/distutils/util.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/Lib/distutils/util.py -+++ b/Lib/distutils/util.py +Index: Python-3.10.5/Lib/compileall.py +=================================================================== +--- Python-3.10.5.orig/Lib/compileall.py ++++ Python-3.10.5/Lib/compileall.py +@@ -96,6 +96,12 @@ def compile_dir(dir, maxlevels=None, ddi + maxlevels = sys.getrecursionlimit() + files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels) + success = True ++ # work around https://github.com/python/cpython/issues/93317 ++ sys.intern('{') ++ sys.intern('{{') ++ sys.intern('}') ++ sys.intern('}}') ++ + if workers != 1 and ProcessPoolExecutor is not None: + # If workers == 0, let ProcessPoolExecutor choose + workers = workers or None +Index: Python-3.10.5/Lib/distutils/util.py +=================================================================== +--- Python-3.10.5.orig/Lib/distutils/util.py ++++ Python-3.10.5/Lib/distutils/util.py @@ -436,7 +436,7 @@ byte_compile(files, optimize=%r, force=% else: from py_compile import compile diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index 85be030..9d62100 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -29,7 +29,7 @@ Create a Python.framework rather than a traditional Unix install. Optional --- a/Misc/NEWS +++ b/Misc/NEWS -@@ -2370,7 +2370,7 @@ C API +@@ -2783,7 +2783,7 @@ C API ----- - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name diff --git a/import_failed.map b/import_failed.map index 9f01f41..12e5b30 100644 --- a/import_failed.map +++ b/import_failed.map @@ -1,7 +1,7 @@ -python39-curses: curses _curses _curses_panel -python39-dbm: dbm _dbm _gdbm -python39-idle: idlelib -python39-testsuite: test _ctypes_test _testbuffer _testcapi _testinternalcapi _testimportmultiple _testmultiphase xxlimited -python39-tk: tkinter _tkinter -python39-tools: turtledemo -python39: sqlite3 readline _sqlite3 nis +python310-curses: curses _curses _curses_panel +python310-dbm: dbm _dbm _gdbm +python310-idle: idlelib +python310-testsuite: test _ctypes_test _testbuffer _testcapi _testinternalcapi _testimportmultiple _testmultiphase xxlimited +python310-tk: tkinter _tkinter +python310-tools: turtledemo +python310: sqlite3 readline _sqlite3 nis diff --git a/no-skipif-doctests.patch b/no-skipif-doctests.patch index 62758d3..2601c01 100644 --- a/no-skipif-doctests.patch +++ b/no-skipif-doctests.patch @@ -1,16 +1,9 @@ only in patch2: unchanged: ---- a/Doc/conf.py -+++ b/Doc/conf.py -@@ -46,7 +46,7 @@ today_fmt = '%B %d, %Y' - highlight_language = 'python3' - - # Minimum version of sphinx required --needs_sphinx = '1.8' -+needs_sphinx = '1.7.6' - - # Ignore any .rst files in the venv/ directory. - exclude_patterns = ['venv/*', 'README.rst'] +--- + Doc/library/turtle.rst | 82 ------------------------------------------------- + 1 file changed, 82 deletions(-) + --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -250,7 +250,6 @@ Turtle motion @@ -314,7 +307,7 @@ unchanged: >>> turtle.color("black", "red") >>> turtle.begin_fill() -@@ -1075,7 +1036,6 @@ More drawing control +@@ -1074,7 +1035,6 @@ More drawing control variables to the default values. .. doctest:: @@ -322,7 +315,7 @@ unchanged: >>> turtle.goto(0,-22) >>> turtle.left(100) -@@ -1127,7 +1087,6 @@ Visibility +@@ -1125,7 +1085,6 @@ Visibility drawing observably. .. doctest:: @@ -330,7 +323,7 @@ unchanged: >>> turtle.hideturtle() -@@ -1138,7 +1097,6 @@ Visibility +@@ -1136,7 +1095,6 @@ Visibility Make the turtle visible. .. doctest:: @@ -338,7 +331,7 @@ unchanged: >>> turtle.showturtle() -@@ -1169,7 +1127,6 @@ Appearance +@@ -1167,7 +1125,6 @@ Appearance deal with shapes see Screen method :func:`register_shape`. .. doctest:: @@ -346,7 +339,7 @@ unchanged: >>> turtle.shape() 'classic' -@@ -1195,7 +1152,6 @@ Appearance +@@ -1193,7 +1150,6 @@ Appearance ``resizemode("user")`` is called by :func:`shapesize` when used with arguments. .. doctest:: @@ -354,7 +347,7 @@ unchanged: >>> turtle.resizemode() 'noresize' -@@ -1219,7 +1175,6 @@ Appearance +@@ -1217,7 +1173,6 @@ Appearance of the shapes's outline. .. doctest:: @@ -362,7 +355,7 @@ unchanged: >>> turtle.shapesize() (1.0, 1.0, 1) -@@ -1244,7 +1199,6 @@ Appearance +@@ -1242,7 +1197,6 @@ Appearance heading of the turtle are sheared. .. doctest:: @@ -370,7 +363,7 @@ unchanged: >>> turtle.shape("circle") >>> turtle.shapesize(5,2) -@@ -1261,7 +1215,6 @@ Appearance +@@ -1259,7 +1213,6 @@ Appearance change the turtle's heading (direction of movement). .. doctest:: @@ -378,7 +371,7 @@ unchanged: >>> turtle.reset() >>> turtle.shape("circle") -@@ -1281,7 +1234,6 @@ Appearance +@@ -1279,7 +1232,6 @@ Appearance (direction of movement). .. doctest:: @@ -386,7 +379,7 @@ unchanged: >>> turtle.reset() >>> turtle.shape("circle") -@@ -1307,7 +1259,6 @@ Appearance +@@ -1305,7 +1257,6 @@ Appearance turtle (its direction of movement). .. doctest:: @@ -394,7 +387,7 @@ unchanged: >>> turtle.reset() >>> turtle.shape("circle") -@@ -1336,7 +1287,6 @@ Appearance +@@ -1334,7 +1285,6 @@ Appearance given matrix. .. doctest:: @@ -402,7 +395,7 @@ unchanged: >>> turtle = Turtle() >>> turtle.shape("square") -@@ -1352,7 +1302,6 @@ Appearance +@@ -1350,7 +1300,6 @@ Appearance can be used to define a new shape or components of a compound shape. .. doctest:: @@ -410,7 +403,7 @@ unchanged: >>> turtle.shape("square") >>> turtle.shapetransform(4, -1, 0, 2) -@@ -1377,7 +1326,6 @@ Using events +@@ -1375,7 +1324,6 @@ Using events procedural way: .. doctest:: @@ -418,7 +411,7 @@ unchanged: >>> def turn(x, y): ... left(180) -@@ -1398,7 +1346,6 @@ Using events +@@ -1396,7 +1344,6 @@ Using events ``None``, existing bindings are removed. .. doctest:: @@ -426,7 +419,7 @@ unchanged: >>> class MyTurtle(Turtle): ... def glow(self,x,y): -@@ -1426,7 +1373,6 @@ Using events +@@ -1424,7 +1371,6 @@ Using events mouse-click event on that turtle. .. doctest:: @@ -434,7 +427,7 @@ unchanged: >>> turtle.ondrag(turtle.goto) -@@ -1454,7 +1400,6 @@ Special Turtle methods +@@ -1452,7 +1398,6 @@ Special Turtle methods Return the last recorded polygon. .. doctest:: @@ -442,7 +435,7 @@ unchanged: >>> turtle.home() >>> turtle.begin_poly() -@@ -1474,7 +1419,6 @@ Special Turtle methods +@@ -1472,7 +1417,6 @@ Special Turtle methods turtle properties. .. doctest:: @@ -450,7 +443,7 @@ unchanged: >>> mick = Turtle() >>> joe = mick.clone() -@@ -1487,7 +1431,6 @@ Special Turtle methods +@@ -1485,7 +1429,6 @@ Special Turtle methods return the "anonymous turtle": .. doctest:: @@ -458,7 +451,7 @@ unchanged: >>> pet = getturtle() >>> pet.fd(50) -@@ -1501,7 +1444,6 @@ Special Turtle methods +@@ -1499,7 +1442,6 @@ Special Turtle methods TurtleScreen methods can then be called for that object. .. doctest:: @@ -466,7 +459,7 @@ unchanged: >>> ts = turtle.getscreen() >>> ts -@@ -1519,7 +1461,6 @@ Special Turtle methods +@@ -1517,7 +1459,6 @@ Special Turtle methods ``None``, the undobuffer is disabled. .. doctest:: @@ -474,7 +467,7 @@ unchanged: >>> turtle.setundobuffer(42) -@@ -1529,7 +1470,6 @@ Special Turtle methods +@@ -1527,7 +1468,6 @@ Special Turtle methods Return number of entries in the undobuffer. .. doctest:: @@ -482,7 +475,7 @@ unchanged: >>> while undobufferentries(): ... undo() -@@ -1552,7 +1492,6 @@ below: +@@ -1550,7 +1490,6 @@ below: For example: .. doctest:: @@ -490,7 +483,7 @@ unchanged: >>> s = Shape("compound") >>> poly1 = ((0,0),(10,-5),(0,10),(-10,-5)) -@@ -1563,7 +1502,6 @@ below: +@@ -1561,7 +1500,6 @@ below: 3. Now add the Shape to the Screen's shapelist and use it: .. doctest:: @@ -498,7 +491,7 @@ unchanged: >>> register_shape("myshape", s) >>> shape("myshape") -@@ -1583,7 +1521,6 @@ Most of the examples in this section ref +@@ -1581,7 +1519,6 @@ Most of the examples in this section ref ``screen``. .. doctest:: @@ -506,7 +499,7 @@ unchanged: :hide: >>> screen = Screen() -@@ -1600,7 +1537,6 @@ Window control +@@ -1598,7 +1535,6 @@ Window control Set or return background color of the TurtleScreen. .. doctest:: @@ -514,7 +507,7 @@ unchanged: >>> screen.bgcolor("orange") >>> screen.bgcolor() -@@ -1686,7 +1622,6 @@ Window control +@@ -1690,7 +1626,6 @@ Window control distorted. .. doctest:: @@ -522,7 +515,7 @@ unchanged: >>> screen.reset() >>> screen.setworldcoordinates(-50,-7.5,50,7.5) -@@ -1697,7 +1632,6 @@ Window control +@@ -1701,7 +1636,6 @@ Window control ... left(45); fd(2) # a regular octagon .. doctest:: @@ -530,7 +523,7 @@ unchanged: :hide: >>> screen.reset() -@@ -1719,7 +1653,6 @@ Animation control +@@ -1723,7 +1657,6 @@ Animation control Optional argument: .. doctest:: @@ -538,7 +531,7 @@ unchanged: >>> screen.delay() 10 -@@ -1741,7 +1674,6 @@ Animation control +@@ -1745,7 +1678,6 @@ Animation control :func:`delay`). .. doctest:: @@ -546,7 +539,7 @@ unchanged: >>> screen.tracer(8, 25) >>> dist = 2 -@@ -1778,7 +1710,6 @@ Using screen events +@@ -1782,7 +1714,6 @@ Using screen events must have the focus. (See method :func:`listen`.) .. doctest:: @@ -554,7 +547,7 @@ unchanged: >>> def f(): ... fd(50) -@@ -1799,7 +1730,6 @@ Using screen events +@@ -1803,7 +1734,6 @@ Using screen events must have focus. (See method :func:`listen`.) .. doctest:: @@ -562,7 +555,7 @@ unchanged: >>> def f(): ... fd(50) -@@ -1824,7 +1754,6 @@ Using screen events +@@ -1828,7 +1758,6 @@ Using screen events named ``turtle``: .. doctest:: @@ -570,7 +563,7 @@ unchanged: >>> screen.onclick(turtle.goto) # Subsequently clicking into the TurtleScreen will >>> # make the turtle move to the clicked point. -@@ -1844,7 +1773,6 @@ Using screen events +@@ -1848,7 +1777,6 @@ Using screen events Install a timer that calls *fun* after *t* milliseconds. .. doctest:: @@ -578,7 +571,7 @@ unchanged: >>> running = True >>> def f(): -@@ -1926,7 +1854,6 @@ Settings and special methods +@@ -1930,7 +1858,6 @@ Settings and special methods ============ ========================= =================== .. doctest:: @@ -586,7 +579,7 @@ unchanged: >>> mode("logo") # resets turtle heading to north >>> mode() -@@ -1941,7 +1868,6 @@ Settings and special methods +@@ -1945,7 +1872,6 @@ Settings and special methods values of color triples have to be in the range 0..\ *cmode*. .. doctest:: @@ -594,7 +587,7 @@ unchanged: >>> screen.colormode(1) >>> turtle.pencolor(240, 160, 80) -@@ -1962,7 +1888,6 @@ Settings and special methods +@@ -1966,7 +1892,6 @@ Settings and special methods do with a Tkinter Canvas. .. doctest:: @@ -602,7 +595,7 @@ unchanged: >>> cv = screen.getcanvas() >>> cv -@@ -1974,7 +1899,6 @@ Settings and special methods +@@ -1978,7 +1903,6 @@ Settings and special methods Return a list of names of all currently available turtle shapes. .. doctest:: @@ -610,7 +603,7 @@ unchanged: >>> screen.getshapes() ['arrow', 'blank', 'circle', ..., 'turtle'] -@@ -1998,7 +1922,6 @@ Settings and special methods +@@ -2002,7 +1926,6 @@ Settings and special methods coordinates: Install the corresponding polygon shape. .. doctest:: @@ -618,7 +611,7 @@ unchanged: >>> screen.register_shape("triangle", ((5,-3), (0,5), (-5,-3))) -@@ -2014,7 +1937,6 @@ Settings and special methods +@@ -2018,7 +1941,6 @@ Settings and special methods Return the list of turtles on the screen. .. doctest:: @@ -626,7 +619,7 @@ unchanged: >>> for turtle in screen.turtles(): ... turtle.color("red") -@@ -2076,7 +1998,6 @@ Methods specific to Screen, not inherite +@@ -2080,7 +2002,6 @@ Methods specific to Screen, not inherite center window vertically .. doctest:: @@ -634,7 +627,7 @@ unchanged: >>> screen.setup (width=200, height=200, startx=0, starty=0) >>> # sets window to 200x200 pixels, in upper left of screen -@@ -2092,7 +2013,6 @@ Methods specific to Screen, not inherite +@@ -2096,7 +2017,6 @@ Methods specific to Screen, not inherite Set title of turtle window to *titlestring*. .. doctest:: @@ -642,7 +635,7 @@ unchanged: >>> screen.title("Welcome to the turtle zoo!") -@@ -2163,7 +2083,6 @@ Public classes +@@ -2167,7 +2087,6 @@ Public classes Example: .. doctest:: @@ -650,7 +643,7 @@ unchanged: >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) >>> s = Shape("compound") -@@ -2510,7 +2429,6 @@ Changes since Python 3.0 +@@ -2514,7 +2433,6 @@ Changes since Python 3.0 .. doctest:: diff --git a/python310.changes b/python310.changes index 57e429c..529a368 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,280 @@ +------------------------------------------------------------------- +Sun Sep 18 08:48:51 UTC 2022 - Andreas Schwab + +- test-int-timing.patch: gh-96710: Make the test timing more lenient for + the int/str DoS regression test. (#96717) + +------------------------------------------------------------------- +Sun Sep 11 08:32:53 UTC 2022 - Matej Cepl + +- Update to 3.10.7: + - Fix for CVE-2020-10735 (bsc#1203125) Converting between int + and str in bases other than 2 (binary), 4, 8 (octal), 16 + (hexadecimal), or 32 such as base 10 (decimal) now raises + a ValueError if the number of digits in string form is above + a limit to avoid potential denial of service attacks due to + the algorithmic complexity. + - Other bug fixes: + - Fixed a bug that caused _PyCode_GetExtra to return garbage + for negative indexes. + - Fix format string in _PyPegen_raise_error_known_location + that can lead to memory corruption on some 64bit systems. + The function was building a tuple with i (int) instead of + n (Py_ssize_t) for Py_ssize_t arguments. + - Fix misleading contents of error message when converting an + all-whitespace string to float. + - coroutine.throw() now properly initializes the frame.f_back + when resuming a stack of coroutines. This allows e.g. + traceback.print_stack() to work correctly when an exception + (such as CancelledError) is thrown into a coroutine. + - ast.parse() will no longer parse function definitions with + positional-only params when passed feature_version less + than (3, 8). + - Correct conversion of numbers.Rational’s to float. + - Fix a performance regression in logging + TimedRotatingFileHandler. Only check for special files when + the rollover time has passed. + - Fix unused localName parameter in the Attr class in + xml.dom.minidom. + - Update bundled pip to 22.2.2. + - Fail gracefully if EPERM or ENOSYS is raised when loading + crypt methods. This may happen when trying to load MD5 on + a Linux kernel with FIPS enabled. + - Improve discoverability of the higher level + concurrent.futures module by providing clearer links from + the lower level threading and multiprocessing modules. + - Update the default RFC base URL from deprecated + tools.ietf.org to datatracker.ietf.org + - Fix stylesheet not working in Windows CHM htmlhelp docs. + - The documentation now lists which members of C structs are + part of the Limited API/Stable ABI. + - Mitigate the inherent race condition from using + find_unused_port() in testSockName() by trying to find an + unused port a few times before failing. + - Build and test with OpenSSL 1.1.1q + - Document handling of extensions in Save As dialogs. + - Include prompts when saving Shell (interactive input and + output). + +------------------------------------------------------------------- +Wed Aug 17 11:08:56 UTC 2022 - Dirk Müller + +- fix import_failed.map to refer to the python 3.10 package versions + +------------------------------------------------------------------- +Tue Aug 2 17:13:37 UTC 2022 - Matej Cepl + +- Update to 3.10.6: + - gh-87389: http.server: Fix an open redirection vulnerability + in the HTTP server when an URI path starts with //. + Vulnerability discovered, and initial fix proposed, by Hamza + Avvan. (bsc#1202624, CVE-2021-28861) + - gh-92888: Fix memoryview use after free when accessing the + backing buffer in certain cases. + - gh-95355: _PyPegen_Parser_New now properly detects token + memory allocation errors. Patch by Honglin Zhu. + - gh-94938: Fix error detection in some builtin functions when + keyword argument name is an instance of a str subclass with + overloaded __eq__ and __hash__. Previously it could cause + SystemError or other undesired behavior. + - gh-94949: ast.parse() will no longer parse parenthesized + context managers when passed feature_version less than + (3, 9). Patch by Shantanu Jain. + - gh-94947: ast.parse() will no longer parse assignment + expressions when passed feature_version less than + (3, 8). Patch by Shantanu Jain. + - gh-94869: Fix the column offsets for some expressions in + multi-line f-strings ast nodes. Patch by Pablo Galindo. + - gh-91153: Fix an issue where a bytearray item assignment + could crash if it’s resized by the new value’s __index__() + method. + - gh-94329: Compile and run code with unpacking of extremely + large sequences (1000s of elements). Such code failed to + compile. It now compiles and runs correctly. + - gh-94360: Fixed a tokenizer crash when reading encoded + files with syntax errors from stdin with non utf-8 encoded + text. Patch by Pablo Galindo + - gh-94192: Fix error for dictionary literals with invalid + expression as value. + - gh-93964: Strengthened compiler overflow checks to prevent + crashes when compiling very large source files. + - gh-93671: Fix some exponential backtrace case happening with + deeply nested sequence patterns in match statements. Patch by + Pablo Galindo + - gh-93021: Fix the __text_signature__ for __get__() methods + implemented in C. Patch by Jelle Zijlstra. + - gh-92930: Fixed a crash in _pickle.c from mutating + collections during __reduce__ or persistent_id. + - gh-92914: Always round the allocated size for lists up to the + nearest even number. + - gh-92858: Improve error message for some suites with syntax + error before ‘:’ + - gh-95339: Update bundled pip to 22.2.1. + - gh-95045: Fix GC crash when deallocating _lsprof.Profiler by + untracking it before calling any callbacks. Patch by Kumar + Aditya. + - gh-95087: Fix IndexError in parsing invalid date in the email + module. + - gh-95199: Upgrade bundled setuptools to 63.2.0. + - gh-95194: Upgrade bundled pip to 22.2. + - gh-93899: Fix check for existence of os.EFD_CLOEXEC, + os.EFD_NONBLOCK and os.EFD_SEMAPHORE flags on older kernel + versions where these flags are not present. Patch by Kumar + Aditya. + - gh-95166: Fix concurrent.futures.Executor.map() to cancel the + currently waiting on future on an error - e.g. TimeoutError + or KeyboardInterrupt. + - gh-93157: Fix fileinput module didn’t support errors option + when inplace is true. + - gh-94821: Fix binding of unix socket to empty address + on Linux to use an available address from the abstract + namespace, instead of “0”. + - gh-94736: Fix crash when deallocating an instance of a + subclass of _multiprocessing.SemLock. Patch by Kumar Aditya. + - gh-94637: SSLContext.set_default_verify_paths() now releases + the GIL around SSL_CTX_set_default_verify_paths call. The + function call performs I/O and CPU intensive work. + - gh-94510: Re-entrant calls to sys.setprofile() and + sys.settrace() now raise RuntimeError. Patch by Pablo + Galindo. + - gh-92336: Fix bug where linecache.getline() fails on bad + files with UnicodeDecodeError or SyntaxError. It now returns + an empty string as per the documentation. + - gh-89988: Fix memory leak in pickle.Pickler when looking up + dispatch_table. Patch by Kumar Aditya. + - gh-94254: Fixed types of struct module to be immutable. Patch + by Kumar Aditya. + - gh-94245: Fix pickling and copying of typing.Tuple[()]. + - gh-94207: Made _struct.Struct GC-tracked in order to fix a + reference leak in the _struct module. + - gh-94101: Manual instantiation of ssl.SSLSession objects is + no longer allowed as it lead to misconfigured instances that + crashed the interpreter when attributes where accessed on + them. + - gh-84753: inspect.iscoroutinefunction(), + inspect.isgeneratorfunction(), and + inspect.isasyncgenfunction() now properly return True + for duck-typed function-like objects like instances of + unittest.mock.AsyncMock. + - This makes inspect.iscoroutinefunction() consistent with the + behavior of asyncio.iscoroutinefunction(). Patch by Mehdi + ABAAKOUK. + - gh-83499: Fix double closing of file description in tempfile. + - gh-79512: Fixed names and __module__ value of weakref classes + ReferenceType, ProxyType, CallableProxyType. It makes them + pickleable. + - gh-90494: copy.copy() and copy.deepcopy() now always raise + a TypeError if __reduce__() returns a tuple with length 6 + instead of silently ignore the 6th item or produce incorrect + result. + - gh-90549: Fix a multiprocessing bug where a global named + resource (such as a semaphore) could leak when a child + process is spawned (as opposed to forked). + - gh-79579: sqlite3 now correctly detects DML queries with + leading comments. Patch by Erlend E. Aasland. + - gh-93421: Update sqlite3.Cursor.rowcount when a DML + statement has run to completion. This fixes the row count + for SQL queries like UPDATE ... RETURNING. Patch by Erlend + E. Aasland. + - gh-91810: Suppress writing an XML declaration in open + files in ElementTree.write() with encoding='unicode' and + xml_declaration=None. + - gh-93353: Fix the importlib.resources.as_file() context + manager to remove the temporary file if destroyed late + during Python finalization: keep a local reference to the + os.remove() function. Patch by Victor Stinner. + - gh-83658: Make multiprocessing.Pool raise an exception if + maxtasksperchild is not None or a positive int. + - gh-74696: shutil.make_archive() no longer temporarily changes + the current working directory during creation of standard + .zip or tar archives. + - gh-91577: Move imports in SharedMemory methods to module + level so that they can be executed late in python + finalization. + - bpo-47231: Fixed an issue with inconsistent trailing slashes + in tarfile longname directories. + - bpo-46755: In QueueHandler, clear stack_info from LogRecord + to prevent stack trace from being written twice. + - bpo-46053: Fix OSS audio support on NetBSD. + - bpo-46197: Fix ensurepip environment isolation for subprocess + running pip. + - bpo-45924: Fix asyncio incorrect traceback when future’s + exception is raised multiple times. Patch by Kumar Aditya. + - bpo-34828: sqlite3.Connection.iterdump() now handles + databases that use AUTOINCREMENT in one or more tables. + - gh-94321: Document the PEP 246 style protocol type + sqlite3.PrepareProtocol. + - gh-86128: Document a limitation in ThreadPoolExecutor where + its exit handler is executed before any handlers in atexit. + - gh-61162: Clarify sqlite3 behavior when Using the connection + as a context manager. + - gh-87260: Align sqlite3 argument specs with the actual + implementation. + - gh-86986: The minimum Sphinx version required to build the + documentation is now 3.2. + - gh-88831: Augmented documentation of + asyncio.create_task(). Clarified the need to keep strong + references to tasks and added a code snippet detailing how to + to this. + - bpo-47161: Document that pathlib.PurePath does not collapse + initial double slashes because they denote UNC paths. + - gh-95280: Fix problem with test_ssl test_get_ciphers on + systems that require perfect forward secrecy (PFS) ciphers. + - gh-95212: Make multiprocessing test case + test_shared_memory_recreate parallel-safe. + - gh-91330: Added more tests for dataclasses to cover behavior + with data descriptor-based fields. + - gh-94208: test_ssl is now checking for supported TLS version + and protocols in more tests. + - gh-93951: In test_bdb.StateTestCase.test_skip, avoid + including auxiliary importers. + - gh-93957: Provide nicer error reporting from subprocesses in + test_venv.EnsurePipTest.test_with_pip. + - gh-57539: Increase calendar test coverage for + calendar.LocaleTextCalendar.formatweekday(). + - gh-92886: Fixing tests that fail when running with + optimizations (-O) in test_zipimport.py + - bpo-47016: Create a GitHub Actions workflow for verifying + bundled pip and setuptools. Patch by Illia Volochii and Adam + Turner. + - gh-94841: Fix the possible performance regression of + PyObject_Free() compiled with MSVC version 1932. + - gh-95511: Fix the Shell context menu copy-with-prompts bug of + copying an extra line when one selects whole lines. + - gh-95471: In the Edit menu, move Select All and add a new + separator. + - gh-95411: Enable using IDLE’s module browser with .pyw files. + - gh-89610: Add .pyi as a recognized extension for IDLE on + macOS. This allows opening stub files by double clicking on + them in the Finder. + - gh-94538: Fix Argument Clinic output to custom file + destinations. Patch by Erlend E. Aasland. + - gh-94430: Allow parameters named module and self with custom + C names in Argument Clinic. Patch by Erlend E. Aasland + - gh-94930: Fix SystemError raised when + PyArg_ParseTupleAndKeywords() is used with # in (...) but + without PY_SSIZE_T_CLEAN defined. + - gh-94864: Fix PyArg_Parse* with deprecated format units “u” + and “Z”. It returned 1 (success) when warnings are turned + into exceptions. +- Reapply patches + - bpo-31046_ensurepip_honours_prefix.patch + - fix_configure_rst.patch + - no-skipif-doctests.patch + - skip-test_pyobject_freed_is_freed.patch + +------------------------------------------------------------------- +Sun Jul 31 09:41:30 UTC 2022 - Stephan Kulow + +- Extend distutils-reproducible-compile.patch with a workaround + for non reproducible pyc files issue 93317 + +------------------------------------------------------------------- +Thu Jul 21 14:19:52 UTC 2022 - Matej Cepl + +- Switch from %primary_interpreter to prjconf-defined + %primary_python (gh#openSUSE/python-rpm-macros#127). + ------------------------------------------------------------------- Thu Jun 9 16:43:30 UTC 2022 - Matej Cepl diff --git a/python310.spec b/python310.spec index 7f7da63..72d1ce2 100644 --- a/python310.spec +++ b/python310.spec @@ -36,6 +36,13 @@ %bcond_without general %endif +%define python_pkg_name python310 +%if "%{python_pkg_name}" == "%{primary_python}" +%define primary_interpreter 1 +%else +%define primary_interpreter 0 +%endif + %if 0%{?sle_version} && 0%{?suse_version} < 1550 # Obsoleting previous "latest" Python versions # Next versions will get more lines like for older versions @@ -59,16 +66,8 @@ Obsoletes: python39%{?1:-%{1}} %else %define tarversion %{version} %endif -%define python_pkg_name python310 -# Will provide the python3-* provides -# Will do the /usr/bin/python3 and all the core links -%if 0%{?sle_version} || 0%{?suse_version} < 1550 -%define primary_interpreter 0 -%else -%define primary_interpreter 1 -%endif # We don't process beta signs well -%define folderversion 3.10.5 +%define folderversion 3.10.7 %define tarname Python-%{tarversion} %define sitedir %{_libdir}/python%{python_version} # three possible ABI kinds: m - pymalloc, d - debug build; see PEP 3149 @@ -104,7 +103,7 @@ Obsoletes: python39%{?1:-%{1}} %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so %bcond_without profileopt Name: %{python_pkg_name}%{psuffix} -Version: 3.10.5 +Version: 3.10.7 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -170,6 +169,8 @@ Patch36: support-expat-CVE-2022-25236-patched.patch # PATCH-FIX-UPSTREAM CVE-2015-20107-mailcap-unsafe-filenames.patch bsc#1198511 mcepl@suse.com # avoid the command injection in the mailcap module. Patch37: CVE-2015-20107-mailcap-unsafe-filenames.patch +# PATCH-FIX-UPSTREAM gh-96710: Make the test timing more lenient for the int/str DoS regression test. (#96717) +Patch38: test-int-timing.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -439,6 +440,7 @@ other applications. %patch35 -p1 %patch36 -p1 %patch37 -p1 +%patch38 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac diff --git a/skip-test_pyobject_freed_is_freed.patch b/skip-test_pyobject_freed_is_freed.patch index a13a2da..7be01a7 100644 --- a/skip-test_pyobject_freed_is_freed.patch +++ b/skip-test_pyobject_freed_is_freed.patch @@ -4,7 +4,7 @@ --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py -@@ -794,6 +794,7 @@ class PyMemDebugTests(unittest.TestCase) +@@ -941,6 +941,7 @@ class PyMemDebugTests(unittest.TestCase) def test_pyobject_forbidden_bytes_is_freed(self): self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed') diff --git a/support-expat-CVE-2022-25236-patched.patch b/support-expat-CVE-2022-25236-patched.patch index e7b3acb..5b26c99 100644 --- a/support-expat-CVE-2022-25236-patched.patch +++ b/support-expat-CVE-2022-25236-patched.patch @@ -23,8 +23,8 @@ Also, test_minidom.py: Support Expat >=2.4.5 Co-authored-by: Sebastian Pipping --- - Lib/test/test_minidom.py | 25 ++++++++++--------------- - 1 file changed, 10 insertions(+), 15 deletions(-) + Lib/test/test_minidom.py | 23 +++++++++-------------- + 1 file changed, 9 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst --- a/Lib/test/test_minidom.py @@ -36,8 +36,8 @@ Co-authored-by: Sebastian Pipping -import pyexpat import xml.dom.minidom - from xml.dom.minidom import parse, Node, Document, parseString -@@ -1149,13 +1148,11 @@ class MinidomTest(unittest.TestCase): + from xml.dom.minidom import parse, Attr, Node, Document, parseString +@@ -1163,13 +1162,11 @@ class MinidomTest(unittest.TestCase): # Verify that character decoding errors raise exceptions instead # of crashing @@ -56,7 +56,7 @@ Co-authored-by: Sebastian Pipping b'Comment \xe7a va ? Tr\xe8s bien ?') doc.unlink() -@@ -1617,12 +1614,10 @@ class MinidomTest(unittest.TestCase): +@@ -1631,12 +1628,10 @@ class MinidomTest(unittest.TestCase): self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) def testExceptionOnSpacesInXMLNSValue(self): diff --git a/test-int-timing.patch b/test-int-timing.patch new file mode 100644 index 0000000..4f7ef27 --- /dev/null +++ b/test-int-timing.patch @@ -0,0 +1,78 @@ +From 11e3548fd1d3445ccde971d613633b58d73c3016 Mon Sep 17 00:00:00 2001 +From: "Gregory P. Smith" +Date: Fri, 9 Sep 2022 12:51:34 -0700 +Subject: [PATCH] gh-96710: Make the test timing more lenient for the int/str + DoS regression test. (#96717) + +A regression would still absolutely fail and even a flaky pass isn't +harmful as it'd fail most of the time across our N system test runs. + +Windows has a low resolution timer and CI systems are prone to odd +timing so this just gives more leeway to avoid flakiness. +--- + Lib/test/test_int.py | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py +index 800c0b006c..c972b8afb4 100644 +--- a/Lib/test/test_int.py ++++ b/Lib/test/test_int.py +@@ -650,7 +650,8 @@ def test_denial_of_service_prevented_int_to_str(self): + self.assertEqual(len(huge_decimal), digits) + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. +- if seconds_to_convert < 0.005: ++ # Some OSes have a low res 1/64s timer, skip if hard to measure. ++ if seconds_to_convert < 1/64: + raise unittest.SkipTest('"slow" conversion took only ' + f'{seconds_to_convert} seconds.') + +@@ -662,7 +663,7 @@ def test_denial_of_service_prevented_int_to_str(self): + str(huge_int) + seconds_to_fail_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) +- self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) ++ self.assertLessEqual(seconds_to_fail_huge, seconds_to_convert/2) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. +@@ -673,7 +674,7 @@ def test_denial_of_service_prevented_int_to_str(self): + str(extra_huge_int) + seconds_to_fail_extra_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) +- self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) ++ self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/2) + + def test_denial_of_service_prevented_str_to_int(self): + """Regression test: ensure we fail before performing O(N**2) work.""" +@@ -691,7 +692,8 @@ def test_denial_of_service_prevented_str_to_int(self): + seconds_to_convert = get_time() - start + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. +- if seconds_to_convert < 0.005: ++ # Some OSes have a low res 1/64s timer, skip if hard to measure. ++ if seconds_to_convert < 1/64: + raise unittest.SkipTest('"slow" conversion took only ' + f'{seconds_to_convert} seconds.') + +@@ -701,7 +703,7 @@ def test_denial_of_service_prevented_str_to_int(self): + int(huge) + seconds_to_fail_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) +- self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) ++ self.assertLessEqual(seconds_to_fail_huge, seconds_to_convert/2) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. +@@ -712,7 +714,7 @@ def test_denial_of_service_prevented_str_to_int(self): + int(extra_huge) + seconds_to_fail_extra_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) +- self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) ++ self.assertLessEqual(seconds_to_fail_extra_huge, seconds_to_convert/2) + + def test_power_of_two_bases_unlimited(self): + """The limit does not apply to power of 2 bases.""" +-- +2.37.3 + From cb4ba88c9bd7657d53203ec2b877a3a1e2fe709febfe546fdd8b15a439ab9770 Mon Sep 17 00:00:00 2001 From: Ruediger Oertel Date: Tue, 15 Nov 2022 16:10:12 +0000 Subject: [PATCH 04/23] Set link to python310.26738 via maintenance_release request --- ...nx.locale._-as-gettext-in-pyspecific.patch | 54 +++++++ CVE-2015-20107-mailcap-unsafe-filenames.patch | 104 ++------------ ...022-42919-loc-priv-mulitproc-forksrv.patch | 59 ++++++++ CVE-2022-45061-DoS-by-IDNA-decode.patch | 88 ++++++++++++ Python-3.10.7.tar.xz.asc | 16 --- Python-3.10.7.tar.xz => Python-3.10.8.tar.xz | 0 Python-3.10.8.tar.xz.asc | 16 +++ fix_configure_rst.patch | 2 +- python310.changes | 133 ++++++++++++++++++ python310.spec | 17 ++- subprocess-raise-timeout.patch | 2 +- test-int-timing.patch | 78 ---------- 12 files changed, 376 insertions(+), 193 deletions(-) create mode 100644 98437-sphinx.locale._-as-gettext-in-pyspecific.patch create mode 100644 CVE-2022-42919-loc-priv-mulitproc-forksrv.patch create mode 100644 CVE-2022-45061-DoS-by-IDNA-decode.patch delete mode 100644 Python-3.10.7.tar.xz.asc rename Python-3.10.7.tar.xz => Python-3.10.8.tar.xz (100%) create mode 100644 Python-3.10.8.tar.xz.asc delete mode 100644 test-int-timing.patch diff --git a/98437-sphinx.locale._-as-gettext-in-pyspecific.patch b/98437-sphinx.locale._-as-gettext-in-pyspecific.patch new file mode 100644 index 0000000..680c2c1 --- /dev/null +++ b/98437-sphinx.locale._-as-gettext-in-pyspecific.patch @@ -0,0 +1,54 @@ +From 5775f51691d7d64fb676586e008b41261ce64ac2 Mon Sep 17 00:00:00 2001 +From: "Matt.Wang" +Date: Wed, 19 Oct 2022 14:49:08 +0800 +Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for + backward-compatibility in pyspecific.py + +[why] spinix 5.3 changed locale.translators from a defaultdict(gettext.NullTranslations) to a dict, which leads to failure of pyspecific.py. Use sphinx.locale._ as gettext to fix the issue. +--- + Doc/tools/extensions/pyspecific.py | 8 ++++---- + Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst | 1 + + 2 files changed, 5 insertions(+), 4 deletions(-) + +--- a/Doc/tools/extensions/pyspecific.py ++++ b/Doc/tools/extensions/pyspecific.py +@@ -26,7 +26,7 @@ try: + from sphinx.errors import NoUri + except ImportError: + from sphinx.environment import NoUri +-from sphinx.locale import translators ++from sphinx.locale import _ as sphinx_gettext + from sphinx.util import status_iterator, logging + from sphinx.util.nodes import split_explicit_title + from sphinx.writers.text import TextWriter, TextTranslator +@@ -109,7 +109,7 @@ class ImplementationDetail(Directive): + def run(self): + self.assert_has_content() + pnode = nodes.compound(classes=['impl-detail']) +- label = translators['sphinx'].gettext(self.label_text) ++ label = sphinx_gettext(self.label_text) + content = self.content + add_text = nodes.strong(label, label) + self.state.nested_parse(content, self.content_offset, pnode) +@@ -203,7 +203,7 @@ class AuditEvent(Directive): + else: + args = [] + +- label = translators['sphinx'].gettext(self._label[min(2, len(args))]) ++ label = sphinx_gettext(self._label[min(2, len(args))]) + text = label.format(name="``{}``".format(name), + args=", ".join("``{}``".format(a) for a in args if a)) + +@@ -382,7 +382,7 @@ class DeprecatedRemoved(Directive): + else: + label = self._removed_label + +- label = translators['sphinx'].gettext(label) ++ label = sphinx_gettext(label) + text = label.format(deprecated=self.arguments[0], removed=self.arguments[1]) + if len(self.arguments) == 3: + inodes, messages = self.state.inline_text(self.arguments[2], +--- /dev/null ++++ b/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst +@@ -0,0 +1 @@ ++Use sphinx.locale._ as the gettext function in pyspecific.py. diff --git a/CVE-2015-20107-mailcap-unsafe-filenames.patch b/CVE-2015-20107-mailcap-unsafe-filenames.patch index 5ab5999..117ef59 100644 --- a/CVE-2015-20107-mailcap-unsafe-filenames.patch +++ b/CVE-2015-20107-mailcap-unsafe-filenames.patch @@ -5,17 +5,16 @@ Subject: [PATCH 1/4] gh-68966: Make mailcap refuse to match unsafe filenames/types/params --- - Doc/library/mailcap.rst | 12 ++++ - Lib/mailcap.py | 26 +++++++++- - Lib/test/test_mailcap.py | 8 ++- - Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst | 4 + - 4 files changed, 46 insertions(+), 4 deletions(-) + Doc/library/mailcap.rst | 12 ++++++++++ + Lib/mailcap.py | 5 ++++ + Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst | 4 +++ + 3 files changed, 21 insertions(+) --- a/Doc/library/mailcap.rst +++ b/Doc/library/mailcap.rst -@@ -60,6 +60,18 @@ standard. However, mailcap files are su - use) to determine whether or not the mailcap line applies. :func:`findmatch` - will automatically check such conditions and skip the entry if the check fails. +@@ -27,6 +27,18 @@ The mailcap format is documented in :rfc + Mechanism For Multimedia Mail Format Information", but is not an internet + standard. However, mailcap files are supported on most Unix systems. + .. versionchanged:: 3.11 + @@ -30,21 +29,13 @@ Subject: [PATCH 1/4] gh-68966: Make mailcap refuse to match unsafe + ``findmatch`` will ignore all mailcap entries which use that value. + A :mod:`warning ` will be raised in either case. - .. function:: getcaps() + .. function:: findmatch(caps, MIMEtype, key='view', filename='/dev/null', plist=[]) --- a/Lib/mailcap.py +++ b/Lib/mailcap.py -@@ -2,6 +2,7 @@ - - import os - import warnings -+import re - - __all__ = ["getcaps","findmatch"] - -@@ -13,6 +14,11 @@ def lineno_sort_key(entry): - else: - return 1, 0 +@@ -19,6 +19,11 @@ _find_unsafe = re.compile(r'[^\xa1-\U001 + class UnsafeMailcapInput(Warning): + """Warning raised when refusing unsafe input""" +_find_unsafe = re.compile(r'[^\xa1-\U0010FFFF\w@+=:,./-]').search + @@ -54,79 +45,6 @@ Subject: [PATCH 1/4] gh-68966: Make mailcap refuse to match unsafe # Part 1: top-level interface. -@@ -165,15 +171,22 @@ def findmatch(caps, MIMEtype, key='view' - entry to use. - - """ -+ if _find_unsafe(filename): -+ msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (filename,) -+ warnings.warn(msg, UnsafeMailcapInput) -+ return None, None - entries = lookup(caps, MIMEtype, key) - # XXX This code should somehow check for the needsterminal flag. - for e in entries: - if 'test' in e: - test = subst(e['test'], filename, plist) -+ if test is None: -+ continue - if test and os.system(test) != 0: - continue - command = subst(e[key], MIMEtype, filename, plist) -- return command, e -+ if command is not None: -+ return command, e - return None, None - - def lookup(caps, MIMEtype, key=None): -@@ -206,6 +219,10 @@ def subst(field, MIMEtype, filename, pli - elif c == 's': - res = res + filename - elif c == 't': -+ if _find_unsafe(MIMEtype): -+ msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,) -+ warnings.warn(msg, UnsafeMailcapInput) -+ return None - res = res + MIMEtype - elif c == '{': - start = i -@@ -213,7 +230,12 @@ def subst(field, MIMEtype, filename, pli - i = i+1 - name = field[start:i] - i = i+1 -- res = res + findparam(name, plist) -+ param = findparam(name, plist) -+ if _find_unsafe(param): -+ msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name) -+ warnings.warn(msg, UnsafeMailcapInput) -+ return None -+ res = res + param - # XXX To do: - # %n == number of parts if type is multipart/* - # %F == list of alternating type and filename for parts ---- a/Lib/test/test_mailcap.py -+++ b/Lib/test/test_mailcap.py -@@ -123,7 +123,8 @@ class HelperFunctionTest(unittest.TestCa - (["", "audio/*", "foo.txt"], ""), - (["echo foo", "audio/*", "foo.txt"], "echo foo"), - (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"), -- (["echo %t", "audio/*", "foo.txt"], "echo audio/*"), -+ (["echo %t", "audio/*", "foo.txt"], None), -+ (["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"), - (["echo \\%t", "audio/*", "foo.txt"], "echo %t"), - (["echo foo", "audio/*", "foo.txt", plist], "echo foo"), - (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3") -@@ -207,7 +208,10 @@ class FindmatchTest(unittest.TestCase): - ('"An audio fragment"', audio_basic_entry)), - ([c, "audio/*"], - {"filename": fname}, -- ("/usr/local/bin/showaudio audio/*", audio_entry)), -+ (None, None)), -+ ([c, "audio/wav"], -+ {"filename": fname}, -+ ("/usr/local/bin/showaudio audio/wav", audio_entry)), - ([c, "message/external-body"], - {"plist": plist}, - ("showexternal /dev/null default john python.org /tmp foo bar", message_entry)) --- /dev/null +++ b/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst @@ -0,0 +1,4 @@ diff --git a/CVE-2022-42919-loc-priv-mulitproc-forksrv.patch b/CVE-2022-42919-loc-priv-mulitproc-forksrv.patch new file mode 100644 index 0000000..e616e48 --- /dev/null +++ b/CVE-2022-42919-loc-priv-mulitproc-forksrv.patch @@ -0,0 +1,59 @@ +From 85178d5849a4d9b5b46e7b91b1ebad7425139b44 Mon Sep 17 00:00:00 2001 +From: "Gregory P. Smith" +Date: Thu, 20 Oct 2022 15:30:09 -0700 +Subject: [PATCH] gh-97514: Don't use Linux abstract sockets for + multiprocessing (GH-98501) + +Linux abstract sockets are insecure as they lack any form of filesystem +permissions so their use allows anyone on the system to inject code into +the process. + +This removes the default preference for abstract sockets in +multiprocessing introduced in Python 3.9+ via +https://github.com/python/cpython/pull/18866 while fixing +https://github.com/python/cpython/issues/84031. + +Explicit use of an abstract socket by a user now generates a +RuntimeWarning. If we choose to keep this warning, it should be +backported to the 3.7 and 3.8 branches. +(cherry picked from commit 49f61068f49747164988ffc5a442d2a63874fc17) + +Co-authored-by: Gregory P. Smith +--- + Lib/multiprocessing/connection.py | 5 --- + Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst | 15 ++++++++++ + 2 files changed, 15 insertions(+), 5 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst + +--- a/Lib/multiprocessing/connection.py ++++ b/Lib/multiprocessing/connection.py +@@ -73,11 +73,6 @@ def arbitrary_address(family): + if family == 'AF_INET': + return ('localhost', 0) + elif family == 'AF_UNIX': +- # Prefer abstract sockets if possible to avoid problems with the address +- # size. When coding portable applications, some implementations have +- # sun_path as short as 92 bytes in the sockaddr_un struct. +- if util.abstract_sockets_supported: +- return f"\0listener-{os.getpid()}-{next(_mmap_counter)}" + return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir()) + elif family == 'AF_PIPE': + return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' % +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst +@@ -0,0 +1,15 @@ ++On Linux the :mod:`multiprocessing` module returns to using filesystem backed ++unix domain sockets for communication with the *forkserver* process instead of ++the Linux abstract socket namespace. Only code that chooses to use the ++:ref:`"forkserver" start method ` is affected. ++ ++Abstract sockets have no permissions and could allow any user on the system in ++the same `network namespace ++`_ (often the ++whole system) to inject code into the multiprocessing *forkserver* process. ++This was a potential privilege escalation. Filesystem based socket permissions ++restrict this to the *forkserver* process user as was the default in Python 3.8 ++and earlier. ++ ++This prevents Linux `CVE-2022-42919 ++`_. diff --git a/CVE-2022-45061-DoS-by-IDNA-decode.patch b/CVE-2022-45061-DoS-by-IDNA-decode.patch new file mode 100644 index 0000000..ee9fc70 --- /dev/null +++ b/CVE-2022-45061-DoS-by-IDNA-decode.patch @@ -0,0 +1,88 @@ +From b8f89940de09a51fdbd8fe4705d3d1d7f1bb0c6a Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Mon, 7 Nov 2022 18:57:10 -0800 +Subject: [PATCH] [3.11] gh-98433: Fix quadratic time idna decoding. (GH-99092) + (GH-99222) + +There was an unnecessary quadratic loop in idna decoding. This restores +the behavior to linear. + +(cherry picked from commit d315722564927c7202dd6e111dc79eaf14240b0d) + +(cherry picked from commit a6f6c3a3d6f2b580f2d87885c9b8a9350ad7bf15) + +Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> +Co-authored-by: Gregory P. Smith +--- + Lib/encodings/idna.py | 32 ++++------ + Lib/test/test_codecs.py | 6 + + Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst | 6 + + 3 files changed, 27 insertions(+), 17 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst + +--- a/Lib/encodings/idna.py ++++ b/Lib/encodings/idna.py +@@ -39,23 +39,21 @@ def nameprep(label): + + # Check bidi + RandAL = [stringprep.in_table_d1(x) for x in label] +- for c in RandAL: +- if c: +- # There is a RandAL char in the string. Must perform further +- # tests: +- # 1) The characters in section 5.8 MUST be prohibited. +- # This is table C.8, which was already checked +- # 2) If a string contains any RandALCat character, the string +- # MUST NOT contain any LCat character. +- if any(stringprep.in_table_d2(x) for x in label): +- raise UnicodeError("Violation of BIDI requirement 2") +- +- # 3) If a string contains any RandALCat character, a +- # RandALCat character MUST be the first character of the +- # string, and a RandALCat character MUST be the last +- # character of the string. +- if not RandAL[0] or not RandAL[-1]: +- raise UnicodeError("Violation of BIDI requirement 3") ++ if any(RandAL): ++ # There is a RandAL char in the string. Must perform further ++ # tests: ++ # 1) The characters in section 5.8 MUST be prohibited. ++ # This is table C.8, which was already checked ++ # 2) If a string contains any RandALCat character, the string ++ # MUST NOT contain any LCat character. ++ if any(stringprep.in_table_d2(x) for x in label): ++ raise UnicodeError("Violation of BIDI requirement 2") ++ # 3) If a string contains any RandALCat character, a ++ # RandALCat character MUST be the first character of the ++ # string, and a RandALCat character MUST be the last ++ # character of the string. ++ if not RandAL[0] or not RandAL[-1]: ++ raise UnicodeError("Violation of BIDI requirement 3") + + return label + +--- a/Lib/test/test_codecs.py ++++ b/Lib/test/test_codecs.py +@@ -1534,6 +1534,12 @@ class IDNACodecTest(unittest.TestCase): + self.assertEqual("pyth\xf6n.org".encode("idna"), b"xn--pythn-mua.org") + self.assertEqual("pyth\xf6n.org.".encode("idna"), b"xn--pythn-mua.org.") + ++ def test_builtin_decode_length_limit(self): ++ with self.assertRaisesRegex(UnicodeError, "too long"): ++ (b"xn--016c"+b"a"*1100).decode("idna") ++ with self.assertRaisesRegex(UnicodeError, "too long"): ++ (b"xn--016c"+b"a"*70).decode("idna") ++ + def test_stream(self): + r = codecs.getreader("idna")(io.BytesIO(b"abc")) + r.read(3) +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst +@@ -0,0 +1,6 @@ ++The IDNA codec decoder used on DNS hostnames by :mod:`socket` or :mod:`asyncio` ++related name resolution functions no longer involves a quadratic algorithm. ++This prevents a potential CPU denial of service if an out-of-spec excessive ++length hostname involving bidirectional characters were decoded. Some protocols ++such as :mod:`urllib` http ``3xx`` redirects potentially allow for an attacker ++to supply such a name. diff --git a/Python-3.10.7.tar.xz.asc b/Python-3.10.7.tar.xz.asc deleted file mode 100644 index 1fc8949..0000000 --- a/Python-3.10.7.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmMV9eMACgkQ/+h0BBaL -2Ec6FQ//eXrKOb5NoocNPIs9o5Jcbk5jtWxnOp3mqjO8D/LBYc8gwovZMPd+903w -In2QWtPSK22ZWdaR4DqizK9GjBRi+Y/ZEFfh3uiPM0I2/jGkr5BXizRZNdTo3GyB -/OuPiBKBVqMgTSGrpO4M24yUekqGdPfzg614GroWSr/16UVVUQADE8DP+BJCTIbk -v+t+AIYsALR0cUO+uqp2QbWf7N2aF+r08g59Vyb09+Nr2ZfjjINIbHQgRtHv7ZoI -7BsWiGW3qIeY8XxYt1/kWv4yMqaTyABdmdEHeM0vCzeEUpurj5072isGvOpI92N4 -LZ6nK8GR4pBS+OfOB7bgzUTC+tQ48wPQwb9lZTuWfSXGYotVdTXs1zW6o1T+vply -MrMZcsc6Y9o8fX3Mkkv4zT9S6JkXtq/MUTIZ27cZr492DzJNaKBG+NqF22FKR35M -ojLK24YpGyw2PCIlUSiFfAqkGNu53U5rP3N71mh7Ao00nx8WhKj4YAci0tBkfHyw -NYoX4tz7ybiR3zV5kyrmJv4G2x89cgosfHuEL2Lr+Irf3PV5vgjXHteAwu8Egdej -myokqzWEwoqNtrK9JsSYE3bcWmrVU9R/siQnNJXKWj+AkHKG0jMsrIh6iRvQGDhp -Q3Avu3ZQ/K9rreZ4Jk1DHX3BoDvRIFdEjBDAB+b6UUQEGE32nj8= -=O8kG ------END PGP SIGNATURE----- diff --git a/Python-3.10.7.tar.xz b/Python-3.10.8.tar.xz similarity index 100% rename from Python-3.10.7.tar.xz rename to Python-3.10.8.tar.xz diff --git a/Python-3.10.8.tar.xz.asc b/Python-3.10.8.tar.xz.asc new file mode 100644 index 0000000..f4690b4 --- /dev/null +++ b/Python-3.10.8.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmNFVREACgkQ/+h0BBaL +2EfmBhAAlIlx22S5RL7ehPDOWlEj06uK87EKEfMXch2DMapEXsrBR2Z+Q3Kb0Le+ +T3vru6k9MUbDGI0pei+o5k621jvg8Gj+0rUKTydAd46Pt9ZoCPWuIdyWKaJBknLu +XsYs6Xiv6Ug7Q3JGy67j8ei6bFoqATyYEe45ljReVfug7VmisjMXHdiyZoAkAFMO +fDZvtfXRY/ZwLcCfK5SkaJqSRVfYowAh1lQqiXDnbfaX40BVCw78YKFsYN//PCpU +DrsE7JFapXQGvCJmcZ+WC8A/WMjyLoKI36w2WDcy8AFKsf49xQWPEWnUzXcJyF2n +zKGxn6kUEMdzelSWWWqMYlckL6Xf87E+CRTRS0MRX6OIrf+zJkeEoWRW8cGT/U8I +1o0hawm9O50nSIjMGzaXGKjWsHLSIeOA9ToLz19TzzO7VikNkXAx5gQcHQu3bJyT +SrMCw/VXJy+0BinBpSX/qZoptX2+6lFfArb/xOZGX2ZhU44+ecPrUHxB8xiw0qr8 +pw16k6nCkW3f0aZ0jrlsfNLsXr9G/ZRu+ugrcTTQ53rfXO0pQ5nxm0CJ5O9twDjw +DIKvuqnOHlSGEcFM4bNDvpqskDnXrK6oyqBvtVhsjdFAp8YHYoM0yALTBlS4v2Xt +Em1BPUXHps0M1AY59KrXe0OpwibJjKhvWoogAS5bqe/mYInBE/Y= +=2vcv +-----END PGP SIGNATURE----- diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index 9d62100..c91498d 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -29,7 +29,7 @@ Create a Python.framework rather than a traditional Unix install. Optional --- a/Misc/NEWS +++ b/Misc/NEWS -@@ -2783,7 +2783,7 @@ C API +@@ -2979,7 +2979,7 @@ C API ----- - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name diff --git a/python310.changes b/python310.changes index 529a368..4380d4b 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,136 @@ +------------------------------------------------------------------- +Wed Nov 9 18:31:23 UTC 2022 - Matej Cepl + +- Add CVE-2022-45061-DoS-by-IDNA-decode.patch to avoid + CVE-2022-45061 (bsc#1205244) allowing DoS by IDNA decoding + extremely long domain names. + +------------------------------------------------------------------- +Thu Nov 3 21:35:28 UTC 2022 - Matej Cepl + +- Add CVE-2022-42919-loc-priv-mulitproc-forksrv.patch to avoid + CVE-2022-42919 (bsc#1204886) avoiding Linux specific local + privilege escalation via the multiprocessing forkserver start + method. + +------------------------------------------------------------------- +Fri Oct 21 10:14:03 UTC 2022 - Matej Cepl + +- Add 98437-sphinx.locale._-as-gettext-in-pyspecific.patch to + allow building of documentation with the latest Sphinx 5.3.0 + (gh#python/cpython#98366). + +------------------------------------------------------------------- +Wed Oct 19 07:12:23 UTC 2022 - Matej Cepl + +- Update to 3.10.8: + - Fix multiplying a list by an integer (list *= int): detect + the integer overflow when the new allocated length is close + to the maximum size. + - Fix a shell code injection vulnerability in the + get-remote-certificate.py example script. The script no + longer uses a shell to run openssl commands. (originally + filed as CVE-2022-37460, later withdrawn) + - Fix command line parsing: reject -X int_max_str_digits option + with no value (invalid) when the PYTHONINTMAXSTRDIGITS + environment variable is set to a valid limit. + - When ValueError is raised if an integer is larger than the + limit, mention the sys.set_int_max_str_digits() function in + the error message. + - The deprecated mailcap module now refuses to inject unsafe + text (filenames, MIME types, parameters) into shell + commands. Instead of using such text, it will warn and act + as if a match was not found (or for test commands, as if the + test failed). + - os.sched_yield() now release the GIL while calling + sched_yield(2). + - Bugfix: PyFunction_GetAnnotations() should return a borrowed + reference. It was returning a new reference. + - Fixed a missing incref/decref pair in + Exception.__setstate__(). + - Fix overly-broad source position information for chained + comparisons used as branching conditions. + - Fix undefined behaviour in _testcapimodule.c. + - At Python exit, sometimes a thread holding the GIL can + wait forever for a thread (usually a daemon thread) which + requested to drop the GIL, whereas the thread already + exited. To fix the race condition, the thread which requested + the GIL drop now resets its request before exiting. + - Fix a possible assertion failure, fatal error, or SystemError + if a line tracing event raises an exception while opcode + tracing is enabled. + - Fix undefined behaviour in C code of null pointer arithmetic. + - Do not expose KeyWrapper in _functools. + - When loading a file with invalid UTF-8 inside a multi-line + string, a correct SyntaxError is emitted. + - Disable incorrect pickling of the C implemented classmethod + descriptors. + - Fix AttributeError missing name and obj attributes in . + object.__getattribute__() bpo-42316: Document some places . + where an assignment expression needs parentheses . + - Wrap network errors consistently in urllib FTP support, so + the test suite doesn’t fail when a network is available but + the public internet is not reachable. + - Fixes AttributeError when subprocess.check_output() is used + with argument input=None and either of the arguments encoding + or errors are used. + - Avoid spurious tracebacks from asyncio when default executor + cleanup is delayed until after the event loop is closed (e.g. + as the result of a keyboard interrupt). + - Avoid a crash in the C version of + asyncio.Future.remove_done_callback() when an evil argument + is passed. + - Remove tokenize.NL check from tabnanny. + - Make Semaphore run faster. + - Fix generation of the default name of + tkinter.Checkbutton. Previously, checkbuttons in different + parent widgets could have the same short name and share + the same state if arguments “name” and “variable” are not + specified. Now they are globally unique. + - Update bundled libexpat to 2.4.9 + - Fix race condition in asyncio where process_exited() called + before the pipe_data_received() leading to inconsistent + output. + - Fixed check in multiprocessing.resource_tracker that + guarantees that the length of a write to a pipe is not + greater than PIPE_BUF. + - Corrected type annotation for dataclass attribute + pstats.FunctionProfile.ncalls to be str. + - Fix the faulthandler implementation of + faulthandler.register(signal, chain=True) if the sigaction() + function is not available: don’t call the previous signal + handler if it’s NULL. + - In inspect, fix overeager replacement of “typing.” in + formatting annotations. + - Fix asyncio.streams.StreamReaderProtocol to keep a strong + reference to the created task, so that it’s not garbage + collected + - Fix handling compiler warnings (SyntaxWarning and + DeprecationWarning) in codeop.compile_command() when checking + for incomplete input. Previously it emitted warnings and + raised a SyntaxError. Now it always returns None for + incomplete input without emitting any warnings. + - Fixed flickering of the turtle window when the tracer is + turned off. + - Allow asyncio.StreamWriter.drain() to be awaited concurrently + by multiple tasks. + - Fix broken asyncio.Semaphore when acquire is cancelled. + - Fix ast.unparse() when ImportFrom.level is None + - Improve performance of urllib.request.getproxies_environment + when there are many environment variables + - Fix ! in c domain ref target syntax via a conf.py patch, so + it works as intended to disable ref target resolution. + - Clarified the conflicting advice given in the ast + documentation about ast.literal_eval() being “safe” for use + on untrusted input while at the same time warning that it + can crash the process. The latter statement is true and is + deemed unfixable without a large amount of work unsuitable + for a bugfix. So we keep the warning and no longer claim that + literal_eval is safe. + - Update tutorial introduction output to use 3.10+ SyntaxError + invalid range. +- Remove upstreamed test-int-timing.patch. + ------------------------------------------------------------------- Sun Sep 18 08:48:51 UTC 2022 - Andreas Schwab diff --git a/python310.spec b/python310.spec index 72d1ce2..d7fee4e 100644 --- a/python310.spec +++ b/python310.spec @@ -67,7 +67,7 @@ Obsoletes: python39%{?1:-%{1}} %define tarversion %{version} %endif # We don't process beta signs well -%define folderversion 3.10.7 +%define folderversion %{tarversion} %define tarname Python-%{tarversion} %define sitedir %{_libdir}/python%{python_version} # three possible ABI kinds: m - pymalloc, d - debug build; see PEP 3149 @@ -103,7 +103,7 @@ Obsoletes: python39%{?1:-%{1}} %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so %bcond_without profileopt Name: %{python_pkg_name}%{psuffix} -Version: 3.10.7 +Version: 3.10.8 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -169,8 +169,15 @@ Patch36: support-expat-CVE-2022-25236-patched.patch # PATCH-FIX-UPSTREAM CVE-2015-20107-mailcap-unsafe-filenames.patch bsc#1198511 mcepl@suse.com # avoid the command injection in the mailcap module. Patch37: CVE-2015-20107-mailcap-unsafe-filenames.patch -# PATCH-FIX-UPSTREAM gh-96710: Make the test timing more lenient for the int/str DoS regression test. (#96717) -Patch38: test-int-timing.patch +# PATCH-FIX-UPSTREAM 98437-sphinx.locale._-as-gettext-in-pyspecific.patch gh#python/cpython#98366 mcepl@suse.com +# this patch makes things totally awesome +Patch38: 98437-sphinx.locale._-as-gettext-in-pyspecific.patch +# PATCH-FIX-UPSTREAM CVE-2022-42919-loc-priv-mulitproc-forksrv.patch bsc#1204886 mcepl@suse.com +# Avoid Linux specific local privilege escalation via the multiprocessing forkserver start method +Patch39: CVE-2022-42919-loc-priv-mulitproc-forksrv.patch +# PATCH-FIX-UPSTREAM CVE-2022-45061-DoS-by-IDNA-decode.patch bsc#1205244 mcepl@suse.com +# Avoid DoS by decoding IDNA for too long domain names +Patch40: CVE-2022-45061-DoS-by-IDNA-decode.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -441,6 +448,8 @@ other applications. %patch36 -p1 %patch37 -p1 %patch38 -p1 +%patch39 -p1 +%patch40 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac diff --git a/subprocess-raise-timeout.patch b/subprocess-raise-timeout.patch index cb39e1a..ef80b3b 100644 --- a/subprocess-raise-timeout.patch +++ b/subprocess-raise-timeout.patch @@ -4,7 +4,7 @@ --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py -@@ -261,7 +261,8 @@ class ProcessTestCase(BaseTestCase): +@@ -267,7 +267,8 @@ class ProcessTestCase(BaseTestCase): "time.sleep(3600)"], # Some heavily loaded buildbots (sparc Debian 3.x) require # this much time to start and print. diff --git a/test-int-timing.patch b/test-int-timing.patch deleted file mode 100644 index 4f7ef27..0000000 --- a/test-int-timing.patch +++ /dev/null @@ -1,78 +0,0 @@ -From 11e3548fd1d3445ccde971d613633b58d73c3016 Mon Sep 17 00:00:00 2001 -From: "Gregory P. Smith" -Date: Fri, 9 Sep 2022 12:51:34 -0700 -Subject: [PATCH] gh-96710: Make the test timing more lenient for the int/str - DoS regression test. (#96717) - -A regression would still absolutely fail and even a flaky pass isn't -harmful as it'd fail most of the time across our N system test runs. - -Windows has a low resolution timer and CI systems are prone to odd -timing so this just gives more leeway to avoid flakiness. ---- - Lib/test/test_int.py | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py -index 800c0b006c..c972b8afb4 100644 ---- a/Lib/test/test_int.py -+++ b/Lib/test/test_int.py -@@ -650,7 +650,8 @@ def test_denial_of_service_prevented_int_to_str(self): - self.assertEqual(len(huge_decimal), digits) - # Ensuring that we chose a slow enough conversion to measure. - # It takes 0.1 seconds on a Zen based cloud VM in an opt build. -- if seconds_to_convert < 0.005: -+ # Some OSes have a low res 1/64s timer, skip if hard to measure. -+ if seconds_to_convert < 1/64: - raise unittest.SkipTest('"slow" conversion took only ' - f'{seconds_to_convert} seconds.') - -@@ -662,7 +663,7 @@ def test_denial_of_service_prevented_int_to_str(self): - str(huge_int) - seconds_to_fail_huge = get_time() - start - self.assertIn('conversion', str(err.exception)) -- self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) -+ self.assertLessEqual(seconds_to_fail_huge, seconds_to_convert/2) - - # Now we test that a conversion that would take 30x as long also fails - # in a similarly fast fashion. -@@ -673,7 +674,7 @@ def test_denial_of_service_prevented_int_to_str(self): - str(extra_huge_int) - seconds_to_fail_extra_huge = get_time() - start - self.assertIn('conversion', str(err.exception)) -- self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) -+ self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/2) - - def test_denial_of_service_prevented_str_to_int(self): - """Regression test: ensure we fail before performing O(N**2) work.""" -@@ -691,7 +692,8 @@ def test_denial_of_service_prevented_str_to_int(self): - seconds_to_convert = get_time() - start - # Ensuring that we chose a slow enough conversion to measure. - # It takes 0.1 seconds on a Zen based cloud VM in an opt build. -- if seconds_to_convert < 0.005: -+ # Some OSes have a low res 1/64s timer, skip if hard to measure. -+ if seconds_to_convert < 1/64: - raise unittest.SkipTest('"slow" conversion took only ' - f'{seconds_to_convert} seconds.') - -@@ -701,7 +703,7 @@ def test_denial_of_service_prevented_str_to_int(self): - int(huge) - seconds_to_fail_huge = get_time() - start - self.assertIn('conversion', str(err.exception)) -- self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) -+ self.assertLessEqual(seconds_to_fail_huge, seconds_to_convert/2) - - # Now we test that a conversion that would take 30x as long also fails - # in a similarly fast fashion. -@@ -712,7 +714,7 @@ def test_denial_of_service_prevented_str_to_int(self): - int(extra_huge) - seconds_to_fail_extra_huge = get_time() - start - self.assertIn('conversion', str(err.exception)) -- self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) -+ self.assertLessEqual(seconds_to_fail_extra_huge, seconds_to_convert/2) - - def test_power_of_two_bases_unlimited(self): - """The limit does not apply to power of 2 bases.""" --- -2.37.3 - From 85ed813980636dff5ad16700b35b288ff558b380e6799b9b723cda7606131e16 Mon Sep 17 00:00:00 2001 From: Ruediger Oertel Date: Wed, 15 Mar 2023 19:34:05 +0000 Subject: [PATCH 05/23] Set link to python310.28117 via maintenance_release request --- ...nx.locale._-as-gettext-in-pyspecific.patch | 54 ---- CVE-2015-20107-mailcap-unsafe-filenames.patch | 54 ---- ...022-42919-loc-priv-mulitproc-forksrv.patch | 59 ---- CVE-2022-45061-DoS-by-IDNA-decode.patch | 88 ------ CVE-2023-24329-blank-URL-bypass.patch | 55 ++++ Python-3.10.8.tar.xz => Python-3.10.10.tar.xz | 0 Python-3.10.10.tar.xz.asc | 16 + Python-3.10.8.tar.xz.asc | 16 - bpo-31046_ensurepip_honours_prefix.patch | 8 +- fix_configure_rst.patch | 2 +- invalid-json.patch | 44 +++ python310.changes | 283 +++++++++++++++--- python310.spec | 36 +-- 13 files changed, 381 insertions(+), 334 deletions(-) delete mode 100644 98437-sphinx.locale._-as-gettext-in-pyspecific.patch delete mode 100644 CVE-2015-20107-mailcap-unsafe-filenames.patch delete mode 100644 CVE-2022-42919-loc-priv-mulitproc-forksrv.patch delete mode 100644 CVE-2022-45061-DoS-by-IDNA-decode.patch create mode 100644 CVE-2023-24329-blank-URL-bypass.patch rename Python-3.10.8.tar.xz => Python-3.10.10.tar.xz (100%) create mode 100644 Python-3.10.10.tar.xz.asc delete mode 100644 Python-3.10.8.tar.xz.asc create mode 100644 invalid-json.patch diff --git a/98437-sphinx.locale._-as-gettext-in-pyspecific.patch b/98437-sphinx.locale._-as-gettext-in-pyspecific.patch deleted file mode 100644 index 680c2c1..0000000 --- a/98437-sphinx.locale._-as-gettext-in-pyspecific.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 5775f51691d7d64fb676586e008b41261ce64ac2 Mon Sep 17 00:00:00 2001 -From: "Matt.Wang" -Date: Wed, 19 Oct 2022 14:49:08 +0800 -Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for - backward-compatibility in pyspecific.py - -[why] spinix 5.3 changed locale.translators from a defaultdict(gettext.NullTranslations) to a dict, which leads to failure of pyspecific.py. Use sphinx.locale._ as gettext to fix the issue. ---- - Doc/tools/extensions/pyspecific.py | 8 ++++---- - Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst | 1 + - 2 files changed, 5 insertions(+), 4 deletions(-) - ---- a/Doc/tools/extensions/pyspecific.py -+++ b/Doc/tools/extensions/pyspecific.py -@@ -26,7 +26,7 @@ try: - from sphinx.errors import NoUri - except ImportError: - from sphinx.environment import NoUri --from sphinx.locale import translators -+from sphinx.locale import _ as sphinx_gettext - from sphinx.util import status_iterator, logging - from sphinx.util.nodes import split_explicit_title - from sphinx.writers.text import TextWriter, TextTranslator -@@ -109,7 +109,7 @@ class ImplementationDetail(Directive): - def run(self): - self.assert_has_content() - pnode = nodes.compound(classes=['impl-detail']) -- label = translators['sphinx'].gettext(self.label_text) -+ label = sphinx_gettext(self.label_text) - content = self.content - add_text = nodes.strong(label, label) - self.state.nested_parse(content, self.content_offset, pnode) -@@ -203,7 +203,7 @@ class AuditEvent(Directive): - else: - args = [] - -- label = translators['sphinx'].gettext(self._label[min(2, len(args))]) -+ label = sphinx_gettext(self._label[min(2, len(args))]) - text = label.format(name="``{}``".format(name), - args=", ".join("``{}``".format(a) for a in args if a)) - -@@ -382,7 +382,7 @@ class DeprecatedRemoved(Directive): - else: - label = self._removed_label - -- label = translators['sphinx'].gettext(label) -+ label = sphinx_gettext(label) - text = label.format(deprecated=self.arguments[0], removed=self.arguments[1]) - if len(self.arguments) == 3: - inodes, messages = self.state.inline_text(self.arguments[2], ---- /dev/null -+++ b/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst -@@ -0,0 +1 @@ -+Use sphinx.locale._ as the gettext function in pyspecific.py. diff --git a/CVE-2015-20107-mailcap-unsafe-filenames.patch b/CVE-2015-20107-mailcap-unsafe-filenames.patch deleted file mode 100644 index 117ef59..0000000 --- a/CVE-2015-20107-mailcap-unsafe-filenames.patch +++ /dev/null @@ -1,54 +0,0 @@ -From c3e7f139b440d7424986204e9f3fc2275aea3377 Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Wed, 27 Apr 2022 18:17:33 +0200 -Subject: [PATCH 1/4] gh-68966: Make mailcap refuse to match unsafe - filenames/types/params - ---- - Doc/library/mailcap.rst | 12 ++++++++++ - Lib/mailcap.py | 5 ++++ - Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst | 4 +++ - 3 files changed, 21 insertions(+) - ---- a/Doc/library/mailcap.rst -+++ b/Doc/library/mailcap.rst -@@ -27,6 +27,18 @@ The mailcap format is documented in :rfc - Mechanism For Multimedia Mail Format Information", but is not an internet - standard. However, mailcap files are supported on most Unix systems. - -+ .. versionchanged:: 3.11 -+ -+ To prevent security issues with shell metacharacters (symbols that have -+ special effects in a shell command line), ``findmatch`` will refuse -+ to inject ASCII characters other than alphanumerics and ``@+=:,./-_`` -+ into the returned command line. -+ -+ If a disallowed character appears in *filename*, ``findmatch`` will always -+ return ``(None, None)`` as if no entry was found. -+ If such a character appears elsewhere (a value in *plist* or in *MIMEtype*), -+ ``findmatch`` will ignore all mailcap entries which use that value. -+ A :mod:`warning ` will be raised in either case. - - .. function:: findmatch(caps, MIMEtype, key='view', filename='/dev/null', plist=[]) - ---- a/Lib/mailcap.py -+++ b/Lib/mailcap.py -@@ -19,6 +19,11 @@ _find_unsafe = re.compile(r'[^\xa1-\U001 - class UnsafeMailcapInput(Warning): - """Warning raised when refusing unsafe input""" - -+_find_unsafe = re.compile(r'[^\xa1-\U0010FFFF\w@+=:,./-]').search -+ -+class UnsafeMailcapInput(Warning): -+ """Warning raised when refusing unsafe input""" -+ - - # Part 1: top-level interface. - ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst -@@ -0,0 +1,4 @@ -+The deprecated mailcap module now refuses to inject unsafe text (filenames, -+MIME types, parameters) into shell commands. Instead of using such text, it -+will warn and act as if a match was not found (or for test commands, as if -+the test failed). diff --git a/CVE-2022-42919-loc-priv-mulitproc-forksrv.patch b/CVE-2022-42919-loc-priv-mulitproc-forksrv.patch deleted file mode 100644 index e616e48..0000000 --- a/CVE-2022-42919-loc-priv-mulitproc-forksrv.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 85178d5849a4d9b5b46e7b91b1ebad7425139b44 Mon Sep 17 00:00:00 2001 -From: "Gregory P. Smith" -Date: Thu, 20 Oct 2022 15:30:09 -0700 -Subject: [PATCH] gh-97514: Don't use Linux abstract sockets for - multiprocessing (GH-98501) - -Linux abstract sockets are insecure as they lack any form of filesystem -permissions so their use allows anyone on the system to inject code into -the process. - -This removes the default preference for abstract sockets in -multiprocessing introduced in Python 3.9+ via -https://github.com/python/cpython/pull/18866 while fixing -https://github.com/python/cpython/issues/84031. - -Explicit use of an abstract socket by a user now generates a -RuntimeWarning. If we choose to keep this warning, it should be -backported to the 3.7 and 3.8 branches. -(cherry picked from commit 49f61068f49747164988ffc5a442d2a63874fc17) - -Co-authored-by: Gregory P. Smith ---- - Lib/multiprocessing/connection.py | 5 --- - Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst | 15 ++++++++++ - 2 files changed, 15 insertions(+), 5 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst - ---- a/Lib/multiprocessing/connection.py -+++ b/Lib/multiprocessing/connection.py -@@ -73,11 +73,6 @@ def arbitrary_address(family): - if family == 'AF_INET': - return ('localhost', 0) - elif family == 'AF_UNIX': -- # Prefer abstract sockets if possible to avoid problems with the address -- # size. When coding portable applications, some implementations have -- # sun_path as short as 92 bytes in the sockaddr_un struct. -- if util.abstract_sockets_supported: -- return f"\0listener-{os.getpid()}-{next(_mmap_counter)}" - return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir()) - elif family == 'AF_PIPE': - return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' % ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst -@@ -0,0 +1,15 @@ -+On Linux the :mod:`multiprocessing` module returns to using filesystem backed -+unix domain sockets for communication with the *forkserver* process instead of -+the Linux abstract socket namespace. Only code that chooses to use the -+:ref:`"forkserver" start method ` is affected. -+ -+Abstract sockets have no permissions and could allow any user on the system in -+the same `network namespace -+`_ (often the -+whole system) to inject code into the multiprocessing *forkserver* process. -+This was a potential privilege escalation. Filesystem based socket permissions -+restrict this to the *forkserver* process user as was the default in Python 3.8 -+and earlier. -+ -+This prevents Linux `CVE-2022-42919 -+`_. diff --git a/CVE-2022-45061-DoS-by-IDNA-decode.patch b/CVE-2022-45061-DoS-by-IDNA-decode.patch deleted file mode 100644 index ee9fc70..0000000 --- a/CVE-2022-45061-DoS-by-IDNA-decode.patch +++ /dev/null @@ -1,88 +0,0 @@ -From b8f89940de09a51fdbd8fe4705d3d1d7f1bb0c6a Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Mon, 7 Nov 2022 18:57:10 -0800 -Subject: [PATCH] [3.11] gh-98433: Fix quadratic time idna decoding. (GH-99092) - (GH-99222) - -There was an unnecessary quadratic loop in idna decoding. This restores -the behavior to linear. - -(cherry picked from commit d315722564927c7202dd6e111dc79eaf14240b0d) - -(cherry picked from commit a6f6c3a3d6f2b580f2d87885c9b8a9350ad7bf15) - -Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> -Co-authored-by: Gregory P. Smith ---- - Lib/encodings/idna.py | 32 ++++------ - Lib/test/test_codecs.py | 6 + - Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst | 6 + - 3 files changed, 27 insertions(+), 17 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst - ---- a/Lib/encodings/idna.py -+++ b/Lib/encodings/idna.py -@@ -39,23 +39,21 @@ def nameprep(label): - - # Check bidi - RandAL = [stringprep.in_table_d1(x) for x in label] -- for c in RandAL: -- if c: -- # There is a RandAL char in the string. Must perform further -- # tests: -- # 1) The characters in section 5.8 MUST be prohibited. -- # This is table C.8, which was already checked -- # 2) If a string contains any RandALCat character, the string -- # MUST NOT contain any LCat character. -- if any(stringprep.in_table_d2(x) for x in label): -- raise UnicodeError("Violation of BIDI requirement 2") -- -- # 3) If a string contains any RandALCat character, a -- # RandALCat character MUST be the first character of the -- # string, and a RandALCat character MUST be the last -- # character of the string. -- if not RandAL[0] or not RandAL[-1]: -- raise UnicodeError("Violation of BIDI requirement 3") -+ if any(RandAL): -+ # There is a RandAL char in the string. Must perform further -+ # tests: -+ # 1) The characters in section 5.8 MUST be prohibited. -+ # This is table C.8, which was already checked -+ # 2) If a string contains any RandALCat character, the string -+ # MUST NOT contain any LCat character. -+ if any(stringprep.in_table_d2(x) for x in label): -+ raise UnicodeError("Violation of BIDI requirement 2") -+ # 3) If a string contains any RandALCat character, a -+ # RandALCat character MUST be the first character of the -+ # string, and a RandALCat character MUST be the last -+ # character of the string. -+ if not RandAL[0] or not RandAL[-1]: -+ raise UnicodeError("Violation of BIDI requirement 3") - - return label - ---- a/Lib/test/test_codecs.py -+++ b/Lib/test/test_codecs.py -@@ -1534,6 +1534,12 @@ class IDNACodecTest(unittest.TestCase): - self.assertEqual("pyth\xf6n.org".encode("idna"), b"xn--pythn-mua.org") - self.assertEqual("pyth\xf6n.org.".encode("idna"), b"xn--pythn-mua.org.") - -+ def test_builtin_decode_length_limit(self): -+ with self.assertRaisesRegex(UnicodeError, "too long"): -+ (b"xn--016c"+b"a"*1100).decode("idna") -+ with self.assertRaisesRegex(UnicodeError, "too long"): -+ (b"xn--016c"+b"a"*70).decode("idna") -+ - def test_stream(self): - r = codecs.getreader("idna")(io.BytesIO(b"abc")) - r.read(3) ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst -@@ -0,0 +1,6 @@ -+The IDNA codec decoder used on DNS hostnames by :mod:`socket` or :mod:`asyncio` -+related name resolution functions no longer involves a quadratic algorithm. -+This prevents a potential CPU denial of service if an out-of-spec excessive -+length hostname involving bidirectional characters were decoded. Some protocols -+such as :mod:`urllib` http ``3xx`` redirects potentially allow for an attacker -+to supply such a name. diff --git a/CVE-2023-24329-blank-URL-bypass.patch b/CVE-2023-24329-blank-URL-bypass.patch new file mode 100644 index 0000000..d88dcfe --- /dev/null +++ b/CVE-2023-24329-blank-URL-bypass.patch @@ -0,0 +1,55 @@ +From a284d69de1d1a42714576d4a9562145a94e62127 Mon Sep 17 00:00:00 2001 +From: Ben Kallus +Date: Sat, 12 Nov 2022 15:43:33 -0500 +Subject: [PATCH 1/2] gh-99418: Prevent urllib.parse.urlparse from accepting + schemes that don't begin with an alphabetical ASCII character. + +--- + Lib/test/test_urlparse.py | 18 ++++++++++ + Lib/urllib/parse.py | 2 - + Misc/NEWS.d/next/Library/2022-11-12-15-45-51.gh-issue-99418.FxfAXS.rst | 2 + + 3 files changed, 21 insertions(+), 1 deletion(-) + +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -668,6 +668,24 @@ class UrlParseTestCase(unittest.TestCase + with self.assertRaises(ValueError): + p.port + ++ def test_attributes_bad_scheme(self): ++ """Check handling of invalid schemes.""" ++ for bytes in (False, True): ++ for parse in (urllib.parse.urlsplit, urllib.parse.urlparse): ++ for scheme in (".", "+", "-", "0", "http&", "६http"): ++ with self.subTest(bytes=bytes, parse=parse, scheme=scheme): ++ url = scheme + "://www.example.net" ++ if bytes: ++ if url.isascii(): ++ url = url.encode("ascii") ++ else: ++ continue ++ p = parse(url) ++ if bytes: ++ self.assertEqual(p.scheme, b"") ++ else: ++ self.assertEqual(p.scheme, "") ++ + def test_attributes_without_netloc(self): + # This example is straight from RFC 3261. It looks like it + # should allow the username, hostname, and port to be filled +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -469,7 +469,7 @@ def urlsplit(url, scheme='', allow_fragm + clear_cache() + netloc = query = fragment = '' + i = url.find(':') +- if i > 0: ++ if i > 0 and url[0].isascii() and url[0].isalpha(): + for c in url[:i]: + if c not in scheme_chars: + break +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2022-11-12-15-45-51.gh-issue-99418.FxfAXS.rst +@@ -0,0 +1,2 @@ ++Fix bug in :func:`urllib.parse.urlparse` that causes URL schemes that begin ++with a digit, a plus sign, or a minus sign to be parsed incorrectly. diff --git a/Python-3.10.8.tar.xz b/Python-3.10.10.tar.xz similarity index 100% rename from Python-3.10.8.tar.xz rename to Python-3.10.10.tar.xz diff --git a/Python-3.10.10.tar.xz.asc b/Python-3.10.10.tar.xz.asc new file mode 100644 index 0000000..0bd900c --- /dev/null +++ b/Python-3.10.10.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmPiQfoACgkQ/+h0BBaL +2EcB8hAAmFEIHZopWn+A4tDxd001eViLrOmjygqPn1doAQ3dAgyESt4Z/HDtN6rB ++6z5rsx+qdcP9kfb/+3V0gKBh/3V4bEpnD+EQtpONWhKbCcqOfq1ok1V+uNH8uOF +ixxWkY+MWJzPPhlQiW/sm9FP6CdnaeriKf1JMCUt9aiganpo2CQv5gPE/0PlSGO5 +BEKjCcyHHPIEAxC6jLm/+33PSzbhGq+YstK/1tcqUrJfkifipovmSZeFyzULPonK +MATPyliOupo3ixPs3LoJUjNpGD4fH+p2Lg1ZOgYv7vGmeLcadNVanRlqRg76m+ke +zvp/MAqQg4Fr75m2+mfDG/Md+PrSMvz71i55a1Q1NcYdW6QR62m08FCZg7/+t5pD +H91ywhMqTv1nySsEZGfuETPTs7gMCtyBeDjIhXBMcfbhGivd7r5zZJ8MUD/FSASC +fQ/vEVeHWQeWpfFgxLfLmRnkjIS7JCGlM9z6zsZqbppWqeA94sBIf4ka2JG2DnGP +1Pvn+ragiHt1++i2yVhmoAB0t44/SgXacCce5AT3yB71brT21cOXQs0Gq80MwVPI +nVbzdOtuGNGcvEi2fbO2IEcgegSHaOHo9PvYTRropSz3V7A95x8mA1xjZf2y77H5 +/mfJ4687YIItCIcNE5Zzj6GspWlWP31OvRFIIefnKYf2JuU+qt8= +=B3xo +-----END PGP SIGNATURE----- diff --git a/Python-3.10.8.tar.xz.asc b/Python-3.10.8.tar.xz.asc deleted file mode 100644 index f4690b4..0000000 --- a/Python-3.10.8.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmNFVREACgkQ/+h0BBaL -2EfmBhAAlIlx22S5RL7ehPDOWlEj06uK87EKEfMXch2DMapEXsrBR2Z+Q3Kb0Le+ -T3vru6k9MUbDGI0pei+o5k621jvg8Gj+0rUKTydAd46Pt9ZoCPWuIdyWKaJBknLu -XsYs6Xiv6Ug7Q3JGy67j8ei6bFoqATyYEe45ljReVfug7VmisjMXHdiyZoAkAFMO -fDZvtfXRY/ZwLcCfK5SkaJqSRVfYowAh1lQqiXDnbfaX40BVCw78YKFsYN//PCpU -DrsE7JFapXQGvCJmcZ+WC8A/WMjyLoKI36w2WDcy8AFKsf49xQWPEWnUzXcJyF2n -zKGxn6kUEMdzelSWWWqMYlckL6Xf87E+CRTRS0MRX6OIrf+zJkeEoWRW8cGT/U8I -1o0hawm9O50nSIjMGzaXGKjWsHLSIeOA9ToLz19TzzO7VikNkXAx5gQcHQu3bJyT -SrMCw/VXJy+0BinBpSX/qZoptX2+6lFfArb/xOZGX2ZhU44+ecPrUHxB8xiw0qr8 -pw16k6nCkW3f0aZ0jrlsfNLsXr9G/ZRu+ugrcTTQ53rfXO0pQ5nxm0CJ5O9twDjw -DIKvuqnOHlSGEcFM4bNDvpqskDnXrK6oyqBvtVhsjdFAp8YHYoM0yALTBlS4v2Xt -Em1BPUXHps0M1AY59KrXe0OpwibJjKhvWoogAS5bqe/mYInBE/Y= -=2vcv ------END PGP SIGNATURE----- diff --git a/bpo-31046_ensurepip_honours_prefix.patch b/bpo-31046_ensurepip_honours_prefix.patch index 1c1f3a7..6afc257 100644 --- a/bpo-31046_ensurepip_honours_prefix.patch +++ b/bpo-31046_ensurepip_honours_prefix.patch @@ -15,7 +15,7 @@ Co-Authored-By: Xavier de Gaye --- a/Doc/library/ensurepip.rst +++ b/Doc/library/ensurepip.rst -@@ -56,8 +56,9 @@ is at least as recent as the one availab +@@ -58,8 +58,9 @@ is at least as recent as the one availab By default, ``pip`` is installed into the current virtual environment (if one is active) or into the system site packages (if there is no active virtual environment). The installation location can be controlled @@ -26,7 +26,7 @@ Co-Authored-By: Xavier de Gaye * ``--root ``: Installs ``pip`` relative to the given root directory rather than the root of the currently active virtual environment (if any) or the default root for the current Python installation. -@@ -89,7 +90,7 @@ Module API +@@ -91,7 +92,7 @@ Module API Returns a string specifying the available version of pip that will be installed when bootstrapping an environment. @@ -35,7 +35,7 @@ Co-Authored-By: Xavier de Gaye altinstall=False, default_pip=False, \ verbosity=0) -@@ -99,6 +100,8 @@ Module API +@@ -101,6 +102,8 @@ Module API If *root* is ``None``, then installation uses the default install location for the current environment. @@ -44,7 +44,7 @@ Co-Authored-By: Xavier de Gaye *upgrade* indicates whether or not to upgrade an existing installation of an earlier version of ``pip`` to the available version. -@@ -119,6 +122,8 @@ Module API +@@ -121,6 +124,8 @@ Module API *verbosity* controls the level of output to :data:`sys.stdout` from the bootstrapping operation. diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index c91498d..7e81a09 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -29,7 +29,7 @@ Create a Python.framework rather than a traditional Unix install. Optional --- a/Misc/NEWS +++ b/Misc/NEWS -@@ -2979,7 +2979,7 @@ C API +@@ -3422,7 +3422,7 @@ C API ----- - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name diff --git a/invalid-json.patch b/invalid-json.patch new file mode 100644 index 0000000..d3a25ba --- /dev/null +++ b/invalid-json.patch @@ -0,0 +1,44 @@ +--- + Doc/howto/logging-cookbook.rst | 24 ++++++++++++++---------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +--- a/Doc/howto/logging-cookbook.rst ++++ b/Doc/howto/logging-cookbook.rst +@@ -340,10 +340,12 @@ adding a ``filters`` section parallel to + + .. code-block:: json + +- "filters": { +- "warnings_and_below": { +- "()" : "__main__.filter_maker", +- "level": "WARNING" ++ { ++ "filters": { ++ "warnings_and_below": { ++ "()" : "__main__.filter_maker", ++ "level": "WARNING" ++ } + } + } + +@@ -351,12 +353,14 @@ and changing the section on the ``stdout + + .. code-block:: json + +- "stdout": { +- "class": "logging.StreamHandler", +- "level": "INFO", +- "formatter": "simple", +- "stream": "ext://sys.stdout", +- "filters": ["warnings_and_below"] ++ { ++ "stdout": { ++ "class": "logging.StreamHandler", ++ "level": "INFO", ++ "formatter": "simple", ++ "stream": "ext://sys.stdout", ++ "filters": ["warnings_and_below"] ++ } + } + + A filter is just a function, so we can define the ``filter_maker`` (a factory diff --git a/python310.changes b/python310.changes index 4380d4b..21c207e 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,206 @@ +------------------------------------------------------------------- +Mon Mar 13 08:39:53 UTC 2023 - Matej Cepl + +- Add invalid-json.patch fixing invalid JSON in + Doc/howto/logging-cookbook.rst (somehow similar to + gh#python/cpython#102582). + +------------------------------------------------------------------- +Wed Mar 1 20:59:04 UTC 2023 - Matej Cepl + +- Update to 3.10.10: + Bug fixes and regressions handling, no change of behaviour and + no security bugs fixed. +- Add CVE-2023-24329-blank-URL-bypass.patch (CVE-2023-24329, + bsc#1208471) blocklists bypass via the urllib.parse component + when supplying a URL that starts with blank characters + +------------------------------------------------------------------- +Tue Feb 21 11:34:49 UTC 2023 - Matej Cepl + +- Add provides for readline and sqlite3 to the main Python + package. + +------------------------------------------------------------------- +Fri Jan 27 15:00:21 UTC 2023 - Thorsten Kukuk + +- Disable NIS for new products, it's deprecated and gets removed + +------------------------------------------------------------------- +Thu Dec 8 14:42:15 UTC 2022 - Matej Cepl + +- Update to 3.10.9: + - python -m http.server no longer allows terminal + control characters sent within a garbage request to be + printed to the stderr server lo This is done by changing + the http.server BaseHTTPRequestHandler .log_message method + to replace control characters with a \xHH hex escape before + printin + - Avoid publishing list of active per-interpreter + audit hooks via the gc module + - The IDNA codec decoder used on DNS hostnames by + socket or asyncio related name resolution functions no + longer involves a quadratic algorithm. This prevents a + potential CPU denial of service if an out-of-spec excessive + length hostname involving bidirectional characters were + decoded. Some protocols such as urllib http 3xx redirects + potentially allow for an attacker to supply such a name. + - Update bundled libexpat to 2.5.0 + - Port XKCP’s fix for the buffer overflows in SHA-3 + (CVE-2022-37454). + - On Linux the multiprocessing module returns + to using filesystem backed unix domain sockets for + communication with the forkserver process instead of the + Linux abstract socket namespace. Only code that chooses + to use the “forkserver” start method is affected Abstract + sockets have no permissions and could allow any user + on the system in the same network namespace (often the + whole system) to inject code into the multiprocessing + forkserver process. This was a potential privilege + escalation. Filesystem based socket permissions restrict + this to the forkserver process user as was the default in + Python 3.8 and earlier This prevents Linux CVE-2022-42919 + - Fix a reference bug in _imp.create_builtin() + after the creation of the first sub-interpreter for modules + builtins and sys. Patch by Victor Stinner. + - Fixed a bug that was causing a buffer overflow if + the tokenizer copies a line missing the newline caracter + from a file that is as long as the available tokenizer + buffer. Patch by Pablo galindo + - Update faulthandler to emit an error message with + the proper unexpected signal number. Patch by Dong-hee Na. + - Fix subscription of types.GenericAlias instances + containing bare generic types: for example tuple[A, T][int], + where A is a generic type, and T is a type variable. + - Fix detection of MAC addresses for uuid on certain + OSs. Patch by Chaim Sanders + - Print exception class name instead of its string + representation when raising errors from ctypes calls. + - Allow pdb to locate source for frozen modules in + the standard library. + - Raise ValueError instead of SystemError when + methods of uninitialized io.IncrementalNewlineDecoder objects + are called. Patch by Oren Milman. + - Fix a possible assertion failure in io.FileIO when + the opener returns an invalid file descriptor. + - Also escape s in the http.server + BaseHTTPRequestHandler.log_message so that it is technically + possible to parse the line and reconstruct what the original + data was. Without this a xHH is ambiguious as to if it is a + hex replacement we put in or the characters r”x” came through + in the original request line. + - asyncio.get_event_loop() now only emits a + deprecation warning when a new event loop was created + implicitly. It no longer emits a deprecation warning if the + current event loop was set. + - Fix bug when calling trace.CoverageResults with + valid infile. + - Fix a bug in handling class cleanups in + unittest.TestCase. Now addClassCleanup() uses separate lists + for different TestCase subclasses, and doClassCleanups() only + cleans up the particular class. + - Release the GIL when calling termios APIs to avoid + blocking threads. + - Fix ast.increment_lineno() to also cover + ast.TypeIgnore when changing line numbers. + - Fixed bug where inspect.signature() reported + incorrect arguments for decorated methods. + - Fix SystemError in ctypes when exception was not + set during __initsubclass__. + - Fix statistics.NormalDist pickle with 0 and 1 + protocols. + - Update the bundled copy of pip to version 22.3.1. + - Apply bugfixes from importlib_metadata 4.11.4, + namely: In PathDistribution._name_from_stem, avoid + including parts of the extension in the result. In + PathDistribution._normalized_name, ensure names loaded from + the stem of the filename are also normalized, ensuring + duplicate entry points by packages varying only by + non-normalized name are hidden. + - Clean up refleak on failed module initialisation in + _zoneinfo + - Clean up refleaks on failed module initialisation + in in _pickle + - Clean up refleak on failed module initialisation in + _io. + - Fix memory leak in math.dist() when both points + don’t have the same dimension. Patch by Kumar Aditya. + - Fix argument typechecks in _overlapped.WSAConnect() + and _overlapped.Overlapped.WSASendTo() functions. + - Fix internal error in the re module which in + very rare circumstances prevented compilation of a regular + expression containing a conditional expression without the + “else” branch. + - Fix asyncio.StreamWriter.drain() to call + protocol.connection_lost callback only once on Windows. + - Add a mutex to unittest.mock.NonCallableMock to + protect concurrent access to mock attributes. + - Fix hang on Windows in subprocess.wait_closed() in + asyncio with ProactorEventLoop. Patch by Kumar Aditya. + - Fix infinite loop in unittest when a + self-referencing chained exception is raised + - tkinter.Text.count() raises now an exception for + options starting with “-” instead of silently ignoring them. + - On uname_result, restored expectation that _fields + and _asdict would include all six properties including + processor. + - Update the bundled copies of pip and setuptools to + versions 22.3 and 65.5.0 respectively. + - Fix bug in urllib.parse.urlparse() that causes + certain port numbers containing whitespace, underscores, + plus and minus signs, or non-ASCII digits to be incorrectly + accepted. + - Allow venv to pass along PYTHON* variables to + ensurepip and pip when they do not impact path resolution + - On macOS, fix a crash in syslog.syslog() in + multi-threaded applications. On macOS, the libc syslog() + function is not thread-safe, so syslog.syslog() no longer + releases the GIL to call it. Patch by Victor Stinner. + - Allow BUILTINS to be a valid field name for frozen + dataclasses. + - Make sure patch.dict() can be applied on async + functions. + - To avoid apparent memory leaks when + asyncio.open_connection() raises, break reference cycles + generated by local exception and future instances (which has + exception instance as its member var). Patch by Dong Uk, + Kang. + - Prevent error when activating venv in nested fish + instances. + - Restrict use of sockets instead of pipes for stdin + of subprocesses created by asyncio to AIX platform only. + - shutil.copytree() now applies the + ignore_dangling_symlinks argument recursively. + - Fix IndexError in argparse.ArgumentParser when a + store_true action is given an explicit argument. + - Document that calling variadic functions with + ctypes requires special care on macOS/arm64 (and possibly + other platforms). + - Skip test_normalization() of test_unicodedata + if it fails to download NormalizationTest.txt file from + pythontest.net. Patch by Victor Stinner. + - Some C API tests were moved into the new + Lib/test/test_capi/ directory. + - Fix -Wimplicit-int, -Wstrict-prototypes, and + -Wimplicit-function-declaration compiler warnings in + configure checks. + - Fix -Wimplicit-int compiler warning in configure + check for PTHREAD_SCOPE_SYSTEM. + - Specify the full path to the source location for + make docclean (needed for cross-builds). + - Fix NO_MISALIGNED_ACCESSES being not defined + for the SHA3 extension when HAVE_ALIGNED_REQUIRED is + set. Allowing builds on hardware that unaligned memory + accesses are not allowed. + - Fix handling of module docstrings in + Tools/i18n/pygettext.py. + +- Remove upstreamed patches: + - 98437-sphinx.locale._-as-gettext-in-pyspecific.patch + - CVE-2015-20107-mailcap-unsafe-filenames.patch + - CVE-2022-42919-loc-priv-mulitproc-forksrv.patch + - CVE-2022-45061-DoS-by-IDNA-decode.patch + ------------------------------------------------------------------- Wed Nov 9 18:31:23 UTC 2022 - Matej Cepl @@ -955,7 +1158,7 @@ Thu Mar 24 18:55:46 UTC 2022 - David Anes Tue Feb 22 05:53:06 UTC 2022 - Steve Kowalik - Add patch support-expat-245.patch: - * Support Expat >= 2.4.5 + * Support Expat >= 2.4.5 ------------------------------------------------------------------- Tue Feb 15 23:05:55 UTC 2022 - Matej Cepl @@ -1145,7 +1348,7 @@ Sat Jun 5 21:21:38 UTC 2021 - Matej Cepl ------------------------------------------------------------------- Fri Jun 4 21:36:30 UTC 2021 - Dirk Müller -- allow build with Sphinx >= 3.x +- allow build with Sphinx >= 3.x ------------------------------------------------------------------- Wed Jun 2 13:12:04 UTC 2021 - Dan Čermák @@ -1697,7 +1900,7 @@ Sat Dec 12 14:29:33 UTC 2020 - Matej Cepl Thu Dec 10 00:26:51 UTC 2020 - Benjamin Greiner - Last try before this results in an editwar: - * remove importlib_resources and importlib-metadata + * remove importlib_resources and importlib-metadata provides/obsoletes * import importlib_resources is not the same as import importlib.resources, same for metadata @@ -1814,54 +2017,54 @@ Tue Jul 21 09:53:06 UTC 2020 - Callum Farmer - Removed CVE-2019-20907_tarfile-inf-loop.patch: fixed in upstream - Removed recursion.tar: contained in upstream - Update to 3.9.0b5: - - bpo-41304: Fixes python3x._pth being ignored on Windows, caused + - bpo-41304: Fixes python3x._pth being ignored on Windows, caused by the fix for bpo-29778 (CVE-2020-15801). - bpo-41162: Audit hooks are now cleared later during finalization to avoid missing events. - - bpo-29778: Ensure python3.dll is loaded from correct locations + - bpo-29778: Ensure python3.dll is loaded from correct locations when Python is embedded (CVE-2020-15523). - - bpo-39603: Prevent http header injection by rejecting control + - bpo-39603: Prevent http header injection by rejecting control characters in http.client.putrequest(…). - bpo-41295: Resolve a regression in CPython 3.8.4 where defining - “__setattr__” in a multi-inheritance setup and + “__setattr__” in a multi-inheritance setup and calling up the hierarchy chain could fail if builtins/extension types were involved in the base types. - - bpo-41247: Always cache the running loop holder when running + - bpo-41247: Always cache the running loop holder when running asyncio.set_running_loop. - - bpo-41252: Fix incorrect refcounting in + - bpo-41252: Fix incorrect refcounting in _ssl.c’s _servername_callback(). - - bpo-41215: Use non-NULL default values in the PEG parser + - bpo-41215: Use non-NULL default values in the PEG parser keyword list to overcome a bug that was ' preventing Python from being properly compiled when using the XLC compiler. Patch by Pablo Galindo. - - bpo-41218: Python 3.8.3 had a regression where compiling with - ast.PyCF_ALLOW_TOP_LEVEL_AWAIT would + - bpo-41218: Python 3.8.3 had a regression where compiling with + ast.PyCF_ALLOW_TOP_LEVEL_AWAIT would aggressively mark list comprehension with CO_COROUTINE. Now only list comprehension making use of async/await will tagged as so. - - bpo-41175: Guard against a NULL pointer dereference within + - bpo-41175: Guard against a NULL pointer dereference within bytearrayobject triggered by the bytearray() + bytearray() operation. - - bpo-39960: The “hackcheck” that prevents sneaking around a type’s - __setattr__() by calling the superclass method was + - bpo-39960: The “hackcheck” that prevents sneaking around a type’s + __setattr__() by calling the superclass method was rewritten to allow C implemented heap types. - - bpo-41288: Unpickling invalid NEWOBJ_EX opcode with the + - bpo-41288: Unpickling invalid NEWOBJ_EX opcode with the C implementation raises now UnpicklingError instead of crashing. - - bpo-39017: Avoid infinite loop when reading specially crafted + - bpo-39017: Avoid infinite loop when reading specially crafted TAR files using the tarfile module (CVE-2019-20907, bsc#1174091). - bpo-41235: Fix the error handling in ssl.SSLContext.load_dh_params(). - - bpo-41207: In distutils.spawn, restore expectation that + - bpo-41207: In distutils.spawn, restore expectation that DistutilsExecError is raised when the command is not found. - bpo-39168: Remove the __new__ method of typing.Generic. - - bpo-41194: Fix a crash in the _ast module: it can no longer be + - bpo-41194: Fix a crash in the _ast module: it can no longer be loaded more than once. It now uses a global state rather than a module state. - - bpo-39384: Fixed email.contentmanager to allow set_content() to set a + - bpo-39384: Fixed email.contentmanager to allow set_content() to set a null string. - - bpo-41300: Save files with non-ascii chars. + - bpo-41300: Save files with non-ascii chars. Fix regression released in 3.9.0b4 and 3.8.4. - - bpo-37765: Add keywords to module name completion list. + - bpo-37765: Add keywords to module name completion list. Rewrite Completions section of IDLE doc. - - bpo-40170: Revert PyType_HasFeature() change: it reads - again directly the PyTypeObject.tp_flags - member when the limited C API is not used, rather than always calling + - bpo-40170: Revert PyType_HasFeature() change: it reads + again directly the PyTypeObject.tp_flags + member when the limited C API is not used, rather than always calling PyType_GetFlags() which hides implementation details. ------------------------------------------------------------------- @@ -2382,7 +2585,7 @@ Wed Jun 5 12:19:09 CEST 2019 - Matej Cepl pickling costs between processes - typed_ast is merged back to CPython - LOAD_GLOBAL is now 40% faster - - pickle now uses Protocol 4 by default, improving performance + - pickle now uses Protocol 4 by default, improving performance - Remove patches which were included in the upstream: - 00251-change-user-install-location.patch - 00316-mark-bdist_wininst-unsupported.patch @@ -2527,7 +2730,7 @@ Mon Dec 17 17:24:49 CET 2018 - mcepl@suse.com - Upgrade to 3.7.2rc1: * bugfix release, for the full list of all changes see - https://docs.python.org/3.7/whatsnew/changelog.html#changelog + https://docs.python.org/3.7/whatsnew/changelog.html#changelog - Make run of the test suite more verbose ------------------------------------------------------------------- @@ -2954,7 +3157,7 @@ Mon Mar 13 14:04:22 UTC 2017 - jmatejek@suse.com Sat Feb 25 20:55:57 UTC 2017 - bwiedemann@suse.com - Add 0001-allow-for-reproducible-builds-of-python-packages.patch - upstream https://github.com/python/cpython/pull/296 + upstream https://github.com/python/cpython/pull/296 ------------------------------------------------------------------- Wed Feb 8 12:30:20 UTC 2017 - jmatejek@suse.com @@ -3020,7 +3223,7 @@ Mon Mar 7 20:38:11 UTC 2016 - toddrme2178@gmail.com - Add Python-3.5.1-fix_lru_cache_copying.patch Fix copying the lru_cache() wrapper object. - Fixes deep-copying lru_cache regression, which worked on + Fixes deep-copying lru_cache regression, which worked on previous versions of python but fails on python 3.5. This fixes a bunch of packages in devel:languages:python3. See: https://bugs.python.org/issue25447 @@ -3158,7 +3361,7 @@ Sun Jan 11 13:01:30 UTC 2015 - p.drouand@gmail.com ------------------------------------------------------------------- Sat Oct 18 20:14:54 UTC 2014 - crrodriguez@opensuse.org -- Only pkgconfig(x11) is required for build, not the whole +- Only pkgconfig(x11) is required for build, not the whole set of packages provided by xorg-x11-devel metapackage. ------------------------------------------------------------------- @@ -3218,7 +3421,7 @@ Wed Mar 26 15:24:46 UTC 2014 - jmatejek@suse.com ------------------------------------------------------------------- Mon Mar 24 17:29:31 UTC 2014 - dmueller@suse.com -- remove blacklisting of test_posix on aarch64: qemu bug is fixed +- remove blacklisting of test_posix on aarch64: qemu bug is fixed ------------------------------------------------------------------- Mon Mar 17 18:26:58 UTC 2014 - jmatejek@suse.com @@ -3321,7 +3524,7 @@ Tue Nov 19 14:28:41 UTC 2013 - jmatejek@suse.com ------------------------------------------------------------------- Tue Oct 15 17:44:08 UTC 2013 - crrodriguez@opensuse.org -- build with -DOPENSSL_LOAD_CONF for the same reasons +- build with -DOPENSSL_LOAD_CONF for the same reasons described in the python2 package. ------------------------------------------------------------------- @@ -3333,7 +3536,7 @@ Fri Aug 16 11:35:15 UTC 2013 - jmatejek@suse.com ------------------------------------------------------------------- Thu Aug 8 14:54:49 UTC 2013 - dvaleev@suse.com -- Exclue test_faulthandler from tests on powerpc due to bnc#831629 +- Exclue test_faulthandler from tests on powerpc due to bnc#831629 ------------------------------------------------------------------- Thu Jun 13 15:05:34 UTC 2013 - jmatejek@suse.com @@ -3392,7 +3595,7 @@ Fri Mar 1 07:42:21 UTC 2013 - dmueller@suse.com - add ctypes-libffi-aarch64.patch: * import aarch64 support for libffi in _ctypes module -- add aarch64 to the list of lib64 based archs +- add aarch64 to the list of lib64 based archs - add movetogetdents64.diff: * port to getdents64, as SYS_getdents is not implemented everywhere @@ -3446,9 +3649,9 @@ Mon Oct 29 18:21:45 UTC 2012 - dmueller@suse.com ------------------------------------------------------------------- Thu Oct 25 08:14:36 UTC 2012 - Rene.vanPaassen@gmail.com -- exclude test_math for SLE 11; math library fails on negative +- exclude test_math for SLE 11; math library fails on negative gamma function values close to integers and 0, probably - due to imprecision in -lm on SLE_11_SP2. + due to imprecision in -lm on SLE_11_SP2. ------------------------------------------------------------------- Tue Oct 16 12:15:34 UTC 2012 - coolo@suse.com @@ -3472,7 +3675,7 @@ Mon Oct 1 08:53:03 UTC 2012 - idonmez@suse.com ------------------------------------------------------------------- Thu Sep 27 12:35:01 UTC 2012 - idonmez@suse.com -- Correct dependency for python3-testsuite, +- Correct dependency for python3-testsuite, python3-tkinter -> python3-tk ------------------------------------------------------------------- @@ -3505,7 +3708,7 @@ Fri Aug 3 12:09:34 UTC 2012 - jmatejek@suse.com ------------------------------------------------------------------- Fri Jul 27 09:02:41 UTC 2012 - dvaleev@suse.com -- skip test_io on ppc +- skip test_io on ppc - drop test_io ppc patch ------------------------------------------------------------------- @@ -3554,8 +3757,8 @@ Wed Jan 18 15:49:47 UTC 2012 - jmatejek@suse.com ------------------------------------------------------------------- Sun Dec 25 13:25:01 UTC 2011 - idonmez@suse.com -- Use system ffi, included one is broken see - http://bugs.python.org/issue11729 and +- Use system ffi, included one is broken see + http://bugs.python.org/issue11729 and http://bugs.python.org/issue12081 ------------------------------------------------------------------- diff --git a/python310.spec b/python310.spec index d7fee4e..6b565a6 100644 --- a/python310.spec +++ b/python310.spec @@ -1,7 +1,7 @@ # # spec file # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -103,7 +103,7 @@ Obsoletes: python39%{?1:-%{1}} %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so %bcond_without profileopt Name: %{python_pkg_name}%{psuffix} -Version: 3.10.8 +Version: 3.10.10 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -166,18 +166,13 @@ Patch35: fix_configure_rst.patch # PATCH-FIX-UPSTREAM bpo-46811 gh#python/cpython#7da97f61816f mcepl@suse.com # NOTE: SUSE version of expat 2.4.4 is patched in SUSE for CVE-2022-25236 Patch36: support-expat-CVE-2022-25236-patched.patch -# PATCH-FIX-UPSTREAM CVE-2015-20107-mailcap-unsafe-filenames.patch bsc#1198511 mcepl@suse.com -# avoid the command injection in the mailcap module. -Patch37: CVE-2015-20107-mailcap-unsafe-filenames.patch -# PATCH-FIX-UPSTREAM 98437-sphinx.locale._-as-gettext-in-pyspecific.patch gh#python/cpython#98366 mcepl@suse.com -# this patch makes things totally awesome -Patch38: 98437-sphinx.locale._-as-gettext-in-pyspecific.patch -# PATCH-FIX-UPSTREAM CVE-2022-42919-loc-priv-mulitproc-forksrv.patch bsc#1204886 mcepl@suse.com -# Avoid Linux specific local privilege escalation via the multiprocessing forkserver start method -Patch39: CVE-2022-42919-loc-priv-mulitproc-forksrv.patch -# PATCH-FIX-UPSTREAM CVE-2022-45061-DoS-by-IDNA-decode.patch bsc#1205244 mcepl@suse.com -# Avoid DoS by decoding IDNA for too long domain names -Patch40: CVE-2022-45061-DoS-by-IDNA-decode.patch +# PATCH-FIX-UPSTREAM CVE-2023-24329-blank-URL-bypass.patch bsc#1208471 mcepl@suse.com +# blocklist bypass via the urllib.parse component when supplying +# a URL that starts with blank characters +Patch37: CVE-2023-24329-blank-URL-bypass.patch +# PATCH-FIX-UPSTREAM invalid-json.patch gh#python/cpython#102582 mcepl@suse.com +# We require valid JSON in documentation +Patch38: invalid-json.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -197,7 +192,7 @@ BuildRequires: pkgconfig(zlib) # The provider for python(abi) is in rpm-build-python BuildRequires: rpm-build-python %endif -%if 0%{?suse_version} >= 1500 +%if 0%{?suse_version} >= 1500 && 0%{?suse_version} < 1599 BuildRequires: pkgconfig(libnsl) BuildRequires: pkgconfig(libtirpc) %endif @@ -228,12 +223,16 @@ BuildRequires: pkgconfig(ncurses) BuildRequires: pkgconfig(tk) BuildRequires: pkgconfig(x11) Requires: %{python_pkg_name}-base = %{version} +Provides: %{python_pkg_name}-readline +Provides: %{python_pkg_name}-sqlite3 Recommends: %{python_pkg_name}-curses Recommends: %{python_pkg_name}-dbm Recommends: %{python_pkg_name}-pip %obsolete_python_versioned %if %{primary_interpreter} Provides: python3 = %{python_version} +Provides: python3-readline +Provides: python3-sqlite3 %endif %endif @@ -448,8 +447,6 @@ other applications. %patch36 -p1 %patch37 -p1 %patch38 -p1 -%patch39 -p1 -%patch40 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -645,7 +642,7 @@ for library in \ _posixsubprocess _queue _random resource select _ssl _socket spwd \ _statistics _struct syslog termios _testbuffer _testimportmultiple \ _testmultiphase unicodedata zlib _ctypes_test _testinternalcapi _testcapi \ - xxlimited xxlimited_35 \ + _testclinic xxlimited xxlimited_35 \ _xxtestfuzz _xxsubinterpreters _elementtree pyexpat _md5 _sha1 \ _sha256 _sha512 _blake2 _sha3 _uuid _zoneinfo do @@ -809,7 +806,9 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %exclude %{sitedir}/sqlite3/test %{dynlib readline} %{dynlib _sqlite3} +%if 0%{?suse_version} >= 1500 && 0%{?suse_version} < 1599 %{dynlib nis} +%endif %files -n %{python_pkg_name}-idle %defattr(644, root, root, 755) @@ -892,6 +891,7 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %{dynlib _ctypes_test} %{dynlib _testbuffer} %{dynlib _testcapi} +%{dynlib _testclinic} %{dynlib _testinternalcapi} %{dynlib _testimportmultiple} %{dynlib _testmultiphase} From 36b1860d24c4f8d851d1bdb026b5f56312f03e2087b2a80e9e8a0e6af0692951 Mon Sep 17 00:00:00 2001 From: Wolfgang Engel Date: Thu, 8 Jun 2023 07:42:25 +0000 Subject: [PATCH 06/23] Set link to python310.29278 via maintenance_release request --- CVE-2007-4559-filter-tarfile_extractall.patch | 2594 +++++++++++++++++ Python-3.10.10.tar.xz.asc | 16 - ...on-3.10.10.tar.xz => Python-3.10.11.tar.xz | 0 Python-3.10.11.tar.xz.asc | 16 + invalid-json.patch | 44 - python310.changes | 90 + python310.spec | 12 +- 7 files changed, 2706 insertions(+), 66 deletions(-) create mode 100644 CVE-2007-4559-filter-tarfile_extractall.patch delete mode 100644 Python-3.10.10.tar.xz.asc rename Python-3.10.10.tar.xz => Python-3.10.11.tar.xz (100%) create mode 100644 Python-3.10.11.tar.xz.asc delete mode 100644 invalid-json.patch diff --git a/CVE-2007-4559-filter-tarfile_extractall.patch b/CVE-2007-4559-filter-tarfile_extractall.patch new file mode 100644 index 0000000..f0c85bf --- /dev/null +++ b/CVE-2007-4559-filter-tarfile_extractall.patch @@ -0,0 +1,2594 @@ +diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst +index 311aae414ae..3864e03898d 100644 +--- a/Doc/library/shutil.rst ++++ b/Doc/library/shutil.rst +@@ -620,7 +620,7 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. + Remove the archive format *name* from the list of supported formats. + + +-.. function:: unpack_archive(filename[, extract_dir[, format]]) ++.. function:: unpack_archive(filename[, extract_dir[, format[, filter]]]) + + Unpack an archive. *filename* is the full path of the archive. + +@@ -634,6 +634,15 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. + registered for that extension. In case none is found, + a :exc:`ValueError` is raised. + ++ The keyword-only *filter* argument, which was added in Python 3.11.4, ++ is passed to the underlying unpacking function. ++ For zip files, *filter* is not accepted. ++ For tar files, it is recommended to set it to ``'data'``, ++ unless using features specific to tar and UNIX-like filesystems. ++ (See :ref:`tarfile-extraction-filter` for details.) ++ The ``'data'`` filter will become the default for tar files ++ in Python 3.14. ++ + .. audit-event:: shutil.unpack_archive filename,extract_dir,format shutil.unpack_archive + + .. warning:: +@@ -646,6 +655,9 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. + .. versionchanged:: 3.7 + Accepts a :term:`path-like object` for *filename* and *extract_dir*. + ++ .. versionchanged:: 3.11.4 ++ Added the *filter* argument. ++ + .. function:: register_unpack_format(name, extensions, function[, extra_args[, description]]) + + Registers an unpack format. *name* is the name of the format and +@@ -653,11 +665,14 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. + ``.zip`` for Zip files. + + *function* is the callable that will be used to unpack archives. The +- callable will receive the path of the archive, followed by the directory +- the archive must be extracted to. +- +- When provided, *extra_args* is a sequence of ``(name, value)`` tuples that +- will be passed as keywords arguments to the callable. ++ callable will receive: ++ ++ - the path of the archive, as a positional argument; ++ - the directory the archive must be extracted to, as a positional argument; ++ - possibly a *filter* keyword argument, if it was given to ++ :func:`unpack_archive`; ++ - additional keyword arguments, specified by *extra_args* as a sequence ++ of ``(name, value)`` tuples. + + *description* can be provided to describe the format, and will be returned + by the :func:`get_unpack_formats` function. +diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst +index 226513f5fc1..836444ebb34 100644 +--- a/Doc/library/tarfile.rst ++++ b/Doc/library/tarfile.rst +@@ -206,6 +206,38 @@ The :mod:`tarfile` module defines the following exceptions: + Is raised by :meth:`TarInfo.frombuf` if the buffer it gets is invalid. + + ++.. exception:: FilterError ++ ++ Base class for members :ref:`refused ` by ++ filters. ++ ++ .. attribute:: tarinfo ++ ++ Information about the member that the filter refused to extract, ++ as :ref:`TarInfo `. ++ ++.. exception:: AbsolutePathError ++ ++ Raised to refuse extracting a member with an absolute path. ++ ++.. exception:: OutsideDestinationError ++ ++ Raised to refuse extracting a member outside the destination directory. ++ ++.. exception:: SpecialFileError ++ ++ Raised to refuse extracting a special file (e.g. a device or pipe). ++ ++.. exception:: AbsoluteLinkError ++ ++ Raised to refuse extracting a symbolic link with an absolute path. ++ ++.. exception:: LinkOutsideDestinationError ++ ++ Raised to refuse extracting a symbolic link pointing outside the destination ++ directory. ++ ++ + The following constants are available at the module level: + + .. data:: ENCODING +@@ -316,11 +348,8 @@ be finalized; only the internally used file object will be closed. See the + *debug* can be set from ``0`` (no debug messages) up to ``3`` (all debug + messages). The messages are written to ``sys.stderr``. + +- If *errorlevel* is ``0``, all errors are ignored when using :meth:`TarFile.extract`. +- Nevertheless, they appear as error messages in the debug output, when debugging +- is enabled. If ``1``, all *fatal* errors are raised as :exc:`OSError` +- exceptions. If ``2``, all *non-fatal* errors are raised as :exc:`TarError` +- exceptions as well. ++ *errorlevel* controls how extraction errors are handled, ++ see :attr:`the corresponding attribute <~TarFile.errorlevel>`. + + The *encoding* and *errors* arguments define the character encoding to be + used for reading or writing the archive and how conversion errors are going +@@ -387,7 +416,7 @@ be finalized; only the internally used file object will be closed. See the + available. + + +-.. method:: TarFile.extractall(path=".", members=None, *, numeric_owner=False) ++.. method:: TarFile.extractall(path=".", members=None, *, numeric_owner=False, filter=None) + + Extract all members from the archive to the current working directory or + directory *path*. If optional *members* is given, it must be a subset of the +@@ -401,6 +430,12 @@ be finalized; only the internally used file object will be closed. See the + are used to set the owner/group for the extracted files. Otherwise, the named + values from the tarfile are used. + ++ The *filter* argument, which was added in Python 3.11.4, specifies how ++ ``members`` are modified or rejected before extraction. ++ See :ref:`tarfile-extraction-filter` for details. ++ It is recommended to set this explicitly depending on which *tar* features ++ you need to support. ++ + .. warning:: + + Never extract archives from untrusted sources without prior inspection. +@@ -408,14 +443,20 @@ be finalized; only the internally used file object will be closed. See the + that have absolute filenames starting with ``"/"`` or filenames with two + dots ``".."``. + ++ Set ``filter='data'`` to prevent the most dangerous security issues, ++ and read the :ref:`tarfile-extraction-filter` section for details. ++ + .. versionchanged:: 3.5 + Added the *numeric_owner* parameter. + + .. versionchanged:: 3.6 + The *path* parameter accepts a :term:`path-like object`. + ++ .. versionchanged:: 3.11.4 ++ Added the *filter* parameter. ++ + +-.. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False) ++.. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False, filter=None) + + Extract a member from the archive to the current working directory, using its + full name. Its file information is extracted as accurately as possible. *member* +@@ -423,9 +464,8 @@ be finalized; only the internally used file object will be closed. See the + directory using *path*. *path* may be a :term:`path-like object`. + File attributes (owner, mtime, mode) are set unless *set_attrs* is false. + +- If *numeric_owner* is :const:`True`, the uid and gid numbers from the tarfile +- are used to set the owner/group for the extracted files. Otherwise, the named +- values from the tarfile are used. ++ The *numeric_owner* and *filter* arguments are the same as ++ for :meth:`extractall`. + + .. note:: + +@@ -436,6 +476,9 @@ be finalized; only the internally used file object will be closed. See the + + See the warning for :meth:`extractall`. + ++ Set ``filter='data'`` to prevent the most dangerous security issues, ++ and read the :ref:`tarfile-extraction-filter` section for details. ++ + .. versionchanged:: 3.2 + Added the *set_attrs* parameter. + +@@ -445,6 +488,9 @@ be finalized; only the internally used file object will be closed. See the + .. versionchanged:: 3.6 + The *path* parameter accepts a :term:`path-like object`. + ++ .. versionchanged:: 3.11.4 ++ Added the *filter* parameter. ++ + + .. method:: TarFile.extractfile(member) + +@@ -457,6 +503,57 @@ be finalized; only the internally used file object will be closed. See the + .. versionchanged:: 3.3 + Return an :class:`io.BufferedReader` object. + ++.. attribute:: TarFile.errorlevel ++ :type: int ++ ++ If *errorlevel* is ``0``, errors are ignored when using :meth:`TarFile.extract` ++ and :meth:`TarFile.extractall`. ++ Nevertheless, they appear as error messages in the debug output when ++ *debug* is greater than 0. ++ If ``1`` (the default), all *fatal* errors are raised as :exc:`OSError` or ++ :exc:`FilterError` exceptions. If ``2``, all *non-fatal* errors are raised ++ as :exc:`TarError` exceptions as well. ++ ++ Some exceptions, e.g. ones caused by wrong argument types or data ++ corruption, are always raised. ++ ++ Custom :ref:`extraction filters ` ++ should raise :exc:`FilterError` for *fatal* errors ++ and :exc:`ExtractError` for *non-fatal* ones. ++ ++ Note that when an exception is raised, the archive may be partially ++ extracted. It is the user’s responsibility to clean up. ++ ++.. attribute:: TarFile.extraction_filter ++ ++ .. versionadded:: 3.11.4 ++ ++ The :ref:`extraction filter ` used ++ as a default for the *filter* argument of :meth:`~TarFile.extract` ++ and :meth:`~TarFile.extractall`. ++ ++ The attribute may be ``None`` or a callable. ++ String names are not allowed for this attribute, unlike the *filter* ++ argument to :meth:`~TarFile.extract`. ++ ++ If ``extraction_filter`` is ``None`` (the default), ++ calling an extraction method without a *filter* argument will ++ use the :func:`fully_trusted ` filter for ++ compatibility with previous Python versions. ++ ++ In Python 3.12+, leaving ``extraction_filter=None`` will emit a ++ ``DeprecationWarning``. ++ ++ In Python 3.14+, leaving ``extraction_filter=None`` will cause ++ extraction methods to use the :func:`data ` filter by default. ++ ++ The attribute may be set on instances or overridden in subclasses. ++ It also is possible to set it on the ``TarFile`` class itself to set a ++ global default, although, since it affects all uses of *tarfile*, ++ it is best practice to only do so in top-level applications or ++ :mod:`site configuration `. ++ To set a global default this way, a filter function needs to be wrapped in ++ :func:`staticmethod()` to prevent injection of a ``self`` argument. + + .. method:: TarFile.add(name, arcname=None, recursive=True, *, filter=None) + +@@ -532,7 +629,27 @@ permissions, owner etc.), it provides some useful methods to determine its type. + It does *not* contain the file's data itself. + + :class:`TarInfo` objects are returned by :class:`TarFile`'s methods +-:meth:`getmember`, :meth:`getmembers` and :meth:`gettarinfo`. ++:meth:`~TarFile.getmember`, :meth:`~TarFile.getmembers` and ++:meth:`~TarFile.gettarinfo`. ++ ++Modifying the objects returned by :meth:`~!TarFile.getmember` or ++:meth:`~!TarFile.getmembers` will affect all subsequent ++operations on the archive. ++For cases where this is unwanted, you can use :mod:`copy.copy() ` or ++call the :meth:`~TarInfo.replace` method to create a modified copy in one step. ++ ++Several attributes can be set to ``None`` to indicate that a piece of metadata ++is unused or unknown. ++Different :class:`TarInfo` methods handle ``None`` differently: ++ ++- The :meth:`~TarFile.extract` or :meth:`~TarFile.extractall` methods will ++ ignore the corresponding metadata, leaving it set to a default. ++- :meth:`~TarFile.addfile` will fail. ++- :meth:`~TarFile.list` will print a placeholder string. ++ ++ ++.. versionchanged:: 3.11.4 ++ Added :meth:`~TarInfo.replace` and handling of ``None``. + + + .. class:: TarInfo(name="") +@@ -566,24 +683,39 @@ A ``TarInfo`` object has the following public data attributes: + + + .. attribute:: TarInfo.name ++ :type: str + + Name of the archive member. + + + .. attribute:: TarInfo.size ++ :type: int + + Size in bytes. + + + .. attribute:: TarInfo.mtime ++ :type: int | float ++ ++ Time of last modification in seconds since the :ref:`epoch `, ++ as in :attr:`os.stat_result.st_mtime`. + +- Time of last modification. ++ .. versionchanged:: 3.11.4 + ++ Can be set to ``None`` for :meth:`~TarFile.extract` and ++ :meth:`~TarFile.extractall`, causing extraction to skip applying this ++ attribute. + + .. attribute:: TarInfo.mode ++ :type: int + +- Permission bits. ++ Permission bits, as for :func:`os.chmod`. + ++ .. versionchanged:: 3.11.4 ++ ++ Can be set to ``None`` for :meth:`~TarFile.extract` and ++ :meth:`~TarFile.extractall`, causing extraction to skip applying this ++ attribute. + + .. attribute:: TarInfo.type + +@@ -595,35 +727,76 @@ A ``TarInfo`` object has the following public data attributes: + + + .. attribute:: TarInfo.linkname ++ :type: str + + Name of the target file name, which is only present in :class:`TarInfo` objects + of type :const:`LNKTYPE` and :const:`SYMTYPE`. + + + .. attribute:: TarInfo.uid ++ :type: int + + User ID of the user who originally stored this member. + ++ .. versionchanged:: 3.11.4 ++ ++ Can be set to ``None`` for :meth:`~TarFile.extract` and ++ :meth:`~TarFile.extractall`, causing extraction to skip applying this ++ attribute. + + .. attribute:: TarInfo.gid ++ :type: int + + Group ID of the user who originally stored this member. + ++ .. versionchanged:: 3.11.4 ++ ++ Can be set to ``None`` for :meth:`~TarFile.extract` and ++ :meth:`~TarFile.extractall`, causing extraction to skip applying this ++ attribute. + + .. attribute:: TarInfo.uname ++ :type: str + + User name. + ++ .. versionchanged:: 3.11.4 ++ ++ Can be set to ``None`` for :meth:`~TarFile.extract` and ++ :meth:`~TarFile.extractall`, causing extraction to skip applying this ++ attribute. + + .. attribute:: TarInfo.gname ++ :type: str + + Group name. + ++ .. versionchanged:: 3.11.4 ++ ++ Can be set to ``None`` for :meth:`~TarFile.extract` and ++ :meth:`~TarFile.extractall`, causing extraction to skip applying this ++ attribute. + + .. attribute:: TarInfo.pax_headers ++ :type: dict + + A dictionary containing key-value pairs of an associated pax extended header. + ++.. method:: TarInfo.replace(name=..., mtime=..., mode=..., linkname=..., ++ uid=..., gid=..., uname=..., gname=..., ++ deep=True) ++ ++ .. versionadded:: 3.11.4 ++ ++ Return a *new* copy of the :class:`!TarInfo` object with the given attributes ++ changed. For example, to return a ``TarInfo`` with the group name set to ++ ``'staff'``, use:: ++ ++ new_tarinfo = old_tarinfo.replace(gname='staff') ++ ++ By default, a deep copy is made. ++ If *deep* is false, the copy is shallow, i.e. ``pax_headers`` ++ and any custom attributes are shared with the original ``TarInfo`` object. + + A :class:`TarInfo` object also provides some convenient query methods: + +@@ -673,9 +846,259 @@ A :class:`TarInfo` object also provides some convenient query methods: + Return :const:`True` if it is one of character device, block device or FIFO. + + ++.. _tarfile-extraction-filter: ++ ++Extraction filters ++------------------ ++ ++.. versionadded:: 3.11.4 ++ ++The *tar* format is designed to capture all details of a UNIX-like filesystem, ++which makes it very powerful. ++Unfortunately, the features make it easy to create tar files that have ++unintended -- and possibly malicious -- effects when extracted. ++For example, extracting a tar file can overwrite arbitrary files in various ++ways (e.g. by using absolute paths, ``..`` path components, or symlinks that ++affect later members). ++ ++In most cases, the full functionality is not needed. ++Therefore, *tarfile* supports extraction filters: a mechanism to limit ++functionality, and thus mitigate some of the security issues. ++ ++.. seealso:: ++ ++ :pep:`706` ++ Contains further motivation and rationale behind the design. ++ ++The *filter* argument to :meth:`TarFile.extract` or :meth:`~TarFile.extractall` ++can be: ++ ++* the string ``'fully_trusted'``: Honor all metadata as specified in the ++ archive. ++ Should be used if the user trusts the archive completely, or implements ++ their own complex verification. ++ ++* the string ``'tar'``: Honor most *tar*-specific features (i.e. features of ++ UNIX-like filesystems), but block features that are very likely to be ++ surprising or malicious. See :func:`tar_filter` for details. ++ ++* the string ``'data'``: Ignore or block most features specific to UNIX-like ++ filesystems. Intended for extracting cross-platform data archives. ++ See :func:`data_filter` for details. ++ ++* ``None`` (default): Use :attr:`TarFile.extraction_filter`. ++ ++ If that is also ``None`` (the default), the ``'fully_trusted'`` ++ filter will be used (for compatibility with earlier versions of Python). ++ ++ In Python 3.12, the default will emit a ``DeprecationWarning``. ++ ++ In Python 3.14, the ``'data'`` filter will become the default instead. ++ It's possible to switch earlier; see :attr:`TarFile.extraction_filter`. ++ ++* A callable which will be called for each extracted member with a ++ :ref:`TarInfo ` describing the member and the destination ++ path to where the archive is extracted (i.e. the same path is used for all ++ members):: ++ ++ filter(/, member: TarInfo, path: str) -> TarInfo | None ++ ++ The callable is called just before each member is extracted, so it can ++ take the current state of the disk into account. ++ It can: ++ ++ - return a :class:`TarInfo` object which will be used instead of the metadata ++ in the archive, or ++ - return ``None``, in which case the member will be skipped, or ++ - raise an exception to abort the operation or skip the member, ++ depending on :attr:`~TarFile.errorlevel`. ++ Note that when extraction is aborted, :meth:`~TarFile.extractall` may leave ++ the archive partially extracted. It does not attempt to clean up. ++ ++Default named filters ++~~~~~~~~~~~~~~~~~~~~~ ++ ++The pre-defined, named filters are available as functions, so they can be ++reused in custom filters: ++ ++.. function:: fully_trusted_filter(/, member, path) ++ ++ Return *member* unchanged. ++ ++ This implements the ``'fully_trusted'`` filter. ++ ++.. function:: tar_filter(/, member, path) ++ ++ Implements the ``'tar'`` filter. ++ ++ - Strip leading slashes (``/`` and :attr:`os.sep`) from filenames. ++ - :ref:`Refuse ` to extract files with absolute ++ paths (in case the name is absolute ++ even after stripping slashes, e.g. ``C:/foo`` on Windows). ++ This raises :class:`~tarfile.AbsolutePathError`. ++ - :ref:`Refuse ` to extract files whose absolute ++ path (after following symlinks) would end up outside the destination. ++ This raises :class:`~tarfile.OutsideDestinationError`. ++ - Clear high mode bits (setuid, setgid, sticky) and group/other write bits ++ (:attr:`~stat.S_IWGRP`|:attr:`~stat.S_IWOTH`). ++ ++ Return the modified ``TarInfo`` member. ++ ++.. function:: data_filter(/, member, path) ++ ++ Implements the ``'data'`` filter. ++ In addition to what ``tar_filter`` does: ++ ++ - :ref:`Refuse ` to extract links (hard or soft) ++ that link to absolute paths, or ones that link outside the destination. ++ ++ This raises :class:`~tarfile.AbsoluteLinkError` or ++ :class:`~tarfile.LinkOutsideDestinationError`. ++ ++ Note that such files are refused even on platforms that do not support ++ symbolic links. ++ ++ - :ref:`Refuse ` to extract device files ++ (including pipes). ++ This raises :class:`~tarfile.SpecialFileError`. ++ ++ - For regular files, including hard links: ++ ++ - Set the owner read and write permissions ++ (:attr:`~stat.S_IRUSR`|:attr:`~stat.S_IWUSR`). ++ - Remove the group & other executable permission ++ (:attr:`~stat.S_IXGRP`|:attr:`~stat.S_IXOTH`) ++ if the owner doesn’t have it (:attr:`~stat.S_IXUSR`). ++ ++ - For other files (directories), set ``mode`` to ``None``, so ++ that extraction methods skip applying permission bits. ++ - Set user and group info (``uid``, ``gid``, ``uname``, ``gname``) ++ to ``None``, so that extraction methods skip setting it. ++ ++ Return the modified ``TarInfo`` member. ++ ++ ++.. _tarfile-extraction-refuse: ++ ++Filter errors ++~~~~~~~~~~~~~ ++ ++When a filter refuses to extract a file, it will raise an appropriate exception, ++a subclass of :class:`~tarfile.FilterError`. ++This will abort the extraction if :attr:`TarFile.errorlevel` is 1 or more. ++With ``errorlevel=0`` the error will be logged and the member will be skipped, ++but extraction will continue. ++ ++ ++Hints for further verification ++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ ++Even with ``filter='data'``, *tarfile* is not suited for extracting untrusted ++files without prior inspection. ++Among other issues, the pre-defined filters do not prevent denial-of-service ++attacks. Users should do additional checks. ++ ++Here is an incomplete list of things to consider: ++ ++* Extract to a :func:`new temporary directory ` ++ to prevent e.g. exploiting pre-existing links, and to make it easier to ++ clean up after a failed extraction. ++* When working with untrusted data, use external (e.g. OS-level) limits on ++ disk, memory and CPU usage. ++* Check filenames against an allow-list of characters ++ (to filter out control characters, confusables, foreign path separators, ++ etc.). ++* Check that filenames have expected extensions (discouraging files that ++ execute when you “click on them”, or extension-less files like Windows special device names). ++* Limit the number of extracted files, total size of extracted data, ++ filename length (including symlink length), and size of individual files. ++* Check for files that would be shadowed on case-insensitive filesystems. ++ ++Also note that: ++ ++* Tar files may contain multiple versions of the same file. ++ Later ones are expected to overwrite any earlier ones. ++ This feature is crucial to allow updating tape archives, but can be abused ++ maliciously. ++* *tarfile* does not protect against issues with “live” data, ++ e.g. an attacker tinkering with the destination (or source) directory while ++ extraction (or archiving) is in progress. ++ ++ ++Supporting older Python versions ++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ ++Extraction filters were added to Python 3.12, and are backported to older ++versions as security updates. ++To check whether the feature is available, use e.g. ++``hasattr(tarfile, 'data_filter')`` rather than checking the Python version. ++ ++The following examples show how to support Python versions with and without ++the feature. ++Note that setting ``extraction_filter`` will affect any subsequent operations. ++ ++* Fully trusted archive:: ++ ++ my_tarfile.extraction_filter = (lambda member, path: member) ++ my_tarfile.extractall() ++ ++* Use the ``'data'`` filter if available, but revert to Python 3.11 behavior ++ (``'fully_trusted'``) if this feature is not available:: ++ ++ my_tarfile.extraction_filter = getattr(tarfile, 'data_filter', ++ (lambda member, path: member)) ++ my_tarfile.extractall() ++ ++* Use the ``'data'`` filter; *fail* if it is not available:: ++ ++ my_tarfile.extractall(filter=tarfile.data_filter) ++ ++ or:: ++ ++ my_tarfile.extraction_filter = tarfile.data_filter ++ my_tarfile.extractall() ++ ++* Use the ``'data'`` filter; *warn* if it is not available:: ++ ++ if hasattr(tarfile, 'data_filter'): ++ my_tarfile.extractall(filter='data') ++ else: ++ # remove this when no longer needed ++ warn_the_user('Extracting may be unsafe; consider updating Python') ++ my_tarfile.extractall() ++ ++ ++Stateful extraction filter example ++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ ++While *tarfile*'s extraction methods take a simple *filter* callable, ++custom filters may be more complex objects with an internal state. ++It may be useful to write these as context managers, to be used like this:: ++ ++ with StatefulFilter() as filter_func: ++ tar.extractall(path, filter=filter_func) ++ ++Such a filter can be written as, for example:: ++ ++ class StatefulFilter: ++ def __init__(self): ++ self.file_count = 0 ++ ++ def __enter__(self): ++ return self ++ ++ def __call__(self, member, path): ++ self.file_count += 1 ++ return member ++ ++ def __exit__(self, *exc_info): ++ print(f'{self.file_count} files extracted') ++ ++ + .. _tarfile-commandline: + .. program:: tarfile + ++ + Command-Line Interface + ---------------------- + +@@ -745,6 +1168,15 @@ Command-line options + + Verbose output. + ++.. cmdoption:: --filter ++ ++ Specifies the *filter* for ``--extract``. ++ See :ref:`tarfile-extraction-filter` for details. ++ Only string names are accepted (that is, ``fully_trusted``, ``tar``, ++ and ``data``). ++ ++ .. versionadded:: 3.11.4 ++ + .. _tar-examples: + + Examples +diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst +index 47e38ae76ba..43da72aece9 100644 +--- a/Doc/whatsnew/3.10.rst ++++ b/Doc/whatsnew/3.10.rst +@@ -2332,3 +2332,19 @@ The deprecated :mod:`mailcap` module now refuses to inject unsafe text + text, it will warn and act as if a match was not found (or for test commands, + as if the test failed). + (Contributed by Petr Viktorin in :gh:`98966`.) ++ ++Notable Changes in 3.10.12 ++========================== ++ ++tarfile ++------- ++ ++* The extraction methods in :mod:`tarfile`, and :func:`shutil.unpack_archive`, ++ have a new a *filter* argument that allows limiting tar features than may be ++ surprising or dangerous, such as creating files outside the destination ++ directory. ++ See :ref:`tarfile-extraction-filter` for details. ++ In Python 3.12, use without the *filter* argument will show a ++ :exc:`DeprecationWarning`. ++ In Python 3.14, the default will switch to ``'data'``. ++ (Contributed by Petr Viktorin in :pep:`706`.) +diff --git a/Lib/shutil.py b/Lib/shutil.py +index b7bffa3ea41..482ce95a7b2 100644 +--- a/Lib/shutil.py ++++ b/Lib/shutil.py +@@ -1222,7 +1222,7 @@ def _unpack_zipfile(filename, extract_dir): + finally: + zip.close() + +-def _unpack_tarfile(filename, extract_dir): ++def _unpack_tarfile(filename, extract_dir, *, filter=None): + """Unpack tar/tar.gz/tar.bz2/tar.xz `filename` to `extract_dir` + """ + import tarfile # late import for breaking circular dependency +@@ -1232,7 +1232,7 @@ def _unpack_tarfile(filename, extract_dir): + raise ReadError( + "%s is not a compressed or uncompressed tar file" % filename) + try: +- tarobj.extractall(extract_dir) ++ tarobj.extractall(extract_dir, filter=filter) + finally: + tarobj.close() + +@@ -1265,7 +1265,7 @@ def _find_unpack_format(filename): + return name + return None + +-def unpack_archive(filename, extract_dir=None, format=None): ++def unpack_archive(filename, extract_dir=None, format=None, *, filter=None): + """Unpack an archive. + + `filename` is the name of the archive. +@@ -1279,6 +1279,9 @@ def unpack_archive(filename, extract_dir=None, format=None): + was registered for that extension. + + In case none is found, a ValueError is raised. ++ ++ If `filter` is given, it is passed to the underlying ++ extraction function. + """ + sys.audit("shutil.unpack_archive", filename, extract_dir, format) + +@@ -1288,6 +1291,10 @@ def unpack_archive(filename, extract_dir=None, format=None): + extract_dir = os.fspath(extract_dir) + filename = os.fspath(filename) + ++ if filter is None: ++ filter_kwargs = {} ++ else: ++ filter_kwargs = {'filter': filter} + if format is not None: + try: + format_info = _UNPACK_FORMATS[format] +@@ -1295,7 +1302,7 @@ def unpack_archive(filename, extract_dir=None, format=None): + raise ValueError("Unknown unpack format '{0}'".format(format)) from None + + func = format_info[1] +- func(filename, extract_dir, **dict(format_info[2])) ++ func(filename, extract_dir, **dict(format_info[2]), **filter_kwargs) + else: + # we need to look at the registered unpackers supported extensions + format = _find_unpack_format(filename) +@@ -1303,7 +1310,7 @@ def unpack_archive(filename, extract_dir=None, format=None): + raise ReadError("Unknown archive format '{0}'".format(filename)) + + func = _UNPACK_FORMATS[format][1] +- kwargs = dict(_UNPACK_FORMATS[format][2]) ++ kwargs = dict(_UNPACK_FORMATS[format][2]) | filter_kwargs + func(filename, extract_dir, **kwargs) + + +diff --git a/Lib/tarfile.py b/Lib/tarfile.py +index dea150e8dbb..40599f27bce 100755 +--- a/Lib/tarfile.py ++++ b/Lib/tarfile.py +@@ -46,6 +46,7 @@ + import struct + import copy + import re ++import warnings + + try: + import pwd +@@ -71,6 +72,7 @@ + "ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT", + "DEFAULT_FORMAT", "open"] + ++ + #--------------------------------------------------------- + # tar constants + #--------------------------------------------------------- +@@ -158,6 +160,8 @@ + def stn(s, length, encoding, errors): + """Convert a string to a null-terminated bytes object. + """ ++ if s is None: ++ raise ValueError("metadata cannot contain None") + s = s.encode(encoding, errors) + return s[:length] + (length - len(s)) * NUL + +@@ -709,9 +713,127 @@ def __init__(self, tarfile, tarinfo): + super().__init__(fileobj) + #class ExFileObject + ++ ++#----------------------------- ++# extraction filters (PEP 706) ++#----------------------------- ++ ++class FilterError(TarError): ++ pass ++ ++class AbsolutePathError(FilterError): ++ def __init__(self, tarinfo): ++ self.tarinfo = tarinfo ++ super().__init__(f'member {tarinfo.name!r} has an absolute path') ++ ++class OutsideDestinationError(FilterError): ++ def __init__(self, tarinfo, path): ++ self.tarinfo = tarinfo ++ self._path = path ++ super().__init__(f'{tarinfo.name!r} would be extracted to {path!r}, ' ++ + 'which is outside the destination') ++ ++class SpecialFileError(FilterError): ++ def __init__(self, tarinfo): ++ self.tarinfo = tarinfo ++ super().__init__(f'{tarinfo.name!r} is a special file') ++ ++class AbsoluteLinkError(FilterError): ++ def __init__(self, tarinfo): ++ self.tarinfo = tarinfo ++ super().__init__(f'{tarinfo.name!r} is a symlink to an absolute path') ++ ++class LinkOutsideDestinationError(FilterError): ++ def __init__(self, tarinfo, path): ++ self.tarinfo = tarinfo ++ self._path = path ++ super().__init__(f'{tarinfo.name!r} would link to {path!r}, ' ++ + 'which is outside the destination') ++ ++def _get_filtered_attrs(member, dest_path, for_data=True): ++ new_attrs = {} ++ name = member.name ++ dest_path = os.path.realpath(dest_path) ++ # Strip leading / (tar's directory separator) from filenames. ++ # Include os.sep (target OS directory separator) as well. ++ if name.startswith(('/', os.sep)): ++ name = new_attrs['name'] = member.path.lstrip('/' + os.sep) ++ if os.path.isabs(name): ++ # Path is absolute even after stripping. ++ # For example, 'C:/foo' on Windows. ++ raise AbsolutePathError(member) ++ # Ensure we stay in the destination ++ target_path = os.path.realpath(os.path.join(dest_path, name)) ++ if os.path.commonpath([target_path, dest_path]) != dest_path: ++ raise OutsideDestinationError(member, target_path) ++ # Limit permissions (no high bits, and go-w) ++ mode = member.mode ++ if mode is not None: ++ # Strip high bits & group/other write bits ++ mode = mode & 0o755 ++ if for_data: ++ # For data, handle permissions & file types ++ if member.isreg() or member.islnk(): ++ if not mode & 0o100: ++ # Clear executable bits if not executable by user ++ mode &= ~0o111 ++ # Ensure owner can read & write ++ mode |= 0o600 ++ elif member.isdir() or member.issym(): ++ # Ignore mode for directories & symlinks ++ mode = None ++ else: ++ # Reject special files ++ raise SpecialFileError(member) ++ if mode != member.mode: ++ new_attrs['mode'] = mode ++ if for_data: ++ # Ignore ownership for 'data' ++ if member.uid is not None: ++ new_attrs['uid'] = None ++ if member.gid is not None: ++ new_attrs['gid'] = None ++ if member.uname is not None: ++ new_attrs['uname'] = None ++ if member.gname is not None: ++ new_attrs['gname'] = None ++ # Check link destination for 'data' ++ 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 os.path.commonpath([target_path, dest_path]) != dest_path: ++ raise LinkOutsideDestinationError(member, target_path) ++ return new_attrs ++ ++def fully_trusted_filter(member, dest_path): ++ return member ++ ++def tar_filter(member, dest_path): ++ new_attrs = _get_filtered_attrs(member, dest_path, False) ++ if new_attrs: ++ return member.replace(**new_attrs, deep=False) ++ return member ++ ++def data_filter(member, dest_path): ++ new_attrs = _get_filtered_attrs(member, dest_path, True) ++ if new_attrs: ++ return member.replace(**new_attrs, deep=False) ++ return member ++ ++_NAMED_FILTERS = { ++ "fully_trusted": fully_trusted_filter, ++ "tar": tar_filter, ++ "data": data_filter, ++} ++ + #------------------ + # Exported Classes + #------------------ ++ ++# Sentinel for replace() defaults, meaning "don't change the attribute" ++_KEEP = object() ++ + class TarInfo(object): + """Informational class which holds the details about an + archive member given by a tar header block. +@@ -792,12 +914,44 @@ def linkpath(self, linkname): + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + ++ def replace(self, *, ++ name=_KEEP, mtime=_KEEP, mode=_KEEP, linkname=_KEEP, ++ uid=_KEEP, gid=_KEEP, uname=_KEEP, gname=_KEEP, ++ deep=True, _KEEP=_KEEP): ++ """Return a deep copy of self with the given attributes replaced. ++ """ ++ if deep: ++ result = copy.deepcopy(self) ++ else: ++ result = copy.copy(self) ++ if name is not _KEEP: ++ result.name = name ++ if mtime is not _KEEP: ++ result.mtime = mtime ++ if mode is not _KEEP: ++ result.mode = mode ++ if linkname is not _KEEP: ++ result.linkname = linkname ++ if uid is not _KEEP: ++ result.uid = uid ++ if gid is not _KEEP: ++ result.gid = gid ++ if uname is not _KEEP: ++ result.uname = uname ++ if gname is not _KEEP: ++ result.gname = gname ++ return result ++ + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ ++ if self.mode is None: ++ mode = None ++ else: ++ mode = self.mode & 0o7777 + info = { + "name": self.name, +- "mode": self.mode & 0o7777, ++ "mode": mode, + "uid": self.uid, + "gid": self.gid, + "size": self.size, +@@ -820,6 +974,9 @@ def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescap + """Return a tar header as a string of 512 byte blocks. + """ + info = self.get_info() ++ for name, value in info.items(): ++ if value is None: ++ raise ValueError("%s may not be None" % name) + + if format == USTAR_FORMAT: + return self.create_ustar_header(info, encoding, errors) +@@ -950,6 +1107,12 @@ def _create_header(info, format, encoding, errors): + devmajor = stn("", 8, encoding, errors) + devminor = stn("", 8, encoding, errors) + ++ # None values in metadata should cause ValueError. ++ # itn()/stn() do this for all fields except type. ++ filetype = info.get("type", REGTYPE) ++ if filetype is None: ++ raise ValueError("TarInfo.type must not be None") ++ + parts = [ + stn(info.get("name", ""), 100, encoding, errors), + itn(info.get("mode", 0) & 0o7777, 8, format), +@@ -958,7 +1121,7 @@ def _create_header(info, format, encoding, errors): + itn(info.get("size", 0), 12, format), + itn(info.get("mtime", 0), 12, format), + b" ", # checksum field +- info.get("type", REGTYPE), ++ filetype, + stn(info.get("linkname", ""), 100, encoding, errors), + info.get("magic", POSIX_MAGIC), + stn(info.get("uname", ""), 32, encoding, errors), +@@ -1468,6 +1631,8 @@ class TarFile(object): + + fileobject = ExFileObject # The file-object for extractfile(). + ++ extraction_filter = None # The default filter for extraction. ++ + def __init__(self, name=None, mode="r", fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + errors="surrogateescape", pax_headers=None, debug=None, +@@ -1940,7 +2105,10 @@ def list(self, verbose=True, *, members=None): + members = self + for tarinfo in members: + if verbose: +- _safe_print(stat.filemode(tarinfo.mode)) ++ if tarinfo.mode is None: ++ _safe_print("??????????") ++ else: ++ _safe_print(stat.filemode(tarinfo.mode)) + _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid)) + if tarinfo.ischr() or tarinfo.isblk(): +@@ -1948,8 +2116,11 @@ def list(self, verbose=True, *, members=None): + ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor))) + else: + _safe_print("%10d" % tarinfo.size) +- _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ +- % time.localtime(tarinfo.mtime)[:6]) ++ if tarinfo.mtime is None: ++ _safe_print("????-??-?? ??:??:??") ++ else: ++ _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ ++ % time.localtime(tarinfo.mtime)[:6]) + + _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else "")) + +@@ -2036,32 +2207,58 @@ def addfile(self, tarinfo, fileobj=None): + + self.members.append(tarinfo) + +- def extractall(self, path=".", members=None, *, numeric_owner=False): ++ def _get_filter_function(self, filter): ++ if filter is None: ++ filter = self.extraction_filter ++ if filter is None: ++ return fully_trusted_filter ++ if isinstance(filter, str): ++ raise TypeError( ++ 'String names are not supported for ' ++ + 'TarFile.extraction_filter. Use a function such as ' ++ + 'tarfile.data_filter directly.') ++ return filter ++ if callable(filter): ++ return filter ++ try: ++ return _NAMED_FILTERS[filter] ++ except KeyError: ++ raise ValueError(f"filter {filter!r} not found") from None ++ ++ def extractall(self, path=".", members=None, *, numeric_owner=False, ++ filter=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). If `numeric_owner` is True, only + the numbers for user/group names are used and not the names. ++ ++ The `filter` function will be called on each member just ++ before extraction. ++ It can return a changed TarInfo or None to skip the member. ++ String names of common filters are accepted. + """ + directories = [] + ++ filter_function = self._get_filter_function(filter) + if members is None: + members = self + +- for tarinfo in members: ++ for member in members: ++ tarinfo = self._get_extract_tarinfo(member, filter_function, path) ++ if tarinfo is None: ++ continue + if tarinfo.isdir(): +- # Extract directories with a safe mode. ++ # For directories, delay setting attributes until later, ++ # since permissions can interfere with extraction and ++ # extracting contents can reset mtime. + directories.append(tarinfo) +- tarinfo = copy.copy(tarinfo) +- tarinfo.mode = 0o700 +- # Do not set_attrs directories, as we will do that further down +- self.extract(tarinfo, path, set_attrs=not tarinfo.isdir(), +- numeric_owner=numeric_owner) ++ self._extract_one(tarinfo, path, set_attrs=not tarinfo.isdir(), ++ numeric_owner=numeric_owner) + + # Reverse sort directories. +- directories.sort(key=lambda a: a.name) +- directories.reverse() ++ directories.sort(key=lambda a: a.name, reverse=True) + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: +@@ -2071,12 +2268,10 @@ def extractall(self, path=".", members=None, *, numeric_owner=False): + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: +- if self.errorlevel > 1: +- raise +- else: +- self._dbg(1, "tarfile: %s" % e) ++ self._handle_nonfatal_error(e) + +- def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): ++ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False, ++ filter=None): + """Extract a member from the archive to the current working directory, + using its full name. Its file information is extracted as accurately + as possible. `member' may be a filename or a TarInfo object. You can +@@ -2084,35 +2279,70 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): + mtime, mode) are set unless `set_attrs' is False. If `numeric_owner` + is True, only the numbers for user/group names are used and not + the names. ++ ++ The `filter` function will be called before extraction. ++ It can return a changed TarInfo or None to skip the member. ++ String names of common filters are accepted. + """ +- self._check("r") ++ filter_function = self._get_filter_function(filter) ++ tarinfo = self._get_extract_tarinfo(member, filter_function, path) ++ if tarinfo is not None: ++ self._extract_one(tarinfo, path, set_attrs, numeric_owner) + ++ def _get_extract_tarinfo(self, member, filter_function, path): ++ """Get filtered TarInfo (or None) from member, which might be a str""" + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + ++ unfiltered = tarinfo ++ try: ++ tarinfo = filter_function(tarinfo, path) ++ except (OSError, FilterError) as e: ++ self._handle_fatal_error(e) ++ except ExtractError as e: ++ self._handle_nonfatal_error(e) ++ if tarinfo is None: ++ self._dbg(2, "tarfile: Excluded %r" % unfiltered.name) ++ return None + # Prepare the link target for makelink(). + if tarinfo.islnk(): ++ tarinfo = copy.copy(tarinfo) + tarinfo._link_target = os.path.join(path, tarinfo.linkname) ++ return tarinfo ++ ++ def _extract_one(self, tarinfo, path, set_attrs, numeric_owner): ++ """Extract from filtered tarinfo to disk""" ++ self._check("r") + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs, + numeric_owner=numeric_owner) + except OSError as e: +- if self.errorlevel > 0: +- raise +- else: +- if e.filename is None: +- self._dbg(1, "tarfile: %s" % e.strerror) +- else: +- self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) ++ self._handle_fatal_error(e) + except ExtractError as e: +- if self.errorlevel > 1: +- raise ++ self._handle_nonfatal_error(e) ++ ++ def _handle_nonfatal_error(self, e): ++ """Handle non-fatal error (ExtractError) according to errorlevel""" ++ if self.errorlevel > 1: ++ raise ++ else: ++ self._dbg(1, "tarfile: %s" % e) ++ ++ def _handle_fatal_error(self, e): ++ """Handle "fatal" error according to self.errorlevel""" ++ if self.errorlevel > 0: ++ raise ++ elif isinstance(e, OSError): ++ if e.filename is None: ++ self._dbg(1, "tarfile: %s" % e.strerror) + else: +- self._dbg(1, "tarfile: %s" % e) ++ self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) ++ else: ++ self._dbg(1, "tarfile: %s %s" % (type(e).__name__, e)) + + def extractfile(self, member): + """Extract a member from the archive as a file object. `member' may be +@@ -2199,9 +2429,13 @@ def makedir(self, tarinfo, targetpath): + """Make a directory called targetpath. + """ + try: +- # Use a safe mode for the directory, the real mode is set +- # later in _extract_member(). +- os.mkdir(targetpath, 0o700) ++ if tarinfo.mode is None: ++ # Use the system's default mode ++ os.mkdir(targetpath) ++ else: ++ # Use a safe mode for the directory, the real mode is set ++ # later in _extract_member(). ++ os.mkdir(targetpath, 0o700) + except FileExistsError: + pass + +@@ -2244,6 +2478,9 @@ def makedev(self, tarinfo, targetpath): + raise ExtractError("special devices not supported by system") + + mode = tarinfo.mode ++ if mode is None: ++ # Use mknod's default ++ mode = 0o600 + if tarinfo.isblk(): + mode |= stat.S_IFBLK + else: +@@ -2265,7 +2502,6 @@ def makelink(self, tarinfo, targetpath): + os.unlink(targetpath) + os.symlink(tarinfo.linkname, targetpath) + else: +- # See extract(). + if os.path.exists(tarinfo._link_target): + os.link(tarinfo._link_target, targetpath) + else: +@@ -2290,15 +2526,19 @@ def chown(self, tarinfo, targetpath, numeric_owner): + u = tarinfo.uid + if not numeric_owner: + try: +- if grp: ++ if grp and tarinfo.gname: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + pass + try: +- if pwd: ++ if pwd and tarinfo.uname: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + pass ++ if g is None: ++ g = -1 ++ if u is None: ++ u = -1 + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) +@@ -2310,6 +2550,8 @@ def chown(self, tarinfo, targetpath, numeric_owner): + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ ++ if tarinfo.mode is None: ++ return + try: + os.chmod(targetpath, tarinfo.mode) + except OSError as e: +@@ -2318,10 +2560,13 @@ def chmod(self, tarinfo, targetpath): + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ ++ mtime = tarinfo.mtime ++ if mtime is None: ++ return + if not hasattr(os, 'utime'): + return + try: +- os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) ++ os.utime(targetpath, (mtime, mtime)) + except OSError as e: + raise ExtractError("could not change modification time") from e + +@@ -2397,13 +2642,26 @@ def _getmember(self, name, tarinfo=None, normalize=False): + members = self.getmembers() + + # Limit the member search list up to tarinfo. ++ skipping = False + if tarinfo is not None: +- members = members[:members.index(tarinfo)] ++ try: ++ index = members.index(tarinfo) ++ except ValueError: ++ # The given starting point might be a (modified) copy. ++ # We'll later skip members until we find an equivalent. ++ skipping = True ++ else: ++ # Happy fast path ++ members = members[:index] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): ++ if skipping: ++ if tarinfo.offset == member.offset: ++ skipping = False ++ continue + if normalize: + member_name = os.path.normpath(member.name) + else: +@@ -2412,6 +2670,10 @@ def _getmember(self, name, tarinfo=None, normalize=False): + if name == member_name: + return member + ++ if skipping: ++ # Starting point was not found ++ raise ValueError(tarinfo) ++ + def _load(self): + """Read through the entire archive file and look for readable + members. +@@ -2504,6 +2766,7 @@ def __exit__(self, type, value, traceback): + #-------------------- + # exported functions + #-------------------- ++ + def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. +@@ -2530,6 +2793,10 @@ def main(): + parser = argparse.ArgumentParser(description=description) + parser.add_argument('-v', '--verbose', action='store_true', default=False, + help='Verbose output') ++ parser.add_argument('--filter', metavar='', ++ choices=_NAMED_FILTERS, ++ help='Filter for extraction') ++ + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-l', '--list', metavar='', + help='Show listing of a tarfile') +@@ -2541,8 +2808,12 @@ def main(): + help='Create tarfile from sources') + group.add_argument('-t', '--test', metavar='', + help='Test if a tarfile is valid') ++ + args = parser.parse_args() + ++ if args.filter and args.extract is None: ++ parser.exit(1, '--filter is only valid for extraction\n') ++ + if args.test is not None: + src = args.test + if is_tarfile(src): +@@ -2573,7 +2844,7 @@ def main(): + + if is_tarfile(src): + with TarFile.open(src, 'r:*') as tf: +- tf.extractall(path=curdir) ++ tf.extractall(path=curdir, filter=args.filter) + if args.verbose: + if curdir == '.': + msg = '{!r} file is extracted.'.format(src) +diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py +index 0935b60d4c2..72fb3afcbef 100644 +--- a/Lib/test/test_shutil.py ++++ b/Lib/test/test_shutil.py +@@ -32,6 +32,7 @@ + from test import support + from test.support import os_helper + from test.support.os_helper import TESTFN, FakePath ++from test.support import warnings_helper + + TESTFN2 = TESTFN + "2" + TESTFN_SRC = TESTFN + "_SRC" +@@ -1610,12 +1611,14 @@ def test_register_archive_format(self): + + ### shutil.unpack_archive + +- def check_unpack_archive(self, format): +- self.check_unpack_archive_with_converter(format, lambda path: path) +- self.check_unpack_archive_with_converter(format, pathlib.Path) +- self.check_unpack_archive_with_converter(format, FakePath) ++ def check_unpack_archive(self, format, **kwargs): ++ self.check_unpack_archive_with_converter( ++ format, lambda path: path, **kwargs) ++ self.check_unpack_archive_with_converter( ++ format, pathlib.Path, **kwargs) ++ self.check_unpack_archive_with_converter(format, FakePath, **kwargs) + +- def check_unpack_archive_with_converter(self, format, converter): ++ def check_unpack_archive_with_converter(self, format, converter, **kwargs): + root_dir, base_dir = self._create_files() + expected = rlistdir(root_dir) + expected.remove('outer') +@@ -1625,36 +1628,47 @@ def check_unpack_archive_with_converter(self, format, converter): + + # let's try to unpack it now + tmpdir2 = self.mkdtemp() +- unpack_archive(converter(filename), converter(tmpdir2)) ++ unpack_archive(converter(filename), converter(tmpdir2), **kwargs) + self.assertEqual(rlistdir(tmpdir2), expected) + + # and again, this time with the format specified + tmpdir3 = self.mkdtemp() +- unpack_archive(converter(filename), converter(tmpdir3), format=format) ++ unpack_archive(converter(filename), converter(tmpdir3), format=format, ++ **kwargs) + self.assertEqual(rlistdir(tmpdir3), expected) + +- self.assertRaises(shutil.ReadError, unpack_archive, converter(TESTFN)) +- self.assertRaises(ValueError, unpack_archive, converter(TESTFN), format='xxx') ++ with self.assertRaises(shutil.ReadError): ++ unpack_archive(converter(TESTFN), **kwargs) ++ with self.assertRaises(ValueError): ++ unpack_archive(converter(TESTFN), format='xxx', **kwargs) ++ ++ def check_unpack_tarball(self, format): ++ self.check_unpack_archive(format, filter='fully_trusted') ++ self.check_unpack_archive(format, filter='data') ++ with warnings_helper.check_no_warnings(self): ++ self.check_unpack_archive(format) + + def test_unpack_archive_tar(self): +- self.check_unpack_archive('tar') ++ self.check_unpack_tarball('tar') + + @support.requires_zlib() + def test_unpack_archive_gztar(self): +- self.check_unpack_archive('gztar') ++ self.check_unpack_tarball('gztar') + + @support.requires_bz2() + def test_unpack_archive_bztar(self): +- self.check_unpack_archive('bztar') ++ self.check_unpack_tarball('bztar') + + @support.requires_lzma() + @unittest.skipIf(AIX and not _maxdataOK(), "AIX MAXDATA must be 0x20000000 or larger") + def test_unpack_archive_xztar(self): +- self.check_unpack_archive('xztar') ++ self.check_unpack_tarball('xztar') + + @support.requires_zlib() + def test_unpack_archive_zip(self): + self.check_unpack_archive('zip') ++ with self.assertRaises(TypeError): ++ self.check_unpack_archive('zip', filter='data') + + def test_unpack_registry(self): + +diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py +index 89f5a561b4a..0d8d91b4d03 100644 +--- a/Lib/test/test_tarfile.py ++++ b/Lib/test/test_tarfile.py +@@ -5,6 +5,10 @@ + from contextlib import contextmanager + from random import Random + import pathlib ++import shutil ++import re ++import warnings ++import stat + + import unittest + import unittest.mock +@@ -13,6 +17,7 @@ + from test import support + from test.support import os_helper + from test.support import script_helper ++from test.support import warnings_helper + + # Check for our compression modules. + try: +@@ -108,7 +113,7 @@ def test_fileobj_regular_file(self): + "regular file extraction failed") + + def test_fileobj_readlines(self): +- self.tar.extract("ustar/regtype", TEMPDIR) ++ self.tar.extract("ustar/regtype", TEMPDIR, filter='data') + tarinfo = self.tar.getmember("ustar/regtype") + with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1: + lines1 = fobj1.readlines() +@@ -126,7 +131,7 @@ def test_fileobj_readlines(self): + "fileobj.readlines() failed") + + def test_fileobj_iter(self): +- self.tar.extract("ustar/regtype", TEMPDIR) ++ self.tar.extract("ustar/regtype", TEMPDIR, filter='data') + tarinfo = self.tar.getmember("ustar/regtype") + with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1: + lines1 = fobj1.readlines() +@@ -136,7 +141,8 @@ def test_fileobj_iter(self): + "fileobj.__iter__() failed") + + def test_fileobj_seek(self): +- self.tar.extract("ustar/regtype", TEMPDIR) ++ self.tar.extract("ustar/regtype", TEMPDIR, ++ filter='data') + with open(os.path.join(TEMPDIR, "ustar/regtype"), "rb") as fobj: + data = fobj.read() + +@@ -455,7 +461,7 @@ def test_premature_end_of_archive(self): + t = tar.next() + + with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"): +- tar.extract(t, TEMPDIR) ++ tar.extract(t, TEMPDIR, filter='data') + + with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"): + tar.extractfile(t).read() +@@ -610,16 +616,16 @@ def test_find_members(self): + def test_extract_hardlink(self): + # Test hardlink extraction (e.g. bug #857297). + with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar: +- tar.extract("ustar/regtype", TEMPDIR) ++ tar.extract("ustar/regtype", TEMPDIR, filter='data') + self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/regtype")) + +- tar.extract("ustar/lnktype", TEMPDIR) ++ tar.extract("ustar/lnktype", TEMPDIR, filter='data') + self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/lnktype")) + with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f: + data = f.read() + self.assertEqual(sha256sum(data), sha256_regtype) + +- tar.extract("ustar/symtype", TEMPDIR) ++ tar.extract("ustar/symtype", TEMPDIR, filter='data') + self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/symtype")) + with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f: + data = f.read() +@@ -633,13 +639,14 @@ def test_extractall(self): + os.mkdir(DIR) + try: + directories = [t for t in tar if t.isdir()] +- tar.extractall(DIR, directories) ++ tar.extractall(DIR, directories, filter='fully_trusted') + for tarinfo in directories: + path = os.path.join(DIR, tarinfo.name) + if sys.platform != "win32": + # Win32 has no support for fine grained permissions. + self.assertEqual(tarinfo.mode & 0o777, +- os.stat(path).st_mode & 0o777) ++ os.stat(path).st_mode & 0o777, ++ tarinfo.name) + def format_mtime(mtime): + if isinstance(mtime, float): + return "{} ({})".format(mtime, mtime.hex()) +@@ -662,7 +669,7 @@ def test_extract_directory(self): + try: + with tarfile.open(tarname, encoding="iso8859-1") as tar: + tarinfo = tar.getmember(dirtype) +- tar.extract(tarinfo, path=DIR) ++ tar.extract(tarinfo, path=DIR, filter='fully_trusted') + extracted = os.path.join(DIR, dirtype) + self.assertEqual(os.path.getmtime(extracted), tarinfo.mtime) + if sys.platform != "win32": +@@ -675,7 +682,7 @@ def test_extractall_pathlike_name(self): + with os_helper.temp_dir(DIR), \ + tarfile.open(tarname, encoding="iso8859-1") as tar: + directories = [t for t in tar if t.isdir()] +- tar.extractall(DIR, directories) ++ tar.extractall(DIR, directories, filter='fully_trusted') + for tarinfo in directories: + path = DIR / tarinfo.name + self.assertEqual(os.path.getmtime(path), tarinfo.mtime) +@@ -686,7 +693,7 @@ def test_extract_pathlike_name(self): + with os_helper.temp_dir(DIR), \ + tarfile.open(tarname, encoding="iso8859-1") as tar: + tarinfo = tar.getmember(dirtype) +- tar.extract(tarinfo, path=DIR) ++ tar.extract(tarinfo, path=DIR, filter='fully_trusted') + extracted = DIR / dirtype + self.assertEqual(os.path.getmtime(extracted), tarinfo.mtime) + +@@ -1042,7 +1049,7 @@ class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase): + # an all platforms, and after that a test that will work only on + # platforms/filesystems that prove to support sparse files. + def _test_sparse_file(self, name): +- self.tar.extract(name, TEMPDIR) ++ self.tar.extract(name, TEMPDIR, filter='data') + filename = os.path.join(TEMPDIR, name) + with open(filename, "rb") as fobj: + data = fobj.read() +@@ -1409,7 +1416,8 @@ def test_extractall_symlinks(self): + with tarfile.open(temparchive, errorlevel=2) as tar: + # this should not raise OSError: [Errno 17] File exists + try: +- tar.extractall(path=tempdir) ++ tar.extractall(path=tempdir, ++ filter='fully_trusted') + except OSError: + self.fail("extractall failed with symlinked files") + finally: +@@ -2406,7 +2414,12 @@ def test__all__(self): + 'PAX_NUMBER_FIELDS', 'stn', 'nts', 'nti', 'itn', 'calc_chksums', + 'copyfileobj', 'filemode', 'EmptyHeaderError', + 'TruncatedHeaderError', 'EOFHeaderError', 'InvalidHeaderError', +- 'SubsequentHeaderError', 'ExFileObject', 'main'} ++ 'SubsequentHeaderError', 'ExFileObject', 'main', ++ "fully_trusted_filter", "data_filter", ++ "tar_filter", "FilterError", "AbsoluteLinkError", ++ "OutsideDestinationError", "SpecialFileError", "AbsolutePathError", ++ "LinkOutsideDestinationError", ++ } + support.check__all__(self, tarfile, not_exported=not_exported) + + def test_useful_error_message_when_modules_missing(self): +@@ -2441,6 +2454,15 @@ def make_simple_tarfile(self, tar_name): + for tardata in files: + tf.add(tardata, arcname=os.path.basename(tardata)) + ++ def make_evil_tarfile(self, tar_name): ++ files = [support.findfile('tokenize_tests.txt')] ++ self.addCleanup(os_helper.unlink, tar_name) ++ with tarfile.open(tar_name, 'w') as tf: ++ benign = tarfile.TarInfo('benign') ++ tf.addfile(benign, fileobj=io.BytesIO(b'')) ++ evil = tarfile.TarInfo('../evil') ++ tf.addfile(evil, fileobj=io.BytesIO(b'')) ++ + def test_bad_use(self): + rc, out, err = self.tarfilecmd_failure() + self.assertEqual(out, b'') +@@ -2597,6 +2619,25 @@ def test_extract_command_verbose(self): + finally: + os_helper.rmtree(tarextdir) + ++ def test_extract_command_filter(self): ++ self.make_evil_tarfile(tmpname) ++ # Make an inner directory, so the member named '../evil' ++ # is still extracted into `tarextdir` ++ destdir = os.path.join(tarextdir, 'dest') ++ os.mkdir(tarextdir) ++ try: ++ with os_helper.temp_cwd(destdir): ++ self.tarfilecmd_failure('-e', tmpname, ++ '-v', ++ '--filter', 'data') ++ out = self.tarfilecmd('-e', tmpname, ++ '-v', ++ '--filter', 'fully_trusted', ++ PYTHONIOENCODING='utf-8') ++ self.assertIn(b' file is extracted.', out) ++ finally: ++ os_helper.rmtree(tarextdir) ++ + def test_extract_command_different_directory(self): + self.make_simple_tarfile(tmpname) + try: +@@ -2680,7 +2721,7 @@ class LinkEmulationTest(ReadTest, unittest.TestCase): + # symbolic or hard links tarfile tries to extract these types of members + # as the regular files they point to. + def _test_link_extraction(self, name): +- self.tar.extract(name, TEMPDIR) ++ self.tar.extract(name, TEMPDIR, filter='fully_trusted') + with open(os.path.join(TEMPDIR, name), "rb") as f: + data = f.read() + self.assertEqual(sha256sum(data), sha256_regtype) +@@ -2812,8 +2853,10 @@ def test_extract_with_numeric_owner(self, mock_geteuid, mock_chmod, + mock_chown): + with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, + filename_2): +- tarfl.extract(filename_1, TEMPDIR, numeric_owner=True) +- tarfl.extract(filename_2 , TEMPDIR, numeric_owner=True) ++ tarfl.extract(filename_1, TEMPDIR, numeric_owner=True, ++ filter='fully_trusted') ++ tarfl.extract(filename_2 , TEMPDIR, numeric_owner=True, ++ filter='fully_trusted') + + # convert to filesystem paths + f_filename_1 = os.path.join(TEMPDIR, filename_1) +@@ -2831,7 +2874,8 @@ def test_extractall_with_numeric_owner(self, mock_geteuid, mock_chmod, + mock_chown): + with self._setup_test(mock_geteuid) as (tarfl, filename_1, dirname_1, + filename_2): +- tarfl.extractall(TEMPDIR, numeric_owner=True) ++ tarfl.extractall(TEMPDIR, numeric_owner=True, ++ filter='fully_trusted') + + # convert to filesystem paths + f_filename_1 = os.path.join(TEMPDIR, filename_1) +@@ -2856,7 +2900,8 @@ def test_extractall_with_numeric_owner(self, mock_geteuid, mock_chmod, + def test_extract_without_numeric_owner(self, mock_geteuid, mock_chmod, + mock_chown): + with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, _): +- tarfl.extract(filename_1, TEMPDIR, numeric_owner=False) ++ tarfl.extract(filename_1, TEMPDIR, numeric_owner=False, ++ filter='fully_trusted') + + # convert to filesystem paths + f_filename_1 = os.path.join(TEMPDIR, filename_1) +@@ -2870,6 +2915,888 @@ def test_keyword_only(self, mock_geteuid): + tarfl.extract, filename_1, TEMPDIR, False, True) + + ++class ReplaceTests(ReadTest, unittest.TestCase): ++ def test_replace_name(self): ++ member = self.tar.getmember('ustar/regtype') ++ replaced = member.replace(name='misc/other') ++ self.assertEqual(replaced.name, 'misc/other') ++ self.assertEqual(member.name, 'ustar/regtype') ++ self.assertEqual(self.tar.getmember('ustar/regtype').name, ++ 'ustar/regtype') ++ ++ def test_replace_deep(self): ++ member = self.tar.getmember('pax/regtype1') ++ replaced = member.replace() ++ replaced.pax_headers['gname'] = 'not-bar' ++ self.assertEqual(member.pax_headers['gname'], 'bar') ++ self.assertEqual( ++ self.tar.getmember('pax/regtype1').pax_headers['gname'], 'bar') ++ ++ def test_replace_shallow(self): ++ member = self.tar.getmember('pax/regtype1') ++ replaced = member.replace(deep=False) ++ replaced.pax_headers['gname'] = 'not-bar' ++ self.assertEqual(member.pax_headers['gname'], 'not-bar') ++ self.assertEqual( ++ self.tar.getmember('pax/regtype1').pax_headers['gname'], 'not-bar') ++ ++ def test_replace_all(self): ++ member = self.tar.getmember('ustar/regtype') ++ for attr_name in ('name', 'mtime', 'mode', 'linkname', ++ 'uid', 'gid', 'uname', 'gname'): ++ with self.subTest(attr_name=attr_name): ++ replaced = member.replace(**{attr_name: None}) ++ self.assertEqual(getattr(replaced, attr_name), None) ++ self.assertNotEqual(getattr(member, attr_name), None) ++ ++ def test_replace_internal(self): ++ member = self.tar.getmember('ustar/regtype') ++ with self.assertRaises(TypeError): ++ member.replace(offset=123456789) ++ ++ ++class NoneInfoExtractTests(ReadTest): ++ # These mainly check that all kinds of members are extracted successfully ++ # if some metadata is None. ++ # Some of the methods do additional spot checks. ++ ++ # We also test that the default filters can deal with None. ++ ++ extraction_filter = None ++ ++ @classmethod ++ def setUpClass(cls): ++ 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) ++ tar.close() ++ cls.control_paths = set( ++ p.relative_to(cls.control_dir) ++ for p in pathlib.Path(cls.control_dir).glob('**/*')) ++ ++ @classmethod ++ def tearDownClass(cls): ++ shutil.rmtree(cls.control_dir) ++ ++ def check_files_present(self, directory): ++ got_paths = set( ++ p.relative_to(directory) ++ for p in pathlib.Path(directory).glob('**/*')) ++ self.assertEqual(self.control_paths, got_paths) ++ ++ @contextmanager ++ def extract_with_none(self, *attr_names): ++ DIR = pathlib.Path(TEMPDIR) / "extractall_none" ++ self.tar.errorlevel = 0 ++ for member in self.tar.getmembers(): ++ for attr_name in attr_names: ++ setattr(member, attr_name, None) ++ with os_helper.temp_dir(DIR): ++ self.tar.extractall(DIR, filter='fully_trusted') ++ self.check_files_present(DIR) ++ yield DIR ++ ++ def test_extractall_none_mtime(self): ++ # mtimes of extracted files should be later than 'now' -- the mtime ++ # of a previously created directory. ++ now = pathlib.Path(TEMPDIR).stat().st_mtime ++ with self.extract_with_none('mtime') as DIR: ++ for path in pathlib.Path(DIR).glob('**/*'): ++ with self.subTest(path=path): ++ try: ++ mtime = path.stat().st_mtime ++ except OSError: ++ # Some systems can't stat symlinks, ignore those ++ if not path.is_symlink(): ++ raise ++ else: ++ self.assertGreaterEqual(path.stat().st_mtime, now) ++ ++ def test_extractall_none_mode(self): ++ # modes of directories and regular files should match the mode ++ # of a "normally" created directory or regular file ++ dir_mode = pathlib.Path(TEMPDIR).stat().st_mode ++ regular_file = pathlib.Path(TEMPDIR) / 'regular_file' ++ regular_file.write_text('') ++ regular_file_mode = regular_file.stat().st_mode ++ with self.extract_with_none('mode') as DIR: ++ for path in pathlib.Path(DIR).glob('**/*'): ++ with self.subTest(path=path): ++ if path.is_dir(): ++ self.assertEqual(path.stat().st_mode, dir_mode) ++ elif path.is_file(): ++ self.assertEqual(path.stat().st_mode, ++ regular_file_mode) ++ ++ def test_extractall_none_uid(self): ++ with self.extract_with_none('uid'): ++ pass ++ ++ def test_extractall_none_gid(self): ++ with self.extract_with_none('gid'): ++ pass ++ ++ def test_extractall_none_uname(self): ++ with self.extract_with_none('uname'): ++ pass ++ ++ def test_extractall_none_gname(self): ++ with self.extract_with_none('gname'): ++ pass ++ ++ def test_extractall_none_ownership(self): ++ with self.extract_with_none('uid', 'gid', 'uname', 'gname'): ++ pass ++ ++class NoneInfoExtractTests_Data(NoneInfoExtractTests, unittest.TestCase): ++ extraction_filter = 'data' ++ ++class NoneInfoExtractTests_FullyTrusted(NoneInfoExtractTests, ++ unittest.TestCase): ++ extraction_filter = 'fully_trusted' ++ ++class NoneInfoExtractTests_Tar(NoneInfoExtractTests, unittest.TestCase): ++ extraction_filter = 'tar' ++ ++class NoneInfoExtractTests_Default(NoneInfoExtractTests, ++ unittest.TestCase): ++ extraction_filter = None ++ ++class NoneInfoTests_Misc(unittest.TestCase): ++ def test_add(self): ++ # When addfile() encounters None metadata, it raises a ValueError ++ bio = io.BytesIO() ++ for tarformat in (tarfile.USTAR_FORMAT, tarfile.GNU_FORMAT, ++ tarfile.PAX_FORMAT): ++ with self.subTest(tarformat=tarformat): ++ tar = tarfile.open(fileobj=bio, mode='w', format=tarformat) ++ tarinfo = tar.gettarinfo(tarname) ++ try: ++ tar.addfile(tarinfo) ++ except Exception: ++ if tarformat == tarfile.USTAR_FORMAT: ++ # In the old, limited format, adding might fail for ++ # reasons like the UID being too large ++ pass ++ else: ++ raise ++ else: ++ for attr_name in ('mtime', 'mode', 'uid', 'gid', ++ 'uname', 'gname'): ++ with self.subTest(attr_name=attr_name): ++ replaced = tarinfo.replace(**{attr_name: None}) ++ with self.assertRaisesRegex(ValueError, ++ f"{attr_name}"): ++ tar.addfile(replaced) ++ ++ def test_list(self): ++ # Change some metadata to None, then compare list() output ++ # word-for-word. We want list() to not raise, and to only change ++ # printout for the affected piece of metadata. ++ # (n.b.: some contents of the test archive are hardcoded.) ++ for attr_names in ({'mtime'}, {'mode'}, {'uid'}, {'gid'}, ++ {'uname'}, {'gname'}, ++ {'uid', 'uname'}, {'gid', 'gname'}): ++ with (self.subTest(attr_names=attr_names), ++ tarfile.open(tarname, encoding="iso8859-1") as tar): ++ tio_prev = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') ++ with support.swap_attr(sys, 'stdout', tio_prev): ++ tar.list() ++ for member in tar.getmembers(): ++ for attr_name in attr_names: ++ setattr(member, attr_name, None) ++ tio_new = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') ++ with support.swap_attr(sys, 'stdout', tio_new): ++ tar.list() ++ for expected, got in zip(tio_prev.detach().getvalue().split(), ++ tio_new.detach().getvalue().split()): ++ if attr_names == {'mtime'} and re.match(rb'2003-01-\d\d', expected): ++ self.assertEqual(got, b'????-??-??') ++ elif attr_names == {'mtime'} and re.match(rb'\d\d:\d\d:\d\d', expected): ++ self.assertEqual(got, b'??:??:??') ++ elif attr_names == {'mode'} and re.match( ++ rb'.([r-][w-][x-]){3}', expected): ++ self.assertEqual(got, b'??????????') ++ elif attr_names == {'uname'} and expected.startswith( ++ (b'tarfile/', b'lars/', b'foo/')): ++ exp_user, exp_group = expected.split(b'/') ++ got_user, got_group = got.split(b'/') ++ self.assertEqual(got_group, exp_group) ++ self.assertRegex(got_user, b'[0-9]+') ++ elif attr_names == {'gname'} and expected.endswith( ++ (b'/tarfile', b'/users', b'/bar')): ++ exp_user, exp_group = expected.split(b'/') ++ got_user, got_group = got.split(b'/') ++ self.assertEqual(got_user, exp_user) ++ self.assertRegex(got_group, b'[0-9]+') ++ elif attr_names == {'uid'} and expected.startswith( ++ (b'1000/')): ++ exp_user, exp_group = expected.split(b'/') ++ got_user, got_group = got.split(b'/') ++ self.assertEqual(got_group, exp_group) ++ self.assertEqual(got_user, b'None') ++ elif attr_names == {'gid'} and expected.endswith((b'/100')): ++ exp_user, exp_group = expected.split(b'/') ++ got_user, got_group = got.split(b'/') ++ self.assertEqual(got_user, exp_user) ++ self.assertEqual(got_group, b'None') ++ elif attr_names == {'uid', 'uname'} and expected.startswith( ++ (b'tarfile/', b'lars/', b'foo/', b'1000/')): ++ exp_user, exp_group = expected.split(b'/') ++ got_user, got_group = got.split(b'/') ++ self.assertEqual(got_group, exp_group) ++ self.assertEqual(got_user, b'None') ++ elif attr_names == {'gname', 'gid'} and expected.endswith( ++ (b'/tarfile', b'/users', b'/bar', b'/100')): ++ exp_user, exp_group = expected.split(b'/') ++ got_user, got_group = got.split(b'/') ++ self.assertEqual(got_user, exp_user) ++ self.assertEqual(got_group, b'None') ++ else: ++ # In other cases the output should be the same ++ self.assertEqual(expected, got) ++ ++def _filemode_to_int(mode): ++ """Inverse of `stat.filemode` (for permission bits) ++ ++ Using mode strings rather than numbers makes the later tests more readable. ++ """ ++ str_mode = mode[1:] ++ result = ( ++ {'r': stat.S_IRUSR, '-': 0}[str_mode[0]] ++ | {'w': stat.S_IWUSR, '-': 0}[str_mode[1]] ++ | {'x': stat.S_IXUSR, '-': 0, ++ 's': stat.S_IXUSR | stat.S_ISUID, ++ 'S': stat.S_ISUID}[str_mode[2]] ++ | {'r': stat.S_IRGRP, '-': 0}[str_mode[3]] ++ | {'w': stat.S_IWGRP, '-': 0}[str_mode[4]] ++ | {'x': stat.S_IXGRP, '-': 0, ++ 's': stat.S_IXGRP | stat.S_ISGID, ++ 'S': stat.S_ISGID}[str_mode[5]] ++ | {'r': stat.S_IROTH, '-': 0}[str_mode[6]] ++ | {'w': stat.S_IWOTH, '-': 0}[str_mode[7]] ++ | {'x': stat.S_IXOTH, '-': 0, ++ 't': stat.S_IXOTH | stat.S_ISVTX, ++ 'T': stat.S_ISVTX}[str_mode[8]] ++ ) ++ # check we did this right ++ assert stat.filemode(result)[1:] == mode[1:] ++ ++ return result ++ ++class ArchiveMaker: ++ """Helper to create a tar file with specific contents ++ ++ Usage: ++ ++ with ArchiveMaker() as t: ++ t.add('filename', ...) ++ ++ with t.open() as tar: ++ ... # `tar` is now a TarFile with 'filename' in it! ++ """ ++ def __init__(self): ++ self.bio = io.BytesIO() ++ ++ def __enter__(self): ++ self.tar_w = tarfile.TarFile(mode='w', fileobj=self.bio) ++ return self ++ ++ def __exit__(self, *exc): ++ self.tar_w.close() ++ self.contents = self.bio.getvalue() ++ self.bio = None ++ ++ def add(self, name, *, type=None, symlink_to=None, hardlink_to=None, ++ mode=None, **kwargs): ++ """Add a member to the test archive. Call within `with`.""" ++ name = str(name) ++ tarinfo = tarfile.TarInfo(name).replace(**kwargs) ++ if mode: ++ tarinfo.mode = _filemode_to_int(mode) ++ if symlink_to is not None: ++ type = tarfile.SYMTYPE ++ tarinfo.linkname = str(symlink_to) ++ if hardlink_to is not None: ++ type = tarfile.LNKTYPE ++ tarinfo.linkname = str(hardlink_to) ++ if name.endswith('/') and type is None: ++ type = tarfile.DIRTYPE ++ if type is not None: ++ tarinfo.type = type ++ if tarinfo.isreg(): ++ fileobj = io.BytesIO(bytes(tarinfo.size)) ++ else: ++ fileobj = None ++ self.tar_w.addfile(tarinfo, fileobj) ++ ++ def open(self, **kwargs): ++ """Open the resulting archive as TarFile. Call after `with`.""" ++ bio = io.BytesIO(self.contents) ++ return tarfile.open(fileobj=bio, **kwargs) ++ ++ ++class TestExtractionFilters(unittest.TestCase): ++ ++ # A temporary directory for the extraction results. ++ # All files that "escape" the destination path should still end ++ # up in this directory. ++ outerdir = pathlib.Path(TEMPDIR) / 'outerdir' ++ ++ # The destination for the extraction, within `outerdir` ++ destdir = outerdir / 'dest' ++ ++ @contextmanager ++ def check_context(self, tar, filter): ++ """Extracts `tar` to `self.destdir` and allows checking the result ++ ++ If an error occurs, it must be checked using `expect_exception` ++ ++ Otherwise, all resulting files must be checked using `expect_file`, ++ except the destination directory itself and parent directories of ++ other files. ++ When checking directories, do so before their contents. ++ """ ++ with os_helper.temp_dir(self.outerdir): ++ try: ++ tar.extractall(self.destdir, filter=filter) ++ except Exception as exc: ++ self.raised_exception = exc ++ self.expected_paths = set() ++ else: ++ self.raised_exception = None ++ self.expected_paths = set(self.outerdir.glob('**/*')) ++ self.expected_paths.discard(self.destdir) ++ try: ++ yield ++ finally: ++ tar.close() ++ if self.raised_exception: ++ raise self.raised_exception ++ self.assertEqual(self.expected_paths, set()) ++ ++ def expect_file(self, name, type=None, symlink_to=None, mode=None): ++ """Check a single file. See check_context.""" ++ if self.raised_exception: ++ raise self.raised_exception ++ # use normpath() rather than resolve() so we don't follow symlinks ++ path = pathlib.Path(os.path.normpath(self.destdir / name)) ++ self.assertIn(path, self.expected_paths) ++ self.expected_paths.remove(path) ++ if mode is not None: ++ got = stat.filemode(stat.S_IMODE(path.stat().st_mode)) ++ self.assertEqual(got, mode) ++ if type is None and isinstance(name, str) and name.endswith('/'): ++ type = tarfile.DIRTYPE ++ if symlink_to is not None: ++ got = (self.destdir / name).readlink() ++ expected = pathlib.Path(symlink_to) ++ # The symlink might be the same (textually) as what we expect, ++ # but some systems change the link to an equivalent path, so ++ # we fall back to samefile(). ++ if expected != got: ++ self.assertTrue(got.samefile(expected)) ++ elif type == tarfile.REGTYPE or type is None: ++ self.assertTrue(path.is_file()) ++ elif type == tarfile.DIRTYPE: ++ self.assertTrue(path.is_dir()) ++ elif type == tarfile.FIFOTYPE: ++ self.assertTrue(path.is_fifo()) ++ else: ++ raise NotImplementedError(type) ++ for parent in path.parents: ++ self.expected_paths.discard(parent) ++ ++ def expect_exception(self, exc_type, message_re='.'): ++ with self.assertRaisesRegex(exc_type, message_re): ++ if self.raised_exception is not None: ++ raise self.raised_exception ++ self.raised_exception = None ++ ++ def test_benign_file(self): ++ with ArchiveMaker() as arc: ++ arc.add('benign.txt') ++ for filter in 'fully_trusted', 'tar', 'data': ++ with self.check_context(arc.open(), filter): ++ self.expect_file('benign.txt') ++ ++ def test_absolute(self): ++ # Test handling a member with an absolute path ++ # Inspired by 'absolute1' in https://github.com/jwilk/traversal-archives ++ with ArchiveMaker() as arc: ++ arc.add(self.outerdir / 'escaped.evil') ++ ++ with self.check_context(arc.open(), 'fully_trusted'): ++ self.expect_file('../escaped.evil') ++ ++ for filter in 'tar', 'data': ++ with self.check_context(arc.open(), filter): ++ if str(self.outerdir).startswith('/'): ++ # We strip leading slashes, as e.g. GNU tar does ++ # (without --absolute-filenames). ++ outerdir_stripped = str(self.outerdir).lstrip('/') ++ self.expect_file(f'{outerdir_stripped}/escaped.evil') ++ else: ++ # On this system, absolute paths don't have leading ++ # slashes. ++ # So, there's nothing to strip. We refuse to unpack ++ # to an absolute path, nonetheless. ++ self.expect_exception( ++ tarfile.AbsolutePathError, ++ """['"].*escaped.evil['"] has an absolute path""") ++ ++ def test_parent_symlink(self): ++ # Test interplaying symlinks ++ # Inspired by 'dirsymlink2a' in jwilk/traversal-archives ++ with ArchiveMaker() as arc: ++ arc.add('current', symlink_to='.') ++ arc.add('parent', symlink_to='current/..') ++ arc.add('parent/evil') ++ ++ if os_helper.can_symlink(): ++ with self.check_context(arc.open(), 'fully_trusted'): ++ if self.raised_exception is not None: ++ # Windows will refuse to create a file that's a symlink to itself ++ # (and tarfile doesn't swallow that exception) ++ self.expect_exception(FileExistsError) ++ # The other cases will fail with this error too. ++ # Skip the rest of this test. ++ return ++ else: ++ self.expect_file('current', symlink_to='.') ++ self.expect_file('parent', symlink_to='current/..') ++ self.expect_file('../evil') ++ ++ with self.check_context(arc.open(), 'tar'): ++ self.expect_exception( ++ tarfile.OutsideDestinationError, ++ """'parent/evil' would be extracted to ['"].*evil['"], """ ++ + "which is outside the destination") ++ ++ with self.check_context(arc.open(), 'data'): ++ self.expect_exception( ++ tarfile.LinkOutsideDestinationError, ++ """'parent' would link to ['"].*outerdir['"], """ ++ + "which is outside the destination") ++ ++ else: ++ # No symlink support. The symlinks are ignored. ++ with self.check_context(arc.open(), 'fully_trusted'): ++ self.expect_file('parent/evil') ++ with self.check_context(arc.open(), 'tar'): ++ self.expect_file('parent/evil') ++ with self.check_context(arc.open(), 'data'): ++ self.expect_file('parent/evil') ++ ++ def test_parent_symlink2(self): ++ # Test interplaying symlinks ++ # Inspired by 'dirsymlink2b' in jwilk/traversal-archives ++ with ArchiveMaker() as arc: ++ arc.add('current', symlink_to='.') ++ arc.add('current/parent', symlink_to='..') ++ arc.add('parent/evil') ++ ++ with self.check_context(arc.open(), 'fully_trusted'): ++ if os_helper.can_symlink(): ++ self.expect_file('current', symlink_to='.') ++ self.expect_file('parent', symlink_to='..') ++ self.expect_file('../evil') ++ else: ++ self.expect_file('current/') ++ self.expect_file('parent/evil') ++ ++ with self.check_context(arc.open(), 'tar'): ++ if os_helper.can_symlink(): ++ self.expect_exception( ++ tarfile.OutsideDestinationError, ++ "'parent/evil' would be extracted to " ++ + """['"].*evil['"], which is outside """ ++ + "the destination") ++ else: ++ self.expect_file('current/') ++ self.expect_file('parent/evil') ++ ++ with self.check_context(arc.open(), 'data'): ++ self.expect_exception( ++ tarfile.LinkOutsideDestinationError, ++ """'current/parent' would link to ['"].*['"], """ ++ + "which is outside the destination") ++ ++ def test_absolute_symlink(self): ++ # Test symlink to an absolute path ++ # Inspired by 'dirsymlink' in jwilk/traversal-archives ++ with ArchiveMaker() as arc: ++ arc.add('parent', symlink_to=self.outerdir) ++ arc.add('parent/evil') ++ ++ with self.check_context(arc.open(), 'fully_trusted'): ++ if os_helper.can_symlink(): ++ self.expect_file('parent', symlink_to=self.outerdir) ++ self.expect_file('../evil') ++ else: ++ self.expect_file('parent/evil') ++ ++ with self.check_context(arc.open(), 'tar'): ++ if os_helper.can_symlink(): ++ self.expect_exception( ++ tarfile.OutsideDestinationError, ++ "'parent/evil' would be extracted to " ++ + """['"].*evil['"], which is outside """ ++ + "the destination") ++ else: ++ self.expect_file('parent/evil') ++ ++ with self.check_context(arc.open(), 'data'): ++ self.expect_exception( ++ tarfile.AbsoluteLinkError, ++ "'parent' is a symlink to an absolute path") ++ ++ def test_sly_relative0(self): ++ # Inspired by 'relative0' in jwilk/traversal-archives ++ with ArchiveMaker() as arc: ++ arc.add('../moo', symlink_to='..//tmp/moo') ++ ++ try: ++ with self.check_context(arc.open(), filter='fully_trusted'): ++ if os_helper.can_symlink(): ++ if isinstance(self.raised_exception, FileExistsError): ++ # XXX TarFile happens to fail creating a parent ++ # directory. ++ # This might be a bug, but fixing it would hurt ++ # security. ++ # Note that e.g. GNU `tar` rejects '..' components, ++ # so you could argue this is an invalid archive and we ++ # just raise an bad type of exception. ++ self.expect_exception(FileExistsError) ++ else: ++ self.expect_file('../moo', symlink_to='..//tmp/moo') ++ else: ++ # The symlink can't be extracted and is ignored ++ pass ++ except FileExistsError: ++ pass ++ ++ for filter in 'tar', 'data': ++ with self.check_context(arc.open(), filter): ++ self.expect_exception( ++ tarfile.OutsideDestinationError, ++ "'../moo' would be extracted to " ++ + "'.*moo', which is outside " ++ + "the destination") ++ ++ def test_sly_relative2(self): ++ # Inspired by 'relative2' in jwilk/traversal-archives ++ with ArchiveMaker() as arc: ++ arc.add('tmp/') ++ arc.add('tmp/../../moo', symlink_to='tmp/../..//tmp/moo') ++ ++ with self.check_context(arc.open(), 'fully_trusted'): ++ self.expect_file('tmp', type=tarfile.DIRTYPE) ++ if os_helper.can_symlink(): ++ self.expect_file('../moo', symlink_to='tmp/../../tmp/moo') ++ ++ for filter in 'tar', 'data': ++ with self.check_context(arc.open(), filter): ++ self.expect_exception( ++ tarfile.OutsideDestinationError, ++ "'tmp/../../moo' would be extracted to " ++ + """['"].*moo['"], which is outside the """ ++ + "destination") ++ ++ def test_modes(self): ++ # Test how file modes are extracted ++ # (Note that the modes are ignored on platforms without working chmod) ++ with ArchiveMaker() as arc: ++ arc.add('all_bits', mode='?rwsrwsrwt') ++ arc.add('perm_bits', mode='?rwxrwxrwx') ++ arc.add('exec_group_other', mode='?rw-rwxrwx') ++ arc.add('read_group_only', mode='?---r-----') ++ arc.add('no_bits', mode='?---------') ++ arc.add('dir/', mode='?---rwsrwt') ++ ++ # On some systems, setting the sticky bit is a no-op. ++ # Check if that's the case. ++ tmp_filename = os.path.join(TEMPDIR, "tmp.file") ++ with open(tmp_filename, 'w'): ++ pass ++ os.chmod(tmp_filename, os.stat(tmp_filename).st_mode | stat.S_ISVTX) ++ have_sticky_files = (os.stat(tmp_filename).st_mode & stat.S_ISVTX) ++ os.unlink(tmp_filename) ++ ++ os.mkdir(tmp_filename) ++ os.chmod(tmp_filename, os.stat(tmp_filename).st_mode | stat.S_ISVTX) ++ have_sticky_dirs = (os.stat(tmp_filename).st_mode & stat.S_ISVTX) ++ os.rmdir(tmp_filename) ++ ++ with self.check_context(arc.open(), 'fully_trusted'): ++ if have_sticky_files: ++ self.expect_file('all_bits', mode='?rwsrwsrwt') ++ else: ++ self.expect_file('all_bits', mode='?rwsrwsrwx') ++ self.expect_file('perm_bits', mode='?rwxrwxrwx') ++ self.expect_file('exec_group_other', mode='?rw-rwxrwx') ++ self.expect_file('read_group_only', mode='?---r-----') ++ self.expect_file('no_bits', mode='?---------') ++ if have_sticky_dirs: ++ self.expect_file('dir/', mode='?---rwsrwt') ++ else: ++ self.expect_file('dir/', mode='?---rwsrwx') ++ ++ with self.check_context(arc.open(), 'tar'): ++ self.expect_file('all_bits', mode='?rwxr-xr-x') ++ self.expect_file('perm_bits', mode='?rwxr-xr-x') ++ self.expect_file('exec_group_other', mode='?rw-r-xr-x') ++ self.expect_file('read_group_only', mode='?---r-----') ++ self.expect_file('no_bits', mode='?---------') ++ self.expect_file('dir/', mode='?---r-xr-x') ++ ++ with self.check_context(arc.open(), 'data'): ++ normal_dir_mode = stat.filemode(stat.S_IMODE( ++ self.outerdir.stat().st_mode)) ++ self.expect_file('all_bits', mode='?rwxr-xr-x') ++ self.expect_file('perm_bits', mode='?rwxr-xr-x') ++ self.expect_file('exec_group_other', mode='?rw-r--r--') ++ self.expect_file('read_group_only', mode='?rw-r-----') ++ self.expect_file('no_bits', mode='?rw-------') ++ self.expect_file('dir/', mode=normal_dir_mode) ++ ++ def test_pipe(self): ++ # Test handling of a special file ++ with ArchiveMaker() as arc: ++ arc.add('foo', type=tarfile.FIFOTYPE) ++ ++ for filter in 'fully_trusted', 'tar': ++ with self.check_context(arc.open(), filter): ++ if hasattr(os, 'mkfifo'): ++ self.expect_file('foo', type=tarfile.FIFOTYPE) ++ else: ++ # The pipe can't be extracted and is skipped. ++ pass ++ ++ with self.check_context(arc.open(), 'data'): ++ self.expect_exception( ++ tarfile.SpecialFileError, ++ "'foo' is a special file") ++ ++ def test_special_files(self): ++ # Creating device files is tricky. Instead of attempting that let's ++ # only check the filter result. ++ for special_type in tarfile.FIFOTYPE, tarfile.CHRTYPE, tarfile.BLKTYPE: ++ tarinfo = tarfile.TarInfo('foo') ++ tarinfo.type = special_type ++ trusted = tarfile.fully_trusted_filter(tarinfo, '') ++ self.assertIs(trusted, tarinfo) ++ tar = tarfile.tar_filter(tarinfo, '') ++ self.assertEqual(tar.type, special_type) ++ with self.assertRaises(tarfile.SpecialFileError) as cm: ++ tarfile.data_filter(tarinfo, '') ++ self.assertIsInstance(cm.exception.tarinfo, tarfile.TarInfo) ++ self.assertEqual(cm.exception.tarinfo.name, 'foo') ++ ++ def test_fully_trusted_filter(self): ++ # The 'fully_trusted' filter returns the original TarInfo objects. ++ with tarfile.TarFile.open(tarname) as tar: ++ for tarinfo in tar.getmembers(): ++ filtered = tarfile.fully_trusted_filter(tarinfo, '') ++ self.assertIs(filtered, tarinfo) ++ ++ def test_tar_filter(self): ++ # The 'tar' filter returns TarInfo objects with the same name/type. ++ # (It can also fail for particularly "evil" input, but we don't have ++ # that in the test archive.) ++ with tarfile.TarFile.open(tarname) as tar: ++ for tarinfo in tar.getmembers(): ++ filtered = tarfile.tar_filter(tarinfo, '') ++ self.assertIs(filtered.name, tarinfo.name) ++ self.assertIs(filtered.type, tarinfo.type) ++ ++ def test_data_filter(self): ++ # The 'data' filter either raises, or returns TarInfo with the same ++ # name/type. ++ with tarfile.TarFile.open(tarname) as tar: ++ for tarinfo in tar.getmembers(): ++ try: ++ filtered = tarfile.data_filter(tarinfo, '') ++ except tarfile.FilterError: ++ continue ++ self.assertIs(filtered.name, tarinfo.name) ++ self.assertIs(filtered.type, tarinfo.type) ++ ++ def test_default_filter_warns_not(self): ++ """Ensure the default filter does not warn (like in 3.12)""" ++ with ArchiveMaker() as arc: ++ arc.add('foo') ++ with warnings_helper.check_no_warnings(self): ++ with self.check_context(arc.open(), None): ++ self.expect_file('foo') ++ ++ def test_change_default_filter_on_instance(self): ++ tar = tarfile.TarFile(tarname, 'r') ++ def strict_filter(tarinfo, path): ++ if tarinfo.name == 'ustar/regtype': ++ return tarinfo ++ else: ++ return None ++ tar.extraction_filter = strict_filter ++ with self.check_context(tar, None): ++ self.expect_file('ustar/regtype') ++ ++ def test_change_default_filter_on_class(self): ++ def strict_filter(tarinfo, path): ++ if tarinfo.name == 'ustar/regtype': ++ return tarinfo ++ else: ++ return None ++ tar = tarfile.TarFile(tarname, 'r') ++ with support.swap_attr(tarfile.TarFile, 'extraction_filter', ++ staticmethod(strict_filter)): ++ with self.check_context(tar, None): ++ self.expect_file('ustar/regtype') ++ ++ def test_change_default_filter_on_subclass(self): ++ class TarSubclass(tarfile.TarFile): ++ def extraction_filter(self, tarinfo, path): ++ if tarinfo.name == 'ustar/regtype': ++ return tarinfo ++ else: ++ return None ++ ++ tar = TarSubclass(tarname, 'r') ++ with self.check_context(tar, None): ++ self.expect_file('ustar/regtype') ++ ++ def test_change_default_filter_to_string(self): ++ tar = tarfile.TarFile(tarname, 'r') ++ tar.extraction_filter = 'data' ++ with self.check_context(tar, None): ++ self.expect_exception(TypeError) ++ ++ def test_custom_filter(self): ++ def custom_filter(tarinfo, path): ++ self.assertIs(path, self.destdir) ++ if tarinfo.name == 'move_this': ++ return tarinfo.replace(name='moved') ++ if tarinfo.name == 'ignore_this': ++ return None ++ return tarinfo ++ ++ with ArchiveMaker() as arc: ++ arc.add('move_this') ++ arc.add('ignore_this') ++ arc.add('keep') ++ with self.check_context(arc.open(), custom_filter): ++ self.expect_file('moved') ++ self.expect_file('keep') ++ ++ def test_bad_filter_name(self): ++ with ArchiveMaker() as arc: ++ arc.add('foo') ++ with self.check_context(arc.open(), 'bad filter name'): ++ self.expect_exception(ValueError) ++ ++ def test_stateful_filter(self): ++ # Stateful filters should be possible. ++ # (This doesn't really test tarfile. Rather, it demonstrates ++ # that third parties can implement a stateful filter.) ++ class StatefulFilter: ++ def __enter__(self): ++ self.num_files_processed = 0 ++ return self ++ ++ def __call__(self, tarinfo, path): ++ try: ++ tarinfo = tarfile.data_filter(tarinfo, path) ++ except tarfile.FilterError: ++ return None ++ self.num_files_processed += 1 ++ return tarinfo ++ ++ def __exit__(self, *exc_info): ++ self.done = True ++ ++ with ArchiveMaker() as arc: ++ arc.add('good') ++ arc.add('bad', symlink_to='/') ++ arc.add('good') ++ with StatefulFilter() as custom_filter: ++ with self.check_context(arc.open(), custom_filter): ++ self.expect_file('good') ++ self.assertEqual(custom_filter.num_files_processed, 2) ++ self.assertEqual(custom_filter.done, True) ++ ++ def test_errorlevel(self): ++ def extracterror_filter(tarinfo, path): ++ raise tarfile.ExtractError('failed with ExtractError') ++ def filtererror_filter(tarinfo, path): ++ raise tarfile.FilterError('failed with FilterError') ++ def oserror_filter(tarinfo, path): ++ raise OSError('failed with OSError') ++ def tarerror_filter(tarinfo, path): ++ raise tarfile.TarError('failed with base TarError') ++ def valueerror_filter(tarinfo, path): ++ raise ValueError('failed with ValueError') ++ ++ with ArchiveMaker() as arc: ++ arc.add('file') ++ ++ # If errorlevel is 0, errors affected by errorlevel are ignored ++ ++ with self.check_context(arc.open(errorlevel=0), extracterror_filter): ++ self.expect_file('file') ++ ++ with self.check_context(arc.open(errorlevel=0), filtererror_filter): ++ self.expect_file('file') ++ ++ with self.check_context(arc.open(errorlevel=0), oserror_filter): ++ self.expect_file('file') ++ ++ with self.check_context(arc.open(errorlevel=0), tarerror_filter): ++ self.expect_exception(tarfile.TarError) ++ ++ with self.check_context(arc.open(errorlevel=0), valueerror_filter): ++ self.expect_exception(ValueError) ++ ++ # If 1, all fatal errors are raised ++ ++ with self.check_context(arc.open(errorlevel=1), extracterror_filter): ++ self.expect_file('file') ++ ++ with self.check_context(arc.open(errorlevel=1), filtererror_filter): ++ self.expect_exception(tarfile.FilterError) ++ ++ with self.check_context(arc.open(errorlevel=1), oserror_filter): ++ self.expect_exception(OSError) ++ ++ with self.check_context(arc.open(errorlevel=1), tarerror_filter): ++ self.expect_exception(tarfile.TarError) ++ ++ with self.check_context(arc.open(errorlevel=1), valueerror_filter): ++ self.expect_exception(ValueError) ++ ++ # If 2, all non-fatal errors are raised as well. ++ ++ with self.check_context(arc.open(errorlevel=2), extracterror_filter): ++ self.expect_exception(tarfile.ExtractError) ++ ++ with self.check_context(arc.open(errorlevel=2), filtererror_filter): ++ self.expect_exception(tarfile.FilterError) ++ ++ with self.check_context(arc.open(errorlevel=2), oserror_filter): ++ self.expect_exception(OSError) ++ ++ with self.check_context(arc.open(errorlevel=2), tarerror_filter): ++ self.expect_exception(tarfile.TarError) ++ ++ with self.check_context(arc.open(errorlevel=2), valueerror_filter): ++ self.expect_exception(ValueError) ++ ++ # We only handle ExtractionError, FilterError & OSError specially. ++ ++ with self.check_context(arc.open(errorlevel='boo!'), filtererror_filter): ++ self.expect_exception(TypeError) # errorlevel is not int ++ ++ + def setUpModule(): + os_helper.unlink(TEMPDIR) + os.makedirs(TEMPDIR) +diff --git a/Misc/NEWS.d/next/Library/2023-03-23-15-24-38.gh-issue-102953.YR4KaK.rst b/Misc/NEWS.d/next/Library/2023-03-23-15-24-38.gh-issue-102953.YR4KaK.rst +new file mode 100644 +index 00000000000..48a105a4a17 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2023-03-23-15-24-38.gh-issue-102953.YR4KaK.rst +@@ -0,0 +1,4 @@ ++The extraction methods in :mod:`tarfile`, and :func:`shutil.unpack_archive`, ++have a new a *filter* argument that allows limiting tar features than may be ++surprising or dangerous, such as creating files outside the destination ++directory. See :ref:`tarfile-extraction-filter` for details. diff --git a/Python-3.10.10.tar.xz.asc b/Python-3.10.10.tar.xz.asc deleted file mode 100644 index 0bd900c..0000000 --- a/Python-3.10.10.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmPiQfoACgkQ/+h0BBaL -2EcB8hAAmFEIHZopWn+A4tDxd001eViLrOmjygqPn1doAQ3dAgyESt4Z/HDtN6rB -+6z5rsx+qdcP9kfb/+3V0gKBh/3V4bEpnD+EQtpONWhKbCcqOfq1ok1V+uNH8uOF -ixxWkY+MWJzPPhlQiW/sm9FP6CdnaeriKf1JMCUt9aiganpo2CQv5gPE/0PlSGO5 -BEKjCcyHHPIEAxC6jLm/+33PSzbhGq+YstK/1tcqUrJfkifipovmSZeFyzULPonK -MATPyliOupo3ixPs3LoJUjNpGD4fH+p2Lg1ZOgYv7vGmeLcadNVanRlqRg76m+ke -zvp/MAqQg4Fr75m2+mfDG/Md+PrSMvz71i55a1Q1NcYdW6QR62m08FCZg7/+t5pD -H91ywhMqTv1nySsEZGfuETPTs7gMCtyBeDjIhXBMcfbhGivd7r5zZJ8MUD/FSASC -fQ/vEVeHWQeWpfFgxLfLmRnkjIS7JCGlM9z6zsZqbppWqeA94sBIf4ka2JG2DnGP -1Pvn+ragiHt1++i2yVhmoAB0t44/SgXacCce5AT3yB71brT21cOXQs0Gq80MwVPI -nVbzdOtuGNGcvEi2fbO2IEcgegSHaOHo9PvYTRropSz3V7A95x8mA1xjZf2y77H5 -/mfJ4687YIItCIcNE5Zzj6GspWlWP31OvRFIIefnKYf2JuU+qt8= -=B3xo ------END PGP SIGNATURE----- diff --git a/Python-3.10.10.tar.xz b/Python-3.10.11.tar.xz similarity index 100% rename from Python-3.10.10.tar.xz rename to Python-3.10.11.tar.xz diff --git a/Python-3.10.11.tar.xz.asc b/Python-3.10.11.tar.xz.asc new file mode 100644 index 0000000..d3012ea --- /dev/null +++ b/Python-3.10.11.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmQsoHwACgkQ/+h0BBaL +2Efs9BAAheWCnenhHhXi0m0DgyB6eEVH8xmZrBqA1WMgGQOqWVZmEnJdc0IXyFWQ +1A4C59d6rEvu8jvXTLvsqGEmehofKqq0bXB1tMUBn9CwSiELOm19WvCHc/Htwo2U +DsvAsXXO7vBkKBT9+CQ4BmkGzPUTrBLZRHsQX/M/tpx81jnQVunoMojyPK19sf1I +C+YnxE0cQVL9+INd0WtbVByJIwzBBDCLqTQWL//73CqFs8IO6PsjFXqmlVqVfpmz +aEXuGeRkRgy7kZaDdLcnhBq7a6vgaecfgfRUGyBgwgakfrHA5SOdsWdAonjA676J +6DHmFIf82R4wo7Vu0WAfFAq9jJfVxXN7n5Y/N/cxzqjhrfO341vCflN1c16VAFnu +ok7n50poENO/tMRerOEj5baL+mToi8Wh+cYHY6tNpaM2iP+bSyjoS+Ff225xhdNV +fqGuyaH7cPgGgoXECrSb7iTWYZxJxQV9S8OlR2gX8IlA+XrbGWQl0PvmErhO3FqN +W88gBmYrzrSl6+dzF62yn2gKFc2K5k6NmCcySFfjY87G7RhEf1ixPeDyMSvbKlVw +sJWeoXuCNPL+PQV+V76UAbn3bEvH87fyImxoYHNAIbHh8JaTvO5vIKDrrsw92siQ +6Pud3Oy6DcD5gWX2KcaAjQjruh18dljsbYN+2KVFfQHM8SYeXns= +=enP4 +-----END PGP SIGNATURE----- diff --git a/invalid-json.patch b/invalid-json.patch deleted file mode 100644 index d3a25ba..0000000 --- a/invalid-json.patch +++ /dev/null @@ -1,44 +0,0 @@ ---- - Doc/howto/logging-cookbook.rst | 24 ++++++++++++++---------- - 1 file changed, 14 insertions(+), 10 deletions(-) - ---- a/Doc/howto/logging-cookbook.rst -+++ b/Doc/howto/logging-cookbook.rst -@@ -340,10 +340,12 @@ adding a ``filters`` section parallel to - - .. code-block:: json - -- "filters": { -- "warnings_and_below": { -- "()" : "__main__.filter_maker", -- "level": "WARNING" -+ { -+ "filters": { -+ "warnings_and_below": { -+ "()" : "__main__.filter_maker", -+ "level": "WARNING" -+ } - } - } - -@@ -351,12 +353,14 @@ and changing the section on the ``stdout - - .. code-block:: json - -- "stdout": { -- "class": "logging.StreamHandler", -- "level": "INFO", -- "formatter": "simple", -- "stream": "ext://sys.stdout", -- "filters": ["warnings_and_below"] -+ { -+ "stdout": { -+ "class": "logging.StreamHandler", -+ "level": "INFO", -+ "formatter": "simple", -+ "stream": "ext://sys.stdout", -+ "filters": ["warnings_and_below"] -+ } - } - - A filter is just a function, so we can define the ``filter_maker`` (a factory diff --git a/python310.changes b/python310.changes index 21c207e..1ff6218 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,93 @@ +------------------------------------------------------------------- +Thu Apr 27 21:23:19 UTC 2023 - Matej Cepl + +- Add CVE-2007-4559-filter-tarfile_extractall.patch to fix + CVE-2007-4559 (bsc#1203750) by adding the filter for + tarfile.extractall (PEP 706). + +------------------------------------------------------------------- +Thu Apr 27 21:19:52 UTC 2023 - Matej Cepl + +- Update to 3.10.11: + - Core and Builtins + - gh-102416: Do not memoize incorrectly automatically + generated loop rules in the parser. Patch by Pablo Galindo. + - gh-102356: Fix a bug that caused a crash when deallocating + deeply nested filter objects. Patch by Marta Gómez Macías. + - gh-102397: Fix segfault from race condition in signal + handling during garbage collection. Patch by Kumar Aditya. + - gh-102126: Fix deadlock at shutdown when clearing thread + states if any finalizer tries to acquire the runtime head + lock. Patch by Kumar Aditya. + - gh-102027: Fix SSE2 and SSE3 detection in _blake2 internal + module. Patch by Max Bachmann. + - gh-101967: Fix possible segfault in + positional_only_passed_as_keyword function, when new list + created. + - gh-101765: Fix SystemError / segmentation fault in iter + __reduce__ when internal access of builtins.__dict__ keys + mutates the iter object. + - Library + - gh-102947: Improve traceback when dataclasses.fields() is + called on a non-dataclass. Patch by Alex Waygood + - gh-101979: Fix a bug where parentheses in the metavar + argument to argparse.ArgumentParser.add_argument() were + dropped. Patch by Yeojin Kim. + - gh-102179: Fix os.dup2() error message for negative fds. + - gh-101961: For the binary mode, fileinput.hookcompressed() + doesn’t set the encoding value even if the value is + None. Patch by Gihwan Kim. + - gh-101936: The default value of fp becomes io.BytesIO + if HTTPError is initialized without a designated fp + parameter. Patch by Long Vo. + - gh-101566: In zipfile, apply fix for extractall on the + underlying zipfile after being wrapped in Path. + - gh-101997: Upgrade pip wheel bundled with ensurepip (pip + 23.0.1) + - gh-101892: Callable iterators no longer raise SystemError + when the callable object exhausts the iterator but forgets + to either return a sentinel value or raise StopIteration. + - gh-97786: Fix potential undefined behaviour in corner cases + of floating-point-to-time conversions. + - gh-101517: Fixed bug where bdb looks up the source line + with linecache with a lineno=None, which causes it to fail + with an unhandled exception. + - gh-101673: Fix a pdb bug where ll clears the changes to + local variables. + - gh-96931: Fix incorrect results from + ssl.SSLSocket.shared_ciphers() + - gh-88233: Correctly preserve “extra” fields in zipfile + regardless of their ordering relative to a zip64 “extra.” + - gh-95495: When built against OpenSSL 3.0, the ssl module + had a bug where it reported unauthenticated EOFs (i.e. + without close_notify) as a clean TLS-level EOF. It now + raises SSLEOFError, matching the behavior in previous + versions of OpenSSL. The options attribute on SSLContext + also no longer includes OP_IGNORE_UNEXPECTED_EOF by + default. This option may be set to specify the previous + OpenSSL 3.0 behavior. + - gh-94440: Fix a concurrent.futures.process bug where + ProcessPoolExecutor shutdown could hang after a future has + been quickly submitted and canceled. + - Documentation + - gh-103112: Add docstring to http.client.HTTPResponse.read() + to fix pydoc output. + - gh-85417: Update cmath documentation to clarify behaviour + on branch cuts. + - gh-97725: Fix asyncio.Task.print_stack() description for + file=None. Patch by Oleg Iarygin. + - Tests + - gh-102980: Improve test coverage on pdb. + - gh-102537: Adjust the error handling strategy in + test_zoneinfo.TzPathTest.python_tzpath_context. Patch by + Paul Ganssle. + - gh-101377: Improved test_locale_calendar_formatweekday of + calendar. + - Build + - gh-102711: Fix -Wstrict-prototypes compiler warnings. +- Removed upstreamed: + - invalid-json.patch + ------------------------------------------------------------------- Mon Mar 13 08:39:53 UTC 2023 - Matej Cepl diff --git a/python310.spec b/python310.spec index 6b565a6..c9cc36a 100644 --- a/python310.spec +++ b/python310.spec @@ -103,13 +103,13 @@ Obsoletes: python39%{?1:-%{1}} %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so %bcond_without profileopt Name: %{python_pkg_name}%{psuffix} -Version: 3.10.10 +Version: 3.10.11 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 URL: https://www.python.org/ -Source0: http://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz -Source1: http://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.asc +Source0: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz +Source1: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.asc Source2: baselibs.conf Source3: README.SUSE Source7: macros.python3 @@ -170,9 +170,9 @@ Patch36: support-expat-CVE-2022-25236-patched.patch # blocklist bypass via the urllib.parse component when supplying # a URL that starts with blank characters Patch37: CVE-2023-24329-blank-URL-bypass.patch -# PATCH-FIX-UPSTREAM invalid-json.patch gh#python/cpython#102582 mcepl@suse.com -# We require valid JSON in documentation -Patch38: invalid-json.patch +# PATCH-FIX-UPSTREAM CVE-2007-4559-filter-tarfile_extractall.patch bsc#1203750 mcepl@suse.com +# PEP 706 – Filter for tarfile.extractall +Patch38: CVE-2007-4559-filter-tarfile_extractall.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes From 8bdb667986d820a891ee0edc97fccd4d07068408bbf2a11eec491c962706a65b Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Wed, 19 Jul 2023 14:55:24 +0000 Subject: [PATCH 07/23] Set link to python310.29655 via maintenance_release request --- CVE-2007-4559-filter-tarfile_extractall.patch | 2594 ----------------- CVE-2023-24329-blank-URL-bypass.patch | 55 - Python-3.10.11.tar.xz.asc | 16 - ...on-3.10.11.tar.xz => Python-3.10.12.tar.xz | 0 Python-3.10.12.tar.xz.asc | 16 + bpo-37596-make-set-marshalling.patch | 103 + python310.changes | 37 + python310.spec | 16 +- 8 files changed, 161 insertions(+), 2676 deletions(-) delete mode 100644 CVE-2007-4559-filter-tarfile_extractall.patch delete mode 100644 CVE-2023-24329-blank-URL-bypass.patch delete mode 100644 Python-3.10.11.tar.xz.asc rename Python-3.10.11.tar.xz => Python-3.10.12.tar.xz (100%) create mode 100644 Python-3.10.12.tar.xz.asc create mode 100644 bpo-37596-make-set-marshalling.patch diff --git a/CVE-2007-4559-filter-tarfile_extractall.patch b/CVE-2007-4559-filter-tarfile_extractall.patch deleted file mode 100644 index f0c85bf..0000000 --- a/CVE-2007-4559-filter-tarfile_extractall.patch +++ /dev/null @@ -1,2594 +0,0 @@ -diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst -index 311aae414ae..3864e03898d 100644 ---- a/Doc/library/shutil.rst -+++ b/Doc/library/shutil.rst -@@ -620,7 +620,7 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. - Remove the archive format *name* from the list of supported formats. - - --.. function:: unpack_archive(filename[, extract_dir[, format]]) -+.. function:: unpack_archive(filename[, extract_dir[, format[, filter]]]) - - Unpack an archive. *filename* is the full path of the archive. - -@@ -634,6 +634,15 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. - registered for that extension. In case none is found, - a :exc:`ValueError` is raised. - -+ The keyword-only *filter* argument, which was added in Python 3.11.4, -+ is passed to the underlying unpacking function. -+ For zip files, *filter* is not accepted. -+ For tar files, it is recommended to set it to ``'data'``, -+ unless using features specific to tar and UNIX-like filesystems. -+ (See :ref:`tarfile-extraction-filter` for details.) -+ The ``'data'`` filter will become the default for tar files -+ in Python 3.14. -+ - .. audit-event:: shutil.unpack_archive filename,extract_dir,format shutil.unpack_archive - - .. warning:: -@@ -646,6 +655,9 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. - .. versionchanged:: 3.7 - Accepts a :term:`path-like object` for *filename* and *extract_dir*. - -+ .. versionchanged:: 3.11.4 -+ Added the *filter* argument. -+ - .. function:: register_unpack_format(name, extensions, function[, extra_args[, description]]) - - Registers an unpack format. *name* is the name of the format and -@@ -653,11 +665,14 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. - ``.zip`` for Zip files. - - *function* is the callable that will be used to unpack archives. The -- callable will receive the path of the archive, followed by the directory -- the archive must be extracted to. -- -- When provided, *extra_args* is a sequence of ``(name, value)`` tuples that -- will be passed as keywords arguments to the callable. -+ callable will receive: -+ -+ - the path of the archive, as a positional argument; -+ - the directory the archive must be extracted to, as a positional argument; -+ - possibly a *filter* keyword argument, if it was given to -+ :func:`unpack_archive`; -+ - additional keyword arguments, specified by *extra_args* as a sequence -+ of ``(name, value)`` tuples. - - *description* can be provided to describe the format, and will be returned - by the :func:`get_unpack_formats` function. -diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst -index 226513f5fc1..836444ebb34 100644 ---- a/Doc/library/tarfile.rst -+++ b/Doc/library/tarfile.rst -@@ -206,6 +206,38 @@ The :mod:`tarfile` module defines the following exceptions: - Is raised by :meth:`TarInfo.frombuf` if the buffer it gets is invalid. - - -+.. exception:: FilterError -+ -+ Base class for members :ref:`refused ` by -+ filters. -+ -+ .. attribute:: tarinfo -+ -+ Information about the member that the filter refused to extract, -+ as :ref:`TarInfo `. -+ -+.. exception:: AbsolutePathError -+ -+ Raised to refuse extracting a member with an absolute path. -+ -+.. exception:: OutsideDestinationError -+ -+ Raised to refuse extracting a member outside the destination directory. -+ -+.. exception:: SpecialFileError -+ -+ Raised to refuse extracting a special file (e.g. a device or pipe). -+ -+.. exception:: AbsoluteLinkError -+ -+ Raised to refuse extracting a symbolic link with an absolute path. -+ -+.. exception:: LinkOutsideDestinationError -+ -+ Raised to refuse extracting a symbolic link pointing outside the destination -+ directory. -+ -+ - The following constants are available at the module level: - - .. data:: ENCODING -@@ -316,11 +348,8 @@ be finalized; only the internally used file object will be closed. See the - *debug* can be set from ``0`` (no debug messages) up to ``3`` (all debug - messages). The messages are written to ``sys.stderr``. - -- If *errorlevel* is ``0``, all errors are ignored when using :meth:`TarFile.extract`. -- Nevertheless, they appear as error messages in the debug output, when debugging -- is enabled. If ``1``, all *fatal* errors are raised as :exc:`OSError` -- exceptions. If ``2``, all *non-fatal* errors are raised as :exc:`TarError` -- exceptions as well. -+ *errorlevel* controls how extraction errors are handled, -+ see :attr:`the corresponding attribute <~TarFile.errorlevel>`. - - The *encoding* and *errors* arguments define the character encoding to be - used for reading or writing the archive and how conversion errors are going -@@ -387,7 +416,7 @@ be finalized; only the internally used file object will be closed. See the - available. - - --.. method:: TarFile.extractall(path=".", members=None, *, numeric_owner=False) -+.. method:: TarFile.extractall(path=".", members=None, *, numeric_owner=False, filter=None) - - Extract all members from the archive to the current working directory or - directory *path*. If optional *members* is given, it must be a subset of the -@@ -401,6 +430,12 @@ be finalized; only the internally used file object will be closed. See the - are used to set the owner/group for the extracted files. Otherwise, the named - values from the tarfile are used. - -+ The *filter* argument, which was added in Python 3.11.4, specifies how -+ ``members`` are modified or rejected before extraction. -+ See :ref:`tarfile-extraction-filter` for details. -+ It is recommended to set this explicitly depending on which *tar* features -+ you need to support. -+ - .. warning:: - - Never extract archives from untrusted sources without prior inspection. -@@ -408,14 +443,20 @@ be finalized; only the internally used file object will be closed. See the - that have absolute filenames starting with ``"/"`` or filenames with two - dots ``".."``. - -+ Set ``filter='data'`` to prevent the most dangerous security issues, -+ and read the :ref:`tarfile-extraction-filter` section for details. -+ - .. versionchanged:: 3.5 - Added the *numeric_owner* parameter. - - .. versionchanged:: 3.6 - The *path* parameter accepts a :term:`path-like object`. - -+ .. versionchanged:: 3.11.4 -+ Added the *filter* parameter. -+ - --.. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False) -+.. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False, filter=None) - - Extract a member from the archive to the current working directory, using its - full name. Its file information is extracted as accurately as possible. *member* -@@ -423,9 +464,8 @@ be finalized; only the internally used file object will be closed. See the - directory using *path*. *path* may be a :term:`path-like object`. - File attributes (owner, mtime, mode) are set unless *set_attrs* is false. - -- If *numeric_owner* is :const:`True`, the uid and gid numbers from the tarfile -- are used to set the owner/group for the extracted files. Otherwise, the named -- values from the tarfile are used. -+ The *numeric_owner* and *filter* arguments are the same as -+ for :meth:`extractall`. - - .. note:: - -@@ -436,6 +476,9 @@ be finalized; only the internally used file object will be closed. See the - - See the warning for :meth:`extractall`. - -+ Set ``filter='data'`` to prevent the most dangerous security issues, -+ and read the :ref:`tarfile-extraction-filter` section for details. -+ - .. versionchanged:: 3.2 - Added the *set_attrs* parameter. - -@@ -445,6 +488,9 @@ be finalized; only the internally used file object will be closed. See the - .. versionchanged:: 3.6 - The *path* parameter accepts a :term:`path-like object`. - -+ .. versionchanged:: 3.11.4 -+ Added the *filter* parameter. -+ - - .. method:: TarFile.extractfile(member) - -@@ -457,6 +503,57 @@ be finalized; only the internally used file object will be closed. See the - .. versionchanged:: 3.3 - Return an :class:`io.BufferedReader` object. - -+.. attribute:: TarFile.errorlevel -+ :type: int -+ -+ If *errorlevel* is ``0``, errors are ignored when using :meth:`TarFile.extract` -+ and :meth:`TarFile.extractall`. -+ Nevertheless, they appear as error messages in the debug output when -+ *debug* is greater than 0. -+ If ``1`` (the default), all *fatal* errors are raised as :exc:`OSError` or -+ :exc:`FilterError` exceptions. If ``2``, all *non-fatal* errors are raised -+ as :exc:`TarError` exceptions as well. -+ -+ Some exceptions, e.g. ones caused by wrong argument types or data -+ corruption, are always raised. -+ -+ Custom :ref:`extraction filters ` -+ should raise :exc:`FilterError` for *fatal* errors -+ and :exc:`ExtractError` for *non-fatal* ones. -+ -+ Note that when an exception is raised, the archive may be partially -+ extracted. It is the user’s responsibility to clean up. -+ -+.. attribute:: TarFile.extraction_filter -+ -+ .. versionadded:: 3.11.4 -+ -+ The :ref:`extraction filter ` used -+ as a default for the *filter* argument of :meth:`~TarFile.extract` -+ and :meth:`~TarFile.extractall`. -+ -+ The attribute may be ``None`` or a callable. -+ String names are not allowed for this attribute, unlike the *filter* -+ argument to :meth:`~TarFile.extract`. -+ -+ If ``extraction_filter`` is ``None`` (the default), -+ calling an extraction method without a *filter* argument will -+ use the :func:`fully_trusted ` filter for -+ compatibility with previous Python versions. -+ -+ In Python 3.12+, leaving ``extraction_filter=None`` will emit a -+ ``DeprecationWarning``. -+ -+ In Python 3.14+, leaving ``extraction_filter=None`` will cause -+ extraction methods to use the :func:`data ` filter by default. -+ -+ The attribute may be set on instances or overridden in subclasses. -+ It also is possible to set it on the ``TarFile`` class itself to set a -+ global default, although, since it affects all uses of *tarfile*, -+ it is best practice to only do so in top-level applications or -+ :mod:`site configuration `. -+ To set a global default this way, a filter function needs to be wrapped in -+ :func:`staticmethod()` to prevent injection of a ``self`` argument. - - .. method:: TarFile.add(name, arcname=None, recursive=True, *, filter=None) - -@@ -532,7 +629,27 @@ permissions, owner etc.), it provides some useful methods to determine its type. - It does *not* contain the file's data itself. - - :class:`TarInfo` objects are returned by :class:`TarFile`'s methods --:meth:`getmember`, :meth:`getmembers` and :meth:`gettarinfo`. -+:meth:`~TarFile.getmember`, :meth:`~TarFile.getmembers` and -+:meth:`~TarFile.gettarinfo`. -+ -+Modifying the objects returned by :meth:`~!TarFile.getmember` or -+:meth:`~!TarFile.getmembers` will affect all subsequent -+operations on the archive. -+For cases where this is unwanted, you can use :mod:`copy.copy() ` or -+call the :meth:`~TarInfo.replace` method to create a modified copy in one step. -+ -+Several attributes can be set to ``None`` to indicate that a piece of metadata -+is unused or unknown. -+Different :class:`TarInfo` methods handle ``None`` differently: -+ -+- The :meth:`~TarFile.extract` or :meth:`~TarFile.extractall` methods will -+ ignore the corresponding metadata, leaving it set to a default. -+- :meth:`~TarFile.addfile` will fail. -+- :meth:`~TarFile.list` will print a placeholder string. -+ -+ -+.. versionchanged:: 3.11.4 -+ Added :meth:`~TarInfo.replace` and handling of ``None``. - - - .. class:: TarInfo(name="") -@@ -566,24 +683,39 @@ A ``TarInfo`` object has the following public data attributes: - - - .. attribute:: TarInfo.name -+ :type: str - - Name of the archive member. - - - .. attribute:: TarInfo.size -+ :type: int - - Size in bytes. - - - .. attribute:: TarInfo.mtime -+ :type: int | float -+ -+ Time of last modification in seconds since the :ref:`epoch `, -+ as in :attr:`os.stat_result.st_mtime`. - -- Time of last modification. -+ .. versionchanged:: 3.11.4 - -+ Can be set to ``None`` for :meth:`~TarFile.extract` and -+ :meth:`~TarFile.extractall`, causing extraction to skip applying this -+ attribute. - - .. attribute:: TarInfo.mode -+ :type: int - -- Permission bits. -+ Permission bits, as for :func:`os.chmod`. - -+ .. versionchanged:: 3.11.4 -+ -+ Can be set to ``None`` for :meth:`~TarFile.extract` and -+ :meth:`~TarFile.extractall`, causing extraction to skip applying this -+ attribute. - - .. attribute:: TarInfo.type - -@@ -595,35 +727,76 @@ A ``TarInfo`` object has the following public data attributes: - - - .. attribute:: TarInfo.linkname -+ :type: str - - Name of the target file name, which is only present in :class:`TarInfo` objects - of type :const:`LNKTYPE` and :const:`SYMTYPE`. - - - .. attribute:: TarInfo.uid -+ :type: int - - User ID of the user who originally stored this member. - -+ .. versionchanged:: 3.11.4 -+ -+ Can be set to ``None`` for :meth:`~TarFile.extract` and -+ :meth:`~TarFile.extractall`, causing extraction to skip applying this -+ attribute. - - .. attribute:: TarInfo.gid -+ :type: int - - Group ID of the user who originally stored this member. - -+ .. versionchanged:: 3.11.4 -+ -+ Can be set to ``None`` for :meth:`~TarFile.extract` and -+ :meth:`~TarFile.extractall`, causing extraction to skip applying this -+ attribute. - - .. attribute:: TarInfo.uname -+ :type: str - - User name. - -+ .. versionchanged:: 3.11.4 -+ -+ Can be set to ``None`` for :meth:`~TarFile.extract` and -+ :meth:`~TarFile.extractall`, causing extraction to skip applying this -+ attribute. - - .. attribute:: TarInfo.gname -+ :type: str - - Group name. - -+ .. versionchanged:: 3.11.4 -+ -+ Can be set to ``None`` for :meth:`~TarFile.extract` and -+ :meth:`~TarFile.extractall`, causing extraction to skip applying this -+ attribute. - - .. attribute:: TarInfo.pax_headers -+ :type: dict - - A dictionary containing key-value pairs of an associated pax extended header. - -+.. method:: TarInfo.replace(name=..., mtime=..., mode=..., linkname=..., -+ uid=..., gid=..., uname=..., gname=..., -+ deep=True) -+ -+ .. versionadded:: 3.11.4 -+ -+ Return a *new* copy of the :class:`!TarInfo` object with the given attributes -+ changed. For example, to return a ``TarInfo`` with the group name set to -+ ``'staff'``, use:: -+ -+ new_tarinfo = old_tarinfo.replace(gname='staff') -+ -+ By default, a deep copy is made. -+ If *deep* is false, the copy is shallow, i.e. ``pax_headers`` -+ and any custom attributes are shared with the original ``TarInfo`` object. - - A :class:`TarInfo` object also provides some convenient query methods: - -@@ -673,9 +846,259 @@ A :class:`TarInfo` object also provides some convenient query methods: - Return :const:`True` if it is one of character device, block device or FIFO. - - -+.. _tarfile-extraction-filter: -+ -+Extraction filters -+------------------ -+ -+.. versionadded:: 3.11.4 -+ -+The *tar* format is designed to capture all details of a UNIX-like filesystem, -+which makes it very powerful. -+Unfortunately, the features make it easy to create tar files that have -+unintended -- and possibly malicious -- effects when extracted. -+For example, extracting a tar file can overwrite arbitrary files in various -+ways (e.g. by using absolute paths, ``..`` path components, or symlinks that -+affect later members). -+ -+In most cases, the full functionality is not needed. -+Therefore, *tarfile* supports extraction filters: a mechanism to limit -+functionality, and thus mitigate some of the security issues. -+ -+.. seealso:: -+ -+ :pep:`706` -+ Contains further motivation and rationale behind the design. -+ -+The *filter* argument to :meth:`TarFile.extract` or :meth:`~TarFile.extractall` -+can be: -+ -+* the string ``'fully_trusted'``: Honor all metadata as specified in the -+ archive. -+ Should be used if the user trusts the archive completely, or implements -+ their own complex verification. -+ -+* the string ``'tar'``: Honor most *tar*-specific features (i.e. features of -+ UNIX-like filesystems), but block features that are very likely to be -+ surprising or malicious. See :func:`tar_filter` for details. -+ -+* the string ``'data'``: Ignore or block most features specific to UNIX-like -+ filesystems. Intended for extracting cross-platform data archives. -+ See :func:`data_filter` for details. -+ -+* ``None`` (default): Use :attr:`TarFile.extraction_filter`. -+ -+ If that is also ``None`` (the default), the ``'fully_trusted'`` -+ filter will be used (for compatibility with earlier versions of Python). -+ -+ In Python 3.12, the default will emit a ``DeprecationWarning``. -+ -+ In Python 3.14, the ``'data'`` filter will become the default instead. -+ It's possible to switch earlier; see :attr:`TarFile.extraction_filter`. -+ -+* A callable which will be called for each extracted member with a -+ :ref:`TarInfo ` describing the member and the destination -+ path to where the archive is extracted (i.e. the same path is used for all -+ members):: -+ -+ filter(/, member: TarInfo, path: str) -> TarInfo | None -+ -+ The callable is called just before each member is extracted, so it can -+ take the current state of the disk into account. -+ It can: -+ -+ - return a :class:`TarInfo` object which will be used instead of the metadata -+ in the archive, or -+ - return ``None``, in which case the member will be skipped, or -+ - raise an exception to abort the operation or skip the member, -+ depending on :attr:`~TarFile.errorlevel`. -+ Note that when extraction is aborted, :meth:`~TarFile.extractall` may leave -+ the archive partially extracted. It does not attempt to clean up. -+ -+Default named filters -+~~~~~~~~~~~~~~~~~~~~~ -+ -+The pre-defined, named filters are available as functions, so they can be -+reused in custom filters: -+ -+.. function:: fully_trusted_filter(/, member, path) -+ -+ Return *member* unchanged. -+ -+ This implements the ``'fully_trusted'`` filter. -+ -+.. function:: tar_filter(/, member, path) -+ -+ Implements the ``'tar'`` filter. -+ -+ - Strip leading slashes (``/`` and :attr:`os.sep`) from filenames. -+ - :ref:`Refuse ` to extract files with absolute -+ paths (in case the name is absolute -+ even after stripping slashes, e.g. ``C:/foo`` on Windows). -+ This raises :class:`~tarfile.AbsolutePathError`. -+ - :ref:`Refuse ` to extract files whose absolute -+ path (after following symlinks) would end up outside the destination. -+ This raises :class:`~tarfile.OutsideDestinationError`. -+ - Clear high mode bits (setuid, setgid, sticky) and group/other write bits -+ (:attr:`~stat.S_IWGRP`|:attr:`~stat.S_IWOTH`). -+ -+ Return the modified ``TarInfo`` member. -+ -+.. function:: data_filter(/, member, path) -+ -+ Implements the ``'data'`` filter. -+ In addition to what ``tar_filter`` does: -+ -+ - :ref:`Refuse ` to extract links (hard or soft) -+ that link to absolute paths, or ones that link outside the destination. -+ -+ This raises :class:`~tarfile.AbsoluteLinkError` or -+ :class:`~tarfile.LinkOutsideDestinationError`. -+ -+ Note that such files are refused even on platforms that do not support -+ symbolic links. -+ -+ - :ref:`Refuse ` to extract device files -+ (including pipes). -+ This raises :class:`~tarfile.SpecialFileError`. -+ -+ - For regular files, including hard links: -+ -+ - Set the owner read and write permissions -+ (:attr:`~stat.S_IRUSR`|:attr:`~stat.S_IWUSR`). -+ - Remove the group & other executable permission -+ (:attr:`~stat.S_IXGRP`|:attr:`~stat.S_IXOTH`) -+ if the owner doesn’t have it (:attr:`~stat.S_IXUSR`). -+ -+ - For other files (directories), set ``mode`` to ``None``, so -+ that extraction methods skip applying permission bits. -+ - Set user and group info (``uid``, ``gid``, ``uname``, ``gname``) -+ to ``None``, so that extraction methods skip setting it. -+ -+ Return the modified ``TarInfo`` member. -+ -+ -+.. _tarfile-extraction-refuse: -+ -+Filter errors -+~~~~~~~~~~~~~ -+ -+When a filter refuses to extract a file, it will raise an appropriate exception, -+a subclass of :class:`~tarfile.FilterError`. -+This will abort the extraction if :attr:`TarFile.errorlevel` is 1 or more. -+With ``errorlevel=0`` the error will be logged and the member will be skipped, -+but extraction will continue. -+ -+ -+Hints for further verification -+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ -+Even with ``filter='data'``, *tarfile* is not suited for extracting untrusted -+files without prior inspection. -+Among other issues, the pre-defined filters do not prevent denial-of-service -+attacks. Users should do additional checks. -+ -+Here is an incomplete list of things to consider: -+ -+* Extract to a :func:`new temporary directory ` -+ to prevent e.g. exploiting pre-existing links, and to make it easier to -+ clean up after a failed extraction. -+* When working with untrusted data, use external (e.g. OS-level) limits on -+ disk, memory and CPU usage. -+* Check filenames against an allow-list of characters -+ (to filter out control characters, confusables, foreign path separators, -+ etc.). -+* Check that filenames have expected extensions (discouraging files that -+ execute when you “click on them”, or extension-less files like Windows special device names). -+* Limit the number of extracted files, total size of extracted data, -+ filename length (including symlink length), and size of individual files. -+* Check for files that would be shadowed on case-insensitive filesystems. -+ -+Also note that: -+ -+* Tar files may contain multiple versions of the same file. -+ Later ones are expected to overwrite any earlier ones. -+ This feature is crucial to allow updating tape archives, but can be abused -+ maliciously. -+* *tarfile* does not protect against issues with “live” data, -+ e.g. an attacker tinkering with the destination (or source) directory while -+ extraction (or archiving) is in progress. -+ -+ -+Supporting older Python versions -+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ -+Extraction filters were added to Python 3.12, and are backported to older -+versions as security updates. -+To check whether the feature is available, use e.g. -+``hasattr(tarfile, 'data_filter')`` rather than checking the Python version. -+ -+The following examples show how to support Python versions with and without -+the feature. -+Note that setting ``extraction_filter`` will affect any subsequent operations. -+ -+* Fully trusted archive:: -+ -+ my_tarfile.extraction_filter = (lambda member, path: member) -+ my_tarfile.extractall() -+ -+* Use the ``'data'`` filter if available, but revert to Python 3.11 behavior -+ (``'fully_trusted'``) if this feature is not available:: -+ -+ my_tarfile.extraction_filter = getattr(tarfile, 'data_filter', -+ (lambda member, path: member)) -+ my_tarfile.extractall() -+ -+* Use the ``'data'`` filter; *fail* if it is not available:: -+ -+ my_tarfile.extractall(filter=tarfile.data_filter) -+ -+ or:: -+ -+ my_tarfile.extraction_filter = tarfile.data_filter -+ my_tarfile.extractall() -+ -+* Use the ``'data'`` filter; *warn* if it is not available:: -+ -+ if hasattr(tarfile, 'data_filter'): -+ my_tarfile.extractall(filter='data') -+ else: -+ # remove this when no longer needed -+ warn_the_user('Extracting may be unsafe; consider updating Python') -+ my_tarfile.extractall() -+ -+ -+Stateful extraction filter example -+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ -+While *tarfile*'s extraction methods take a simple *filter* callable, -+custom filters may be more complex objects with an internal state. -+It may be useful to write these as context managers, to be used like this:: -+ -+ with StatefulFilter() as filter_func: -+ tar.extractall(path, filter=filter_func) -+ -+Such a filter can be written as, for example:: -+ -+ class StatefulFilter: -+ def __init__(self): -+ self.file_count = 0 -+ -+ def __enter__(self): -+ return self -+ -+ def __call__(self, member, path): -+ self.file_count += 1 -+ return member -+ -+ def __exit__(self, *exc_info): -+ print(f'{self.file_count} files extracted') -+ -+ - .. _tarfile-commandline: - .. program:: tarfile - -+ - Command-Line Interface - ---------------------- - -@@ -745,6 +1168,15 @@ Command-line options - - Verbose output. - -+.. cmdoption:: --filter -+ -+ Specifies the *filter* for ``--extract``. -+ See :ref:`tarfile-extraction-filter` for details. -+ Only string names are accepted (that is, ``fully_trusted``, ``tar``, -+ and ``data``). -+ -+ .. versionadded:: 3.11.4 -+ - .. _tar-examples: - - Examples -diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst -index 47e38ae76ba..43da72aece9 100644 ---- a/Doc/whatsnew/3.10.rst -+++ b/Doc/whatsnew/3.10.rst -@@ -2332,3 +2332,19 @@ The deprecated :mod:`mailcap` module now refuses to inject unsafe text - text, it will warn and act as if a match was not found (or for test commands, - as if the test failed). - (Contributed by Petr Viktorin in :gh:`98966`.) -+ -+Notable Changes in 3.10.12 -+========================== -+ -+tarfile -+------- -+ -+* The extraction methods in :mod:`tarfile`, and :func:`shutil.unpack_archive`, -+ have a new a *filter* argument that allows limiting tar features than may be -+ surprising or dangerous, such as creating files outside the destination -+ directory. -+ See :ref:`tarfile-extraction-filter` for details. -+ In Python 3.12, use without the *filter* argument will show a -+ :exc:`DeprecationWarning`. -+ In Python 3.14, the default will switch to ``'data'``. -+ (Contributed by Petr Viktorin in :pep:`706`.) -diff --git a/Lib/shutil.py b/Lib/shutil.py -index b7bffa3ea41..482ce95a7b2 100644 ---- a/Lib/shutil.py -+++ b/Lib/shutil.py -@@ -1222,7 +1222,7 @@ def _unpack_zipfile(filename, extract_dir): - finally: - zip.close() - --def _unpack_tarfile(filename, extract_dir): -+def _unpack_tarfile(filename, extract_dir, *, filter=None): - """Unpack tar/tar.gz/tar.bz2/tar.xz `filename` to `extract_dir` - """ - import tarfile # late import for breaking circular dependency -@@ -1232,7 +1232,7 @@ def _unpack_tarfile(filename, extract_dir): - raise ReadError( - "%s is not a compressed or uncompressed tar file" % filename) - try: -- tarobj.extractall(extract_dir) -+ tarobj.extractall(extract_dir, filter=filter) - finally: - tarobj.close() - -@@ -1265,7 +1265,7 @@ def _find_unpack_format(filename): - return name - return None - --def unpack_archive(filename, extract_dir=None, format=None): -+def unpack_archive(filename, extract_dir=None, format=None, *, filter=None): - """Unpack an archive. - - `filename` is the name of the archive. -@@ -1279,6 +1279,9 @@ def unpack_archive(filename, extract_dir=None, format=None): - was registered for that extension. - - In case none is found, a ValueError is raised. -+ -+ If `filter` is given, it is passed to the underlying -+ extraction function. - """ - sys.audit("shutil.unpack_archive", filename, extract_dir, format) - -@@ -1288,6 +1291,10 @@ def unpack_archive(filename, extract_dir=None, format=None): - extract_dir = os.fspath(extract_dir) - filename = os.fspath(filename) - -+ if filter is None: -+ filter_kwargs = {} -+ else: -+ filter_kwargs = {'filter': filter} - if format is not None: - try: - format_info = _UNPACK_FORMATS[format] -@@ -1295,7 +1302,7 @@ def unpack_archive(filename, extract_dir=None, format=None): - raise ValueError("Unknown unpack format '{0}'".format(format)) from None - - func = format_info[1] -- func(filename, extract_dir, **dict(format_info[2])) -+ func(filename, extract_dir, **dict(format_info[2]), **filter_kwargs) - else: - # we need to look at the registered unpackers supported extensions - format = _find_unpack_format(filename) -@@ -1303,7 +1310,7 @@ def unpack_archive(filename, extract_dir=None, format=None): - raise ReadError("Unknown archive format '{0}'".format(filename)) - - func = _UNPACK_FORMATS[format][1] -- kwargs = dict(_UNPACK_FORMATS[format][2]) -+ kwargs = dict(_UNPACK_FORMATS[format][2]) | filter_kwargs - func(filename, extract_dir, **kwargs) - - -diff --git a/Lib/tarfile.py b/Lib/tarfile.py -index dea150e8dbb..40599f27bce 100755 ---- a/Lib/tarfile.py -+++ b/Lib/tarfile.py -@@ -46,6 +46,7 @@ - import struct - import copy - import re -+import warnings - - try: - import pwd -@@ -71,6 +72,7 @@ - "ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT", - "DEFAULT_FORMAT", "open"] - -+ - #--------------------------------------------------------- - # tar constants - #--------------------------------------------------------- -@@ -158,6 +160,8 @@ - def stn(s, length, encoding, errors): - """Convert a string to a null-terminated bytes object. - """ -+ if s is None: -+ raise ValueError("metadata cannot contain None") - s = s.encode(encoding, errors) - return s[:length] + (length - len(s)) * NUL - -@@ -709,9 +713,127 @@ def __init__(self, tarfile, tarinfo): - super().__init__(fileobj) - #class ExFileObject - -+ -+#----------------------------- -+# extraction filters (PEP 706) -+#----------------------------- -+ -+class FilterError(TarError): -+ pass -+ -+class AbsolutePathError(FilterError): -+ def __init__(self, tarinfo): -+ self.tarinfo = tarinfo -+ super().__init__(f'member {tarinfo.name!r} has an absolute path') -+ -+class OutsideDestinationError(FilterError): -+ def __init__(self, tarinfo, path): -+ self.tarinfo = tarinfo -+ self._path = path -+ super().__init__(f'{tarinfo.name!r} would be extracted to {path!r}, ' -+ + 'which is outside the destination') -+ -+class SpecialFileError(FilterError): -+ def __init__(self, tarinfo): -+ self.tarinfo = tarinfo -+ super().__init__(f'{tarinfo.name!r} is a special file') -+ -+class AbsoluteLinkError(FilterError): -+ def __init__(self, tarinfo): -+ self.tarinfo = tarinfo -+ super().__init__(f'{tarinfo.name!r} is a symlink to an absolute path') -+ -+class LinkOutsideDestinationError(FilterError): -+ def __init__(self, tarinfo, path): -+ self.tarinfo = tarinfo -+ self._path = path -+ super().__init__(f'{tarinfo.name!r} would link to {path!r}, ' -+ + 'which is outside the destination') -+ -+def _get_filtered_attrs(member, dest_path, for_data=True): -+ new_attrs = {} -+ name = member.name -+ dest_path = os.path.realpath(dest_path) -+ # Strip leading / (tar's directory separator) from filenames. -+ # Include os.sep (target OS directory separator) as well. -+ if name.startswith(('/', os.sep)): -+ name = new_attrs['name'] = member.path.lstrip('/' + os.sep) -+ if os.path.isabs(name): -+ # Path is absolute even after stripping. -+ # For example, 'C:/foo' on Windows. -+ raise AbsolutePathError(member) -+ # Ensure we stay in the destination -+ target_path = os.path.realpath(os.path.join(dest_path, name)) -+ if os.path.commonpath([target_path, dest_path]) != dest_path: -+ raise OutsideDestinationError(member, target_path) -+ # Limit permissions (no high bits, and go-w) -+ mode = member.mode -+ if mode is not None: -+ # Strip high bits & group/other write bits -+ mode = mode & 0o755 -+ if for_data: -+ # For data, handle permissions & file types -+ if member.isreg() or member.islnk(): -+ if not mode & 0o100: -+ # Clear executable bits if not executable by user -+ mode &= ~0o111 -+ # Ensure owner can read & write -+ mode |= 0o600 -+ elif member.isdir() or member.issym(): -+ # Ignore mode for directories & symlinks -+ mode = None -+ else: -+ # Reject special files -+ raise SpecialFileError(member) -+ if mode != member.mode: -+ new_attrs['mode'] = mode -+ if for_data: -+ # Ignore ownership for 'data' -+ if member.uid is not None: -+ new_attrs['uid'] = None -+ if member.gid is not None: -+ new_attrs['gid'] = None -+ if member.uname is not None: -+ new_attrs['uname'] = None -+ if member.gname is not None: -+ new_attrs['gname'] = None -+ # Check link destination for 'data' -+ 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 os.path.commonpath([target_path, dest_path]) != dest_path: -+ raise LinkOutsideDestinationError(member, target_path) -+ return new_attrs -+ -+def fully_trusted_filter(member, dest_path): -+ return member -+ -+def tar_filter(member, dest_path): -+ new_attrs = _get_filtered_attrs(member, dest_path, False) -+ if new_attrs: -+ return member.replace(**new_attrs, deep=False) -+ return member -+ -+def data_filter(member, dest_path): -+ new_attrs = _get_filtered_attrs(member, dest_path, True) -+ if new_attrs: -+ return member.replace(**new_attrs, deep=False) -+ return member -+ -+_NAMED_FILTERS = { -+ "fully_trusted": fully_trusted_filter, -+ "tar": tar_filter, -+ "data": data_filter, -+} -+ - #------------------ - # Exported Classes - #------------------ -+ -+# Sentinel for replace() defaults, meaning "don't change the attribute" -+_KEEP = object() -+ - class TarInfo(object): - """Informational class which holds the details about an - archive member given by a tar header block. -@@ -792,12 +914,44 @@ def linkpath(self, linkname): - def __repr__(self): - return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) - -+ def replace(self, *, -+ name=_KEEP, mtime=_KEEP, mode=_KEEP, linkname=_KEEP, -+ uid=_KEEP, gid=_KEEP, uname=_KEEP, gname=_KEEP, -+ deep=True, _KEEP=_KEEP): -+ """Return a deep copy of self with the given attributes replaced. -+ """ -+ if deep: -+ result = copy.deepcopy(self) -+ else: -+ result = copy.copy(self) -+ if name is not _KEEP: -+ result.name = name -+ if mtime is not _KEEP: -+ result.mtime = mtime -+ if mode is not _KEEP: -+ result.mode = mode -+ if linkname is not _KEEP: -+ result.linkname = linkname -+ if uid is not _KEEP: -+ result.uid = uid -+ if gid is not _KEEP: -+ result.gid = gid -+ if uname is not _KEEP: -+ result.uname = uname -+ if gname is not _KEEP: -+ result.gname = gname -+ return result -+ - def get_info(self): - """Return the TarInfo's attributes as a dictionary. - """ -+ if self.mode is None: -+ mode = None -+ else: -+ mode = self.mode & 0o7777 - info = { - "name": self.name, -- "mode": self.mode & 0o7777, -+ "mode": mode, - "uid": self.uid, - "gid": self.gid, - "size": self.size, -@@ -820,6 +974,9 @@ def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescap - """Return a tar header as a string of 512 byte blocks. - """ - info = self.get_info() -+ for name, value in info.items(): -+ if value is None: -+ raise ValueError("%s may not be None" % name) - - if format == USTAR_FORMAT: - return self.create_ustar_header(info, encoding, errors) -@@ -950,6 +1107,12 @@ def _create_header(info, format, encoding, errors): - devmajor = stn("", 8, encoding, errors) - devminor = stn("", 8, encoding, errors) - -+ # None values in metadata should cause ValueError. -+ # itn()/stn() do this for all fields except type. -+ filetype = info.get("type", REGTYPE) -+ if filetype is None: -+ raise ValueError("TarInfo.type must not be None") -+ - parts = [ - stn(info.get("name", ""), 100, encoding, errors), - itn(info.get("mode", 0) & 0o7777, 8, format), -@@ -958,7 +1121,7 @@ def _create_header(info, format, encoding, errors): - itn(info.get("size", 0), 12, format), - itn(info.get("mtime", 0), 12, format), - b" ", # checksum field -- info.get("type", REGTYPE), -+ filetype, - stn(info.get("linkname", ""), 100, encoding, errors), - info.get("magic", POSIX_MAGIC), - stn(info.get("uname", ""), 32, encoding, errors), -@@ -1468,6 +1631,8 @@ class TarFile(object): - - fileobject = ExFileObject # The file-object for extractfile(). - -+ extraction_filter = None # The default filter for extraction. -+ - def __init__(self, name=None, mode="r", fileobj=None, format=None, - tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, - errors="surrogateescape", pax_headers=None, debug=None, -@@ -1940,7 +2105,10 @@ def list(self, verbose=True, *, members=None): - members = self - for tarinfo in members: - if verbose: -- _safe_print(stat.filemode(tarinfo.mode)) -+ if tarinfo.mode is None: -+ _safe_print("??????????") -+ else: -+ _safe_print(stat.filemode(tarinfo.mode)) - _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid, - tarinfo.gname or tarinfo.gid)) - if tarinfo.ischr() or tarinfo.isblk(): -@@ -1948,8 +2116,11 @@ def list(self, verbose=True, *, members=None): - ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor))) - else: - _safe_print("%10d" % tarinfo.size) -- _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ -- % time.localtime(tarinfo.mtime)[:6]) -+ if tarinfo.mtime is None: -+ _safe_print("????-??-?? ??:??:??") -+ else: -+ _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ -+ % time.localtime(tarinfo.mtime)[:6]) - - _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else "")) - -@@ -2036,32 +2207,58 @@ def addfile(self, tarinfo, fileobj=None): - - self.members.append(tarinfo) - -- def extractall(self, path=".", members=None, *, numeric_owner=False): -+ def _get_filter_function(self, filter): -+ if filter is None: -+ filter = self.extraction_filter -+ if filter is None: -+ return fully_trusted_filter -+ if isinstance(filter, str): -+ raise TypeError( -+ 'String names are not supported for ' -+ + 'TarFile.extraction_filter. Use a function such as ' -+ + 'tarfile.data_filter directly.') -+ return filter -+ if callable(filter): -+ return filter -+ try: -+ return _NAMED_FILTERS[filter] -+ except KeyError: -+ raise ValueError(f"filter {filter!r} not found") from None -+ -+ def extractall(self, path=".", members=None, *, numeric_owner=False, -+ filter=None): - """Extract all members from the archive to the current working - directory and set owner, modification time and permissions on - directories afterwards. `path' specifies a different directory - to extract to. `members' is optional and must be a subset of the - list returned by getmembers(). If `numeric_owner` is True, only - the numbers for user/group names are used and not the names. -+ -+ The `filter` function will be called on each member just -+ before extraction. -+ It can return a changed TarInfo or None to skip the member. -+ String names of common filters are accepted. - """ - directories = [] - -+ filter_function = self._get_filter_function(filter) - if members is None: - members = self - -- for tarinfo in members: -+ for member in members: -+ tarinfo = self._get_extract_tarinfo(member, filter_function, path) -+ if tarinfo is None: -+ continue - if tarinfo.isdir(): -- # Extract directories with a safe mode. -+ # For directories, delay setting attributes until later, -+ # since permissions can interfere with extraction and -+ # extracting contents can reset mtime. - directories.append(tarinfo) -- tarinfo = copy.copy(tarinfo) -- tarinfo.mode = 0o700 -- # Do not set_attrs directories, as we will do that further down -- self.extract(tarinfo, path, set_attrs=not tarinfo.isdir(), -- numeric_owner=numeric_owner) -+ self._extract_one(tarinfo, path, set_attrs=not tarinfo.isdir(), -+ numeric_owner=numeric_owner) - - # Reverse sort directories. -- directories.sort(key=lambda a: a.name) -- directories.reverse() -+ directories.sort(key=lambda a: a.name, reverse=True) - - # Set correct owner, mtime and filemode on directories. - for tarinfo in directories: -@@ -2071,12 +2268,10 @@ def extractall(self, path=".", members=None, *, numeric_owner=False): - self.utime(tarinfo, dirpath) - self.chmod(tarinfo, dirpath) - except ExtractError as e: -- if self.errorlevel > 1: -- raise -- else: -- self._dbg(1, "tarfile: %s" % e) -+ self._handle_nonfatal_error(e) - -- def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): -+ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False, -+ filter=None): - """Extract a member from the archive to the current working directory, - using its full name. Its file information is extracted as accurately - as possible. `member' may be a filename or a TarInfo object. You can -@@ -2084,35 +2279,70 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): - mtime, mode) are set unless `set_attrs' is False. If `numeric_owner` - is True, only the numbers for user/group names are used and not - the names. -+ -+ The `filter` function will be called before extraction. -+ It can return a changed TarInfo or None to skip the member. -+ String names of common filters are accepted. - """ -- self._check("r") -+ filter_function = self._get_filter_function(filter) -+ tarinfo = self._get_extract_tarinfo(member, filter_function, path) -+ if tarinfo is not None: -+ self._extract_one(tarinfo, path, set_attrs, numeric_owner) - -+ def _get_extract_tarinfo(self, member, filter_function, path): -+ """Get filtered TarInfo (or None) from member, which might be a str""" - if isinstance(member, str): - tarinfo = self.getmember(member) - else: - tarinfo = member - -+ unfiltered = tarinfo -+ try: -+ tarinfo = filter_function(tarinfo, path) -+ except (OSError, FilterError) as e: -+ self._handle_fatal_error(e) -+ except ExtractError as e: -+ self._handle_nonfatal_error(e) -+ if tarinfo is None: -+ self._dbg(2, "tarfile: Excluded %r" % unfiltered.name) -+ return None - # Prepare the link target for makelink(). - if tarinfo.islnk(): -+ tarinfo = copy.copy(tarinfo) - tarinfo._link_target = os.path.join(path, tarinfo.linkname) -+ return tarinfo -+ -+ def _extract_one(self, tarinfo, path, set_attrs, numeric_owner): -+ """Extract from filtered tarinfo to disk""" -+ self._check("r") - - try: - self._extract_member(tarinfo, os.path.join(path, tarinfo.name), - set_attrs=set_attrs, - numeric_owner=numeric_owner) - except OSError as e: -- if self.errorlevel > 0: -- raise -- else: -- if e.filename is None: -- self._dbg(1, "tarfile: %s" % e.strerror) -- else: -- self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) -+ self._handle_fatal_error(e) - except ExtractError as e: -- if self.errorlevel > 1: -- raise -+ self._handle_nonfatal_error(e) -+ -+ def _handle_nonfatal_error(self, e): -+ """Handle non-fatal error (ExtractError) according to errorlevel""" -+ if self.errorlevel > 1: -+ raise -+ else: -+ self._dbg(1, "tarfile: %s" % e) -+ -+ def _handle_fatal_error(self, e): -+ """Handle "fatal" error according to self.errorlevel""" -+ if self.errorlevel > 0: -+ raise -+ elif isinstance(e, OSError): -+ if e.filename is None: -+ self._dbg(1, "tarfile: %s" % e.strerror) - else: -- self._dbg(1, "tarfile: %s" % e) -+ self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) -+ else: -+ self._dbg(1, "tarfile: %s %s" % (type(e).__name__, e)) - - def extractfile(self, member): - """Extract a member from the archive as a file object. `member' may be -@@ -2199,9 +2429,13 @@ def makedir(self, tarinfo, targetpath): - """Make a directory called targetpath. - """ - try: -- # Use a safe mode for the directory, the real mode is set -- # later in _extract_member(). -- os.mkdir(targetpath, 0o700) -+ if tarinfo.mode is None: -+ # Use the system's default mode -+ os.mkdir(targetpath) -+ else: -+ # Use a safe mode for the directory, the real mode is set -+ # later in _extract_member(). -+ os.mkdir(targetpath, 0o700) - except FileExistsError: - pass - -@@ -2244,6 +2478,9 @@ def makedev(self, tarinfo, targetpath): - raise ExtractError("special devices not supported by system") - - mode = tarinfo.mode -+ if mode is None: -+ # Use mknod's default -+ mode = 0o600 - if tarinfo.isblk(): - mode |= stat.S_IFBLK - else: -@@ -2265,7 +2502,6 @@ def makelink(self, tarinfo, targetpath): - os.unlink(targetpath) - os.symlink(tarinfo.linkname, targetpath) - else: -- # See extract(). - if os.path.exists(tarinfo._link_target): - os.link(tarinfo._link_target, targetpath) - else: -@@ -2290,15 +2526,19 @@ def chown(self, tarinfo, targetpath, numeric_owner): - u = tarinfo.uid - if not numeric_owner: - try: -- if grp: -+ if grp and tarinfo.gname: - g = grp.getgrnam(tarinfo.gname)[2] - except KeyError: - pass - try: -- if pwd: -+ if pwd and tarinfo.uname: - u = pwd.getpwnam(tarinfo.uname)[2] - except KeyError: - pass -+ if g is None: -+ g = -1 -+ if u is None: -+ u = -1 - try: - if tarinfo.issym() and hasattr(os, "lchown"): - os.lchown(targetpath, u, g) -@@ -2310,6 +2550,8 @@ def chown(self, tarinfo, targetpath, numeric_owner): - def chmod(self, tarinfo, targetpath): - """Set file permissions of targetpath according to tarinfo. - """ -+ if tarinfo.mode is None: -+ return - try: - os.chmod(targetpath, tarinfo.mode) - except OSError as e: -@@ -2318,10 +2560,13 @@ def chmod(self, tarinfo, targetpath): - def utime(self, tarinfo, targetpath): - """Set modification time of targetpath according to tarinfo. - """ -+ mtime = tarinfo.mtime -+ if mtime is None: -+ return - if not hasattr(os, 'utime'): - return - try: -- os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) -+ os.utime(targetpath, (mtime, mtime)) - except OSError as e: - raise ExtractError("could not change modification time") from e - -@@ -2397,13 +2642,26 @@ def _getmember(self, name, tarinfo=None, normalize=False): - members = self.getmembers() - - # Limit the member search list up to tarinfo. -+ skipping = False - if tarinfo is not None: -- members = members[:members.index(tarinfo)] -+ try: -+ index = members.index(tarinfo) -+ except ValueError: -+ # The given starting point might be a (modified) copy. -+ # We'll later skip members until we find an equivalent. -+ skipping = True -+ else: -+ # Happy fast path -+ members = members[:index] - - if normalize: - name = os.path.normpath(name) - - for member in reversed(members): -+ if skipping: -+ if tarinfo.offset == member.offset: -+ skipping = False -+ continue - if normalize: - member_name = os.path.normpath(member.name) - else: -@@ -2412,6 +2670,10 @@ def _getmember(self, name, tarinfo=None, normalize=False): - if name == member_name: - return member - -+ if skipping: -+ # Starting point was not found -+ raise ValueError(tarinfo) -+ - def _load(self): - """Read through the entire archive file and look for readable - members. -@@ -2504,6 +2766,7 @@ def __exit__(self, type, value, traceback): - #-------------------- - # exported functions - #-------------------- -+ - def is_tarfile(name): - """Return True if name points to a tar archive that we - are able to handle, else return False. -@@ -2530,6 +2793,10 @@ def main(): - parser = argparse.ArgumentParser(description=description) - parser.add_argument('-v', '--verbose', action='store_true', default=False, - help='Verbose output') -+ parser.add_argument('--filter', metavar='', -+ choices=_NAMED_FILTERS, -+ help='Filter for extraction') -+ - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('-l', '--list', metavar='', - help='Show listing of a tarfile') -@@ -2541,8 +2808,12 @@ def main(): - help='Create tarfile from sources') - group.add_argument('-t', '--test', metavar='', - help='Test if a tarfile is valid') -+ - args = parser.parse_args() - -+ if args.filter and args.extract is None: -+ parser.exit(1, '--filter is only valid for extraction\n') -+ - if args.test is not None: - src = args.test - if is_tarfile(src): -@@ -2573,7 +2844,7 @@ def main(): - - if is_tarfile(src): - with TarFile.open(src, 'r:*') as tf: -- tf.extractall(path=curdir) -+ tf.extractall(path=curdir, filter=args.filter) - if args.verbose: - if curdir == '.': - msg = '{!r} file is extracted.'.format(src) -diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index 0935b60d4c2..72fb3afcbef 100644 ---- a/Lib/test/test_shutil.py -+++ b/Lib/test/test_shutil.py -@@ -32,6 +32,7 @@ - from test import support - from test.support import os_helper - from test.support.os_helper import TESTFN, FakePath -+from test.support import warnings_helper - - TESTFN2 = TESTFN + "2" - TESTFN_SRC = TESTFN + "_SRC" -@@ -1610,12 +1611,14 @@ def test_register_archive_format(self): - - ### shutil.unpack_archive - -- def check_unpack_archive(self, format): -- self.check_unpack_archive_with_converter(format, lambda path: path) -- self.check_unpack_archive_with_converter(format, pathlib.Path) -- self.check_unpack_archive_with_converter(format, FakePath) -+ def check_unpack_archive(self, format, **kwargs): -+ self.check_unpack_archive_with_converter( -+ format, lambda path: path, **kwargs) -+ self.check_unpack_archive_with_converter( -+ format, pathlib.Path, **kwargs) -+ self.check_unpack_archive_with_converter(format, FakePath, **kwargs) - -- def check_unpack_archive_with_converter(self, format, converter): -+ def check_unpack_archive_with_converter(self, format, converter, **kwargs): - root_dir, base_dir = self._create_files() - expected = rlistdir(root_dir) - expected.remove('outer') -@@ -1625,36 +1628,47 @@ def check_unpack_archive_with_converter(self, format, converter): - - # let's try to unpack it now - tmpdir2 = self.mkdtemp() -- unpack_archive(converter(filename), converter(tmpdir2)) -+ unpack_archive(converter(filename), converter(tmpdir2), **kwargs) - self.assertEqual(rlistdir(tmpdir2), expected) - - # and again, this time with the format specified - tmpdir3 = self.mkdtemp() -- unpack_archive(converter(filename), converter(tmpdir3), format=format) -+ unpack_archive(converter(filename), converter(tmpdir3), format=format, -+ **kwargs) - self.assertEqual(rlistdir(tmpdir3), expected) - -- self.assertRaises(shutil.ReadError, unpack_archive, converter(TESTFN)) -- self.assertRaises(ValueError, unpack_archive, converter(TESTFN), format='xxx') -+ with self.assertRaises(shutil.ReadError): -+ unpack_archive(converter(TESTFN), **kwargs) -+ with self.assertRaises(ValueError): -+ unpack_archive(converter(TESTFN), format='xxx', **kwargs) -+ -+ def check_unpack_tarball(self, format): -+ self.check_unpack_archive(format, filter='fully_trusted') -+ self.check_unpack_archive(format, filter='data') -+ with warnings_helper.check_no_warnings(self): -+ self.check_unpack_archive(format) - - def test_unpack_archive_tar(self): -- self.check_unpack_archive('tar') -+ self.check_unpack_tarball('tar') - - @support.requires_zlib() - def test_unpack_archive_gztar(self): -- self.check_unpack_archive('gztar') -+ self.check_unpack_tarball('gztar') - - @support.requires_bz2() - def test_unpack_archive_bztar(self): -- self.check_unpack_archive('bztar') -+ self.check_unpack_tarball('bztar') - - @support.requires_lzma() - @unittest.skipIf(AIX and not _maxdataOK(), "AIX MAXDATA must be 0x20000000 or larger") - def test_unpack_archive_xztar(self): -- self.check_unpack_archive('xztar') -+ self.check_unpack_tarball('xztar') - - @support.requires_zlib() - def test_unpack_archive_zip(self): - self.check_unpack_archive('zip') -+ with self.assertRaises(TypeError): -+ self.check_unpack_archive('zip', filter='data') - - def test_unpack_registry(self): - -diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py -index 89f5a561b4a..0d8d91b4d03 100644 ---- a/Lib/test/test_tarfile.py -+++ b/Lib/test/test_tarfile.py -@@ -5,6 +5,10 @@ - from contextlib import contextmanager - from random import Random - import pathlib -+import shutil -+import re -+import warnings -+import stat - - import unittest - import unittest.mock -@@ -13,6 +17,7 @@ - from test import support - from test.support import os_helper - from test.support import script_helper -+from test.support import warnings_helper - - # Check for our compression modules. - try: -@@ -108,7 +113,7 @@ def test_fileobj_regular_file(self): - "regular file extraction failed") - - def test_fileobj_readlines(self): -- self.tar.extract("ustar/regtype", TEMPDIR) -+ self.tar.extract("ustar/regtype", TEMPDIR, filter='data') - tarinfo = self.tar.getmember("ustar/regtype") - with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1: - lines1 = fobj1.readlines() -@@ -126,7 +131,7 @@ def test_fileobj_readlines(self): - "fileobj.readlines() failed") - - def test_fileobj_iter(self): -- self.tar.extract("ustar/regtype", TEMPDIR) -+ self.tar.extract("ustar/regtype", TEMPDIR, filter='data') - tarinfo = self.tar.getmember("ustar/regtype") - with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1: - lines1 = fobj1.readlines() -@@ -136,7 +141,8 @@ def test_fileobj_iter(self): - "fileobj.__iter__() failed") - - def test_fileobj_seek(self): -- self.tar.extract("ustar/regtype", TEMPDIR) -+ self.tar.extract("ustar/regtype", TEMPDIR, -+ filter='data') - with open(os.path.join(TEMPDIR, "ustar/regtype"), "rb") as fobj: - data = fobj.read() - -@@ -455,7 +461,7 @@ def test_premature_end_of_archive(self): - t = tar.next() - - with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"): -- tar.extract(t, TEMPDIR) -+ tar.extract(t, TEMPDIR, filter='data') - - with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"): - tar.extractfile(t).read() -@@ -610,16 +616,16 @@ def test_find_members(self): - def test_extract_hardlink(self): - # Test hardlink extraction (e.g. bug #857297). - with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar: -- tar.extract("ustar/regtype", TEMPDIR) -+ tar.extract("ustar/regtype", TEMPDIR, filter='data') - self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/regtype")) - -- tar.extract("ustar/lnktype", TEMPDIR) -+ tar.extract("ustar/lnktype", TEMPDIR, filter='data') - self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/lnktype")) - with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f: - data = f.read() - self.assertEqual(sha256sum(data), sha256_regtype) - -- tar.extract("ustar/symtype", TEMPDIR) -+ tar.extract("ustar/symtype", TEMPDIR, filter='data') - self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/symtype")) - with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f: - data = f.read() -@@ -633,13 +639,14 @@ def test_extractall(self): - os.mkdir(DIR) - try: - directories = [t for t in tar if t.isdir()] -- tar.extractall(DIR, directories) -+ tar.extractall(DIR, directories, filter='fully_trusted') - for tarinfo in directories: - path = os.path.join(DIR, tarinfo.name) - if sys.platform != "win32": - # Win32 has no support for fine grained permissions. - self.assertEqual(tarinfo.mode & 0o777, -- os.stat(path).st_mode & 0o777) -+ os.stat(path).st_mode & 0o777, -+ tarinfo.name) - def format_mtime(mtime): - if isinstance(mtime, float): - return "{} ({})".format(mtime, mtime.hex()) -@@ -662,7 +669,7 @@ def test_extract_directory(self): - try: - with tarfile.open(tarname, encoding="iso8859-1") as tar: - tarinfo = tar.getmember(dirtype) -- tar.extract(tarinfo, path=DIR) -+ tar.extract(tarinfo, path=DIR, filter='fully_trusted') - extracted = os.path.join(DIR, dirtype) - self.assertEqual(os.path.getmtime(extracted), tarinfo.mtime) - if sys.platform != "win32": -@@ -675,7 +682,7 @@ def test_extractall_pathlike_name(self): - with os_helper.temp_dir(DIR), \ - tarfile.open(tarname, encoding="iso8859-1") as tar: - directories = [t for t in tar if t.isdir()] -- tar.extractall(DIR, directories) -+ tar.extractall(DIR, directories, filter='fully_trusted') - for tarinfo in directories: - path = DIR / tarinfo.name - self.assertEqual(os.path.getmtime(path), tarinfo.mtime) -@@ -686,7 +693,7 @@ def test_extract_pathlike_name(self): - with os_helper.temp_dir(DIR), \ - tarfile.open(tarname, encoding="iso8859-1") as tar: - tarinfo = tar.getmember(dirtype) -- tar.extract(tarinfo, path=DIR) -+ tar.extract(tarinfo, path=DIR, filter='fully_trusted') - extracted = DIR / dirtype - self.assertEqual(os.path.getmtime(extracted), tarinfo.mtime) - -@@ -1042,7 +1049,7 @@ class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase): - # an all platforms, and after that a test that will work only on - # platforms/filesystems that prove to support sparse files. - def _test_sparse_file(self, name): -- self.tar.extract(name, TEMPDIR) -+ self.tar.extract(name, TEMPDIR, filter='data') - filename = os.path.join(TEMPDIR, name) - with open(filename, "rb") as fobj: - data = fobj.read() -@@ -1409,7 +1416,8 @@ def test_extractall_symlinks(self): - with tarfile.open(temparchive, errorlevel=2) as tar: - # this should not raise OSError: [Errno 17] File exists - try: -- tar.extractall(path=tempdir) -+ tar.extractall(path=tempdir, -+ filter='fully_trusted') - except OSError: - self.fail("extractall failed with symlinked files") - finally: -@@ -2406,7 +2414,12 @@ def test__all__(self): - 'PAX_NUMBER_FIELDS', 'stn', 'nts', 'nti', 'itn', 'calc_chksums', - 'copyfileobj', 'filemode', 'EmptyHeaderError', - 'TruncatedHeaderError', 'EOFHeaderError', 'InvalidHeaderError', -- 'SubsequentHeaderError', 'ExFileObject', 'main'} -+ 'SubsequentHeaderError', 'ExFileObject', 'main', -+ "fully_trusted_filter", "data_filter", -+ "tar_filter", "FilterError", "AbsoluteLinkError", -+ "OutsideDestinationError", "SpecialFileError", "AbsolutePathError", -+ "LinkOutsideDestinationError", -+ } - support.check__all__(self, tarfile, not_exported=not_exported) - - def test_useful_error_message_when_modules_missing(self): -@@ -2441,6 +2454,15 @@ def make_simple_tarfile(self, tar_name): - for tardata in files: - tf.add(tardata, arcname=os.path.basename(tardata)) - -+ def make_evil_tarfile(self, tar_name): -+ files = [support.findfile('tokenize_tests.txt')] -+ self.addCleanup(os_helper.unlink, tar_name) -+ with tarfile.open(tar_name, 'w') as tf: -+ benign = tarfile.TarInfo('benign') -+ tf.addfile(benign, fileobj=io.BytesIO(b'')) -+ evil = tarfile.TarInfo('../evil') -+ tf.addfile(evil, fileobj=io.BytesIO(b'')) -+ - def test_bad_use(self): - rc, out, err = self.tarfilecmd_failure() - self.assertEqual(out, b'') -@@ -2597,6 +2619,25 @@ def test_extract_command_verbose(self): - finally: - os_helper.rmtree(tarextdir) - -+ def test_extract_command_filter(self): -+ self.make_evil_tarfile(tmpname) -+ # Make an inner directory, so the member named '../evil' -+ # is still extracted into `tarextdir` -+ destdir = os.path.join(tarextdir, 'dest') -+ os.mkdir(tarextdir) -+ try: -+ with os_helper.temp_cwd(destdir): -+ self.tarfilecmd_failure('-e', tmpname, -+ '-v', -+ '--filter', 'data') -+ out = self.tarfilecmd('-e', tmpname, -+ '-v', -+ '--filter', 'fully_trusted', -+ PYTHONIOENCODING='utf-8') -+ self.assertIn(b' file is extracted.', out) -+ finally: -+ os_helper.rmtree(tarextdir) -+ - def test_extract_command_different_directory(self): - self.make_simple_tarfile(tmpname) - try: -@@ -2680,7 +2721,7 @@ class LinkEmulationTest(ReadTest, unittest.TestCase): - # symbolic or hard links tarfile tries to extract these types of members - # as the regular files they point to. - def _test_link_extraction(self, name): -- self.tar.extract(name, TEMPDIR) -+ self.tar.extract(name, TEMPDIR, filter='fully_trusted') - with open(os.path.join(TEMPDIR, name), "rb") as f: - data = f.read() - self.assertEqual(sha256sum(data), sha256_regtype) -@@ -2812,8 +2853,10 @@ def test_extract_with_numeric_owner(self, mock_geteuid, mock_chmod, - mock_chown): - with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, - filename_2): -- tarfl.extract(filename_1, TEMPDIR, numeric_owner=True) -- tarfl.extract(filename_2 , TEMPDIR, numeric_owner=True) -+ tarfl.extract(filename_1, TEMPDIR, numeric_owner=True, -+ filter='fully_trusted') -+ tarfl.extract(filename_2 , TEMPDIR, numeric_owner=True, -+ filter='fully_trusted') - - # convert to filesystem paths - f_filename_1 = os.path.join(TEMPDIR, filename_1) -@@ -2831,7 +2874,8 @@ def test_extractall_with_numeric_owner(self, mock_geteuid, mock_chmod, - mock_chown): - with self._setup_test(mock_geteuid) as (tarfl, filename_1, dirname_1, - filename_2): -- tarfl.extractall(TEMPDIR, numeric_owner=True) -+ tarfl.extractall(TEMPDIR, numeric_owner=True, -+ filter='fully_trusted') - - # convert to filesystem paths - f_filename_1 = os.path.join(TEMPDIR, filename_1) -@@ -2856,7 +2900,8 @@ def test_extractall_with_numeric_owner(self, mock_geteuid, mock_chmod, - def test_extract_without_numeric_owner(self, mock_geteuid, mock_chmod, - mock_chown): - with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, _): -- tarfl.extract(filename_1, TEMPDIR, numeric_owner=False) -+ tarfl.extract(filename_1, TEMPDIR, numeric_owner=False, -+ filter='fully_trusted') - - # convert to filesystem paths - f_filename_1 = os.path.join(TEMPDIR, filename_1) -@@ -2870,6 +2915,888 @@ def test_keyword_only(self, mock_geteuid): - tarfl.extract, filename_1, TEMPDIR, False, True) - - -+class ReplaceTests(ReadTest, unittest.TestCase): -+ def test_replace_name(self): -+ member = self.tar.getmember('ustar/regtype') -+ replaced = member.replace(name='misc/other') -+ self.assertEqual(replaced.name, 'misc/other') -+ self.assertEqual(member.name, 'ustar/regtype') -+ self.assertEqual(self.tar.getmember('ustar/regtype').name, -+ 'ustar/regtype') -+ -+ def test_replace_deep(self): -+ member = self.tar.getmember('pax/regtype1') -+ replaced = member.replace() -+ replaced.pax_headers['gname'] = 'not-bar' -+ self.assertEqual(member.pax_headers['gname'], 'bar') -+ self.assertEqual( -+ self.tar.getmember('pax/regtype1').pax_headers['gname'], 'bar') -+ -+ def test_replace_shallow(self): -+ member = self.tar.getmember('pax/regtype1') -+ replaced = member.replace(deep=False) -+ replaced.pax_headers['gname'] = 'not-bar' -+ self.assertEqual(member.pax_headers['gname'], 'not-bar') -+ self.assertEqual( -+ self.tar.getmember('pax/regtype1').pax_headers['gname'], 'not-bar') -+ -+ def test_replace_all(self): -+ member = self.tar.getmember('ustar/regtype') -+ for attr_name in ('name', 'mtime', 'mode', 'linkname', -+ 'uid', 'gid', 'uname', 'gname'): -+ with self.subTest(attr_name=attr_name): -+ replaced = member.replace(**{attr_name: None}) -+ self.assertEqual(getattr(replaced, attr_name), None) -+ self.assertNotEqual(getattr(member, attr_name), None) -+ -+ def test_replace_internal(self): -+ member = self.tar.getmember('ustar/regtype') -+ with self.assertRaises(TypeError): -+ member.replace(offset=123456789) -+ -+ -+class NoneInfoExtractTests(ReadTest): -+ # These mainly check that all kinds of members are extracted successfully -+ # if some metadata is None. -+ # Some of the methods do additional spot checks. -+ -+ # We also test that the default filters can deal with None. -+ -+ extraction_filter = None -+ -+ @classmethod -+ def setUpClass(cls): -+ 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) -+ tar.close() -+ cls.control_paths = set( -+ p.relative_to(cls.control_dir) -+ for p in pathlib.Path(cls.control_dir).glob('**/*')) -+ -+ @classmethod -+ def tearDownClass(cls): -+ shutil.rmtree(cls.control_dir) -+ -+ def check_files_present(self, directory): -+ got_paths = set( -+ p.relative_to(directory) -+ for p in pathlib.Path(directory).glob('**/*')) -+ self.assertEqual(self.control_paths, got_paths) -+ -+ @contextmanager -+ def extract_with_none(self, *attr_names): -+ DIR = pathlib.Path(TEMPDIR) / "extractall_none" -+ self.tar.errorlevel = 0 -+ for member in self.tar.getmembers(): -+ for attr_name in attr_names: -+ setattr(member, attr_name, None) -+ with os_helper.temp_dir(DIR): -+ self.tar.extractall(DIR, filter='fully_trusted') -+ self.check_files_present(DIR) -+ yield DIR -+ -+ def test_extractall_none_mtime(self): -+ # mtimes of extracted files should be later than 'now' -- the mtime -+ # of a previously created directory. -+ now = pathlib.Path(TEMPDIR).stat().st_mtime -+ with self.extract_with_none('mtime') as DIR: -+ for path in pathlib.Path(DIR).glob('**/*'): -+ with self.subTest(path=path): -+ try: -+ mtime = path.stat().st_mtime -+ except OSError: -+ # Some systems can't stat symlinks, ignore those -+ if not path.is_symlink(): -+ raise -+ else: -+ self.assertGreaterEqual(path.stat().st_mtime, now) -+ -+ def test_extractall_none_mode(self): -+ # modes of directories and regular files should match the mode -+ # of a "normally" created directory or regular file -+ dir_mode = pathlib.Path(TEMPDIR).stat().st_mode -+ regular_file = pathlib.Path(TEMPDIR) / 'regular_file' -+ regular_file.write_text('') -+ regular_file_mode = regular_file.stat().st_mode -+ with self.extract_with_none('mode') as DIR: -+ for path in pathlib.Path(DIR).glob('**/*'): -+ with self.subTest(path=path): -+ if path.is_dir(): -+ self.assertEqual(path.stat().st_mode, dir_mode) -+ elif path.is_file(): -+ self.assertEqual(path.stat().st_mode, -+ regular_file_mode) -+ -+ def test_extractall_none_uid(self): -+ with self.extract_with_none('uid'): -+ pass -+ -+ def test_extractall_none_gid(self): -+ with self.extract_with_none('gid'): -+ pass -+ -+ def test_extractall_none_uname(self): -+ with self.extract_with_none('uname'): -+ pass -+ -+ def test_extractall_none_gname(self): -+ with self.extract_with_none('gname'): -+ pass -+ -+ def test_extractall_none_ownership(self): -+ with self.extract_with_none('uid', 'gid', 'uname', 'gname'): -+ pass -+ -+class NoneInfoExtractTests_Data(NoneInfoExtractTests, unittest.TestCase): -+ extraction_filter = 'data' -+ -+class NoneInfoExtractTests_FullyTrusted(NoneInfoExtractTests, -+ unittest.TestCase): -+ extraction_filter = 'fully_trusted' -+ -+class NoneInfoExtractTests_Tar(NoneInfoExtractTests, unittest.TestCase): -+ extraction_filter = 'tar' -+ -+class NoneInfoExtractTests_Default(NoneInfoExtractTests, -+ unittest.TestCase): -+ extraction_filter = None -+ -+class NoneInfoTests_Misc(unittest.TestCase): -+ def test_add(self): -+ # When addfile() encounters None metadata, it raises a ValueError -+ bio = io.BytesIO() -+ for tarformat in (tarfile.USTAR_FORMAT, tarfile.GNU_FORMAT, -+ tarfile.PAX_FORMAT): -+ with self.subTest(tarformat=tarformat): -+ tar = tarfile.open(fileobj=bio, mode='w', format=tarformat) -+ tarinfo = tar.gettarinfo(tarname) -+ try: -+ tar.addfile(tarinfo) -+ except Exception: -+ if tarformat == tarfile.USTAR_FORMAT: -+ # In the old, limited format, adding might fail for -+ # reasons like the UID being too large -+ pass -+ else: -+ raise -+ else: -+ for attr_name in ('mtime', 'mode', 'uid', 'gid', -+ 'uname', 'gname'): -+ with self.subTest(attr_name=attr_name): -+ replaced = tarinfo.replace(**{attr_name: None}) -+ with self.assertRaisesRegex(ValueError, -+ f"{attr_name}"): -+ tar.addfile(replaced) -+ -+ def test_list(self): -+ # Change some metadata to None, then compare list() output -+ # word-for-word. We want list() to not raise, and to only change -+ # printout for the affected piece of metadata. -+ # (n.b.: some contents of the test archive are hardcoded.) -+ for attr_names in ({'mtime'}, {'mode'}, {'uid'}, {'gid'}, -+ {'uname'}, {'gname'}, -+ {'uid', 'uname'}, {'gid', 'gname'}): -+ with (self.subTest(attr_names=attr_names), -+ tarfile.open(tarname, encoding="iso8859-1") as tar): -+ tio_prev = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') -+ with support.swap_attr(sys, 'stdout', tio_prev): -+ tar.list() -+ for member in tar.getmembers(): -+ for attr_name in attr_names: -+ setattr(member, attr_name, None) -+ tio_new = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') -+ with support.swap_attr(sys, 'stdout', tio_new): -+ tar.list() -+ for expected, got in zip(tio_prev.detach().getvalue().split(), -+ tio_new.detach().getvalue().split()): -+ if attr_names == {'mtime'} and re.match(rb'2003-01-\d\d', expected): -+ self.assertEqual(got, b'????-??-??') -+ elif attr_names == {'mtime'} and re.match(rb'\d\d:\d\d:\d\d', expected): -+ self.assertEqual(got, b'??:??:??') -+ elif attr_names == {'mode'} and re.match( -+ rb'.([r-][w-][x-]){3}', expected): -+ self.assertEqual(got, b'??????????') -+ elif attr_names == {'uname'} and expected.startswith( -+ (b'tarfile/', b'lars/', b'foo/')): -+ exp_user, exp_group = expected.split(b'/') -+ got_user, got_group = got.split(b'/') -+ self.assertEqual(got_group, exp_group) -+ self.assertRegex(got_user, b'[0-9]+') -+ elif attr_names == {'gname'} and expected.endswith( -+ (b'/tarfile', b'/users', b'/bar')): -+ exp_user, exp_group = expected.split(b'/') -+ got_user, got_group = got.split(b'/') -+ self.assertEqual(got_user, exp_user) -+ self.assertRegex(got_group, b'[0-9]+') -+ elif attr_names == {'uid'} and expected.startswith( -+ (b'1000/')): -+ exp_user, exp_group = expected.split(b'/') -+ got_user, got_group = got.split(b'/') -+ self.assertEqual(got_group, exp_group) -+ self.assertEqual(got_user, b'None') -+ elif attr_names == {'gid'} and expected.endswith((b'/100')): -+ exp_user, exp_group = expected.split(b'/') -+ got_user, got_group = got.split(b'/') -+ self.assertEqual(got_user, exp_user) -+ self.assertEqual(got_group, b'None') -+ elif attr_names == {'uid', 'uname'} and expected.startswith( -+ (b'tarfile/', b'lars/', b'foo/', b'1000/')): -+ exp_user, exp_group = expected.split(b'/') -+ got_user, got_group = got.split(b'/') -+ self.assertEqual(got_group, exp_group) -+ self.assertEqual(got_user, b'None') -+ elif attr_names == {'gname', 'gid'} and expected.endswith( -+ (b'/tarfile', b'/users', b'/bar', b'/100')): -+ exp_user, exp_group = expected.split(b'/') -+ got_user, got_group = got.split(b'/') -+ self.assertEqual(got_user, exp_user) -+ self.assertEqual(got_group, b'None') -+ else: -+ # In other cases the output should be the same -+ self.assertEqual(expected, got) -+ -+def _filemode_to_int(mode): -+ """Inverse of `stat.filemode` (for permission bits) -+ -+ Using mode strings rather than numbers makes the later tests more readable. -+ """ -+ str_mode = mode[1:] -+ result = ( -+ {'r': stat.S_IRUSR, '-': 0}[str_mode[0]] -+ | {'w': stat.S_IWUSR, '-': 0}[str_mode[1]] -+ | {'x': stat.S_IXUSR, '-': 0, -+ 's': stat.S_IXUSR | stat.S_ISUID, -+ 'S': stat.S_ISUID}[str_mode[2]] -+ | {'r': stat.S_IRGRP, '-': 0}[str_mode[3]] -+ | {'w': stat.S_IWGRP, '-': 0}[str_mode[4]] -+ | {'x': stat.S_IXGRP, '-': 0, -+ 's': stat.S_IXGRP | stat.S_ISGID, -+ 'S': stat.S_ISGID}[str_mode[5]] -+ | {'r': stat.S_IROTH, '-': 0}[str_mode[6]] -+ | {'w': stat.S_IWOTH, '-': 0}[str_mode[7]] -+ | {'x': stat.S_IXOTH, '-': 0, -+ 't': stat.S_IXOTH | stat.S_ISVTX, -+ 'T': stat.S_ISVTX}[str_mode[8]] -+ ) -+ # check we did this right -+ assert stat.filemode(result)[1:] == mode[1:] -+ -+ return result -+ -+class ArchiveMaker: -+ """Helper to create a tar file with specific contents -+ -+ Usage: -+ -+ with ArchiveMaker() as t: -+ t.add('filename', ...) -+ -+ with t.open() as tar: -+ ... # `tar` is now a TarFile with 'filename' in it! -+ """ -+ def __init__(self): -+ self.bio = io.BytesIO() -+ -+ def __enter__(self): -+ self.tar_w = tarfile.TarFile(mode='w', fileobj=self.bio) -+ return self -+ -+ def __exit__(self, *exc): -+ self.tar_w.close() -+ self.contents = self.bio.getvalue() -+ self.bio = None -+ -+ def add(self, name, *, type=None, symlink_to=None, hardlink_to=None, -+ mode=None, **kwargs): -+ """Add a member to the test archive. Call within `with`.""" -+ name = str(name) -+ tarinfo = tarfile.TarInfo(name).replace(**kwargs) -+ if mode: -+ tarinfo.mode = _filemode_to_int(mode) -+ if symlink_to is not None: -+ type = tarfile.SYMTYPE -+ tarinfo.linkname = str(symlink_to) -+ if hardlink_to is not None: -+ type = tarfile.LNKTYPE -+ tarinfo.linkname = str(hardlink_to) -+ if name.endswith('/') and type is None: -+ type = tarfile.DIRTYPE -+ if type is not None: -+ tarinfo.type = type -+ if tarinfo.isreg(): -+ fileobj = io.BytesIO(bytes(tarinfo.size)) -+ else: -+ fileobj = None -+ self.tar_w.addfile(tarinfo, fileobj) -+ -+ def open(self, **kwargs): -+ """Open the resulting archive as TarFile. Call after `with`.""" -+ bio = io.BytesIO(self.contents) -+ return tarfile.open(fileobj=bio, **kwargs) -+ -+ -+class TestExtractionFilters(unittest.TestCase): -+ -+ # A temporary directory for the extraction results. -+ # All files that "escape" the destination path should still end -+ # up in this directory. -+ outerdir = pathlib.Path(TEMPDIR) / 'outerdir' -+ -+ # The destination for the extraction, within `outerdir` -+ destdir = outerdir / 'dest' -+ -+ @contextmanager -+ def check_context(self, tar, filter): -+ """Extracts `tar` to `self.destdir` and allows checking the result -+ -+ If an error occurs, it must be checked using `expect_exception` -+ -+ Otherwise, all resulting files must be checked using `expect_file`, -+ except the destination directory itself and parent directories of -+ other files. -+ When checking directories, do so before their contents. -+ """ -+ with os_helper.temp_dir(self.outerdir): -+ try: -+ tar.extractall(self.destdir, filter=filter) -+ except Exception as exc: -+ self.raised_exception = exc -+ self.expected_paths = set() -+ else: -+ self.raised_exception = None -+ self.expected_paths = set(self.outerdir.glob('**/*')) -+ self.expected_paths.discard(self.destdir) -+ try: -+ yield -+ finally: -+ tar.close() -+ if self.raised_exception: -+ raise self.raised_exception -+ self.assertEqual(self.expected_paths, set()) -+ -+ def expect_file(self, name, type=None, symlink_to=None, mode=None): -+ """Check a single file. See check_context.""" -+ if self.raised_exception: -+ raise self.raised_exception -+ # use normpath() rather than resolve() so we don't follow symlinks -+ path = pathlib.Path(os.path.normpath(self.destdir / name)) -+ self.assertIn(path, self.expected_paths) -+ self.expected_paths.remove(path) -+ if mode is not None: -+ got = stat.filemode(stat.S_IMODE(path.stat().st_mode)) -+ self.assertEqual(got, mode) -+ if type is None and isinstance(name, str) and name.endswith('/'): -+ type = tarfile.DIRTYPE -+ if symlink_to is not None: -+ got = (self.destdir / name).readlink() -+ expected = pathlib.Path(symlink_to) -+ # The symlink might be the same (textually) as what we expect, -+ # but some systems change the link to an equivalent path, so -+ # we fall back to samefile(). -+ if expected != got: -+ self.assertTrue(got.samefile(expected)) -+ elif type == tarfile.REGTYPE or type is None: -+ self.assertTrue(path.is_file()) -+ elif type == tarfile.DIRTYPE: -+ self.assertTrue(path.is_dir()) -+ elif type == tarfile.FIFOTYPE: -+ self.assertTrue(path.is_fifo()) -+ else: -+ raise NotImplementedError(type) -+ for parent in path.parents: -+ self.expected_paths.discard(parent) -+ -+ def expect_exception(self, exc_type, message_re='.'): -+ with self.assertRaisesRegex(exc_type, message_re): -+ if self.raised_exception is not None: -+ raise self.raised_exception -+ self.raised_exception = None -+ -+ def test_benign_file(self): -+ with ArchiveMaker() as arc: -+ arc.add('benign.txt') -+ for filter in 'fully_trusted', 'tar', 'data': -+ with self.check_context(arc.open(), filter): -+ self.expect_file('benign.txt') -+ -+ def test_absolute(self): -+ # Test handling a member with an absolute path -+ # Inspired by 'absolute1' in https://github.com/jwilk/traversal-archives -+ with ArchiveMaker() as arc: -+ arc.add(self.outerdir / 'escaped.evil') -+ -+ with self.check_context(arc.open(), 'fully_trusted'): -+ self.expect_file('../escaped.evil') -+ -+ for filter in 'tar', 'data': -+ with self.check_context(arc.open(), filter): -+ if str(self.outerdir).startswith('/'): -+ # We strip leading slashes, as e.g. GNU tar does -+ # (without --absolute-filenames). -+ outerdir_stripped = str(self.outerdir).lstrip('/') -+ self.expect_file(f'{outerdir_stripped}/escaped.evil') -+ else: -+ # On this system, absolute paths don't have leading -+ # slashes. -+ # So, there's nothing to strip. We refuse to unpack -+ # to an absolute path, nonetheless. -+ self.expect_exception( -+ tarfile.AbsolutePathError, -+ """['"].*escaped.evil['"] has an absolute path""") -+ -+ def test_parent_symlink(self): -+ # Test interplaying symlinks -+ # Inspired by 'dirsymlink2a' in jwilk/traversal-archives -+ with ArchiveMaker() as arc: -+ arc.add('current', symlink_to='.') -+ arc.add('parent', symlink_to='current/..') -+ arc.add('parent/evil') -+ -+ if os_helper.can_symlink(): -+ with self.check_context(arc.open(), 'fully_trusted'): -+ if self.raised_exception is not None: -+ # Windows will refuse to create a file that's a symlink to itself -+ # (and tarfile doesn't swallow that exception) -+ self.expect_exception(FileExistsError) -+ # The other cases will fail with this error too. -+ # Skip the rest of this test. -+ return -+ else: -+ self.expect_file('current', symlink_to='.') -+ self.expect_file('parent', symlink_to='current/..') -+ self.expect_file('../evil') -+ -+ with self.check_context(arc.open(), 'tar'): -+ self.expect_exception( -+ tarfile.OutsideDestinationError, -+ """'parent/evil' would be extracted to ['"].*evil['"], """ -+ + "which is outside the destination") -+ -+ with self.check_context(arc.open(), 'data'): -+ self.expect_exception( -+ tarfile.LinkOutsideDestinationError, -+ """'parent' would link to ['"].*outerdir['"], """ -+ + "which is outside the destination") -+ -+ else: -+ # No symlink support. The symlinks are ignored. -+ with self.check_context(arc.open(), 'fully_trusted'): -+ self.expect_file('parent/evil') -+ with self.check_context(arc.open(), 'tar'): -+ self.expect_file('parent/evil') -+ with self.check_context(arc.open(), 'data'): -+ self.expect_file('parent/evil') -+ -+ def test_parent_symlink2(self): -+ # Test interplaying symlinks -+ # Inspired by 'dirsymlink2b' in jwilk/traversal-archives -+ with ArchiveMaker() as arc: -+ arc.add('current', symlink_to='.') -+ arc.add('current/parent', symlink_to='..') -+ arc.add('parent/evil') -+ -+ with self.check_context(arc.open(), 'fully_trusted'): -+ if os_helper.can_symlink(): -+ self.expect_file('current', symlink_to='.') -+ self.expect_file('parent', symlink_to='..') -+ self.expect_file('../evil') -+ else: -+ self.expect_file('current/') -+ self.expect_file('parent/evil') -+ -+ with self.check_context(arc.open(), 'tar'): -+ if os_helper.can_symlink(): -+ self.expect_exception( -+ tarfile.OutsideDestinationError, -+ "'parent/evil' would be extracted to " -+ + """['"].*evil['"], which is outside """ -+ + "the destination") -+ else: -+ self.expect_file('current/') -+ self.expect_file('parent/evil') -+ -+ with self.check_context(arc.open(), 'data'): -+ self.expect_exception( -+ tarfile.LinkOutsideDestinationError, -+ """'current/parent' would link to ['"].*['"], """ -+ + "which is outside the destination") -+ -+ def test_absolute_symlink(self): -+ # Test symlink to an absolute path -+ # Inspired by 'dirsymlink' in jwilk/traversal-archives -+ with ArchiveMaker() as arc: -+ arc.add('parent', symlink_to=self.outerdir) -+ arc.add('parent/evil') -+ -+ with self.check_context(arc.open(), 'fully_trusted'): -+ if os_helper.can_symlink(): -+ self.expect_file('parent', symlink_to=self.outerdir) -+ self.expect_file('../evil') -+ else: -+ self.expect_file('parent/evil') -+ -+ with self.check_context(arc.open(), 'tar'): -+ if os_helper.can_symlink(): -+ self.expect_exception( -+ tarfile.OutsideDestinationError, -+ "'parent/evil' would be extracted to " -+ + """['"].*evil['"], which is outside """ -+ + "the destination") -+ else: -+ self.expect_file('parent/evil') -+ -+ with self.check_context(arc.open(), 'data'): -+ self.expect_exception( -+ tarfile.AbsoluteLinkError, -+ "'parent' is a symlink to an absolute path") -+ -+ def test_sly_relative0(self): -+ # Inspired by 'relative0' in jwilk/traversal-archives -+ with ArchiveMaker() as arc: -+ arc.add('../moo', symlink_to='..//tmp/moo') -+ -+ try: -+ with self.check_context(arc.open(), filter='fully_trusted'): -+ if os_helper.can_symlink(): -+ if isinstance(self.raised_exception, FileExistsError): -+ # XXX TarFile happens to fail creating a parent -+ # directory. -+ # This might be a bug, but fixing it would hurt -+ # security. -+ # Note that e.g. GNU `tar` rejects '..' components, -+ # so you could argue this is an invalid archive and we -+ # just raise an bad type of exception. -+ self.expect_exception(FileExistsError) -+ else: -+ self.expect_file('../moo', symlink_to='..//tmp/moo') -+ else: -+ # The symlink can't be extracted and is ignored -+ pass -+ except FileExistsError: -+ pass -+ -+ for filter in 'tar', 'data': -+ with self.check_context(arc.open(), filter): -+ self.expect_exception( -+ tarfile.OutsideDestinationError, -+ "'../moo' would be extracted to " -+ + "'.*moo', which is outside " -+ + "the destination") -+ -+ def test_sly_relative2(self): -+ # Inspired by 'relative2' in jwilk/traversal-archives -+ with ArchiveMaker() as arc: -+ arc.add('tmp/') -+ arc.add('tmp/../../moo', symlink_to='tmp/../..//tmp/moo') -+ -+ with self.check_context(arc.open(), 'fully_trusted'): -+ self.expect_file('tmp', type=tarfile.DIRTYPE) -+ if os_helper.can_symlink(): -+ self.expect_file('../moo', symlink_to='tmp/../../tmp/moo') -+ -+ for filter in 'tar', 'data': -+ with self.check_context(arc.open(), filter): -+ self.expect_exception( -+ tarfile.OutsideDestinationError, -+ "'tmp/../../moo' would be extracted to " -+ + """['"].*moo['"], which is outside the """ -+ + "destination") -+ -+ def test_modes(self): -+ # Test how file modes are extracted -+ # (Note that the modes are ignored on platforms without working chmod) -+ with ArchiveMaker() as arc: -+ arc.add('all_bits', mode='?rwsrwsrwt') -+ arc.add('perm_bits', mode='?rwxrwxrwx') -+ arc.add('exec_group_other', mode='?rw-rwxrwx') -+ arc.add('read_group_only', mode='?---r-----') -+ arc.add('no_bits', mode='?---------') -+ arc.add('dir/', mode='?---rwsrwt') -+ -+ # On some systems, setting the sticky bit is a no-op. -+ # Check if that's the case. -+ tmp_filename = os.path.join(TEMPDIR, "tmp.file") -+ with open(tmp_filename, 'w'): -+ pass -+ os.chmod(tmp_filename, os.stat(tmp_filename).st_mode | stat.S_ISVTX) -+ have_sticky_files = (os.stat(tmp_filename).st_mode & stat.S_ISVTX) -+ os.unlink(tmp_filename) -+ -+ os.mkdir(tmp_filename) -+ os.chmod(tmp_filename, os.stat(tmp_filename).st_mode | stat.S_ISVTX) -+ have_sticky_dirs = (os.stat(tmp_filename).st_mode & stat.S_ISVTX) -+ os.rmdir(tmp_filename) -+ -+ with self.check_context(arc.open(), 'fully_trusted'): -+ if have_sticky_files: -+ self.expect_file('all_bits', mode='?rwsrwsrwt') -+ else: -+ self.expect_file('all_bits', mode='?rwsrwsrwx') -+ self.expect_file('perm_bits', mode='?rwxrwxrwx') -+ self.expect_file('exec_group_other', mode='?rw-rwxrwx') -+ self.expect_file('read_group_only', mode='?---r-----') -+ self.expect_file('no_bits', mode='?---------') -+ if have_sticky_dirs: -+ self.expect_file('dir/', mode='?---rwsrwt') -+ else: -+ self.expect_file('dir/', mode='?---rwsrwx') -+ -+ with self.check_context(arc.open(), 'tar'): -+ self.expect_file('all_bits', mode='?rwxr-xr-x') -+ self.expect_file('perm_bits', mode='?rwxr-xr-x') -+ self.expect_file('exec_group_other', mode='?rw-r-xr-x') -+ self.expect_file('read_group_only', mode='?---r-----') -+ self.expect_file('no_bits', mode='?---------') -+ self.expect_file('dir/', mode='?---r-xr-x') -+ -+ with self.check_context(arc.open(), 'data'): -+ normal_dir_mode = stat.filemode(stat.S_IMODE( -+ self.outerdir.stat().st_mode)) -+ self.expect_file('all_bits', mode='?rwxr-xr-x') -+ self.expect_file('perm_bits', mode='?rwxr-xr-x') -+ self.expect_file('exec_group_other', mode='?rw-r--r--') -+ self.expect_file('read_group_only', mode='?rw-r-----') -+ self.expect_file('no_bits', mode='?rw-------') -+ self.expect_file('dir/', mode=normal_dir_mode) -+ -+ def test_pipe(self): -+ # Test handling of a special file -+ with ArchiveMaker() as arc: -+ arc.add('foo', type=tarfile.FIFOTYPE) -+ -+ for filter in 'fully_trusted', 'tar': -+ with self.check_context(arc.open(), filter): -+ if hasattr(os, 'mkfifo'): -+ self.expect_file('foo', type=tarfile.FIFOTYPE) -+ else: -+ # The pipe can't be extracted and is skipped. -+ pass -+ -+ with self.check_context(arc.open(), 'data'): -+ self.expect_exception( -+ tarfile.SpecialFileError, -+ "'foo' is a special file") -+ -+ def test_special_files(self): -+ # Creating device files is tricky. Instead of attempting that let's -+ # only check the filter result. -+ for special_type in tarfile.FIFOTYPE, tarfile.CHRTYPE, tarfile.BLKTYPE: -+ tarinfo = tarfile.TarInfo('foo') -+ tarinfo.type = special_type -+ trusted = tarfile.fully_trusted_filter(tarinfo, '') -+ self.assertIs(trusted, tarinfo) -+ tar = tarfile.tar_filter(tarinfo, '') -+ self.assertEqual(tar.type, special_type) -+ with self.assertRaises(tarfile.SpecialFileError) as cm: -+ tarfile.data_filter(tarinfo, '') -+ self.assertIsInstance(cm.exception.tarinfo, tarfile.TarInfo) -+ self.assertEqual(cm.exception.tarinfo.name, 'foo') -+ -+ def test_fully_trusted_filter(self): -+ # The 'fully_trusted' filter returns the original TarInfo objects. -+ with tarfile.TarFile.open(tarname) as tar: -+ for tarinfo in tar.getmembers(): -+ filtered = tarfile.fully_trusted_filter(tarinfo, '') -+ self.assertIs(filtered, tarinfo) -+ -+ def test_tar_filter(self): -+ # The 'tar' filter returns TarInfo objects with the same name/type. -+ # (It can also fail for particularly "evil" input, but we don't have -+ # that in the test archive.) -+ with tarfile.TarFile.open(tarname) as tar: -+ for tarinfo in tar.getmembers(): -+ filtered = tarfile.tar_filter(tarinfo, '') -+ self.assertIs(filtered.name, tarinfo.name) -+ self.assertIs(filtered.type, tarinfo.type) -+ -+ def test_data_filter(self): -+ # The 'data' filter either raises, or returns TarInfo with the same -+ # name/type. -+ with tarfile.TarFile.open(tarname) as tar: -+ for tarinfo in tar.getmembers(): -+ try: -+ filtered = tarfile.data_filter(tarinfo, '') -+ except tarfile.FilterError: -+ continue -+ self.assertIs(filtered.name, tarinfo.name) -+ self.assertIs(filtered.type, tarinfo.type) -+ -+ def test_default_filter_warns_not(self): -+ """Ensure the default filter does not warn (like in 3.12)""" -+ with ArchiveMaker() as arc: -+ arc.add('foo') -+ with warnings_helper.check_no_warnings(self): -+ with self.check_context(arc.open(), None): -+ self.expect_file('foo') -+ -+ def test_change_default_filter_on_instance(self): -+ tar = tarfile.TarFile(tarname, 'r') -+ def strict_filter(tarinfo, path): -+ if tarinfo.name == 'ustar/regtype': -+ return tarinfo -+ else: -+ return None -+ tar.extraction_filter = strict_filter -+ with self.check_context(tar, None): -+ self.expect_file('ustar/regtype') -+ -+ def test_change_default_filter_on_class(self): -+ def strict_filter(tarinfo, path): -+ if tarinfo.name == 'ustar/regtype': -+ return tarinfo -+ else: -+ return None -+ tar = tarfile.TarFile(tarname, 'r') -+ with support.swap_attr(tarfile.TarFile, 'extraction_filter', -+ staticmethod(strict_filter)): -+ with self.check_context(tar, None): -+ self.expect_file('ustar/regtype') -+ -+ def test_change_default_filter_on_subclass(self): -+ class TarSubclass(tarfile.TarFile): -+ def extraction_filter(self, tarinfo, path): -+ if tarinfo.name == 'ustar/regtype': -+ return tarinfo -+ else: -+ return None -+ -+ tar = TarSubclass(tarname, 'r') -+ with self.check_context(tar, None): -+ self.expect_file('ustar/regtype') -+ -+ def test_change_default_filter_to_string(self): -+ tar = tarfile.TarFile(tarname, 'r') -+ tar.extraction_filter = 'data' -+ with self.check_context(tar, None): -+ self.expect_exception(TypeError) -+ -+ def test_custom_filter(self): -+ def custom_filter(tarinfo, path): -+ self.assertIs(path, self.destdir) -+ if tarinfo.name == 'move_this': -+ return tarinfo.replace(name='moved') -+ if tarinfo.name == 'ignore_this': -+ return None -+ return tarinfo -+ -+ with ArchiveMaker() as arc: -+ arc.add('move_this') -+ arc.add('ignore_this') -+ arc.add('keep') -+ with self.check_context(arc.open(), custom_filter): -+ self.expect_file('moved') -+ self.expect_file('keep') -+ -+ def test_bad_filter_name(self): -+ with ArchiveMaker() as arc: -+ arc.add('foo') -+ with self.check_context(arc.open(), 'bad filter name'): -+ self.expect_exception(ValueError) -+ -+ def test_stateful_filter(self): -+ # Stateful filters should be possible. -+ # (This doesn't really test tarfile. Rather, it demonstrates -+ # that third parties can implement a stateful filter.) -+ class StatefulFilter: -+ def __enter__(self): -+ self.num_files_processed = 0 -+ return self -+ -+ def __call__(self, tarinfo, path): -+ try: -+ tarinfo = tarfile.data_filter(tarinfo, path) -+ except tarfile.FilterError: -+ return None -+ self.num_files_processed += 1 -+ return tarinfo -+ -+ def __exit__(self, *exc_info): -+ self.done = True -+ -+ with ArchiveMaker() as arc: -+ arc.add('good') -+ arc.add('bad', symlink_to='/') -+ arc.add('good') -+ with StatefulFilter() as custom_filter: -+ with self.check_context(arc.open(), custom_filter): -+ self.expect_file('good') -+ self.assertEqual(custom_filter.num_files_processed, 2) -+ self.assertEqual(custom_filter.done, True) -+ -+ def test_errorlevel(self): -+ def extracterror_filter(tarinfo, path): -+ raise tarfile.ExtractError('failed with ExtractError') -+ def filtererror_filter(tarinfo, path): -+ raise tarfile.FilterError('failed with FilterError') -+ def oserror_filter(tarinfo, path): -+ raise OSError('failed with OSError') -+ def tarerror_filter(tarinfo, path): -+ raise tarfile.TarError('failed with base TarError') -+ def valueerror_filter(tarinfo, path): -+ raise ValueError('failed with ValueError') -+ -+ with ArchiveMaker() as arc: -+ arc.add('file') -+ -+ # If errorlevel is 0, errors affected by errorlevel are ignored -+ -+ with self.check_context(arc.open(errorlevel=0), extracterror_filter): -+ self.expect_file('file') -+ -+ with self.check_context(arc.open(errorlevel=0), filtererror_filter): -+ self.expect_file('file') -+ -+ with self.check_context(arc.open(errorlevel=0), oserror_filter): -+ self.expect_file('file') -+ -+ with self.check_context(arc.open(errorlevel=0), tarerror_filter): -+ self.expect_exception(tarfile.TarError) -+ -+ with self.check_context(arc.open(errorlevel=0), valueerror_filter): -+ self.expect_exception(ValueError) -+ -+ # If 1, all fatal errors are raised -+ -+ with self.check_context(arc.open(errorlevel=1), extracterror_filter): -+ self.expect_file('file') -+ -+ with self.check_context(arc.open(errorlevel=1), filtererror_filter): -+ self.expect_exception(tarfile.FilterError) -+ -+ with self.check_context(arc.open(errorlevel=1), oserror_filter): -+ self.expect_exception(OSError) -+ -+ with self.check_context(arc.open(errorlevel=1), tarerror_filter): -+ self.expect_exception(tarfile.TarError) -+ -+ with self.check_context(arc.open(errorlevel=1), valueerror_filter): -+ self.expect_exception(ValueError) -+ -+ # If 2, all non-fatal errors are raised as well. -+ -+ with self.check_context(arc.open(errorlevel=2), extracterror_filter): -+ self.expect_exception(tarfile.ExtractError) -+ -+ with self.check_context(arc.open(errorlevel=2), filtererror_filter): -+ self.expect_exception(tarfile.FilterError) -+ -+ with self.check_context(arc.open(errorlevel=2), oserror_filter): -+ self.expect_exception(OSError) -+ -+ with self.check_context(arc.open(errorlevel=2), tarerror_filter): -+ self.expect_exception(tarfile.TarError) -+ -+ with self.check_context(arc.open(errorlevel=2), valueerror_filter): -+ self.expect_exception(ValueError) -+ -+ # We only handle ExtractionError, FilterError & OSError specially. -+ -+ with self.check_context(arc.open(errorlevel='boo!'), filtererror_filter): -+ self.expect_exception(TypeError) # errorlevel is not int -+ -+ - def setUpModule(): - os_helper.unlink(TEMPDIR) - os.makedirs(TEMPDIR) -diff --git a/Misc/NEWS.d/next/Library/2023-03-23-15-24-38.gh-issue-102953.YR4KaK.rst b/Misc/NEWS.d/next/Library/2023-03-23-15-24-38.gh-issue-102953.YR4KaK.rst -new file mode 100644 -index 00000000000..48a105a4a17 ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2023-03-23-15-24-38.gh-issue-102953.YR4KaK.rst -@@ -0,0 +1,4 @@ -+The extraction methods in :mod:`tarfile`, and :func:`shutil.unpack_archive`, -+have a new a *filter* argument that allows limiting tar features than may be -+surprising or dangerous, such as creating files outside the destination -+directory. See :ref:`tarfile-extraction-filter` for details. diff --git a/CVE-2023-24329-blank-URL-bypass.patch b/CVE-2023-24329-blank-URL-bypass.patch deleted file mode 100644 index d88dcfe..0000000 --- a/CVE-2023-24329-blank-URL-bypass.patch +++ /dev/null @@ -1,55 +0,0 @@ -From a284d69de1d1a42714576d4a9562145a94e62127 Mon Sep 17 00:00:00 2001 -From: Ben Kallus -Date: Sat, 12 Nov 2022 15:43:33 -0500 -Subject: [PATCH 1/2] gh-99418: Prevent urllib.parse.urlparse from accepting - schemes that don't begin with an alphabetical ASCII character. - ---- - Lib/test/test_urlparse.py | 18 ++++++++++ - Lib/urllib/parse.py | 2 - - Misc/NEWS.d/next/Library/2022-11-12-15-45-51.gh-issue-99418.FxfAXS.rst | 2 + - 3 files changed, 21 insertions(+), 1 deletion(-) - ---- a/Lib/test/test_urlparse.py -+++ b/Lib/test/test_urlparse.py -@@ -668,6 +668,24 @@ class UrlParseTestCase(unittest.TestCase - with self.assertRaises(ValueError): - p.port - -+ def test_attributes_bad_scheme(self): -+ """Check handling of invalid schemes.""" -+ for bytes in (False, True): -+ for parse in (urllib.parse.urlsplit, urllib.parse.urlparse): -+ for scheme in (".", "+", "-", "0", "http&", "६http"): -+ with self.subTest(bytes=bytes, parse=parse, scheme=scheme): -+ url = scheme + "://www.example.net" -+ if bytes: -+ if url.isascii(): -+ url = url.encode("ascii") -+ else: -+ continue -+ p = parse(url) -+ if bytes: -+ self.assertEqual(p.scheme, b"") -+ else: -+ self.assertEqual(p.scheme, "") -+ - def test_attributes_without_netloc(self): - # This example is straight from RFC 3261. It looks like it - # should allow the username, hostname, and port to be filled ---- a/Lib/urllib/parse.py -+++ b/Lib/urllib/parse.py -@@ -469,7 +469,7 @@ def urlsplit(url, scheme='', allow_fragm - clear_cache() - netloc = query = fragment = '' - i = url.find(':') -- if i > 0: -+ if i > 0 and url[0].isascii() and url[0].isalpha(): - for c in url[:i]: - if c not in scheme_chars: - break ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2022-11-12-15-45-51.gh-issue-99418.FxfAXS.rst -@@ -0,0 +1,2 @@ -+Fix bug in :func:`urllib.parse.urlparse` that causes URL schemes that begin -+with a digit, a plus sign, or a minus sign to be parsed incorrectly. diff --git a/Python-3.10.11.tar.xz.asc b/Python-3.10.11.tar.xz.asc deleted file mode 100644 index d3012ea..0000000 --- a/Python-3.10.11.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmQsoHwACgkQ/+h0BBaL -2Efs9BAAheWCnenhHhXi0m0DgyB6eEVH8xmZrBqA1WMgGQOqWVZmEnJdc0IXyFWQ -1A4C59d6rEvu8jvXTLvsqGEmehofKqq0bXB1tMUBn9CwSiELOm19WvCHc/Htwo2U -DsvAsXXO7vBkKBT9+CQ4BmkGzPUTrBLZRHsQX/M/tpx81jnQVunoMojyPK19sf1I -C+YnxE0cQVL9+INd0WtbVByJIwzBBDCLqTQWL//73CqFs8IO6PsjFXqmlVqVfpmz -aEXuGeRkRgy7kZaDdLcnhBq7a6vgaecfgfRUGyBgwgakfrHA5SOdsWdAonjA676J -6DHmFIf82R4wo7Vu0WAfFAq9jJfVxXN7n5Y/N/cxzqjhrfO341vCflN1c16VAFnu -ok7n50poENO/tMRerOEj5baL+mToi8Wh+cYHY6tNpaM2iP+bSyjoS+Ff225xhdNV -fqGuyaH7cPgGgoXECrSb7iTWYZxJxQV9S8OlR2gX8IlA+XrbGWQl0PvmErhO3FqN -W88gBmYrzrSl6+dzF62yn2gKFc2K5k6NmCcySFfjY87G7RhEf1ixPeDyMSvbKlVw -sJWeoXuCNPL+PQV+V76UAbn3bEvH87fyImxoYHNAIbHh8JaTvO5vIKDrrsw92siQ -6Pud3Oy6DcD5gWX2KcaAjQjruh18dljsbYN+2KVFfQHM8SYeXns= -=enP4 ------END PGP SIGNATURE----- diff --git a/Python-3.10.11.tar.xz b/Python-3.10.12.tar.xz similarity index 100% rename from Python-3.10.11.tar.xz rename to Python-3.10.12.tar.xz diff --git a/Python-3.10.12.tar.xz.asc b/Python-3.10.12.tar.xz.asc new file mode 100644 index 0000000..05de3f2 --- /dev/null +++ b/Python-3.10.12.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmR/tqIACgkQ/+h0BBaL +2EfUfg/9FW0m6nngtGIKTJ+Gk4G13pQvnELgc3eq70t7Sn1g2kGxDpO+rs0WptYG +gGcHs6v4rE/3uQ0mf4QCvnnXffQEQ+bRDaj1ZBY/rJjCdgQeUNMElV0KbvADiTqS ++akmsXaK3KqLHJesZo65lZ4HSADWKosBU3zxE2/CRMMsz1aLMDLIoaQo+pqDcFl7 +ZfGMlmiJNyD2jZVYGdwCbhG0BymOTU02BxkH2Dkd9OGzj9A3zDPCO6RcDFtw4dkK +lngHQGijYaFV11FqIaApnUkz7aAPk//2KRLwpf5D5z8p8T8QsHAJyTmIm1gMQiQA +tMThI1tFGN6lF1QSrfwGooXs3AdeEY0VoL4CpQi8TVRLyi6HE4AU4hEQdPqVmpm1 ++U2K0MpYhkwtPp0E9E7y9v82fMSzUKvGgpTstnblKTfDmgGUGb47Ncj3XvxH8SZz +p93YK2xpfl4V2ltLio8ONmwP9lQhxk5L34dQR20cjbOoj622VofqGUV7Zr6UHVLD +pqYgnj3zgiTPmbCzgVxZOyaLD3ezsY8oAtfLgX6cjCfsTtV27TvQUD8Br0oKQYS/ +h5KJBdytokqPa+JWr59hvQpcLSbmCB2y7USminoS2yL1hpXidTvVDUALF3vorvZi +BS8prxUIFT2dgerUpWmMrKYih7pJNKdySGgI3zXtxIt5TE0TRag= +=9Zqh +-----END PGP SIGNATURE----- diff --git a/bpo-37596-make-set-marshalling.patch b/bpo-37596-make-set-marshalling.patch new file mode 100644 index 0000000..44bc85f --- /dev/null +++ b/bpo-37596-make-set-marshalling.patch @@ -0,0 +1,103 @@ +From 33d95c6facdfda3c8c0feffa7a99184e4abc2f63 Mon Sep 17 00:00:00 2001 +From: Brandt Bucher +Date: Wed, 25 Aug 2021 04:14:34 -0700 +Subject: [PATCH] bpo-37596: Make `set` and `frozenset` marshalling + deterministic (GH-27926) + +--- + Lib/test/test_marshal.py | 26 ++++++++ + Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst | 2 + Python/marshal.c | 32 ++++++++++ + 3 files changed, 60 insertions(+) + create mode 100644 Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst + +--- a/Lib/test/test_marshal.py ++++ b/Lib/test/test_marshal.py +@@ -1,5 +1,6 @@ + from test import support + from test.support import os_helper ++from test.support.script_helper import assert_python_ok + import array + import io + import marshal +@@ -318,6 +319,31 @@ class BugsTestCase(unittest.TestCase): + for i in range(len(data)): + self.assertRaises(EOFError, marshal.loads, data[0: i]) + ++ def test_deterministic_sets(self): ++ # bpo-37596: To support reproducible builds, sets and frozensets need to ++ # have their elements serialized in a consistent order (even when they ++ # have been scrambled by hash randomization): ++ for kind in ("set", "frozenset"): ++ for elements in ( ++ "float('nan'), b'a', b'b', b'c', 'x', 'y', 'z'", ++ # Also test for bad interactions with backreferencing: ++ "('string', 1), ('string', 2), ('string', 3)", ++ ): ++ s = f"{kind}([{elements}])" ++ with self.subTest(s): ++ # First, make sure that our test case still has different ++ # orders under hash seeds 0 and 1. If this check fails, we ++ # need to update this test with different elements: ++ args = ["-c", f"print({s})"] ++ _, repr_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0") ++ _, repr_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1") ++ self.assertNotEqual(repr_0, repr_1) ++ # Then, perform the actual test: ++ args = ["-c", f"import marshal; print(marshal.dumps({s}))"] ++ _, dump_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0") ++ _, dump_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1") ++ self.assertEqual(dump_0, dump_1) ++ + LARGE_SIZE = 2**31 + pointer_size = 8 if sys.maxsize > 0xFFFFFFFF else 4 + +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst +@@ -0,0 +1,2 @@ ++Ensure that :class:`set` and :class:`frozenset` objects are always ++:mod:`marshalled ` reproducibly. +--- a/Python/marshal.c ++++ b/Python/marshal.c +@@ -502,9 +502,41 @@ w_complex_object(PyObject *v, char flag, + W_TYPE(TYPE_SET, p); + n = PySet_GET_SIZE(v); + W_SIZE(n, p); ++ // bpo-37596: To support reproducible builds, sets and frozensets need ++ // to have their elements serialized in a consistent order (even when ++ // they have been scrambled by hash randomization). To ensure this, we ++ // use an order equivalent to sorted(v, key=marshal.dumps): ++ PyObject *pairs = PyList_New(0); ++ if (pairs == NULL) { ++ p->error = WFERR_NOMEMORY; ++ return; ++ } + while (_PySet_NextEntry(v, &pos, &value, &hash)) { ++ PyObject *dump = PyMarshal_WriteObjectToString(value, p->version); ++ if (dump == NULL) { ++ p->error = WFERR_UNMARSHALLABLE; ++ goto anyset_done; ++ } ++ PyObject *pair = PyTuple_Pack(2, dump, value); ++ Py_DECREF(dump); ++ if (pair == NULL || PyList_Append(pairs, pair)) { ++ p->error = WFERR_NOMEMORY; ++ Py_XDECREF(pair); ++ goto anyset_done; ++ } ++ Py_DECREF(pair); ++ } ++ if (PyList_Sort(pairs)) { ++ p->error = WFERR_NOMEMORY; ++ goto anyset_done; ++ } ++ for (Py_ssize_t i = 0; i < n; i++) { ++ PyObject *pair = PyList_GET_ITEM(pairs, i); ++ value = PyTuple_GET_ITEM(pair, 1); + w_object(value, p); + } ++ anyset_done: ++ Py_DECREF(pairs); + } + else if (PyCode_Check(v)) { + PyCodeObject *co = (PyCodeObject *)v; diff --git a/python310.changes b/python310.changes index 1ff6218..cecc8f8 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,40 @@ +------------------------------------------------------------------- +Wed Jun 28 16:57:46 UTC 2023 - Matej Cepl + +- Update to 3.10.12: + - gh-103142: The version of OpenSSL used in Windows and + Mac installers has been upgraded to 1.1.1u to address + CVE-2023-2650, CVE-2023-0465, CVE-2023-0466, CVE-2023-0464, + as well as CVE-2023-0286, CVE-2022-4303, and CVE-2022-4303 + fixed previously in 1.1.1t (gh-101727). + - gh-102153: urllib.parse.urlsplit() now strips leading C0 + control and space characters following the specification for + URLs defined by WHATWG in response to CVE-2023-24329 + (bsc#1208471). + - gh-99889: Fixed a security in flaw in uu.decode() that could + allow for directory traversal based on the input if no + out_file was specified. + - gh-104049: Do not expose the local on-disk + location in directory indexes produced by + http.client.SimpleHTTPRequestHandler. + - gh-103935: trace.__main__ now uses io.open_code() for files + to be executed instead of raw open(). + - gh-102953: The extraction methods in tarfile, and + shutil.unpack_archive(), have a new filter argument that + allows limiting tar features than may be surprising or + dangerous, such as creating files outside the destination + directory. See Extraction filters for details (fixing + CVE-2007-4559, bsc#1203750). +- Remove upstreamed patches: + - CVE-2023-24329-blank-URL-bypass.patch + - CVE-2007-4559-filter-tarfile_extractall.patch + +------------------------------------------------------------------- +Tue Jun 20 21:39:58 UTC 2023 - Matej Cepl + +- Add bpo-37596-make-set-marshalling.patch making marshalling of + `set` and `frozenset` deterministic (bsc#1211765). + ------------------------------------------------------------------- Thu Apr 27 21:23:19 UTC 2023 - Matej Cepl diff --git a/python310.spec b/python310.spec index c9cc36a..4ff1b07 100644 --- a/python310.spec +++ b/python310.spec @@ -103,7 +103,7 @@ Obsoletes: python39%{?1:-%{1}} %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so %bcond_without profileopt Name: %{python_pkg_name}%{psuffix} -Version: 3.10.11 +Version: 3.10.12 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -166,13 +166,9 @@ Patch35: fix_configure_rst.patch # PATCH-FIX-UPSTREAM bpo-46811 gh#python/cpython#7da97f61816f mcepl@suse.com # NOTE: SUSE version of expat 2.4.4 is patched in SUSE for CVE-2022-25236 Patch36: support-expat-CVE-2022-25236-patched.patch -# PATCH-FIX-UPSTREAM CVE-2023-24329-blank-URL-bypass.patch bsc#1208471 mcepl@suse.com -# blocklist bypass via the urllib.parse component when supplying -# a URL that starts with blank characters -Patch37: CVE-2023-24329-blank-URL-bypass.patch -# PATCH-FIX-UPSTREAM CVE-2007-4559-filter-tarfile_extractall.patch bsc#1203750 mcepl@suse.com -# PEP 706 – Filter for tarfile.extractall -Patch38: CVE-2007-4559-filter-tarfile_extractall.patch +# PATCH-FIX-UPSTREAM bpo-37596-make-set-marshalling.patch bsc#1211765 mcepl@suse.com +# Make `set` and `frozenset` marshalling deterministic +Patch39: bpo-37596-make-set-marshalling.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -432,7 +428,6 @@ other applications. %prep %setup -q -n %{tarname} %patch02 -p1 - %patch06 -p1 %patch07 -p1 %patch08 -p1 @@ -445,8 +440,7 @@ other applications. %endif %patch35 -p1 %patch36 -p1 -%patch37 -p1 -%patch38 -p1 +%patch39 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac From aa5d3ba9db15745f430a45ccab8ae7d3d3a305dfd9127eaeb56f9086ba22dcaf Mon Sep 17 00:00:00 2001 From: Ruediger Oertel Date: Wed, 27 Sep 2023 16:43:48 +0000 Subject: [PATCH 08/23] Set link to python310.30576 via maintenance_release request --- CVE-2023-27043-email-parsing-errors.patch | 249 +++++++++++++++ Python-3.10.12.tar.xz.asc | 16 - ...on-3.10.12.tar.xz => Python-3.10.13.tar.xz | 0 Python-3.10.13.tar.xz.asc | 16 + Revert-gh105127-left-tests.patch | 283 ++++++++++++++++++ fix_configure_rst.patch | 16 +- gh-78214-marshal_stabilize_FLAG_REF.patch | 30 ++ python310.changes | 41 +++ python310.spec | 18 +- subprocess-raise-timeout.patch | 8 +- 10 files changed, 650 insertions(+), 27 deletions(-) create mode 100644 CVE-2023-27043-email-parsing-errors.patch delete mode 100644 Python-3.10.12.tar.xz.asc rename Python-3.10.12.tar.xz => Python-3.10.13.tar.xz (100%) create mode 100644 Python-3.10.13.tar.xz.asc create mode 100644 Revert-gh105127-left-tests.patch create mode 100644 gh-78214-marshal_stabilize_FLAG_REF.patch diff --git a/CVE-2023-27043-email-parsing-errors.patch b/CVE-2023-27043-email-parsing-errors.patch new file mode 100644 index 0000000..873514a --- /dev/null +++ b/CVE-2023-27043-email-parsing-errors.patch @@ -0,0 +1,249 @@ +--- + Doc/library/email.utils.rst | 26 +++ + Lib/email/utils.py | 63 +++++++ + Lib/test/test_email/test_email.py | 81 +++++++++- + Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst | 4 + 4 files changed, 164 insertions(+), 10 deletions(-) + +Index: Python-3.10.12/Doc/library/email.utils.rst +=================================================================== +--- Python-3.10.12.orig/Doc/library/email.utils.rst ++++ Python-3.10.12/Doc/library/email.utils.rst +@@ -67,6 +67,11 @@ of the new API. + *email address* parts. Returns a tuple of that information, unless the parse + fails, in which case a 2-tuple of ``('', '')`` is returned. + ++ .. versionchanged:: 3.12 ++ For security reasons, addresses that were ambiguous and could parse into ++ multiple different addresses now cause ``('', '')`` to be returned ++ instead of only one of the *potential* addresses. ++ + + .. function:: formataddr(pair, charset='utf-8') + +@@ -89,7 +94,7 @@ of the new API. + This method returns a list of 2-tuples of the form returned by ``parseaddr()``. + *fieldvalues* is a sequence of header field values as might be returned by + :meth:`Message.get_all `. Here's a simple +- example that gets all the recipients of a message:: ++ example that gets all the recipients of a message: + + from email.utils import getaddresses + +@@ -99,6 +104,25 @@ of the new API. + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + ++ When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')`` ++ is returned in its place. Other errors in parsing the list of ++ addresses such as a fieldvalue seemingly parsing into multiple ++ addresses may result in a list containing a single empty 2-tuple ++ ``[('', '')]`` being returned rather than returning potentially ++ invalid output. ++ ++ Example malformed input parsing: ++ ++ .. doctest:: ++ ++ >>> from email.utils import getaddresses ++ >>> getaddresses(['alice@example.com ', 'me@example.com']) ++ [('', '')] ++ ++ .. versionchanged:: 3.12 ++ The 2-tuple of ``('', '')`` in the returned values when parsing ++ fails were added as to address a security issue. ++ + + .. function:: parsedate(date) + +Index: Python-3.10.12/Lib/email/utils.py +=================================================================== +--- Python-3.10.12.orig/Lib/email/utils.py ++++ Python-3.10.12/Lib/email/utils.py +@@ -106,12 +106,54 @@ def formataddr(pair, charset='utf-8'): + return address + + ++def _pre_parse_validation(email_header_fields): ++ accepted_values = [] ++ for v in email_header_fields: ++ s = v.replace('\\(', '').replace('\\)', '') ++ if s.count('(') != s.count(')'): ++ 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 getaddresses(fieldvalues): +- """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" +- all = COMMASPACE.join(str(v) for v in fieldvalues) ++ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. ++ ++ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in ++ its place. ++ ++ If the resulting list of parsed address is not the same as the number of ++ fieldvalues in the input list a parsing error has occurred. A list ++ containing a single empty 2-tuple [('', '')] is returned in its place. ++ This is done to avoid invalid output. ++ """ ++ fieldvalues = [str(v) for v in fieldvalues] ++ fieldvalues = _pre_parse_validation(fieldvalues) ++ all = COMMASPACE.join(v for v in fieldvalues) + a = _AddressList(all) +- return a.addresslist ++ result = _post_parse_validation(a.addresslist) ++ ++ n = 0 ++ for v in fieldvalues: ++ n += v.count(',') + 1 ++ ++ if len(result) != n: ++ return [('', '')] ++ ++ return result + + + def _format_timetuple_and_zone(timetuple, zone): +@@ -212,9 +254,18 @@ def parseaddr(addr): + Return a tuple of realname and email address, unless the parse fails, in + which case return a 2-tuple of ('', ''). + """ +- addrs = _AddressList(addr).addresslist +- if not addrs: +- return '', '' ++ 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] + + +Index: Python-3.10.12/Lib/test/test_email/test_email.py +=================================================================== +--- Python-3.10.12.orig/Lib/test/test_email/test_email.py ++++ Python-3.10.12/Lib/test/test_email/test_email.py +@@ -3288,15 +3288,90 @@ Foo + [('Al Person', 'aperson@dom.ain'), + ('Bud Person', 'bperson@dom.ain')]) + ++ def test_getaddresses_parsing_errors(self): ++ """Test for parsing errors from CVE-2023-27043""" ++ eq = self.assertEqual ++ eq(utils.getaddresses(['alice@example.org(']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org)']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org<']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org>']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org@']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org,']), ++ [('', 'alice@example.org'), ('', 'bob@example.com')]) ++ eq(utils.getaddresses(['alice@example.org;']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org:']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org.']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org"']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org[']), ++ [('', '')]) ++ eq(utils.getaddresses(['alice@example.org]']), ++ [('', '')]) ++ ++ def test_parseaddr_parsing_errors(self): ++ """Test for parsing errors from CVE-2023-27043""" ++ eq = self.assertEqual ++ eq(utils.parseaddr(['alice@example.org(']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org)']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org<']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org>']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org@']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org,']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org;']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org:']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org.']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org"']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org[']), ++ ('', '')) ++ eq(utils.parseaddr(['alice@example.org]']), ++ ('', '')) ++ + def test_getaddresses_nasty(self): + eq = self.assertEqual + eq(utils.getaddresses(['foo: ;']), [('', '')]) +- eq(utils.getaddresses( +- ['[]*-- =~$']), +- [('', ''), ('', ''), ('', '*--')]) ++ eq(utils.getaddresses(['[]*-- =~$']), [('', '')]) + eq(utils.getaddresses( + ['foo: ;', '"Jason R. Mastaler" ']), + [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) ++ eq(utils.getaddresses( ++ [r'Pete(A nice \) chap) ']), ++ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]) ++ eq(utils.getaddresses( ++ ['(Empty list)(start)Undisclosed recipients :(nobody(I know))']), ++ [('', '')]) ++ eq(utils.getaddresses( ++ ['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']), ++ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]) ++ eq(utils.getaddresses( ++ ['John Doe ']), ++ [('John Doe (comment)', 'jdoe@machine.example')]) ++ eq(utils.getaddresses( ++ ['"Mary Smith: Personal Account" ']), ++ [('Mary Smith: Personal Account', 'smith@home.example')]) ++ eq(utils.getaddresses( ++ ['Undisclosed recipients:;']), ++ [('', '')]) ++ eq(utils.getaddresses( ++ [r', "Giant; \"Big\" Box" ']), ++ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]) + + def test_getaddresses_embedded_comment(self): + """Test proper handling of a nested comment""" +Index: Python-3.10.12/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst +=================================================================== +--- /dev/null ++++ Python-3.10.12/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst +@@ -0,0 +1,4 @@ ++CVE-2023-27043: Prevent :func:`email.utils.parseaddr` ++and :func:`email.utils.getaddresses` from returning the realname portion of an ++invalid RFC2822 email header in the email address portion of the 2-tuple ++returned after being parsed by :class:`email._parseaddr.AddressList`. diff --git a/Python-3.10.12.tar.xz.asc b/Python-3.10.12.tar.xz.asc deleted file mode 100644 index 05de3f2..0000000 --- a/Python-3.10.12.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmR/tqIACgkQ/+h0BBaL -2EfUfg/9FW0m6nngtGIKTJ+Gk4G13pQvnELgc3eq70t7Sn1g2kGxDpO+rs0WptYG -gGcHs6v4rE/3uQ0mf4QCvnnXffQEQ+bRDaj1ZBY/rJjCdgQeUNMElV0KbvADiTqS -+akmsXaK3KqLHJesZo65lZ4HSADWKosBU3zxE2/CRMMsz1aLMDLIoaQo+pqDcFl7 -ZfGMlmiJNyD2jZVYGdwCbhG0BymOTU02BxkH2Dkd9OGzj9A3zDPCO6RcDFtw4dkK -lngHQGijYaFV11FqIaApnUkz7aAPk//2KRLwpf5D5z8p8T8QsHAJyTmIm1gMQiQA -tMThI1tFGN6lF1QSrfwGooXs3AdeEY0VoL4CpQi8TVRLyi6HE4AU4hEQdPqVmpm1 -+U2K0MpYhkwtPp0E9E7y9v82fMSzUKvGgpTstnblKTfDmgGUGb47Ncj3XvxH8SZz -p93YK2xpfl4V2ltLio8ONmwP9lQhxk5L34dQR20cjbOoj622VofqGUV7Zr6UHVLD -pqYgnj3zgiTPmbCzgVxZOyaLD3ezsY8oAtfLgX6cjCfsTtV27TvQUD8Br0oKQYS/ -h5KJBdytokqPa+JWr59hvQpcLSbmCB2y7USminoS2yL1hpXidTvVDUALF3vorvZi -BS8prxUIFT2dgerUpWmMrKYih7pJNKdySGgI3zXtxIt5TE0TRag= -=9Zqh ------END PGP SIGNATURE----- diff --git a/Python-3.10.12.tar.xz b/Python-3.10.13.tar.xz similarity index 100% rename from Python-3.10.12.tar.xz rename to Python-3.10.13.tar.xz diff --git a/Python-3.10.13.tar.xz.asc b/Python-3.10.13.tar.xz.asc new file mode 100644 index 0000000..f5c2fa5 --- /dev/null +++ b/Python-3.10.13.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmTnVFIACgkQ/+h0BBaL +2EfANRAAng1gGOlrpC5O6dU3w37Ubbp4nqRxCgLO3jcOipafkq4PdQN0VrP8Gvbx +NGn+ELdAiBasVkgx3aZcDKCSHQT9PkMOGjNKKcv6NamMTnwp5IeVEY+uXSITxOoQ +VmLRKdnNO9PYkSfh0bB51p5+HtnbFabs6lY6NEwSg9J2ooNCChgaVEEjGJalLkKn +aCy+G9prd4KUXMonm8fqk3kj79qcEdj97CdOdEpE0EvH5oNNfKRwkD8j4nztE5mM +GeymPb9g9gY+u2Ph5bPkkNHSwgO/NVgCrAxcdJC4/tabiWBFZtmLEcEUvMmMVDdM +VAV40PHlIevzY2wm2PIZYKqqVpvyfjk3SRSfqJe7BiOMfifXpG1rYD+pB823U20c +Xzi9Z1p39406XbgOI9EE4dVWSI03kyEcnOth8pv/aYoIlSGnfH7ngST0OGoBMUZ4 +LwwOiBBgqoRJoYKERmbQ+r4hFJTkdFgFZHu9YkT5L+TwCRbVYGK6bptZGCveVn92 +j4t8gOpR+m27Sc3YWxEqp3dW3u3GWEPqtK2MDdbj3omZ00vb+Ukdq3CLLNoxP/KC +PvJsxe2coMYz0R9SbSrKujJeYeb/6P8P5QxzcqiVfWtZRc9SJ+aianaTgfcag08R +9XDTiL4thCq5uaKsYaLECaPDKMF/skQrqbbLShf5rvcvDJO783s= +=UP6h +-----END PGP SIGNATURE----- diff --git a/Revert-gh105127-left-tests.patch b/Revert-gh105127-left-tests.patch new file mode 100644 index 0000000..74a92c2 --- /dev/null +++ b/Revert-gh105127-left-tests.patch @@ -0,0 +1,283 @@ +From 4288c623d62cf90d8e4444facb3379fb06d01140 Mon Sep 17 00:00:00 2001 +From: "Gregory P. Smith" +Date: Thu, 20 Jul 2023 20:30:52 -0700 +Subject: [PATCH] [3.12] gh-106669: Revert "gh-102988: Detect email address + parsing errors ... (GH-105127)" (GH-106733) + +This reverts commit 18dfbd035775c15533d13a98e56b1d2bf5c65f00. +Adds a regression test from the issue. + +See https://github.com/python/cpython/issues/106669.. +(cherry picked from commit a31dea1feb61793e48fa9aa5014f358352205c1d) + +Co-authored-by: Gregory P. Smith +--- + Doc/library/email.utils.rst | 26 -- + Lib/email/utils.py | 63 ------ + Lib/test/test_email/test_email.py | 96 +--------- + Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst | 5 + 4 files changed, 31 insertions(+), 159 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst + +Index: Python-3.10.13/Doc/library/email.utils.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/email.utils.rst ++++ Python-3.10.13/Doc/library/email.utils.rst +@@ -67,11 +67,6 @@ of the new API. + *email address* parts. Returns a tuple of that information, unless the parse + fails, in which case a 2-tuple of ``('', '')`` is returned. + +- .. versionchanged:: 3.12 +- For security reasons, addresses that were ambiguous and could parse into +- multiple different addresses now cause ``('', '')`` to be returned +- instead of only one of the *potential* addresses. +- + + .. function:: formataddr(pair, charset='utf-8') + +@@ -94,7 +89,7 @@ of the new API. + This method returns a list of 2-tuples of the form returned by ``parseaddr()``. + *fieldvalues* is a sequence of header field values as might be returned by + :meth:`Message.get_all `. Here's a simple +- example that gets all the recipients of a message: ++ example that gets all the recipients of a message:: + + from email.utils import getaddresses + +@@ -104,25 +99,6 @@ of the new API. + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + +- When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')`` +- is returned in its place. Other errors in parsing the list of +- addresses such as a fieldvalue seemingly parsing into multiple +- addresses may result in a list containing a single empty 2-tuple +- ``[('', '')]`` being returned rather than returning potentially +- invalid output. +- +- Example malformed input parsing: +- +- .. doctest:: +- +- >>> from email.utils import getaddresses +- >>> getaddresses(['alice@example.com ', 'me@example.com']) +- [('', '')] +- +- .. versionchanged:: 3.12 +- The 2-tuple of ``('', '')`` in the returned values when parsing +- fails were added as to address a security issue. +- + + .. function:: parsedate(date) + +Index: Python-3.10.13/Lib/email/utils.py +=================================================================== +--- Python-3.10.13.orig/Lib/email/utils.py ++++ Python-3.10.13/Lib/email/utils.py +@@ -106,54 +106,12 @@ def formataddr(pair, charset='utf-8'): + return address + + +-def _pre_parse_validation(email_header_fields): +- accepted_values = [] +- for v in email_header_fields: +- s = v.replace('\\(', '').replace('\\)', '') +- if s.count('(') != s.count(')'): +- 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 getaddresses(fieldvalues): +- """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. +- +- When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in +- its place. +- +- If the resulting list of parsed address is not the same as the number of +- fieldvalues in the input list a parsing error has occurred. A list +- containing a single empty 2-tuple [('', '')] is returned in its place. +- This is done to avoid invalid output. +- """ +- fieldvalues = [str(v) for v in fieldvalues] +- fieldvalues = _pre_parse_validation(fieldvalues) +- all = COMMASPACE.join(v for v in fieldvalues) ++ """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" ++ all = COMMASPACE.join(str(v) for v in fieldvalues) + a = _AddressList(all) +- result = _post_parse_validation(a.addresslist) +- +- n = 0 +- for v in fieldvalues: +- n += v.count(',') + 1 +- +- if len(result) != n: +- return [('', '')] +- +- return result ++ return a.addresslist + + + def _format_timetuple_and_zone(timetuple, zone): +@@ -254,18 +212,9 @@ def parseaddr(addr): + Return a tuple of realname and email address, unless the parse fails, in + which case return a 2-tuple of ('', ''). + """ +- 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 ('', '') +- ++ addrs = _AddressList(addr).addresslist ++ if not addrs: ++ return '', '' + return addrs[0] + + +Index: Python-3.10.13/Lib/test/test_email/test_email.py +=================================================================== +--- Python-3.10.13.orig/Lib/test/test_email/test_email.py ++++ Python-3.10.13/Lib/test/test_email/test_email.py +@@ -3288,90 +3288,32 @@ Foo + [('Al Person', 'aperson@dom.ain'), + ('Bud Person', 'bperson@dom.ain')]) + +- def test_getaddresses_parsing_errors(self): +- """Test for parsing errors from CVE-2023-27043""" +- eq = self.assertEqual +- eq(utils.getaddresses(['alice@example.org(']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org)']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org<']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org>']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org@']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org,']), +- [('', 'alice@example.org'), ('', 'bob@example.com')]) +- eq(utils.getaddresses(['alice@example.org;']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org:']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org.']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org"']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org[']), +- [('', '')]) +- eq(utils.getaddresses(['alice@example.org]']), +- [('', '')]) +- +- def test_parseaddr_parsing_errors(self): +- """Test for parsing errors from CVE-2023-27043""" +- eq = self.assertEqual +- eq(utils.parseaddr(['alice@example.org(']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org)']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org<']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org>']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org@']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org,']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org;']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org:']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org.']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org"']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org[']), +- ('', '')) +- eq(utils.parseaddr(['alice@example.org]']), +- ('', '')) ++ def test_getaddresses_comma_in_name(self): ++ """GH-106669 regression test.""" ++ self.assertEqual( ++ utils.getaddresses( ++ [ ++ '"Bud, Person" ', ++ 'aperson@dom.ain (Al Person)', ++ '"Mariusz Felisiak" ', ++ ] ++ ), ++ [ ++ ('Bud, Person', 'bperson@dom.ain'), ++ ('Al Person', 'aperson@dom.ain'), ++ ('Mariusz Felisiak', 'to@example.com'), ++ ], ++ ) + + def test_getaddresses_nasty(self): + eq = self.assertEqual + eq(utils.getaddresses(['foo: ;']), [('', '')]) +- eq(utils.getaddresses(['[]*-- =~$']), [('', '')]) ++ eq(utils.getaddresses( ++ ['[]*-- =~$']), ++ [('', ''), ('', ''), ('', '*--')]) + eq(utils.getaddresses( + ['foo: ;', '"Jason R. Mastaler" ']), + [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) +- eq(utils.getaddresses( +- [r'Pete(A nice \) chap) ']), +- [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]) +- eq(utils.getaddresses( +- ['(Empty list)(start)Undisclosed recipients :(nobody(I know))']), +- [('', '')]) +- eq(utils.getaddresses( +- ['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']), +- [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]) +- eq(utils.getaddresses( +- ['John Doe ']), +- [('John Doe (comment)', 'jdoe@machine.example')]) +- eq(utils.getaddresses( +- ['"Mary Smith: Personal Account" ']), +- [('Mary Smith: Personal Account', 'smith@home.example')]) +- eq(utils.getaddresses( +- ['Undisclosed recipients:;']), +- [('', '')]) +- eq(utils.getaddresses( +- [r', "Giant; \"Big\" Box" ']), +- [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]) + + def test_getaddresses_embedded_comment(self): + """Test proper handling of a nested comment""" +Index: Python-3.10.13/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst +=================================================================== +--- Python-3.10.13.orig/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst ++++ Python-3.10.13/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst +@@ -1,3 +1,8 @@ ++Reverted the :mod:`email.utils` security improvement change released in ++3.12beta4 that unintentionally caused :mod:`email.utils.getaddresses` to fail ++to parse email addresses with a comma in the quoted name field. ++See :gh:`106669`. ++ + CVE-2023-27043: Prevent :func:`email.utils.parseaddr` + and :func:`email.utils.getaddresses` from returning the realname portion of an + invalid RFC2822 email header in the email address portion of the 2-tuple diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index 7e81a09..9a698cc 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -3,8 +3,10 @@ Misc/NEWS | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) ---- a/Doc/using/configure.rst -+++ b/Doc/using/configure.rst +Index: Python-3.10.12/Doc/using/configure.rst +=================================================================== +--- Python-3.10.12.orig/Doc/using/configure.rst ++++ Python-3.10.12/Doc/using/configure.rst @@ -42,7 +42,6 @@ General Options See :data:`sys.int_info.bits_per_digit `. @@ -13,7 +15,7 @@ .. cmdoption:: --with-cxx-main=COMPILER Compile the Python ``main()`` function and link Python executable with C++ -@@ -457,13 +456,11 @@ macOS Options +@@ -473,13 +472,11 @@ macOS Options See ``Mac/README.rst``. @@ -27,9 +29,11 @@ .. cmdoption:: --enable-framework=INSTALLDIR Create a Python.framework rather than a traditional Unix install. Optional ---- a/Misc/NEWS -+++ b/Misc/NEWS -@@ -3422,7 +3422,7 @@ C API +Index: Python-3.10.12/Misc/NEWS +=================================================================== +--- Python-3.10.12.orig/Misc/NEWS ++++ Python-3.10.12/Misc/NEWS +@@ -3618,7 +3618,7 @@ C API ----- - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name diff --git a/gh-78214-marshal_stabilize_FLAG_REF.patch b/gh-78214-marshal_stabilize_FLAG_REF.patch new file mode 100644 index 0000000..3124d2a --- /dev/null +++ b/gh-78214-marshal_stabilize_FLAG_REF.patch @@ -0,0 +1,30 @@ +From 6c8ea7c1dacd42f3ba00440231ec0e6b1a38300d Mon Sep 17 00:00:00 2001 +From: Inada Naoki +Date: Sat, 14 Jul 2018 00:46:11 +0900 +Subject: [PATCH] Use FLAG_REF always for interned strings + +--- + Python/marshal.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +Index: Python-3.10.12/Python/marshal.c +=================================================================== +--- Python-3.10.12.orig/Python/marshal.c ++++ Python-3.10.12/Python/marshal.c +@@ -298,9 +298,14 @@ w_ref(PyObject *v, char *flag, WFILE *p) + if (p->version < 3 || p->hashtable == NULL) + return 0; /* not writing object references */ + +- /* if it has only one reference, it definitely isn't shared */ +- if (Py_REFCNT(v) == 1) ++ /* If it has only one reference, it definitely isn't shared. ++ * But we use TYPE_REF always for interned string, to PYC file stable ++ * as possible. ++ */ ++ if (Py_REFCNT(v) == 1 && ++ !(PyUnicode_CheckExact(v) && PyUnicode_CHECK_INTERNED(v))) { + return 0; ++ } + + entry = _Py_hashtable_get_entry(p->hashtable, v); + if (entry != NULL) { diff --git a/python310.changes b/python310.changes index cecc8f8..e48a6a9 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,44 @@ +------------------------------------------------------------------- +Mon Sep 4 13:18:29 UTC 2023 - Daniel Garcia + +- Update to 3.10.13 (bsc#1214692): + - gh-108310: Fixed an issue where instances of ssl.SSLSocket were + vulnerable to a bypass of the TLS handshake and included + protections (like certificate verification) and treating sent + unencrypted data as if it were post-handshake TLS encrypted data. + Security issue reported as CVE-2023-40217 by Aapo Oksman. Patch by + Gregory P. Smith. + - gh-107845: tarfile.data_filter() now takes the location of + symlinks into account when determining their target, so it will no + longer reject some valid tarballs with + LinkOutsideDestinationError. + - gh-107565: Update multissltests and GitHub CI workflows to use + OpenSSL 1.1.1v, 3.0.10, and 3.1.2. + - gh-99612: Fix PyUnicode_DecodeUTF8Stateful() for ASCII-only data: + *consumed was not set. + +------------------------------------------------------------------- +Thu Aug 3 14:13:30 UTC 2023 - Matej Cepl + +- Add Revert-gh105127-left-tests.patch (gh#python/cpython!106941) + partially reverting CVE-2023-27043-email-parsing-errors.patch, + because of the regression in gh#python/cpython#106669. + +------------------------------------------------------------------- +Wed Jul 19 11:15:39 UTC 2023 - Matej Cepl + +- Add gh-78214-marshal_stabilize_FLAG_REF.patch to marshal.c for + stabilizing FLAG_REF usage (required for reproduceability; + bsc#1213463). + +------------------------------------------------------------------- +Tue Jul 11 07:35:18 UTC 2023 - Matej Cepl + +- (bsc#1210638, CVE-2023-27043) Add + CVE-2023-27043-email-parsing-errors.patch, which detects email + address parsing errors and returns empty tuple to indicate the + parsing error (old API). + ------------------------------------------------------------------- Wed Jun 28 16:57:46 UTC 2023 - Matej Cepl diff --git a/python310.spec b/python310.spec index 4ff1b07..ecf83e5 100644 --- a/python310.spec +++ b/python310.spec @@ -103,7 +103,7 @@ Obsoletes: python39%{?1:-%{1}} %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so %bcond_without profileopt Name: %{python_pkg_name}%{psuffix} -Version: 3.10.12 +Version: 3.10.13 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -168,7 +168,18 @@ Patch35: fix_configure_rst.patch Patch36: support-expat-CVE-2022-25236-patched.patch # PATCH-FIX-UPSTREAM bpo-37596-make-set-marshalling.patch bsc#1211765 mcepl@suse.com # Make `set` and `frozenset` marshalling deterministic -Patch39: bpo-37596-make-set-marshalling.patch +Patch38: bpo-37596-make-set-marshalling.patch +# PATCH-FIX-UPSTREAM gh-78214-marshal_stabilize_FLAG_REF.patch bsc#1213463 mcepl@suse.com +# marshal: Stabilize FLAG_REF usage +Patch39: gh-78214-marshal_stabilize_FLAG_REF.patch +# PATCH-FIX-UPSTREAM CVE-2023-27043-email-parsing-errors.patch bsc#1210638 mcepl@suse.com +# Detect email address parsing errors and return empty tuple to +# indicate the parsing error (old API), from gh#python/cpython!105127 +# Patch carries a REGRESSION (gh#python/cpython#106669), so it has been also partially REVERTED +Patch40: CVE-2023-27043-email-parsing-errors.patch +# PATCH-FIX-UPSTREAM Revert-gh105127-left-tests.patch bsc#1210638 mcepl@suse.com +# Partially revert previous patch +Patch41: Revert-gh105127-left-tests.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -440,7 +451,10 @@ other applications. %endif %patch35 -p1 %patch36 -p1 +%patch38 -p1 %patch39 -p1 +%patch40 -p1 +%patch41 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac diff --git a/subprocess-raise-timeout.patch b/subprocess-raise-timeout.patch index ef80b3b..b0bcc99 100644 --- a/subprocess-raise-timeout.patch +++ b/subprocess-raise-timeout.patch @@ -2,9 +2,11 @@ Lib/test/test_subprocess.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) ---- a/Lib/test/test_subprocess.py -+++ b/Lib/test/test_subprocess.py -@@ -267,7 +267,8 @@ class ProcessTestCase(BaseTestCase): +Index: Python-3.10.12/Lib/test/test_subprocess.py +=================================================================== +--- Python-3.10.12.orig/Lib/test/test_subprocess.py ++++ Python-3.10.12/Lib/test/test_subprocess.py +@@ -268,7 +268,8 @@ class ProcessTestCase(BaseTestCase): "time.sleep(3600)"], # Some heavily loaded buildbots (sparc Debian 3.x) require # this much time to start and print. From 633ff95a90d5b28ecc1fa4ac194d9940d84134441335151158980345bf61eab8 Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Mon, 16 Oct 2023 10:48:11 +0000 Subject: [PATCH 09/23] Set link to python310.30915 via maintenance_release request --- fix-sphinx-72.patch | 3117 +++++++++++++++++++++++++++++++++++++++++++ python310.changes | 6 +- python310.spec | 15 + 3 files changed, 3136 insertions(+), 2 deletions(-) create mode 100644 fix-sphinx-72.patch diff --git a/fix-sphinx-72.patch b/fix-sphinx-72.patch new file mode 100644 index 0000000..22d2366 --- /dev/null +++ b/fix-sphinx-72.patch @@ -0,0 +1,3117 @@ +Index: Python-3.10.13/Doc/conf.py +=================================================================== +--- Python-3.10.13.orig/Doc/conf.py ++++ Python-3.10.13/Doc/conf.py +@@ -61,6 +61,11 @@ smartquotes_excludes = { + # Avoid a warning with Sphinx >= 2.0 + master_doc = 'contents' + ++# Allow translation of index directives ++gettext_additional_targets = [ ++ 'index', ++] ++ + # Options for HTML output + # ----------------------- + +Index: Python-3.10.13/Doc/tools/extensions/pyspecific.py +=================================================================== +--- Python-3.10.13.orig/Doc/tools/extensions/pyspecific.py ++++ Python-3.10.13/Doc/tools/extensions/pyspecific.py +@@ -623,6 +623,30 @@ def process_audit_events(app, doctree, f + node.replace_self(table) + + ++def patch_pairindextypes(app) -> None: ++ if app.builder.name != 'gettext': ++ return ++ ++ # allow translating deprecated index entries ++ try: ++ from sphinx.domains.python import pairindextypes ++ except ImportError: ++ pass ++ else: ++ # Sphinx checks if a 'pair' type entry on an index directive is one of ++ # the Sphinx-translated pairindextypes values. As we intend to move ++ # away from this, we need Sphinx to believe that these values don't ++ # exist, by deleting them when using the gettext builder. ++ ++ pairindextypes.pop('module', None) ++ pairindextypes.pop('keyword', None) ++ pairindextypes.pop('operator', None) ++ pairindextypes.pop('object', None) ++ pairindextypes.pop('exception', None) ++ pairindextypes.pop('statement', None) ++ pairindextypes.pop('builtin', None) ++ ++ + def setup(app): + app.add_role('issue', issue_role) + app.add_role('gh', gh_issue_role) +@@ -645,6 +669,7 @@ def setup(app): + app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod) + app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod) + app.add_directive('miscnews', MiscNews) ++ app.connect('builder-inited', patch_pairindextypes) + app.connect('doctree-resolved', process_audit_events) + app.connect('env-merge-info', audit_events_merge) + app.connect('env-purge-doc', audit_events_purge) +Index: Python-3.10.13/Doc/c-api/exceptions.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/exceptions.rst ++++ Python-3.10.13/Doc/c-api/exceptions.rst +@@ -503,7 +503,7 @@ Signal Handling + .. c:function:: int PyErr_CheckSignals() + + .. index:: +- module: signal ++ pair: module; signal + single: SIGINT + single: KeyboardInterrupt (built-in exception) + +@@ -534,7 +534,7 @@ Signal Handling + .. c:function:: void PyErr_SetInterrupt() + + .. index:: +- module: signal ++ pair: module; signal + single: SIGINT + single: KeyboardInterrupt (built-in exception) + +@@ -549,7 +549,7 @@ Signal Handling + .. c:function:: int PyErr_SetInterruptEx(int signum) + + .. index:: +- module: signal ++ pair: module; signal + single: KeyboardInterrupt (built-in exception) + + Simulate the effect of a signal arriving. The next time +Index: Python-3.10.13/Doc/c-api/init.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/init.rst ++++ Python-3.10.13/Doc/c-api/init.rst +@@ -233,9 +233,9 @@ Initializing and finalizing the interpre + single: PyEval_InitThreads() + single: modules (in module sys) + single: path (in module sys) +- module: builtins +- module: __main__ +- module: sys ++ pair: module; builtins ++ pair: module; __main__ ++ pair: module; sys + triple: module; search; path + single: PySys_SetArgv() + single: PySys_SetArgvEx() +@@ -895,7 +895,7 @@ code, or when embedding the Python inter + + .. deprecated-removed:: 3.9 3.11 + +- .. index:: module: _thread ++ .. index:: pair: module; _thread + + + .. c:function:: int PyEval_ThreadsInitialized() +@@ -1315,9 +1315,9 @@ function. You can create and destroy the + .. c:function:: PyThreadState* Py_NewInterpreter() + + .. index:: +- module: builtins +- module: __main__ +- module: sys ++ pair: module; builtins ++ pair: module; __main__ ++ pair: module; sys + single: stdout (in module sys) + single: stderr (in module sys) + single: stdin (in module sys) +Index: Python-3.10.13/Doc/c-api/intro.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/intro.rst ++++ Python-3.10.13/Doc/c-api/intro.rst +@@ -226,7 +226,7 @@ complete listing. + Objects, Types and Reference Counts + =================================== + +-.. index:: object: type ++.. index:: pair: object; type + + Most Python/C API functions have one or more arguments as well as a return value + of type :c:expr:`PyObject*`. This type is a pointer to an opaque data type +@@ -677,9 +677,9 @@ interpreter can only be used after the i + + .. index:: + single: Py_Initialize() +- module: builtins +- module: __main__ +- module: sys ++ pair: module; builtins ++ pair: module; __main__ ++ pair: module; sys + triple: module; search; path + single: path (in module sys) + +Index: Python-3.10.13/Doc/library/_thread.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/_thread.rst ++++ Python-3.10.13/Doc/library/_thread.rst +@@ -204,7 +204,7 @@ In addition to these methods, lock objec + + **Caveats:** + +- .. index:: module: signal ++ .. index:: pair: module; signal + + * Threads interact strangely with interrupts: the :exc:`KeyboardInterrupt` + exception will be received by an arbitrary thread. (When the :mod:`signal` +Index: Python-3.10.13/Doc/library/cmath.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/cmath.rst ++++ Python-3.10.13/Doc/library/cmath.rst +@@ -301,7 +301,7 @@ Constants + .. versionadded:: 3.6 + + +-.. index:: module: math ++.. index:: pair: module; math + + Note that the selection of functions is similar, but not identical, to that in + module :mod:`math`. The reason for having two modules is that some users aren't +Index: Python-3.10.13/Doc/library/copy.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/copy.rst ++++ Python-3.10.13/Doc/library/copy.rst +@@ -68,7 +68,7 @@ Shallow copies of dictionaries can be ma + of lists by assigning a slice of the entire list, for example, + ``copied_list = original_list[:]``. + +-.. index:: module: pickle ++.. index:: pair: module; pickle + + Classes can use the same interfaces to control copying that they use to control + pickling. See the description of module :mod:`pickle` for information on these +Index: Python-3.10.13/Doc/library/copyreg.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/copyreg.rst ++++ Python-3.10.13/Doc/library/copyreg.rst +@@ -7,8 +7,8 @@ + **Source code:** :source:`Lib/copyreg.py` + + .. index:: +- module: pickle +- module: copy ++ pair: module; pickle ++ pair: module; copy + + -------------- + +Index: Python-3.10.13/Doc/library/exceptions.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/exceptions.rst ++++ Python-3.10.13/Doc/library/exceptions.rst +@@ -4,8 +4,8 @@ Built-in Exceptions + =================== + + .. index:: +- statement: try +- statement: except ++ pair: statement; try ++ pair: statement; except + + In Python, all exceptions must be instances of a class that derives from + :class:`BaseException`. In a :keyword:`try` statement with an :keyword:`except` +@@ -14,7 +14,7 @@ classes derived from that class (but not + derived). Two exception classes that are not related via subclassing are never + equivalent, even if they have the same name. + +-.. index:: statement: raise ++.. index:: pair: statement; raise + + The built-in exceptions listed below can be generated by the interpreter or + built-in functions. Except where mentioned, they have an "associated value" +@@ -160,7 +160,7 @@ The following exceptions are the excepti + + .. exception:: AssertionError + +- .. index:: statement: assert ++ .. index:: pair: statement; assert + + Raised when an :keyword:`assert` statement fails. + +@@ -303,7 +303,7 @@ The following exceptions are the excepti + .. exception:: OSError([arg]) + OSError(errno, strerror[, filename[, winerror[, filename2]]]) + +- .. index:: module: errno ++ .. index:: pair: module; errno + + This exception is raised when a system function returns a system-related + error, including I/O failures such as "file not found" or "disk full" +Index: Python-3.10.13/Doc/library/fnmatch.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/fnmatch.rst ++++ Python-3.10.13/Doc/library/fnmatch.rst +@@ -8,7 +8,7 @@ + + .. index:: single: filenames; wildcard expansion + +-.. index:: module: re ++.. index:: pair: module; re + + -------------- + +@@ -38,7 +38,7 @@ special characters used in shell-style w + For a literal match, wrap the meta-characters in brackets. + For example, ``'[?]'`` matches the character ``'?'``. + +-.. index:: module: glob ++.. index:: pair: module; glob + + Note that the filename separator (``'/'`` on Unix) is *not* special to this + module. See module :mod:`glob` for pathname expansion (:mod:`glob` uses +Index: Python-3.10.13/Doc/library/functions.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/functions.rst ++++ Python-3.10.13/Doc/library/functions.rst +@@ -548,7 +548,7 @@ are always available. They are listed h + Raises an :ref:`auditing event ` ``exec`` with the code object + as the argument. Code compilation events may also be raised. + +-.. index:: builtin: exec ++.. index:: pair: built-in function; exec + + .. function:: exec(object[, globals[, locals]]) + +@@ -1314,7 +1314,7 @@ are always available. They are listed h + single: I/O control; buffering + single: binary mode + single: text mode +- module: sys ++ pair: module; sys + + See also the file handling modules, such as :mod:`fileinput`, :mod:`io` + (where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`, +@@ -1799,7 +1799,7 @@ are always available. They are listed h + .. class:: type(object) + type(name, bases, dict, **kwds) + +- .. index:: object: type ++ .. index:: pair: object; type + + With one argument, return the type of an *object*. The return value is a + type object and generally the same object as returned by +@@ -1954,8 +1954,8 @@ are always available. They are listed h + .. function:: __import__(name, globals=None, locals=None, fromlist=(), level=0) + + .. index:: +- statement: import +- module: imp ++ pair: statement; import ++ pair: module; builtins + + .. note:: + +Index: Python-3.10.13/Doc/library/http.client.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/http.client.rst ++++ Python-3.10.13/Doc/library/http.client.rst +@@ -10,7 +10,7 @@ + pair: HTTP; protocol + single: HTTP; http.client (standard module) + +-.. index:: module: urllib.request ++.. index:: pair: module; urllib.request + + -------------- + +Index: Python-3.10.13/Doc/library/internet.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/internet.rst ++++ Python-3.10.13/Doc/library/internet.rst +@@ -9,7 +9,7 @@ Internet Protocols and Support + single: Internet + single: World Wide Web + +-.. index:: module: socket ++.. index:: pair: module; socket + + The modules described in this chapter implement internet protocols and support + for related technology. They are all implemented in Python. Most of these +Index: Python-3.10.13/Doc/library/locale.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/locale.rst ++++ Python-3.10.13/Doc/library/locale.rst +@@ -16,7 +16,7 @@ functionality. The POSIX locale mechanis + certain cultural issues in an application, without requiring the programmer to + know all the specifics of each country where the software is executed. + +-.. index:: module: _locale ++.. index:: pair: module; _locale + + The :mod:`locale` module is implemented on top of the :mod:`_locale` module, + which in turn uses an ANSI C locale implementation if available. +@@ -452,7 +452,7 @@ The :mod:`locale` module defines the fol + + .. data:: LC_CTYPE + +- .. index:: module: string ++ .. index:: pair: module; string + + Locale category for the character type functions. Depending on the settings of + this category, the functions of module :mod:`string` dealing with case change +Index: Python-3.10.13/Doc/library/marshal.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/marshal.rst ++++ Python-3.10.13/Doc/library/marshal.rst +@@ -15,8 +15,8 @@ undocumented on purpose; it may change b + rarely does). [#]_ + + .. index:: +- module: pickle +- module: shelve ++ pair: module; pickle ++ pair: module; shelve + + This is not a general "persistence" module. For general persistence and + transfer of Python objects through RPC calls, see the modules :mod:`pickle` and +Index: Python-3.10.13/Doc/library/os.path.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/os.path.rst ++++ Python-3.10.13/Doc/library/os.path.rst +@@ -159,7 +159,7 @@ the :mod:`glob` module.) + On Unix and Windows, return the argument with an initial component of ``~`` or + ``~user`` replaced by that *user*'s home directory. + +- .. index:: module: pwd ++ .. index:: pair: module; pwd + + On Unix, an initial ``~`` is replaced by the environment variable :envvar:`HOME` + if it is set; otherwise the current user's home directory is looked up in the +Index: Python-3.10.13/Doc/library/os.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/os.rst ++++ Python-3.10.13/Doc/library/os.rst +@@ -1136,7 +1136,7 @@ or `the MSDN (greater) +- operator: >= +- operator: != +- operator: is +- operator: is not ++ pair: operator; == ++ pair: operator; < (less) ++ pair: operator; <= ++ pair: operator; > (greater) ++ pair: operator; >= ++ pair: operator; != ++ pair: operator; is ++ pair: operator; is not + + There are eight comparison operations in Python. They all have the same + priority (which is higher than that of the Boolean operations). Comparisons can +@@ -192,8 +192,8 @@ customized; also they can be applied to + exception. + + .. index:: +- operator: in +- operator: not in ++ pair: operator; in ++ pair: operator; not in + + Two more operations with the same syntactic priority, :keyword:`in` and + :keyword:`not in`, are supported by types that are :term:`iterable` or +@@ -205,11 +205,11 @@ Numeric Types --- :class:`int`, :class:` + ================================================================ + + .. index:: +- object: numeric +- object: Boolean +- object: integer +- object: floating point +- object: complex number ++ pair: object; numeric ++ pair: object; Boolean ++ pair: object; integer ++ pair: object; floating point ++ pair: object; complex number + pair: C; language + + There are three distinct numeric types: :dfn:`integers`, :dfn:`floating +@@ -244,20 +244,20 @@ and imaginary parts. + + .. index:: + single: arithmetic +- builtin: int +- builtin: float +- builtin: complex ++ pair: built-in function; int ++ pair: built-in function; float ++ pair: built-in function; complex + single: operator; + (plus) + single: + (plus); unary operator + single: + (plus); binary operator + single: operator; - (minus) + single: - (minus); unary operator + single: - (minus); binary operator +- operator: * (asterisk) +- operator: / (slash) +- operator: // +- operator: % (percent) +- operator: ** ++ pair: operator; * (asterisk) ++ pair: operator; / (slash) ++ pair: operator; // ++ pair: operator; % (percent) ++ pair: operator; ** + + Python fully supports mixed arithmetic: when a binary arithmetic operator has + operands of different numeric types, the operand with the "narrower" type is +@@ -330,7 +330,7 @@ Notes: + + (3) + .. index:: +- module: math ++ pair: module; math + single: floor() (in module math) + single: ceil() (in module math) + single: trunc() (in module math) +@@ -392,12 +392,12 @@ Bitwise Operations on Integer Types + pair: bitwise; operations + pair: shifting; operations + pair: masking; operations +- operator: | (vertical bar) +- operator: ^ (caret) +- operator: & (ampersand) +- operator: << +- operator: >> +- operator: ~ (tilde) ++ pair: operator; | (vertical bar) ++ pair: operator; ^ (caret) ++ pair: operator; & (ampersand) ++ pair: operator; << ++ pair: operator; >> ++ pair: operator; ~ (tilde) + + Bitwise operations only make sense for integers. The result of bitwise + operations is calculated as though carried out in two's complement with an +@@ -848,7 +848,7 @@ described in dedicated sections. + Common Sequence Operations + -------------------------- + +-.. index:: object: sequence ++.. index:: pair: object; sequence + + The operations in the following table are supported by most sequence types, + both mutable and immutable. The :class:`collections.abc.Sequence` ABC is +@@ -866,15 +866,15 @@ operations have the same priority as the + + .. index:: + triple: operations on; sequence; types +- builtin: len +- builtin: min +- builtin: max ++ pair: built-in function; len ++ pair: built-in function; min ++ pair: built-in function; max + pair: concatenation; operation + pair: repetition; operation + pair: subscript; operation + pair: slice; operation +- operator: in +- operator: not in ++ pair: operator; in ++ pair: operator; not in + single: count() (sequence method) + single: index() (sequence method) + +@@ -1033,8 +1033,8 @@ Immutable Sequence Types + + .. index:: + triple: immutable; sequence; types +- object: tuple +- builtin: hash ++ pair: object; tuple ++ pair: built-in function; hash + + The only operation that immutable sequence types generally implement that is + not also implemented by mutable sequence types is support for the :func:`hash` +@@ -1055,8 +1055,8 @@ Mutable Sequence Types + + .. index:: + triple: mutable; sequence; types +- object: list +- object: bytearray ++ pair: object; list ++ pair: object; bytearray + + The operations in the following table are defined on mutable sequence types. + The :class:`collections.abc.MutableSequence` ABC is provided to make it +@@ -1073,7 +1073,7 @@ accepts integers that meet the value res + triple: operations on; list; type + pair: subscript; assignment + pair: slice; assignment +- statement: del ++ pair: statement; del + single: append() (sequence method) + single: clear() (sequence method) + single: copy() (sequence method) +@@ -1173,7 +1173,7 @@ Notes: + Lists + ----- + +-.. index:: object: list ++.. index:: pair: object; list + + Lists are mutable sequences, typically used to store collections of + homogeneous items (where the precise degree of similarity will vary by +@@ -1252,7 +1252,7 @@ application). + Tuples + ------ + +-.. index:: object: tuple ++.. index:: pair: object; tuple + + Tuples are immutable sequences, typically used to store collections of + heterogeneous data (such as the 2-tuples produced by the :func:`enumerate` +@@ -1296,7 +1296,7 @@ choice than a simple tuple object. + Ranges + ------ + +-.. index:: object: range ++.. index:: pair: object; range + + The :class:`range` type represents an immutable sequence of numbers and is + commonly used for looping a specific number of times in :keyword:`for` +@@ -1421,7 +1421,7 @@ objects that compare equal might have di + .. index:: + single: string; text sequence type + single: str (built-in class); (see also string) +- object: string ++ pair: object; string + + .. _textseq: + +@@ -1455,7 +1455,7 @@ Since there is no separate "character" t + strings of length 1. That is, for a non-empty string *s*, ``s[0] == s[0:1]``. + + .. index:: +- object: io.StringIO ++ pair: object; io.StringIO + + There is also no mutable string type, but :meth:`str.join` or + :class:`io.StringIO` can be used to efficiently construct strings from +@@ -1521,7 +1521,7 @@ String Methods + -------------- + + .. index:: +- module: re ++ pair: module; re + + Strings implement all of the :ref:`common ` sequence + operations, along with the additional methods described below. +@@ -2422,10 +2422,10 @@ Binary Sequence Types --- :class:`bytes` + ================================================================================= + + .. index:: +- object: bytes +- object: bytearray +- object: memoryview +- module: array ++ pair: object; bytes ++ pair: object; bytearray ++ pair: object; memoryview ++ pair: module; array + + The core built-in types for manipulating binary data are :class:`bytes` and + :class:`bytearray`. They are supported by :class:`memoryview` which uses +@@ -2440,7 +2440,7 @@ The :mod:`array` module supports efficie + Bytes Objects + ------------- + +-.. index:: object: bytes ++.. index:: pair: object; bytes + + Bytes objects are immutable sequences of single bytes. Since many major + binary protocols are based on the ASCII text encoding, bytes objects offer +@@ -2547,7 +2547,7 @@ always convert a bytes object into a lis + Bytearray Objects + ----------------- + +-.. index:: object: bytearray ++.. index:: pair: object; bytearray + + :class:`bytearray` objects are a mutable counterpart to :class:`bytes` + objects. +@@ -4123,7 +4123,7 @@ copying. + Set Types --- :class:`set`, :class:`frozenset` + ============================================== + +-.. index:: object: set ++.. index:: pair: object; set + + A :dfn:`set` object is an unordered collection of distinct :term:`hashable` objects. + Common uses include membership testing, removing duplicates from a sequence, and +@@ -4325,12 +4325,12 @@ Mapping Types --- :class:`dict` + =============================== + + .. index:: +- object: mapping +- object: dictionary ++ pair: object; mapping ++ pair: object; dictionary + triple: operations on; mapping; types + triple: operations on; dictionary; type +- statement: del +- builtin: len ++ pair: statement; del ++ pair: built-in function; len + + A :term:`mapping` object maps :term:`hashable` values to arbitrary objects. + Mappings are mutable objects. There is currently only one standard mapping +@@ -4794,7 +4794,7 @@ Generic Alias Type + ------------------ + + .. index:: +- object: GenericAlias ++ pair: object; GenericAlias + pair: Generic; Alias + + ``GenericAlias`` objects are generally created by +@@ -5040,7 +5040,7 @@ Union Type + ---------- + + .. index:: +- object: Union ++ pair: object; Union + pair: union; type + + A union object holds the value of the ``|`` (bitwise or) operation on +@@ -5197,7 +5197,7 @@ See :ref:`function` for more information + Methods + ------- + +-.. index:: object: method ++.. index:: pair: object; method + + Methods are functions that are called using the attribute notation. There are + two flavors: built-in methods (such as :meth:`append` on lists) and class +@@ -5244,7 +5244,7 @@ Code Objects + ------------ + + .. index:: +- builtin: compile ++ pair: built-in function; compile + single: __code__ (function object attribute) + + Code objects are used by the implementation to represent "pseudo-compiled" +@@ -5258,8 +5258,8 @@ Accessing ``__code__`` raises an :ref:`a + ``object.__getattr__`` with arguments ``obj`` and ``"__code__"``. + + .. index:: +- builtin: exec +- builtin: eval ++ pair: built-in function; exec ++ pair: built-in function; eval + + A code object can be executed or evaluated by passing it (instead of a source + string) to the :func:`exec` or :func:`eval` built-in functions. +@@ -5273,8 +5273,8 @@ Type Objects + ------------ + + .. index:: +- builtin: type +- module: types ++ pair: built-in function; type ++ pair: module; types + + Type objects represent the various object types. An object's type is accessed + by the built-in function :func:`type`. There are no special operations on +Index: Python-3.10.13/Doc/reference/compound_stmts.rst +=================================================================== +--- Python-3.10.13.orig/Doc/reference/compound_stmts.rst ++++ Python-3.10.13/Doc/reference/compound_stmts.rst +@@ -84,9 +84,9 @@ The :keyword:`!if` statement + ============================ + + .. index:: +- ! statement: if +- keyword: elif +- keyword: else ++ ! pair: statement; if ++ pair: keyword; elif ++ pair: keyword; else + single: : (colon); compound statement + + The :keyword:`if` statement is used for conditional execution: +@@ -109,8 +109,8 @@ The :keyword:`!while` statement + =============================== + + .. index:: +- ! statement: while +- keyword: else ++ ! pair: statement; while ++ pair: keyword; else + pair: loop; statement + single: : (colon); compound statement + +@@ -127,8 +127,8 @@ suite of the :keyword:`!else` clause, if + terminates. + + .. index:: +- statement: break +- statement: continue ++ pair: statement; break ++ pair: statement; continue + + A :keyword:`break` statement executed in the first suite terminates the loop + without executing the :keyword:`!else` clause's suite. A :keyword:`continue` +@@ -142,12 +142,12 @@ The :keyword:`!for` statement + ============================= + + .. index:: +- ! statement: for +- keyword: in +- keyword: else ++ ! pair: statement; for ++ pair: keyword; in ++ pair: keyword; else + pair: target; list + pair: loop; statement +- object: sequence ++ pair: object; sequence + single: : (colon); compound statement + + The :keyword:`for` statement is used to iterate over the elements of a sequence +@@ -167,8 +167,8 @@ is empty or an iterator raises a :exc:`S + the :keyword:`!else` clause, if present, is executed, and the loop terminates. + + .. index:: +- statement: break +- statement: continue ++ pair: statement; break ++ pair: statement; continue + + A :keyword:`break` statement executed in the first suite terminates the loop + without executing the :keyword:`!else` clause's suite. A :keyword:`continue` +@@ -188,7 +188,7 @@ those made in the suite of the for-loop: + + + .. index:: +- builtin: range ++ pair: built-in function; range + + Names in the target list are not deleted when the loop is finished, but if the + sequence is empty, they will not have been assigned to at all by the loop. Hint: +@@ -204,11 +204,11 @@ The :keyword:`!try` statement + ============================= + + .. index:: +- ! statement: try +- keyword: except +- keyword: finally +- keyword: else +- keyword: as ++ ! pair: statement; try ++ pair: keyword; except ++ pair: keyword; finally ++ pair: keyword; else ++ pair: keyword; as + single: : (colon); compound statement + + The :keyword:`try` statement specifies exception handlers and/or cleanup code +@@ -275,8 +275,8 @@ traceback attached to them, they form a + keeping all locals in that frame alive until the next garbage collection occurs. + + .. index:: +- module: sys +- object: traceback ++ pair: module; sys ++ pair: object; traceback + + Before an except clause's suite is executed, details about the exception are + stored in the :mod:`sys` module and can be accessed via :func:`sys.exc_info`. +@@ -305,10 +305,10 @@ when leaving an exception handler:: + (None, None, None) + + .. index:: +- keyword: else +- statement: return +- statement: break +- statement: continue ++ pair: keyword; else ++ pair: statement; return ++ pair: statement; break ++ pair: statement; continue + + The optional :keyword:`!else` clause is executed if the control flow leaves the + :keyword:`try` suite, no exception was raised, and no :keyword:`return`, +@@ -316,7 +316,7 @@ The optional :keyword:`!else` clause is + the :keyword:`!else` clause are not handled by the preceding :keyword:`except` + clauses. + +-.. index:: keyword: finally ++.. index:: pair: keyword; finally + + If :keyword:`finally` is present, it specifies a 'cleanup' handler. The + :keyword:`try` clause is executed, including any :keyword:`except` and +@@ -341,9 +341,9 @@ The exception information is not availab + the :keyword:`finally` clause. + + .. index:: +- statement: return +- statement: break +- statement: continue ++ pair: statement; return ++ pair: statement; break ++ pair: statement; continue + + When a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement is + executed in the :keyword:`try` suite of a :keyword:`!try`...\ :keyword:`!finally` +@@ -379,8 +379,8 @@ The :keyword:`!with` statement + ============================== + + .. index:: +- ! statement: with +- keyword: as ++ ! pair: statement; with ++ pair: keyword; as + single: as; with statement + single: , (comma); with statement + single: : (colon); compound statement +@@ -496,11 +496,11 @@ The :keyword:`!match` statement + =============================== + + .. index:: +- ! statement: match +- ! keyword: case ++ ! pair: statement; match ++ ! pair: keyword; case + ! single: pattern matching +- keyword: if +- keyword: as ++ pair: keyword; if ++ pair: keyword; as + pair: match; case + single: as; match statement + single: : (colon); compound statement +@@ -1101,12 +1101,12 @@ Function definitions + ==================== + + .. index:: +- statement: def ++ pair: statement; def + pair: function; definition + pair: function; name + pair: name; binding +- object: user-defined function +- object: function ++ pair: object; user-defined function ++ pair: object; function + pair: function; name + pair: name; binding + single: () (parentheses); function definition +@@ -1274,8 +1274,8 @@ Class definitions + ================= + + .. index:: +- object: class +- statement: class ++ pair: object; class ++ pair: statement; class + pair: class; definition + pair: class; name + pair: name; binding +@@ -1374,7 +1374,7 @@ Coroutines + + .. versionadded:: 3.5 + +-.. index:: statement: async def ++.. index:: pair: statement; async def + .. _`async def`: + + Coroutine function definition +@@ -1385,8 +1385,8 @@ Coroutine function definition + : ["->" `expression`] ":" `suite` + + .. index:: +- keyword: async +- keyword: await ++ pair: keyword; async ++ pair: keyword; await + + Execution of Python coroutines can be suspended and resumed at many points + (see :term:`coroutine`). :keyword:`await` expressions, :keyword:`async for` and +@@ -1408,7 +1408,7 @@ An example of a coroutine function:: + ``await`` and ``async`` are now keywords; previously they were only + treated as such inside the body of a coroutine function. + +-.. index:: statement: async for ++.. index:: pair: statement; async for + .. _`async for`: + + The :keyword:`!async for` statement +@@ -1453,7 +1453,7 @@ It is a :exc:`SyntaxError` to use an ``a + body of a coroutine function. + + +-.. index:: statement: async with ++.. index:: pair: statement; async with + .. _`async with`: + + The :keyword:`!async with` statement +Index: Python-3.10.13/Doc/reference/datamodel.rst +=================================================================== +--- Python-3.10.13.orig/Doc/reference/datamodel.rst ++++ Python-3.10.13/Doc/reference/datamodel.rst +@@ -21,8 +21,8 @@ conformance to Von Neumann's model of a + represented by objects.) + + .. index:: +- builtin: id +- builtin: type ++ pair: built-in function; id ++ pair: built-in function; type + single: identity of an object + single: value of an object + single: type of an object +@@ -142,7 +142,7 @@ attributes.' These are attributes that + are not intended for general use. Their definition may change in the future. + + None +- .. index:: object: None ++ .. index:: pair: object; None + + This type has a single value. There is a single object with this value. This + object is accessed through the built-in name ``None``. It is used to signify the +@@ -150,7 +150,7 @@ None + don't explicitly return anything. Its truth value is false. + + NotImplemented +- .. index:: object: NotImplemented ++ .. index:: pair: object; NotImplemented + + This type has a single value. There is a single object with this value. This + object is accessed through the built-in name ``NotImplemented``. Numeric methods +@@ -171,7 +171,7 @@ NotImplemented + + Ellipsis + .. index:: +- object: Ellipsis ++ pair: object; Ellipsis + single: ...; ellipsis literal + + This type has a single value. There is a single object with this value. This +@@ -179,7 +179,7 @@ Ellipsis + ``Ellipsis``. Its truth value is true. + + :class:`numbers.Number` +- .. index:: object: numeric ++ .. index:: pair: object; numeric + + These are created by numeric literals and returned as results by arithmetic + operators and arithmetic built-in functions. Numeric objects are immutable; +@@ -209,7 +209,7 @@ Ellipsis + numbers: + + :class:`numbers.Integral` +- .. index:: object: integer ++ .. index:: pair: object; integer + + These represent elements from the mathematical set of integers (positive and + negative). +@@ -225,7 +225,7 @@ Ellipsis + + Booleans (:class:`bool`) + .. index:: +- object: Boolean ++ pair: object; Boolean + single: False + single: True + +@@ -242,7 +242,7 @@ Ellipsis + + :class:`numbers.Real` (:class:`float`) + .. index:: +- object: floating point ++ pair: object; floating point + pair: floating point; number + pair: C; language + pair: Java; language +@@ -257,7 +257,7 @@ Ellipsis + + :class:`numbers.Complex` (:class:`complex`) + .. index:: +- object: complex ++ pair: object; complex + pair: complex; number + + These represent complex numbers as a pair of machine-level double precision +@@ -267,8 +267,8 @@ Ellipsis + + Sequences + .. index:: +- builtin: len +- object: sequence ++ pair: built-in function; len ++ pair: object; sequence + single: index operation + single: item selection + single: subscription +@@ -293,8 +293,8 @@ Sequences + + Immutable sequences + .. index:: +- object: immutable sequence +- object: immutable ++ pair: object; immutable sequence ++ pair: object; immutable + + An object of an immutable sequence type cannot change once it is created. (If + the object contains references to other objects, these other objects may be +@@ -308,8 +308,8 @@ Sequences + + Strings + .. index:: +- builtin: chr +- builtin: ord ++ pair: built-in function; chr ++ pair: built-in function; ord + single: character + single: integer + single: Unicode +@@ -328,7 +328,7 @@ Sequences + + Tuples + .. index:: +- object: tuple ++ pair: object; tuple + pair: singleton; tuple + pair: empty; tuple + +@@ -350,8 +350,8 @@ Sequences + + Mutable sequences + .. index:: +- object: mutable sequence +- object: mutable ++ pair: object; mutable sequence ++ pair: object; mutable + pair: assignment; statement + single: subscription + single: slicing +@@ -363,7 +363,7 @@ Sequences + There are currently two intrinsic mutable sequence types: + + Lists +- .. index:: object: list ++ .. index:: pair: object; list + + The items of a list are arbitrary Python objects. Lists are formed by + placing a comma-separated list of expressions in square brackets. (Note +@@ -377,15 +377,15 @@ Sequences + (and hence unhashable), byte arrays otherwise provide the same interface + and functionality as immutable :class:`bytes` objects. + +- .. index:: module: array ++ .. index:: pair: module; array + + The extension module :mod:`array` provides an additional example of a + mutable sequence type, as does the :mod:`collections` module. + + Set types + .. index:: +- builtin: len +- object: set type ++ pair: built-in function; len ++ pair: object; set type + + These represent unordered, finite sets of unique, immutable objects. As such, + they cannot be indexed by any subscript. However, they can be iterated over, and +@@ -402,14 +402,14 @@ Set types + There are currently two intrinsic set types: + + Sets +- .. index:: object: set ++ .. index:: pair: object; set + + These represent a mutable set. They are created by the built-in :func:`set` + constructor and can be modified afterwards by several methods, such as + :meth:`~set.add`. + + Frozen sets +- .. index:: object: frozenset ++ .. index:: pair: object; frozenset + + These represent an immutable set. They are created by the built-in + :func:`frozenset` constructor. As a frozenset is immutable and +@@ -418,9 +418,9 @@ Set types + + Mappings + .. index:: +- builtin: len ++ pair: built-in function; len + single: subscription +- object: mapping ++ pair: object; mapping + + These represent finite sets of objects indexed by arbitrary index sets. The + subscript notation ``a[k]`` selects the item indexed by ``k`` from the mapping +@@ -431,7 +431,7 @@ Mappings + There is currently a single intrinsic mapping type: + + Dictionaries +- .. index:: object: dictionary ++ .. index:: pair: object; dictionary + + These represent finite sets of objects indexed by nearly arbitrary values. The + only types of values not acceptable as keys are values containing lists or +@@ -451,8 +451,8 @@ Mappings + section :ref:`dict`). + + .. index:: +- module: dbm.ndbm +- module: dbm.gnu ++ pair: module; dbm.ndbm ++ pair: module; dbm.gnu + + The extension modules :mod:`dbm.ndbm` and :mod:`dbm.gnu` provide + additional examples of mapping types, as does the :mod:`collections` +@@ -465,7 +465,7 @@ Mappings + + Callable types + .. index:: +- object: callable ++ pair: object; callable + pair: function; call + single: invocation + pair: function; argument +@@ -476,8 +476,8 @@ Callable types + User-defined functions + .. index:: + pair: user-defined; function +- object: function +- object: user-defined function ++ pair: object; function ++ pair: object; user-defined function + + A user-defined function object is created by a function definition (see + section :ref:`function`). It should be called with an argument list +@@ -580,8 +580,8 @@ Callable types + + Instance methods + .. index:: +- object: method +- object: user-defined method ++ pair: object; method ++ pair: object; user-defined method + pair: user-defined; method + + An instance method object combines a class, a class instance and any +@@ -688,8 +688,8 @@ Callable types + + Built-in functions + .. index:: +- object: built-in function +- object: function ++ pair: object; built-in function ++ pair: object; function + pair: C; language + + A built-in function object is a wrapper around a C function. Examples of +@@ -703,8 +703,8 @@ Callable types + + Built-in methods + .. index:: +- object: built-in method +- object: method ++ pair: object; built-in method ++ pair: object; method + pair: built-in; method + + This is really a different disguise of a built-in function, this time containing +@@ -727,8 +727,8 @@ Callable types + + Modules + .. index:: +- statement: import +- object: module ++ pair: statement; import ++ pair: object; module + + Modules are a basic organizational unit of Python code, and are created by + the :ref:`import system ` as invoked either by the +@@ -805,12 +805,12 @@ Custom classes + .. XXX: Could we add that MRO doc as an appendix to the language ref? + + .. index:: +- object: class +- object: class instance +- object: instance ++ pair: object; class ++ pair: object; class instance ++ pair: object; instance + pair: class object; call + single: container +- object: dictionary ++ pair: object; dictionary + pair: class; attribute + + When a class attribute reference (for class :class:`C`, say) would yield a +@@ -865,8 +865,8 @@ Custom classes + + Class instances + .. index:: +- object: class instance +- object: instance ++ pair: object; class instance ++ pair: object; instance + pair: class; instance + pair: class instance; attribute + +@@ -892,9 +892,9 @@ Class instances + dictionary directly. + + .. index:: +- object: numeric +- object: sequence +- object: mapping ++ pair: object; numeric ++ pair: object; sequence ++ pair: object; mapping + + Class instances can pretend to be numbers, sequences, or mappings if they have + methods with certain special names. See section :ref:`specialnames`. +@@ -908,8 +908,8 @@ Class instances + + I/O objects (also known as file objects) + .. index:: +- builtin: open +- module: io ++ pair: built-in function; open ++ pair: module; io + single: popen() (in module os) + single: makefile() (socket method) + single: sys.stdin +@@ -993,7 +993,7 @@ Internal types + required stack size; :attr:`co_flags` is an integer encoding a number + of flags for the interpreter. + +- .. index:: object: generator ++ .. index:: pair: object; generator + + The following flag bits are defined for :attr:`co_flags`: bit ``0x04`` is set if + the function uses the ``*arguments`` syntax to accept an arbitrary number of +@@ -1017,7 +1017,7 @@ Internal types + .. _frame-objects: + + Frame objects +- .. index:: object: frame ++ .. index:: pair: object; frame + + Frame objects represent execution frames. They may occur in traceback objects + (see below), and are also passed to registered trace functions. +@@ -1080,7 +1080,7 @@ Internal types + + Traceback objects + .. index:: +- object: traceback ++ pair: object; traceback + pair: stack; trace + pair: exception; handler + pair: execution; stack +@@ -1114,7 +1114,7 @@ Internal types + single: tb_frame (traceback attribute) + single: tb_lineno (traceback attribute) + single: tb_lasti (traceback attribute) +- statement: try ++ pair: statement; try + + Special read-only attributes: + :attr:`tb_frame` points to the execution frame of the current level; +@@ -1140,7 +1140,7 @@ Internal types + and the ``tb_next`` attribute of existing instances can be updated. + + Slice objects +- .. index:: builtin: slice ++ .. index:: pair: built-in function; slice + + Slice objects are used to represent slices for + :meth:`~object.__getitem__` +@@ -1273,7 +1273,7 @@ Basic customization + .. index:: + single: destructor + single: finalizer +- statement: del ++ pair: statement; del + + Called when the instance is about to be destroyed. This is also called a + finalizer or (improperly) a destructor. If a base class has a +@@ -1374,7 +1374,7 @@ Basic customization + + .. method:: object.__bytes__(self) + +- .. index:: builtin: bytes ++ .. index:: pair: built-in function; bytes + + Called by :ref:`bytes ` to compute a byte-string representation + of an object. This should return a :class:`bytes` object. +@@ -1382,7 +1382,7 @@ Basic customization + .. index:: + single: string; __format__() (object method) + pair: string; conversion +- builtin: print ++ pair: built-in function; print + + + .. method:: object.__format__(self, format_spec) +@@ -1461,8 +1461,8 @@ Basic customization + .. method:: object.__hash__(self) + + .. index:: +- object: dictionary +- builtin: hash ++ pair: object; dictionary ++ pair: built-in function; hash + + Called by built-in function :func:`hash` and for operations on members of + hashed collections including :class:`set`, :class:`frozenset`, and +@@ -1981,7 +1981,7 @@ Metaclasses + + .. index:: + single: metaclass +- builtin: type ++ pair: built-in function; type + single: = (equals); class definition + + By default, classes are constructed using :func:`type`. The class body is +@@ -2395,7 +2395,7 @@ through the object's keys; for sequences + .. method:: object.__len__(self) + + .. index:: +- builtin: len ++ pair: built-in function; len + single: __bool__() (object method) + + Called to implement the built-in function :func:`len`. Should return the length +@@ -2424,7 +2424,7 @@ through the object's keys; for sequences + .. versionadded:: 3.4 + + +-.. index:: object: slice ++.. index:: pair: object; slice + + .. note:: + +@@ -2553,9 +2553,9 @@ left undefined. + object.__or__(self, other) + + .. index:: +- builtin: divmod +- builtin: pow +- builtin: pow ++ pair: built-in function; divmod ++ pair: built-in function; pow ++ pair: built-in function; pow + + These methods are called to implement the binary arithmetic operations + (``+``, ``-``, ``*``, ``@``, ``/``, ``//``, ``%``, :func:`divmod`, +@@ -2588,8 +2588,8 @@ left undefined. + object.__ror__(self, other) + + .. index:: +- builtin: divmod +- builtin: pow ++ pair: built-in function; divmod ++ pair: built-in function; pow + + These methods are called to implement the binary arithmetic operations + (``+``, ``-``, ``*``, ``@``, ``/``, ``//``, ``%``, :func:`divmod`, +@@ -2600,7 +2600,7 @@ left undefined. + an instance of a class that has an :meth:`__rsub__` method, ``y.__rsub__(x)`` + is called if ``x.__sub__(y)`` returns *NotImplemented*. + +- .. index:: builtin: pow ++ .. index:: pair: built-in function; pow + + Note that ternary :func:`pow` will not try calling :meth:`__rpow__` (the + coercion rules would become too complicated). +@@ -2647,7 +2647,7 @@ left undefined. + object.__abs__(self) + object.__invert__(self) + +- .. index:: builtin: abs ++ .. index:: pair: built-in function; abs + + Called to implement the unary arithmetic operations (``-``, ``+``, :func:`abs` + and ``~``). +@@ -2658,9 +2658,9 @@ left undefined. + object.__float__(self) + + .. index:: +- builtin: complex +- builtin: int +- builtin: float ++ pair: built-in function; complex ++ pair: built-in function; int ++ pair: built-in function; float + + Called to implement the built-in functions :func:`complex`, + :func:`int` and :func:`float`. Should return a value +@@ -2685,7 +2685,7 @@ left undefined. + object.__floor__(self) + object.__ceil__(self) + +- .. index:: builtin: round ++ .. index:: pair: built-in function; round + + Called to implement the built-in function :func:`round` and :mod:`math` + functions :func:`~math.trunc`, :func:`~math.floor` and :func:`~math.ceil`. +@@ -2710,7 +2710,7 @@ execution of the block of code. Context + used by directly invoking their methods. + + .. index:: +- statement: with ++ pair: statement; with + single: context manager + + Typical uses of context managers include saving and restoring various kinds of +Index: Python-3.10.13/Doc/reference/executionmodel.rst +=================================================================== +--- Python-3.10.13.orig/Doc/reference/executionmodel.rst ++++ Python-3.10.13/Doc/reference/executionmodel.rst +@@ -151,7 +151,7 @@ to previously bound variables in the nea + :exc:`SyntaxError` is raised at compile time if the given name does not + exist in any enclosing function scope. + +-.. index:: module: __main__ ++.. index:: pair: module; __main__ + + The namespace for a module is automatically created the first time a module is + imported. The main module for a script is always called :mod:`__main__`. +Index: Python-3.10.13/Doc/reference/toplevel_components.rst +=================================================================== +--- Python-3.10.13.orig/Doc/reference/toplevel_components.rst ++++ Python-3.10.13/Doc/reference/toplevel_components.rst +@@ -21,9 +21,9 @@ Complete Python programs + .. index:: single: program + + .. index:: +- module: sys +- module: __main__ +- module: builtins ++ pair: module; sys ++ pair: module; __main__ ++ pair: module; builtins + + While a language specification need not prescribe how the language interpreter + is invoked, it is useful to have a notion of a complete Python program. A +@@ -38,7 +38,7 @@ the next section. + + .. index:: + single: interactive mode +- module: __main__ ++ pair: module; __main__ + + The interpreter may also be invoked in interactive mode; in this case, it does + not read and execute a complete program but reads and executes one statement +@@ -98,7 +98,7 @@ Expression input + ================ + + .. index:: single: input +-.. index:: builtin: eval ++.. index:: pair: built-in function; eval + + :func:`eval` is used for expression input. It ignores leading whitespace. The + string argument to :func:`eval` must have the following form: +Index: Python-3.10.13/Doc/tutorial/inputoutput.rst +=================================================================== +--- Python-3.10.13.orig/Doc/tutorial/inputoutput.rst ++++ Python-3.10.13/Doc/tutorial/inputoutput.rst +@@ -285,8 +285,8 @@ Reading and Writing Files + ========================= + + .. index:: +- builtin: open +- object: file ++ pair: built-in function; open ++ pair: object; file + + :func:`open` returns a :term:`file object`, and is most commonly used with + two positional arguments and one keyword argument: +@@ -466,7 +466,7 @@ Reference for a complete guide to file o + Saving structured data with :mod:`json` + --------------------------------------- + +-.. index:: module: json ++.. index:: pair: module; json + + Strings can easily be written to and read from a file. Numbers take a bit more + effort, since the :meth:`read` method only returns strings, which will have to +Index: Python-3.10.13/Doc/tutorial/modules.rst +=================================================================== +--- Python-3.10.13.orig/Doc/tutorial/modules.rst ++++ Python-3.10.13/Doc/tutorial/modules.rst +@@ -260,7 +260,7 @@ Some tips for experts: + Standard Modules + ================ + +-.. index:: module: sys ++.. index:: pair: module; sys + + Python comes with a library of standard modules, described in a separate + document, the Python Library Reference ("Library Reference" hereafter). Some +@@ -341,7 +341,7 @@ Without arguments, :func:`dir` lists the + + Note that it lists all types of names: variables, modules, functions, etc. + +-.. index:: module: builtins ++.. index:: pair: module; builtins + + :func:`dir` does not list the names of built-in functions and variables. If you + want a list of those, they are defined in the standard module +Index: Python-3.10.13/Doc/reference/expressions.rst +=================================================================== +--- Python-3.10.13.orig/Doc/reference/expressions.rst ++++ Python-3.10.13/Doc/reference/expressions.rst +@@ -71,7 +71,7 @@ An identifier occurring as an atom is a + for lexical definition and section :ref:`naming` for documentation of naming and + binding. + +-.. index:: exception: NameError ++.. index:: pair: exception; NameError + + When the name is bound to an object, evaluation of the atom yields that object. + When a name is not bound, an attempt to evaluate it raises a :exc:`NameError` +@@ -240,7 +240,7 @@ List displays + pair: list; display + pair: list; comprehensions + pair: empty; list +- object: list ++ pair: object; list + single: [] (square brackets); list expression + single: , (comma); expression list + +@@ -265,7 +265,7 @@ Set displays + .. index:: + pair: set; display + pair: set; comprehensions +- object: set ++ pair: object; set + single: {} (curly brackets); set expression + single: , (comma); expression list + +@@ -294,7 +294,7 @@ Dictionary displays + pair: dictionary; display + pair: dictionary; comprehensions + key, datum, key/datum pair +- object: dictionary ++ pair: object; dictionary + single: {} (curly brackets); dictionary expression + single: : (colon); in dictionary expressions + single: , (comma); in dictionary displays +@@ -356,7 +356,7 @@ Generator expressions + + .. index:: + pair: generator; expression +- object: generator ++ pair: object; generator + single: () (parentheses); generator expression + + A generator expression is a compact generator notation in parentheses: +@@ -410,8 +410,8 @@ Yield expressions + ----------------- + + .. index:: +- keyword: yield +- keyword: from ++ pair: keyword; yield ++ pair: keyword; from + pair: yield; expression + pair: generator; function + +@@ -517,7 +517,7 @@ on the right hand side of an assignment + The proposal that expanded on :pep:`492` by adding generator capabilities to + coroutine functions. + +-.. index:: object: generator ++.. index:: pair: object; generator + .. _generator-methods: + + Generator-iterator methods +@@ -529,7 +529,7 @@ be used to control the execution of a ge + Note that calling any of the generator methods below when the generator + is already executing raises a :exc:`ValueError` exception. + +-.. index:: exception: StopIteration ++.. index:: pair: exception; StopIteration + + + .. method:: generator.__next__() +@@ -579,7 +579,7 @@ is already executing raises a :exc:`Valu + :attr:`~BaseException.__traceback__` attribute stored in *value* may + be cleared. + +-.. index:: exception: GeneratorExit ++.. index:: pair: exception; GeneratorExit + + + .. method:: generator.close() +@@ -691,7 +691,7 @@ of a *finalizer* method see the implemen + The expression ``yield from `` is a syntax error when used in an + asynchronous generator function. + +-.. index:: object: asynchronous-generator ++.. index:: pair: object; asynchronous-generator + .. _asynchronous-generator-methods: + + Asynchronous generator-iterator methods +@@ -701,7 +701,7 @@ This subsection describes the methods of + which are used to control the execution of a generator function. + + +-.. index:: exception: StopAsyncIteration ++.. index:: pair: exception; StopAsyncIteration + + .. coroutinemethod:: agen.__anext__() + +@@ -748,7 +748,7 @@ which are used to control the execution + raises a different exception, then when the awaitable is run that exception + propagates to the caller of the awaitable. + +-.. index:: exception: GeneratorExit ++.. index:: pair: exception; GeneratorExit + + + .. coroutinemethod:: agen.aclose() +@@ -795,9 +795,9 @@ An attribute reference is a primary foll + attributeref: `primary` "." `identifier` + + .. index:: +- exception: AttributeError +- object: module +- object: list ++ pair: exception; AttributeError ++ pair: object; module ++ pair: object; list + + The primary must evaluate to an object of a type that supports attribute + references, which most objects do. This object is then asked to produce the +@@ -818,12 +818,12 @@ Subscriptions + single: [] (square brackets); subscription + + .. index:: +- object: sequence +- object: mapping +- object: string +- object: tuple +- object: list +- object: dictionary ++ pair: object; sequence ++ pair: object; mapping ++ pair: object; string ++ pair: object; tuple ++ pair: object; list ++ pair: object; dictionary + pair: sequence; item + + The subscription of an instance of a :ref:`container class ` +@@ -891,10 +891,10 @@ Slicings + single: , (comma); slicing + + .. index:: +- object: sequence +- object: string +- object: tuple +- object: list ++ pair: object; sequence ++ pair: object; string ++ pair: object; tuple ++ pair: object; list + + A slicing selects a range of items in a sequence object (e.g., a string, tuple + or list). Slicings may be used as expressions or as targets in assignment or +@@ -935,7 +935,7 @@ substituting ``None`` for missing expres + + + .. index:: +- object: callable ++ pair: object; callable + single: call + single: argument; call semantics + single: () (parentheses); call +@@ -1085,8 +1085,8 @@ a user-defined function: + .. index:: + pair: function; call + triple: user-defined; function; call +- object: user-defined function +- object: function ++ pair: object; user-defined function ++ pair: object; function + + The code block for the function is executed, passing it the argument list. The + first thing the code block will do is bind the formal parameters to the +@@ -1100,25 +1100,25 @@ a built-in function or method: + pair: built-in function; call + pair: method; call + pair: built-in method; call +- object: built-in method +- object: built-in function +- object: method +- object: function ++ pair: object; built-in method ++ pair: object; built-in function ++ pair: object; method ++ pair: object; function + + The result is up to the interpreter; see :ref:`built-in-funcs` for the + descriptions of built-in functions and methods. + + a class object: + .. index:: +- object: class ++ pair: object; class + pair: class object; call + + A new instance of that class is returned. + + a class instance method: + .. index:: +- object: class instance +- object: instance ++ pair: object; class instance ++ pair: object; instance + pair: class instance; call + + The corresponding user-defined function is called, with an argument list that is +@@ -1134,7 +1134,7 @@ a class instance: + if that method was called. + + +-.. index:: keyword: await ++.. index:: pair: keyword; await + .. _await: + + Await expression +@@ -1156,7 +1156,7 @@ The power operator + + .. index:: + pair: power; operation +- operator: ** ++ pair: operator; ** + + The power operator binds more tightly than unary operators on its left; it binds + less tightly than unary operators on its right. The syntax is: +@@ -1217,7 +1217,7 @@ operation can be overridden with the :me + + .. index:: + single: inversion +- operator: ~ (tilde) ++ pair: operator; ~ (tilde) + + The unary ``~`` (invert) operator yields the bitwise inversion of its integer + argument. The bitwise inversion of ``x`` is defined as ``-(x+1)``. It only +@@ -1226,7 +1226,7 @@ applies to integral numbers or to custom + + + +-.. index:: exception: TypeError ++.. index:: pair: exception; TypeError + + In all three cases, if the argument does not have the proper type, a + :exc:`TypeError` exception is raised. +@@ -1252,7 +1252,7 @@ operators and one for additive operators + + .. index:: + single: multiplication +- operator: * (asterisk) ++ pair: operator; * (asterisk) + + The ``*`` (multiplication) operator yields the product of its arguments. The + arguments must either both be numbers, or one argument must be an integer and +@@ -1265,7 +1265,7 @@ This operation can be customized using t + + .. index:: + single: matrix multiplication +- operator: @ (at) ++ pair: operator; @ (at) + + The ``@`` (at) operator is intended to be used for matrix multiplication. No + builtin Python types implement this operator. +@@ -1273,10 +1273,10 @@ builtin Python types implement this oper + .. versionadded:: 3.5 + + .. index:: +- exception: ZeroDivisionError ++ pair: exception; ZeroDivisionError + single: division +- operator: / (slash) +- operator: // ++ pair: operator; / (slash) ++ pair: operator; // + + The ``/`` (division) and ``//`` (floor division) operators yield the quotient of + their arguments. The numeric arguments are first converted to a common type. +@@ -1290,7 +1290,7 @@ This operation can be customized using t + + .. index:: + single: modulo +- operator: % (percent) ++ pair: operator; % (percent) + + The ``%`` (modulo) operator yields the remainder from the division of the first + argument by the second. The numeric arguments are first converted to a common +@@ -1348,8 +1348,8 @@ Shifting operations + + .. index:: + pair: shifting; operation +- operator: << +- operator: >> ++ pair: operator; << ++ pair: operator; >> + + The shifting operations have lower priority than the arithmetic operations: + +@@ -1362,7 +1362,7 @@ the left or right by the number of bits + This operation can be customized using the special :meth:`__lshift__` and + :meth:`__rshift__` methods. + +-.. index:: exception: ValueError ++.. index:: pair: exception; ValueError + + A right shift by *n* bits is defined as floor division by ``pow(2,n)``. A left + shift by *n* bits is defined as multiplication with ``pow(2,n)``. +@@ -1384,7 +1384,7 @@ Each of the three bitwise operations has + + .. index:: + pair: bitwise; and +- operator: & (ampersand) ++ pair: operator; & (ampersand) + + The ``&`` operator yields the bitwise AND of its arguments, which must be + integers or one of them must be a custom object overriding :meth:`__and__` or +@@ -1393,7 +1393,7 @@ integers or one of them must be a custom + .. index:: + pair: bitwise; xor + pair: exclusive; or +- operator: ^ (caret) ++ pair: operator; ^ (caret) + + The ``^`` operator yields the bitwise XOR (exclusive OR) of its arguments, which + must be integers or one of them must be a custom object overriding :meth:`__xor__` or +@@ -1402,7 +1402,7 @@ must be integers or one of them must be + .. index:: + pair: bitwise; or + pair: inclusive; or +- operator: | (vertical bar) ++ pair: operator; | (vertical bar) + + The ``|`` operator yields the bitwise (inclusive) OR of its arguments, which + must be integers or one of them must be a custom object overriding :meth:`__or__` or +@@ -1417,12 +1417,12 @@ Comparisons + .. index:: + single: comparison + pair: C; language +- operator: < (less) +- operator: > (greater) +- operator: <= +- operator: >= +- operator: == +- operator: != ++ pair: operator; < (less) ++ pair: operator; > (greater) ++ pair: operator; <= ++ pair: operator; >= ++ pair: operator; == ++ pair: operator; != + + Unlike C, all comparison operations in Python have the same priority, which is + lower than that of any arithmetic, shifting or bitwise operation. Also unlike +@@ -1652,17 +1652,17 @@ raises the :exc:`IndexError` exception. + if :keyword:`in` raised that exception). + + .. index:: +- operator: in +- operator: not in ++ pair: operator; in ++ pair: operator; not in + pair: membership; test +- object: sequence ++ pair: object; sequence + + The operator :keyword:`not in` is defined to have the inverse truth value of + :keyword:`in`. + + .. index:: +- operator: is +- operator: is not ++ pair: operator; is ++ pair: operator; is not + pair: identity; test + + +@@ -1702,17 +1702,17 @@ control flow statements, the following v + other values are interpreted as true. User-defined objects can customize their + truth value by providing a :meth:`__bool__` method. + +-.. index:: operator: not ++.. index:: pair: operator; not + + The operator :keyword:`not` yields ``True`` if its argument is false, ``False`` + otherwise. + +-.. index:: operator: and ++.. index:: pair: operator; and + + The expression ``x and y`` first evaluates *x*; if *x* is false, its value is + returned; otherwise, *y* is evaluated and the resulting value is returned. + +-.. index:: operator: or ++.. index:: pair: operator; or + + The expression ``x or y`` first evaluates *x*; if *x* is true, its value is + returned; otherwise, *y* is evaluated and the resulting value is returned. +@@ -1837,7 +1837,7 @@ Expression lists + starred_expression: `expression` | (`starred_item` ",")* [`starred_item`] + starred_item: `assignment_expression` | "*" `or_expr` + +-.. index:: object: tuple ++.. index:: pair: object; tuple + + Except when part of a list or set display, an expression list + containing at least one comma yields a tuple. The length of +Index: Python-3.10.13/Doc/reference/simple_stmts.rst +=================================================================== +--- Python-3.10.13.orig/Doc/reference/simple_stmts.rst ++++ Python-3.10.13/Doc/reference/simple_stmts.rst +@@ -53,8 +53,8 @@ An expression statement evaluates the ex + expression). + + .. index:: +- builtin: repr +- object: None ++ pair: built-in function; repr ++ pair: object; None + pair: string; conversion + single: output + pair: standard; output +@@ -76,7 +76,7 @@ Assignment statements + pair: assignment; statement + pair: binding; name + pair: rebinding; name +- object: mutable ++ pair: object; mutable + pair: attribute; assignment + + Assignment statements are used to (re)bind names to values and to modify +@@ -185,7 +185,7 @@ Assignment of an object to a single targ + + .. index:: + pair: subscription; assignment +- object: mutable ++ pair: object; mutable + + * If the target is a subscription: The primary expression in the reference is + evaluated. It should yield either a mutable sequence object (such as a list) +@@ -193,8 +193,8 @@ Assignment of an object to a single targ + evaluated. + + .. index:: +- object: sequence +- object: list ++ pair: object; sequence ++ pair: object; list + + If the primary is a mutable sequence object (such as a list), the subscript + must yield an integer. If it is negative, the sequence's length is added to +@@ -204,8 +204,8 @@ Assignment of an object to a single targ + raised (assignment to a subscripted sequence cannot add new items to a list). + + .. index:: +- object: mapping +- object: dictionary ++ pair: object; mapping ++ pair: object; dictionary + + If the primary is a mapping object (such as a dictionary), the subscript must + have a type compatible with the mapping's key type, and the mapping is then +@@ -376,7 +376,7 @@ The :keyword:`!assert` statement + ================================ + + .. index:: +- ! statement: assert ++ ! pair: statement; assert + pair: debugging; assertions + single: , (comma); expression list + +@@ -398,7 +398,7 @@ The extended form, ``assert expression1, + + .. index:: + single: __debug__ +- exception: AssertionError ++ pair: exception; AssertionError + + These equivalences assume that :const:`__debug__` and :exc:`AssertionError` refer to + the built-in variables with those names. In the current implementation, the +@@ -419,7 +419,7 @@ The :keyword:`!pass` statement + ============================== + + .. index:: +- statement: pass ++ pair: statement; pass + pair: null; operation + pair: null; operation + +@@ -441,7 +441,7 @@ The :keyword:`!del` statement + ============================= + + .. index:: +- ! statement: del ++ ! pair: statement; del + pair: deletion; target + triple: deletion; target; list + +@@ -454,7 +454,7 @@ Rather than spelling it out in full deta + Deletion of a target list recursively deletes each target, from left to right. + + .. index:: +- statement: global ++ pair: statement; global + pair: unbinding; name + + Deletion of a name removes the binding of that name from the local or global +@@ -480,7 +480,7 @@ The :keyword:`!return` statement + ================================ + + .. index:: +- ! statement: return ++ ! pair: statement; return + pair: function; definition + pair: class; definition + +@@ -495,7 +495,7 @@ If an expression list is present, it is + :keyword:`return` leaves the current function call with the expression list (or + ``None``) as return value. + +-.. index:: keyword: finally ++.. index:: pair: keyword; finally + + When :keyword:`return` passes control out of a :keyword:`try` statement with a + :keyword:`finally` clause, that :keyword:`!finally` clause is executed before +@@ -517,11 +517,11 @@ The :keyword:`!yield` statement + =============================== + + .. index:: +- statement: yield ++ pair: statement; yield + single: generator; function + single: generator; iterator + single: function; generator +- exception: StopIteration ++ pair: exception; StopIteration + + .. productionlist:: python-grammar + yield_stmt: `yield_expression` +@@ -553,7 +553,7 @@ The :keyword:`!raise` statement + =============================== + + .. index:: +- ! statement: raise ++ ! pair: statement; raise + single: exception + pair: raising; exception + single: __traceback__ (exception attribute) +@@ -574,7 +574,7 @@ instantiating the class with no argument + The :dfn:`type` of the exception is the exception instance's class, the + :dfn:`value` is the instance itself. + +-.. index:: object: traceback ++.. index:: pair: object; traceback + + A traceback object is normally created automatically when an exception is raised + and attached to it as the :attr:`__traceback__` attribute, which is writable. +@@ -661,9 +661,9 @@ The :keyword:`!break` statement + =============================== + + .. index:: +- ! statement: break +- statement: for +- statement: while ++ ! pair: statement; break ++ pair: statement; for ++ pair: statement; while + pair: loop; statement + + .. productionlist:: python-grammar +@@ -673,7 +673,7 @@ The :keyword:`!break` statement + :keyword:`while` loop, but not nested in a function or class definition within + that loop. + +-.. index:: keyword: else ++.. index:: pair: keyword; else + pair: loop control; target + + It terminates the nearest enclosing loop, skipping the optional :keyword:`!else` +@@ -682,7 +682,7 @@ clause if the loop has one. + If a :keyword:`for` loop is terminated by :keyword:`break`, the loop control + target keeps its current value. + +-.. index:: keyword: finally ++.. index:: pair: keyword; finally + + When :keyword:`break` passes control out of a :keyword:`try` statement with a + :keyword:`finally` clause, that :keyword:`!finally` clause is executed before +@@ -695,11 +695,11 @@ The :keyword:`!continue` statement + ================================== + + .. index:: +- ! statement: continue +- statement: for +- statement: while ++ ! pair: statement; continue ++ pair: statement; for ++ pair: statement; while + pair: loop; statement +- keyword: finally ++ pair: keyword; finally + + .. productionlist:: python-grammar + continue_stmt: "continue" +@@ -720,12 +720,12 @@ The :keyword:`!import` statement + ================================ + + .. index:: +- ! statement: import ++ ! pair: statement; import + single: module; importing + pair: name; binding +- keyword: from +- keyword: as +- exception: ImportError ++ pair: keyword; from ++ pair: keyword; as ++ pair: exception; ImportError + single: , (comma); import statement + + .. productionlist:: python-grammar +@@ -936,7 +936,7 @@ The :keyword:`!global` statement + ================================ + + .. index:: +- ! statement: global ++ ! pair: statement; global + triple: global; name; binding + single: , (comma); identifier list + +@@ -964,9 +964,9 @@ annotation. + them or silently change the meaning of the program. + + .. index:: +- builtin: exec +- builtin: eval +- builtin: compile ++ pair: built-in function; exec ++ pair: built-in function; eval ++ pair: built-in function; compile + + **Programmer's note:** :keyword:`global` is a directive to the parser. It + applies only to code parsed at the same time as the :keyword:`!global` statement. +@@ -982,7 +982,7 @@ call. The same applies to the :func:`ev + The :keyword:`!nonlocal` statement + ================================== + +-.. index:: statement: nonlocal ++.. index:: pair: statement; nonlocal + single: , (comma); identifier list + + .. productionlist:: python-grammar +Index: Python-3.10.13/Doc/c-api/bytearray.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/bytearray.rst ++++ Python-3.10.13/Doc/c-api/bytearray.rst +@@ -5,7 +5,7 @@ + Byte Array Objects + ------------------ + +-.. index:: object: bytearray ++.. index:: pair: object; bytearray + + + .. c:type:: PyByteArrayObject +Index: Python-3.10.13/Doc/c-api/bytes.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/bytes.rst ++++ Python-3.10.13/Doc/c-api/bytes.rst +@@ -8,7 +8,7 @@ Bytes Objects + These functions raise :exc:`TypeError` when expecting a bytes parameter and + called with a non-bytes parameter. + +-.. index:: object: bytes ++.. index:: pair: object; bytes + + + .. c:type:: PyBytesObject +Index: Python-3.10.13/Doc/c-api/capsule.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/capsule.rst ++++ Python-3.10.13/Doc/c-api/capsule.rst +@@ -5,7 +5,7 @@ + Capsules + -------- + +-.. index:: object: Capsule ++.. index:: pair: object; Capsule + + Refer to :ref:`using-capsules` for more information on using these objects. + +Index: Python-3.10.13/Doc/c-api/complex.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/complex.rst ++++ Python-3.10.13/Doc/c-api/complex.rst +@@ -5,7 +5,7 @@ + Complex Number Objects + ---------------------- + +-.. index:: object: complex number ++.. index:: pair: object; complex number + + Python's complex number objects are implemented as two distinct types when + viewed from the C API: one is the Python object exposed to Python programs, and +Index: Python-3.10.13/Doc/c-api/concrete.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/concrete.rst ++++ Python-3.10.13/Doc/c-api/concrete.rst +@@ -40,7 +40,7 @@ This section describes Python type objec + Numeric Objects + =============== + +-.. index:: object: numeric ++.. index:: pair: object; numeric + + .. toctree:: + +@@ -55,7 +55,7 @@ Numeric Objects + Sequence Objects + ================ + +-.. index:: object: sequence ++.. index:: pair: object; sequence + + Generic operations on sequence objects were discussed in the previous chapter; + this section deals with the specific kinds of sequence objects that are +@@ -77,7 +77,7 @@ intrinsic to the Python language. + Container Objects + ================= + +-.. index:: object: mapping ++.. index:: pair: object; mapping + + .. toctree:: + +Index: Python-3.10.13/Doc/c-api/dict.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/dict.rst ++++ Python-3.10.13/Doc/c-api/dict.rst +@@ -5,7 +5,7 @@ + Dictionary Objects + ------------------ + +-.. index:: object: dictionary ++.. index:: pair: object; dictionary + + + .. c:type:: PyDictObject +@@ -154,7 +154,7 @@ Dictionary Objects + + .. c:function:: Py_ssize_t PyDict_Size(PyObject *p) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the number of items in the dictionary. This is equivalent to + ``len(p)`` on a dictionary. +Index: Python-3.10.13/Doc/c-api/file.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/file.rst ++++ Python-3.10.13/Doc/c-api/file.rst +@@ -5,7 +5,7 @@ + File Objects + ------------ + +-.. index:: object: file ++.. index:: pair: object; file + + These APIs are a minimal emulation of the Python 2 C API for built-in file + objects, which used to rely on the buffered I/O (:c:expr:`FILE*`) support +Index: Python-3.10.13/Doc/c-api/float.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/float.rst ++++ Python-3.10.13/Doc/c-api/float.rst +@@ -5,7 +5,7 @@ + Floating Point Objects + ---------------------- + +-.. index:: object: floating point ++.. index:: pair: object; floating point + + + .. c:type:: PyFloatObject +Index: Python-3.10.13/Doc/c-api/function.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/function.rst ++++ Python-3.10.13/Doc/c-api/function.rst +@@ -5,7 +5,7 @@ + Function Objects + ---------------- + +-.. index:: object: function ++.. index:: pair: object; function + + There are a few functions specific to Python functions. + +Index: Python-3.10.13/Doc/c-api/list.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/list.rst ++++ Python-3.10.13/Doc/c-api/list.rst +@@ -5,7 +5,7 @@ + List Objects + ------------ + +-.. index:: object: list ++.. index:: pair: object; list + + + .. c:type:: PyListObject +@@ -45,7 +45,7 @@ List Objects + + .. c:function:: Py_ssize_t PyList_Size(PyObject *list) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the length of the list object in *list*; this is equivalent to + ``len(list)`` on a list object. +@@ -138,7 +138,7 @@ List Objects + + .. c:function:: PyObject* PyList_AsTuple(PyObject *list) + +- .. index:: builtin: tuple ++ .. index:: pair: built-in function; tuple + + Return a new tuple object containing the contents of *list*; equivalent to + ``tuple(list)``. +Index: Python-3.10.13/Doc/c-api/long.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/long.rst ++++ Python-3.10.13/Doc/c-api/long.rst +@@ -5,8 +5,8 @@ + Integer Objects + --------------- + +-.. index:: object: long integer +- object: integer ++.. index:: pair: object; long integer ++ pair: object; integer + + All integers are implemented as "long" integer objects of arbitrary size. + +Index: Python-3.10.13/Doc/c-api/memoryview.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/memoryview.rst ++++ Python-3.10.13/Doc/c-api/memoryview.rst +@@ -3,7 +3,7 @@ + .. _memoryview-objects: + + .. index:: +- object: memoryview ++ pair: object; memoryview + + MemoryView objects + ------------------ +Index: Python-3.10.13/Doc/c-api/method.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/method.rst ++++ Python-3.10.13/Doc/c-api/method.rst +@@ -5,7 +5,7 @@ + Instance Method Objects + ----------------------- + +-.. index:: object: instancemethod ++.. index:: pair: object; instancemethod + + An instance method is a wrapper for a :c:data:`PyCFunction` and the new way + to bind a :c:data:`PyCFunction` to a class object. It replaces the former call +@@ -47,7 +47,7 @@ to bind a :c:data:`PyCFunction` to a cla + Method Objects + -------------- + +-.. index:: object: method ++.. index:: pair: object; method + + Methods are bound function objects. Methods are always bound to an instance of + a user-defined class. Unbound methods (methods bound to a class object) are +Index: Python-3.10.13/Doc/c-api/module.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/module.rst ++++ Python-3.10.13/Doc/c-api/module.rst +@@ -5,7 +5,7 @@ + Module Objects + -------------- + +-.. index:: object: module ++.. index:: pair: object; module + + + .. c:var:: PyTypeObject PyModule_Type +Index: Python-3.10.13/Doc/c-api/none.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/none.rst ++++ Python-3.10.13/Doc/c-api/none.rst +@@ -5,7 +5,7 @@ + The ``None`` Object + ------------------- + +-.. index:: object: None ++.. index:: pair: object; None + + Note that the :c:type:`PyTypeObject` for ``None`` is not directly exposed in the + Python/C API. Since ``None`` is a singleton, testing for object identity (using +Index: Python-3.10.13/Doc/c-api/set.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/set.rst ++++ Python-3.10.13/Doc/c-api/set.rst +@@ -9,8 +9,8 @@ Set Objects + + + .. index:: +- object: set +- object: frozenset ++ pair: object; set ++ pair: object; frozenset + + This section details the public API for :class:`set` and :class:`frozenset` + objects. Any functionality not listed below is best accessed using either +@@ -107,7 +107,7 @@ or :class:`frozenset` or instances of th + + .. c:function:: Py_ssize_t PySet_Size(PyObject *anyset) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the length of a :class:`set` or :class:`frozenset` object. Equivalent to + ``len(anyset)``. Raises a :exc:`PyExc_SystemError` if *anyset* is not a +Index: Python-3.10.13/Doc/c-api/tuple.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/tuple.rst ++++ Python-3.10.13/Doc/c-api/tuple.rst +@@ -5,7 +5,7 @@ + Tuple Objects + ------------- + +-.. index:: object: tuple ++.. index:: pair: object; tuple + + + .. c:type:: PyTupleObject +Index: Python-3.10.13/Doc/c-api/type.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/type.rst ++++ Python-3.10.13/Doc/c-api/type.rst +@@ -5,7 +5,7 @@ + Type Objects + ------------ + +-.. index:: object: type ++.. index:: pair: object; type + + + .. c:type:: PyTypeObject +Index: Python-3.10.13/Doc/library/sys.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/sys.rst ++++ Python-3.10.13/Doc/library/sys.rst +@@ -398,7 +398,7 @@ always available. + an except clause." For any stack frame, only information about the exception + being currently handled is accessible. + +- .. index:: object: traceback ++ .. index:: pair: object; traceback + + If no exception is being handled anywhere on the stack, a tuple containing + three ``None`` values is returned. Otherwise, the values returned are +Index: Python-3.10.13/Doc/library/traceback.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/traceback.rst ++++ Python-3.10.13/Doc/library/traceback.rst +@@ -14,7 +14,7 @@ interpreter when it prints a stack trace + stack traces under program control, such as in a "wrapper" around the + interpreter. + +-.. index:: object: traceback ++.. index:: pair: object; traceback + + The module uses traceback objects --- this is the object type that is stored in + the :data:`sys.last_traceback` variable and returned as the third item from +Index: Python-3.10.13/Doc/tutorial/classes.rst +=================================================================== +--- Python-3.10.13.orig/Doc/tutorial/classes.rst ++++ Python-3.10.13/Doc/tutorial/classes.rst +@@ -344,7 +344,7 @@ list objects have methods called append, + However, in the following discussion, we'll use the term method exclusively to + mean methods of class instance objects, unless explicitly stated otherwise.) + +-.. index:: object: method ++.. index:: pair: object; method + + Valid method names of an instance object depend on its class. By definition, + all attributes of a class that are function objects define corresponding +Index: Python-3.10.13/Doc/tutorial/controlflow.rst +=================================================================== +--- Python-3.10.13.orig/Doc/tutorial/controlflow.rst ++++ Python-3.10.13/Doc/tutorial/controlflow.rst +@@ -46,7 +46,7 @@ details see :ref:`tut-match`. + ========================== + + .. index:: +- statement: for ++ pair: statement; for + + The :keyword:`for` statement in Python differs a bit from what you may be used + to in C or Pascal. Rather than always iterating over an arithmetic progression +Index: Python-3.10.13/Doc/c-api/import.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/import.rst ++++ Python-3.10.13/Doc/c-api/import.rst +@@ -41,7 +41,7 @@ Importing Modules + + .. c:function:: PyObject* PyImport_ImportModuleEx(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist) + +- .. index:: builtin: __import__ ++ .. index:: pair: built-in function; __import__ + + Import a module. This is best described by referring to the built-in Python + function :func:`__import__`. +@@ -120,7 +120,7 @@ Importing Modules + + .. c:function:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co) + +- .. index:: builtin: compile ++ .. index:: pair: built-in function; compile + + Given a module name (possibly of the form ``package.module``) and a code object + read from a Python bytecode file or obtained from the built-in function +Index: Python-3.10.13/Doc/c-api/mapping.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/mapping.rst ++++ Python-3.10.13/Doc/c-api/mapping.rst +@@ -20,7 +20,7 @@ See also :c:func:`PyObject_GetItem`, :c: + .. c:function:: Py_ssize_t PyMapping_Size(PyObject *o) + Py_ssize_t PyMapping_Length(PyObject *o) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Returns the number of keys in object *o* on success, and ``-1`` on failure. + This is equivalent to the Python expression ``len(o)``. +Index: Python-3.10.13/Doc/c-api/number.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/number.rst ++++ Python-3.10.13/Doc/c-api/number.rst +@@ -64,7 +64,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Divmod(PyObject *o1, PyObject *o2) + +- .. index:: builtin: divmod ++ .. index:: pair: built-in function; divmod + + See the built-in function :func:`divmod`. Returns ``NULL`` on failure. This is + the equivalent of the Python expression ``divmod(o1, o2)``. +@@ -72,7 +72,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Power(PyObject *o1, PyObject *o2, PyObject *o3) + +- .. index:: builtin: pow ++ .. index:: pair: built-in function; pow + + See the built-in function :func:`pow`. Returns ``NULL`` on failure. This is the + equivalent of the Python expression ``pow(o1, o2, o3)``, where *o3* is optional. +@@ -94,7 +94,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Absolute(PyObject *o) + +- .. index:: builtin: abs ++ .. index:: pair: built-in function; abs + + Returns the absolute value of *o*, or ``NULL`` on failure. This is the equivalent + of the Python expression ``abs(o)``. +@@ -192,7 +192,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_InPlacePower(PyObject *o1, PyObject *o2, PyObject *o3) + +- .. index:: builtin: pow ++ .. index:: pair: built-in function; pow + + See the built-in function :func:`pow`. Returns ``NULL`` on failure. The operation + is done *in-place* when *o1* supports it. This is the equivalent of the Python +@@ -238,7 +238,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Long(PyObject *o) + +- .. index:: builtin: int ++ .. index:: pair: built-in function; int + + Returns the *o* converted to an integer object on success, or ``NULL`` on + failure. This is the equivalent of the Python expression ``int(o)``. +@@ -246,7 +246,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Float(PyObject *o) + +- .. index:: builtin: float ++ .. index:: pair: built-in function; float + + Returns the *o* converted to a float object on success, or ``NULL`` on failure. + This is the equivalent of the Python expression ``float(o)``. +Index: Python-3.10.13/Doc/c-api/object.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/object.rst ++++ Python-3.10.13/Doc/c-api/object.rst +@@ -172,7 +172,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_Repr(PyObject *o) + +- .. index:: builtin: repr ++ .. index:: pair: built-in function; repr + + Compute a string representation of object *o*. Returns the string + representation on success, ``NULL`` on failure. This is the equivalent of the +@@ -184,7 +184,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_ASCII(PyObject *o) + +- .. index:: builtin: ascii ++ .. index:: pair: built-in function; ascii + + As :c:func:`PyObject_Repr`, compute a string representation of object *o*, but + escape the non-ASCII characters in the string returned by +@@ -209,7 +209,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_Bytes(PyObject *o) + +- .. index:: builtin: bytes ++ .. index:: pair: built-in function; bytes + + Compute a bytes representation of object *o*. ``NULL`` is returned on + failure and a bytes object on success. This is equivalent to the Python +@@ -260,7 +260,7 @@ Object Protocol + + .. c:function:: Py_hash_t PyObject_Hash(PyObject *o) + +- .. index:: builtin: hash ++ .. index:: pair: built-in function; hash + + Compute and return the hash value of an object *o*. On failure, return ``-1``. + This is the equivalent of the Python expression ``hash(o)``. +@@ -294,7 +294,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_Type(PyObject *o) + +- .. index:: builtin: type ++ .. index:: pair: built-in function; type + + When *o* is non-``NULL``, returns a type object corresponding to the object type + of object *o*. On failure, raises :exc:`SystemError` and returns ``NULL``. This +@@ -315,7 +315,7 @@ Object Protocol + .. c:function:: Py_ssize_t PyObject_Size(PyObject *o) + Py_ssize_t PyObject_Length(PyObject *o) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the length of object *o*. If the object *o* provides either the sequence + and mapping protocols, the sequence length is returned. On error, ``-1`` is +Index: Python-3.10.13/Doc/c-api/sequence.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/sequence.rst ++++ Python-3.10.13/Doc/c-api/sequence.rst +@@ -18,7 +18,7 @@ Sequence Protocol + .. c:function:: Py_ssize_t PySequence_Size(PyObject *o) + Py_ssize_t PySequence_Length(PyObject *o) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Returns the number of objects in sequence *o* on success, and ``-1`` on + failure. This is equivalent to the Python expression ``len(o)``. +@@ -120,7 +120,7 @@ Sequence Protocol + + .. c:function:: PyObject* PySequence_Tuple(PyObject *o) + +- .. index:: builtin: tuple ++ .. index:: pair: built-in function; tuple + + Return a tuple object with the same contents as the sequence or iterable *o*, + or ``NULL`` on failure. If *o* is a tuple, a new reference will be returned, +Index: Python-3.10.13/Doc/c-api/structures.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/structures.rst ++++ Python-3.10.13/Doc/c-api/structures.rst +@@ -351,7 +351,7 @@ method. + + .. data:: METH_CLASS + +- .. index:: builtin: classmethod ++ .. index:: pair: built-in function; classmethod + + The method will be passed the type object as the first parameter rather + than an instance of the type. This is used to create *class methods*, +@@ -361,7 +361,7 @@ method. + + .. data:: METH_STATIC + +- .. index:: builtin: staticmethod ++ .. index:: pair: built-in function; staticmethod + + The method will be passed ``NULL`` as the first parameter rather than an + instance of the type. This is used to create *static methods*, similar to +Index: Python-3.10.13/Doc/c-api/typeobj.rst +=================================================================== +--- Python-3.10.13.orig/Doc/c-api/typeobj.rst ++++ Python-3.10.13/Doc/c-api/typeobj.rst +@@ -803,7 +803,7 @@ and :c:type:`PyType_Type` effectively ac + + .. c:member:: reprfunc PyTypeObject.tp_repr + +- .. index:: builtin: repr ++ .. index:: pair: built-in function; repr + + An optional pointer to a function that implements the built-in function + :func:`repr`. +@@ -868,7 +868,7 @@ and :c:type:`PyType_Type` effectively ac + + .. c:member:: hashfunc PyTypeObject.tp_hash + +- .. index:: builtin: hash ++ .. index:: pair: built-in function; hash + + An optional pointer to a function that implements the built-in function + :func:`hash`. +Index: Python-3.10.13/Doc/extending/newtypes.rst +=================================================================== +--- Python-3.10.13.orig/Doc/extending/newtypes.rst ++++ Python-3.10.13/Doc/extending/newtypes.rst +@@ -149,7 +149,7 @@ done. This can be done using the :c:fun + + .. index:: + single: string; object representation +- builtin: repr ++ pair: built-in function; repr + + Object Presentation + ------------------- +Index: Python-3.10.13/Doc/library/dis.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/dis.rst ++++ Python-3.10.13/Doc/library/dis.rst +@@ -1207,7 +1207,7 @@ All of the following opcodes use their a + + .. opcode:: BUILD_SLICE (argc) + +- .. index:: builtin: slice ++ .. index:: pair: built-in function; slice + + Pushes a slice object on the stack. *argc* must be 2 or 3. If it is 2, + ``slice(TOS1, TOS)`` is pushed; if it is 3, ``slice(TOS2, TOS1, TOS)`` is +Index: Python-3.10.13/Doc/library/pprint.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/pprint.rst ++++ Python-3.10.13/Doc/library/pprint.rst +@@ -171,7 +171,7 @@ The :mod:`pprint` module also provides s + + .. function:: isreadable(object) + +- .. index:: builtin: eval ++ .. index:: pair: built-in function; eval + + Determine if the formatted representation of *object* is "readable", or can be + used to reconstruct the value using :func:`eval`. This always returns ``False`` +@@ -226,7 +226,7 @@ created. + + .. method:: PrettyPrinter.isreadable(object) + +- .. index:: builtin: eval ++ .. index:: pair: built-in function; eval + + Determine if the formatted representation of the object is "readable," or can be + used to reconstruct the value using :func:`eval`. Note that this returns +Index: Python-3.10.13/Doc/library/types.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/types.rst ++++ Python-3.10.13/Doc/library/types.rst +@@ -146,7 +146,7 @@ Standard names are defined for the follo + + .. class:: CodeType(**kwargs) + +- .. index:: builtin: compile ++ .. index:: pair: built-in function; compile + + The type for code objects such as returned by :func:`compile`. + +Index: Python-3.10.13/Doc/tutorial/stdlib.rst +=================================================================== +--- Python-3.10.13.orig/Doc/tutorial/stdlib.rst ++++ Python-3.10.13/Doc/tutorial/stdlib.rst +@@ -24,7 +24,7 @@ Be sure to use the ``import os`` style i + will keep :func:`os.open` from shadowing the built-in :func:`open` function which + operates much differently. + +-.. index:: builtin: help ++.. index:: pair: built-in function; help + + The built-in :func:`dir` and :func:`help` functions are useful as interactive + aids for working with large modules like :mod:`os`:: +Index: Python-3.10.13/Doc/library/imp.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/imp.rst ++++ Python-3.10.13/Doc/library/imp.rst +@@ -10,7 +10,7 @@ + .. deprecated:: 3.4 + The :mod:`imp` module is deprecated in favor of :mod:`importlib`. + +-.. index:: statement: import ++.. index:: pair: statement; import + + -------------- + +Index: Python-3.10.13/Doc/library/binascii.rst +=================================================================== +--- Python-3.10.13.orig/Doc/library/binascii.rst ++++ Python-3.10.13/Doc/library/binascii.rst +@@ -6,9 +6,9 @@ + representations. + + .. index:: +- module: uu +- module: base64 +- module: binhex ++ pair: module; uu ++ pair: module; base64 ++ pair: module; binhex + + -------------- + diff --git a/python310.changes b/python310.changes index e48a6a9..797456c 100644 --- a/python310.changes +++ b/python310.changes @@ -1,13 +1,15 @@ ------------------------------------------------------------------- Mon Sep 4 13:18:29 UTC 2023 - Daniel Garcia -- Update to 3.10.13 (bsc#1214692): +- Add fix-sphinx-72.patch to make it work with latest sphinx version + gh#python/cpython#97950 +- Update to 3.10.13: - gh-108310: Fixed an issue where instances of ssl.SSLSocket were vulnerable to a bypass of the TLS handshake and included protections (like certificate verification) and treating sent unencrypted data as if it were post-handshake TLS encrypted data. Security issue reported as CVE-2023-40217 by Aapo Oksman. Patch by - Gregory P. Smith. + Gregory P. Smith. (bsc#1214692) - gh-107845: tarfile.data_filter() now takes the location of symlinks into account when determining their target, so it will no longer reject some valid tarballs with diff --git a/python310.spec b/python310.spec index ecf83e5..720a3fb 100644 --- a/python310.spec +++ b/python310.spec @@ -180,6 +180,20 @@ Patch40: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM Revert-gh105127-left-tests.patch bsc#1210638 mcepl@suse.com # Partially revert previous patch Patch41: Revert-gh105127-left-tests.patch +# PATCH-FIX-UPSTREAM fix-sphinx-72.patch gh#python/cpython#97950 +# This is a patch with a lot of PR combined to make the doc work with +# sphinx 7.2 +# This patch has the following github pull requests: +# * gh#python/cpython#104151 +# * gh#python/cpython#104154 +# * gh#python/cpython#104155 +# * gh#python/cpython#104157 +# * gh#python/cpython#104159 +# * gh#python/cpython#104161 +# * gh#python/cpython#104163 +# * gh#python/cpython#104221 +# * gh#python/cpython#107246 +Patch42: fix-sphinx-72.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -455,6 +469,7 @@ other applications. %patch39 -p1 %patch40 -p1 %patch41 -p1 +%patch42 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac From fcbca39d564b11a75fdb193405c150083debbe0e653af22a8b599e88306751a8 Mon Sep 17 00:00:00 2001 From: Ruediger Oertel Date: Thu, 22 Feb 2024 19:03:47 +0000 Subject: [PATCH 10/23] Set link to python310.32578 via maintenance_release request --- CVE-2023-27043-email-parsing-errors.patch | 548 +++++-- Revert-gh105127-left-tests.patch | 283 ---- fix-sphinx-72.patch | 1702 ++++++++++----------- python310.changes | 12 +- python310.spec | 8 +- 5 files changed, 1209 insertions(+), 1344 deletions(-) delete mode 100644 Revert-gh105127-left-tests.patch diff --git a/CVE-2023-27043-email-parsing-errors.patch b/CVE-2023-27043-email-parsing-errors.patch index 873514a..df77785 100644 --- a/CVE-2023-27043-email-parsing-errors.patch +++ b/CVE-2023-27043-email-parsing-errors.patch @@ -1,74 +1,185 @@ --- - Doc/library/email.utils.rst | 26 +++ - Lib/email/utils.py | 63 +++++++ - Lib/test/test_email/test_email.py | 81 +++++++++- - Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst | 4 - 4 files changed, 164 insertions(+), 10 deletions(-) + Doc/library/email.utils.rst | 19 - + Lib/email/utils.py | 151 +++++++- + Lib/test/test_email/test_email.py | 187 +++++++++- + Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 + 4 files changed, 344 insertions(+), 21 deletions(-) -Index: Python-3.10.12/Doc/library/email.utils.rst -=================================================================== ---- Python-3.10.12.orig/Doc/library/email.utils.rst -+++ Python-3.10.12/Doc/library/email.utils.rst -@@ -67,6 +67,11 @@ of the new API. +--- 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. -+ .. versionchanged:: 3.12 -+ For security reasons, addresses that were ambiguous and could parse into -+ multiple different addresses now cause ``('', '')`` to be returned -+ instead of only one of the *potential* addresses. ++ 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') -@@ -89,7 +94,7 @@ of the new API. +@@ -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 `. Here's a simple +- :meth:`Message.get_all `. Here's a simple - example that gets all the recipients of a message:: -+ example that gets all the recipients of a message: ++ :meth:`Message.get_all `. ++ ++ If *strict* is true, use a strict parser which rejects malformed inputs. ++ ++ Here's a simple example that gets all the recipients of a message:: from email.utils import getaddresses -@@ -99,6 +104,25 @@ of the new API. +@@ -99,6 +107,9 @@ of the new API. resent_ccs = msg.get_all('resent-cc', []) all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) -+ When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')`` -+ is returned in its place. Other errors in parsing the list of -+ addresses such as a fieldvalue seemingly parsing into multiple -+ addresses may result in a list containing a single empty 2-tuple -+ ``[('', '')]`` being returned rather than returning potentially -+ invalid output. -+ -+ Example malformed input parsing: -+ -+ .. doctest:: -+ -+ >>> from email.utils import getaddresses -+ >>> getaddresses(['alice@example.com ', 'me@example.com']) -+ [('', '')] -+ -+ .. versionchanged:: 3.12 -+ The 2-tuple of ``('', '')`` in the returned values when parsing -+ fails were added as to address a security issue. ++ .. versionchanged:: 3.13 ++ Add *strict* optional parameter and reject malformed inputs by default. + .. function:: parsedate(date) -Index: Python-3.10.12/Lib/email/utils.py -=================================================================== ---- Python-3.10.12.orig/Lib/email/utils.py -+++ Python-3.10.12/Lib/email/utils.py -@@ -106,12 +106,54 @@ def formataddr(pair, charset='utf-8'): +--- a/Lib/email/utils.py ++++ b/Lib/email/utils.py +@@ -48,6 +48,7 @@ TICK = "'" + specialsre = re.compile(r'[][\\()<>@,:;".]') + escapesre = re.compile(r'[\\"]') + ++ + def _has_surrogates(s): + """Return True if s contains surrogate-escaped binary data.""" + # This check is based on the fact that unless there are surrogates, utf8 +@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'): return address ++def _iter_escaped_chars(addr): ++ pos = 0 ++ escape = False ++ for pos, ch in enumerate(addr): ++ if escape: ++ yield (pos, '\\' + ch) ++ escape = False ++ elif ch == '\\': ++ escape = True ++ else: ++ yield (pos, ch) ++ if escape: ++ yield (pos, '\\') ++ ++ ++def _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 + +-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 start < len(addr): ++ result.append(addr[start:]) ++ ++ return ''.join(result) ++ ++ ++supports_strict_parsing = True ++ ++def getaddresses(fieldvalues, *, strict=True): ++ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. ++ ++ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in ++ its place. ++ ++ If strict is true, use a strict parser which rejects malformed inputs. ++ """ ++ ++ # If strict is true, if the resulting list of parsed addresses is greater ++ # than the number of fieldvalues in the input list, a parsing error has ++ # occurred and consequently a list containing a single empty 2-tuple [('', ++ # '')] is returned in its place. This is done to avoid invalid output. ++ # ++ # Malformed input: getaddresses(['alice@example.com ']) ++ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')] ++ # Safe output: [('', '')] ++ ++ if not strict: ++ all = COMMASPACE.join(str(v) for v in fieldvalues) ++ a = _AddressList(all) ++ return a.addresslist ++ ++ fieldvalues = [str(v) for v in fieldvalues] ++ fieldvalues = _pre_parse_validation(fieldvalues) ++ addr = COMMASPACE.join(fieldvalues) ++ a = _AddressList(addr) ++ result = _post_parse_validation(a.addresslist) ++ ++ # Treat output as invalid if the number of addresses is not equal to the ++ # expected number of addresses. ++ n = 0 ++ for v in fieldvalues: ++ # When a comma is used in the Real Name part it is not a deliminator. ++ # So strip those out before counting the commas. ++ v = _strip_quoted_realnames(v) ++ # Expected number of addresses: 1 + number of commas ++ n += 1 + v.count(',') ++ if len(result) != n: ++ return [('', '')] ++ ++ return result ++ ++ ++def _check_parenthesis(addr): ++ # Ignore parenthesis in quoted real names. ++ addr = _strip_quoted_realnames(addr) ++ ++ opens = 0 ++ for pos, ch in _iter_escaped_chars(addr): ++ if ch == '(': ++ opens += 1 ++ elif ch == ')': ++ opens -= 1 ++ if opens < 0: ++ return False ++ return (opens == 0) ++ ++ +def _pre_parse_validation(email_header_fields): + accepted_values = [] + for v in email_header_fields: -+ s = v.replace('\\(', '').replace('\\)', '') -+ if s.count('(') != s.count(')'): ++ if not _check_parenthesis(v): + v = "('', '')" + accepted_values.append(v) + @@ -85,46 +196,32 @@ Index: Python-3.10.12/Lib/email/utils.py + accepted_values.append(v) + + return accepted_values -+ - - def getaddresses(fieldvalues): -- """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" -- all = COMMASPACE.join(str(v) for v in fieldvalues) -+ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. -+ -+ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in -+ its place. -+ -+ If the resulting list of parsed address is not the same as the number of -+ fieldvalues in the input list a parsing error has occurred. A list -+ containing a single empty 2-tuple [('', '')] is returned in its place. -+ This is done to avoid invalid output. -+ """ -+ fieldvalues = [str(v) for v in fieldvalues] -+ fieldvalues = _pre_parse_validation(fieldvalues) -+ all = COMMASPACE.join(v for v in fieldvalues) - a = _AddressList(all) -- return a.addresslist -+ result = _post_parse_validation(a.addresslist) -+ -+ n = 0 -+ for v in fieldvalues: -+ n += v.count(',') + 1 -+ -+ if len(result) != n: -+ return [('', '')] -+ -+ return result def _format_timetuple_and_zone(timetuple, zone): -@@ -212,9 +254,18 @@ def parseaddr(addr): +@@ -205,16 +321,33 @@ def parsedate_to_datetime(data): + tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) + + +-def parseaddr(addr): ++def parseaddr(addr, *, strict=True): + """ + Parse addr into its constituent realname and email address parts. + Return a tuple of realname and email address, unless the parse fails, in which case return a 2-tuple of ('', ''). ++ ++ If strict is True, use a strict parser which rejects malformed inputs. """ - addrs = _AddressList(addr).addresslist - if not addrs: - return '', '' ++ if not strict: ++ addrs = _AddressList(addr).addresslist ++ if not addrs: ++ return ('', '') ++ return addrs[0] ++ + if isinstance(addr, list): + addr = addr[0] + @@ -140,110 +237,225 @@ Index: Python-3.10.12/Lib/email/utils.py return addrs[0] -Index: Python-3.10.12/Lib/test/test_email/test_email.py -=================================================================== ---- Python-3.10.12.orig/Lib/test/test_email/test_email.py -+++ Python-3.10.12/Lib/test/test_email/test_email.py -@@ -3288,15 +3288,90 @@ Foo +--- a/Lib/test/test_email/test_email.py ++++ b/Lib/test/test_email/test_email.py +@@ -16,6 +16,7 @@ from unittest.mock import patch + + import email + import email.policy ++import email.utils + + from email.charset import Charset + from email.generator import Generator, DecodedGenerator, BytesGenerator +@@ -3288,15 +3289,137 @@ Foo [('Al Person', 'aperson@dom.ain'), ('Bud Person', 'bperson@dom.ain')]) -+ def test_getaddresses_parsing_errors(self): -+ """Test for parsing errors from CVE-2023-27043""" -+ eq = self.assertEqual -+ eq(utils.getaddresses(['alice@example.org(']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org)']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org<']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org>']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org@']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org,']), -+ [('', 'alice@example.org'), ('', 'bob@example.com')]) -+ eq(utils.getaddresses(['alice@example.org;']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org:']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org.']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org"']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org[']), -+ [('', '')]) -+ eq(utils.getaddresses(['alice@example.org]']), -+ [('', '')]) ++ 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 = ('', '') + -+ def test_parseaddr_parsing_errors(self): -+ """Test for parsing errors from CVE-2023-27043""" -+ eq = self.assertEqual -+ eq(utils.parseaddr(['alice@example.org(']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org)']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org<']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org>']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org@']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org,']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org;']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org:']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org.']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org"']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org[']), -+ ('', '')) -+ eq(utils.parseaddr(['alice@example.org]']), -+ ('', '')) ++ # Test utils.getaddresses() and utils.parseaddr() on malformed email ++ # addresses: default behavior (strict=True) rejects malformed address, ++ # and strict=False which tolerates malformed address. ++ for invalid_separator, expected_non_strict in ( ++ ('(', [(f'<{bob}>', alice)]), ++ (')', [('', alice), empty, ('', bob)]), ++ ('<', [('', alice), empty, ('', bob), empty]), ++ ('>', [('', alice), empty, ('', bob)]), ++ ('[', [('', f'{alice}[<{bob}>]')]), ++ (']', [('', alice), empty, ('', bob)]), ++ ('@', [empty, empty, ('', bob)]), ++ (';', [('', alice), empty, ('', bob)]), ++ (':', [('', alice), ('', bob)]), ++ ('.', [('', alice + '.'), ('', bob)]), ++ ('"', [('', alice), ('', f'<{bob}>')]), ++ ): ++ address = f'{alice}{invalid_separator}<{bob}>' ++ with self.subTest(address=address): ++ self.assertEqual(utils.getaddresses([address]), ++ [empty]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ expected_non_strict) ++ ++ self.assertEqual(utils.parseaddr([address]), ++ empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Comma (',') is treated differently depending on strict parameter. ++ # Comma without quotes. ++ address = f'{alice},<{bob}>' ++ self.assertEqual(utils.getaddresses([address]), ++ [('', alice), ('', bob)]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('', alice), ('', bob)]) ++ self.assertEqual(utils.parseaddr([address]), ++ empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Real name between quotes containing comma. ++ address = '"Alice, alice@example.org" ' ++ expected_strict = ('Alice, alice@example.org', 'bob@example.com') ++ self.assertEqual(utils.getaddresses([address]), [expected_strict]) ++ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) ++ self.assertEqual(utils.parseaddr([address]), expected_strict) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Valid parenthesis in comments. ++ address = 'alice@example.org (Alice)' ++ expected_strict = ('Alice', 'alice@example.org') ++ self.assertEqual(utils.getaddresses([address]), [expected_strict]) ++ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) ++ self.assertEqual(utils.parseaddr([address]), expected_strict) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Invalid parenthesis in comments. ++ address = 'alice@example.org )Alice(' ++ self.assertEqual(utils.getaddresses([address]), [empty]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('', 'alice@example.org'), ('', ''), ('', 'Alice')]) ++ self.assertEqual(utils.parseaddr([address]), empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Two addresses with quotes separated by comma. ++ address = '"Jane Doe" , "John Doe" ' ++ self.assertEqual(utils.getaddresses([address]), ++ [('Jane Doe', 'jane@example.net'), ++ ('John Doe', 'john@example.net')]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('Jane Doe', 'jane@example.net'), ++ ('John Doe', 'john@example.net')]) ++ self.assertEqual(utils.parseaddr([address]), empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Test email.utils.supports_strict_parsing attribute ++ self.assertEqual(email.utils.supports_strict_parsing, True) + def test_getaddresses_nasty(self): - eq = self.assertEqual - eq(utils.getaddresses(['foo: ;']), [('', '')]) +- eq = self.assertEqual +- eq(utils.getaddresses(['foo: ;']), [('', '')]) - eq(utils.getaddresses( - ['[]*-- =~$']), - [('', ''), ('', ''), ('', '*--')]) -+ eq(utils.getaddresses(['[]*-- =~$']), [('', '')]) - eq(utils.getaddresses( - ['foo: ;', '"Jason R. Mastaler" ']), - [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) -+ eq(utils.getaddresses( -+ [r'Pete(A nice \) chap) ']), -+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]) -+ eq(utils.getaddresses( -+ ['(Empty list)(start)Undisclosed recipients :(nobody(I know))']), -+ [('', '')]) -+ eq(utils.getaddresses( -+ ['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']), -+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]) -+ eq(utils.getaddresses( -+ ['John Doe ']), -+ [('John Doe (comment)', 'jdoe@machine.example')]) -+ eq(utils.getaddresses( -+ ['"Mary Smith: Personal Account" ']), -+ [('Mary Smith: Personal Account', 'smith@home.example')]) -+ eq(utils.getaddresses( -+ ['Undisclosed recipients:;']), -+ [('', '')]) -+ eq(utils.getaddresses( -+ [r', "Giant; \"Big\" Box" ']), -+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]) +- eq(utils.getaddresses( +- ['foo: ;', '"Jason R. Mastaler" ']), +- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) ++ for addresses, expected in ( ++ (['"Sürname, Firstname" '], ++ [('Sürname, Firstname', 'to@example.com')]), ++ ++ (['foo: ;'], ++ [('', '')]), ++ ++ (['foo: ;', '"Jason R. Mastaler" '], ++ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]), ++ ++ ([r'Pete(A nice \) chap) '], ++ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]), ++ ++ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'], ++ [('', '')]), ++ ++ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'], ++ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]), ++ ++ (['John Doe '], ++ [('John Doe (comment)', 'jdoe@machine.example')]), ++ ++ (['"Mary Smith: Personal Account" '], ++ [('Mary Smith: Personal Account', 'smith@home.example')]), ++ ++ (['Undisclosed recipients:;'], ++ [('', '')]), ++ ++ ([r', "Giant; \"Big\" Box" '], ++ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]), ++ ): ++ with self.subTest(addresses=addresses): ++ self.assertEqual(utils.getaddresses(addresses), ++ expected) ++ self.assertEqual(utils.getaddresses(addresses, strict=False), ++ expected) ++ ++ addresses = ['[]*-- =~$'] ++ self.assertEqual(utils.getaddresses(addresses), ++ [('', '')]) ++ self.assertEqual(utils.getaddresses(addresses, strict=False), ++ [('', ''), ('', ''), ('', '*--')]) def test_getaddresses_embedded_comment(self): """Test proper handling of a nested comment""" -Index: Python-3.10.12/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst -=================================================================== +@@ -3485,6 +3608,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" , "John Doe" ', ++ ' , ') ++ check(r'"Jane \"Doe\"." ', ++ ' ') ++ ++ # special cases ++ check(r'before"name"after', 'beforeafter') ++ check(r'before"name"', 'before') ++ check(r'b"name"', 'b') # single char ++ check(r'"name"after', 'after') ++ check(r'"name"a', 'a') # single char ++ check(r'"name"', '') ++ ++ # no change ++ for addr in ( ++ 'Jane Doe , John Doe ', ++ 'lone " quote', ++ ): ++ self.assertEqual(utils._strip_quoted_realnames(addr), addr) ++ ++ ++ def test_check_parenthesis(self): ++ addr = 'alice@example.net' ++ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice(')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)')) ++ ++ # Ignore real name between quotes ++ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}')) ++ + + # Test the iterator/generators + class TestIterators(TestEmailBase): --- /dev/null -+++ Python-3.10.12/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst -@@ -0,0 +1,4 @@ -+CVE-2023-27043: Prevent :func:`email.utils.parseaddr` -+and :func:`email.utils.getaddresses` from returning the realname portion of an -+invalid RFC2822 email header in the email address portion of the 2-tuple -+returned after being parsed by :class:`email._parseaddr.AddressList`. ++++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst +@@ -0,0 +1,8 @@ ++:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now ++return ``('', '')`` 2-tuples in more situations where invalid email ++addresses are encountered instead of potentially inaccurate values. Add ++optional *strict* parameter to these two functions: use ``strict=False`` to ++get the old behavior, accept malformed inputs. ++``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check ++if the *strict* paramater is available. Patch by Thomas Dwyer and Victor ++Stinner to improve the CVE-2023-27043 fix. diff --git a/Revert-gh105127-left-tests.patch b/Revert-gh105127-left-tests.patch deleted file mode 100644 index 74a92c2..0000000 --- a/Revert-gh105127-left-tests.patch +++ /dev/null @@ -1,283 +0,0 @@ -From 4288c623d62cf90d8e4444facb3379fb06d01140 Mon Sep 17 00:00:00 2001 -From: "Gregory P. Smith" -Date: Thu, 20 Jul 2023 20:30:52 -0700 -Subject: [PATCH] [3.12] gh-106669: Revert "gh-102988: Detect email address - parsing errors ... (GH-105127)" (GH-106733) - -This reverts commit 18dfbd035775c15533d13a98e56b1d2bf5c65f00. -Adds a regression test from the issue. - -See https://github.com/python/cpython/issues/106669.. -(cherry picked from commit a31dea1feb61793e48fa9aa5014f358352205c1d) - -Co-authored-by: Gregory P. Smith ---- - Doc/library/email.utils.rst | 26 -- - Lib/email/utils.py | 63 ------ - Lib/test/test_email/test_email.py | 96 +--------- - Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst | 5 - 4 files changed, 31 insertions(+), 159 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst - -Index: Python-3.10.13/Doc/library/email.utils.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/email.utils.rst -+++ Python-3.10.13/Doc/library/email.utils.rst -@@ -67,11 +67,6 @@ of the new API. - *email address* parts. Returns a tuple of that information, unless the parse - fails, in which case a 2-tuple of ``('', '')`` is returned. - -- .. versionchanged:: 3.12 -- For security reasons, addresses that were ambiguous and could parse into -- multiple different addresses now cause ``('', '')`` to be returned -- instead of only one of the *potential* addresses. -- - - .. function:: formataddr(pair, charset='utf-8') - -@@ -94,7 +89,7 @@ of the new API. - This method returns a list of 2-tuples of the form returned by ``parseaddr()``. - *fieldvalues* is a sequence of header field values as might be returned by - :meth:`Message.get_all `. Here's a simple -- example that gets all the recipients of a message: -+ example that gets all the recipients of a message:: - - from email.utils import getaddresses - -@@ -104,25 +99,6 @@ of the new API. - resent_ccs = msg.get_all('resent-cc', []) - all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) - -- When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')`` -- is returned in its place. Other errors in parsing the list of -- addresses such as a fieldvalue seemingly parsing into multiple -- addresses may result in a list containing a single empty 2-tuple -- ``[('', '')]`` being returned rather than returning potentially -- invalid output. -- -- Example malformed input parsing: -- -- .. doctest:: -- -- >>> from email.utils import getaddresses -- >>> getaddresses(['alice@example.com ', 'me@example.com']) -- [('', '')] -- -- .. versionchanged:: 3.12 -- The 2-tuple of ``('', '')`` in the returned values when parsing -- fails were added as to address a security issue. -- - - .. function:: parsedate(date) - -Index: Python-3.10.13/Lib/email/utils.py -=================================================================== ---- Python-3.10.13.orig/Lib/email/utils.py -+++ Python-3.10.13/Lib/email/utils.py -@@ -106,54 +106,12 @@ def formataddr(pair, charset='utf-8'): - return address - - --def _pre_parse_validation(email_header_fields): -- accepted_values = [] -- for v in email_header_fields: -- s = v.replace('\\(', '').replace('\\)', '') -- if s.count('(') != s.count(')'): -- 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 getaddresses(fieldvalues): -- """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. -- -- When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in -- its place. -- -- If the resulting list of parsed address is not the same as the number of -- fieldvalues in the input list a parsing error has occurred. A list -- containing a single empty 2-tuple [('', '')] is returned in its place. -- This is done to avoid invalid output. -- """ -- fieldvalues = [str(v) for v in fieldvalues] -- fieldvalues = _pre_parse_validation(fieldvalues) -- all = COMMASPACE.join(v for v in fieldvalues) -+ """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" -+ all = COMMASPACE.join(str(v) for v in fieldvalues) - a = _AddressList(all) -- result = _post_parse_validation(a.addresslist) -- -- n = 0 -- for v in fieldvalues: -- n += v.count(',') + 1 -- -- if len(result) != n: -- return [('', '')] -- -- return result -+ return a.addresslist - - - def _format_timetuple_and_zone(timetuple, zone): -@@ -254,18 +212,9 @@ def parseaddr(addr): - Return a tuple of realname and email address, unless the parse fails, in - which case return a 2-tuple of ('', ''). - """ -- 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 ('', '') -- -+ addrs = _AddressList(addr).addresslist -+ if not addrs: -+ return '', '' - return addrs[0] - - -Index: Python-3.10.13/Lib/test/test_email/test_email.py -=================================================================== ---- Python-3.10.13.orig/Lib/test/test_email/test_email.py -+++ Python-3.10.13/Lib/test/test_email/test_email.py -@@ -3288,90 +3288,32 @@ Foo - [('Al Person', 'aperson@dom.ain'), - ('Bud Person', 'bperson@dom.ain')]) - -- def test_getaddresses_parsing_errors(self): -- """Test for parsing errors from CVE-2023-27043""" -- eq = self.assertEqual -- eq(utils.getaddresses(['alice@example.org(']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org)']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org<']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org>']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org@']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org,']), -- [('', 'alice@example.org'), ('', 'bob@example.com')]) -- eq(utils.getaddresses(['alice@example.org;']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org:']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org.']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org"']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org[']), -- [('', '')]) -- eq(utils.getaddresses(['alice@example.org]']), -- [('', '')]) -- -- def test_parseaddr_parsing_errors(self): -- """Test for parsing errors from CVE-2023-27043""" -- eq = self.assertEqual -- eq(utils.parseaddr(['alice@example.org(']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org)']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org<']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org>']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org@']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org,']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org;']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org:']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org.']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org"']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org[']), -- ('', '')) -- eq(utils.parseaddr(['alice@example.org]']), -- ('', '')) -+ def test_getaddresses_comma_in_name(self): -+ """GH-106669 regression test.""" -+ self.assertEqual( -+ utils.getaddresses( -+ [ -+ '"Bud, Person" ', -+ 'aperson@dom.ain (Al Person)', -+ '"Mariusz Felisiak" ', -+ ] -+ ), -+ [ -+ ('Bud, Person', 'bperson@dom.ain'), -+ ('Al Person', 'aperson@dom.ain'), -+ ('Mariusz Felisiak', 'to@example.com'), -+ ], -+ ) - - def test_getaddresses_nasty(self): - eq = self.assertEqual - eq(utils.getaddresses(['foo: ;']), [('', '')]) -- eq(utils.getaddresses(['[]*-- =~$']), [('', '')]) -+ eq(utils.getaddresses( -+ ['[]*-- =~$']), -+ [('', ''), ('', ''), ('', '*--')]) - eq(utils.getaddresses( - ['foo: ;', '"Jason R. Mastaler" ']), - [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) -- eq(utils.getaddresses( -- [r'Pete(A nice \) chap) ']), -- [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]) -- eq(utils.getaddresses( -- ['(Empty list)(start)Undisclosed recipients :(nobody(I know))']), -- [('', '')]) -- eq(utils.getaddresses( -- ['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']), -- [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]) -- eq(utils.getaddresses( -- ['John Doe ']), -- [('John Doe (comment)', 'jdoe@machine.example')]) -- eq(utils.getaddresses( -- ['"Mary Smith: Personal Account" ']), -- [('Mary Smith: Personal Account', 'smith@home.example')]) -- eq(utils.getaddresses( -- ['Undisclosed recipients:;']), -- [('', '')]) -- eq(utils.getaddresses( -- [r', "Giant; \"Big\" Box" ']), -- [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]) - - def test_getaddresses_embedded_comment(self): - """Test proper handling of a nested comment""" -Index: Python-3.10.13/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst -=================================================================== ---- Python-3.10.13.orig/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst -+++ Python-3.10.13/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst -@@ -1,3 +1,8 @@ -+Reverted the :mod:`email.utils` security improvement change released in -+3.12beta4 that unintentionally caused :mod:`email.utils.getaddresses` to fail -+to parse email addresses with a comma in the quoted name field. -+See :gh:`106669`. -+ - CVE-2023-27043: Prevent :func:`email.utils.parseaddr` - and :func:`email.utils.getaddresses` from returning the realname portion of an - invalid RFC2822 email header in the email address portion of the 2-tuple diff --git a/fix-sphinx-72.patch b/fix-sphinx-72.patch index 22d2366..dc3d130 100644 --- a/fix-sphinx-72.patch +++ b/fix-sphinx-72.patch @@ -1,66 +1,172 @@ -Index: Python-3.10.13/Doc/conf.py -=================================================================== ---- Python-3.10.13.orig/Doc/conf.py -+++ Python-3.10.13/Doc/conf.py -@@ -61,6 +61,11 @@ smartquotes_excludes = { - # Avoid a warning with Sphinx >= 2.0 - master_doc = 'contents' +--- + Doc/c-api/bytearray.rst | 2 + Doc/c-api/bytes.rst | 2 + Doc/c-api/capsule.rst | 2 + Doc/c-api/complex.rst | 2 + Doc/c-api/concrete.rst | 6 - + Doc/c-api/dict.rst | 4 + Doc/c-api/exceptions.rst | 6 - + Doc/c-api/file.rst | 2 + Doc/c-api/float.rst | 2 + Doc/c-api/function.rst | 2 + Doc/c-api/import.rst | 4 + Doc/c-api/init.rst | 14 +-- + Doc/c-api/intro.rst | 8 - + Doc/c-api/list.rst | 6 - + Doc/c-api/long.rst | 4 + Doc/c-api/mapping.rst | 2 + Doc/c-api/memoryview.rst | 2 + Doc/c-api/method.rst | 4 + Doc/c-api/module.rst | 2 + Doc/c-api/none.rst | 2 + Doc/c-api/number.rst | 12 +- + Doc/c-api/object.rst | 12 +- + Doc/c-api/sequence.rst | 4 + Doc/c-api/set.rst | 6 - + Doc/c-api/structures.rst | 4 + Doc/c-api/tuple.rst | 2 + Doc/c-api/type.rst | 2 + Doc/c-api/typeobj.rst | 4 + Doc/conf.py | 5 + + Doc/extending/newtypes.rst | 2 + Doc/library/_thread.rst | 2 + Doc/library/binascii.rst | 6 - + Doc/library/cmath.rst | 2 + Doc/library/copy.rst | 2 + Doc/library/copyreg.rst | 4 + Doc/library/dis.rst | 2 + Doc/library/exceptions.rst | 10 +- + Doc/library/fnmatch.rst | 4 + Doc/library/functions.rst | 10 +- + Doc/library/http.client.rst | 2 + Doc/library/imp.rst | 2 + Doc/library/internet.rst | 2 + Doc/library/locale.rst | 4 + Doc/library/marshal.rst | 4 + Doc/library/os.path.rst | 2 + Doc/library/os.rst | 4 + Doc/library/pdb.rst | 4 + Doc/library/posix.rst | 2 + Doc/library/pprint.rst | 4 + Doc/library/pwd.rst | 2 + Doc/library/pyexpat.rst | 2 + Doc/library/runpy.rst | 4 + Doc/library/shelve.rst | 6 - + Doc/library/site.rst | 6 - + Doc/library/socket.rst | 4 + Doc/library/stdtypes.rst | 146 ++++++++++++++++---------------- + Doc/library/sys.rst | 2 + Doc/library/traceback.rst | 2 + Doc/library/types.rst | 2 + Doc/reference/compound_stmts.rst | 90 +++++++++---------- + Doc/reference/datamodel.rst | 154 +++++++++++++++++----------------- + Doc/reference/executionmodel.rst | 2 + Doc/reference/expressions.rst | 134 ++++++++++++++--------------- + Doc/reference/simple_stmts.rst | 74 ++++++++-------- + Doc/reference/toplevel_components.rst | 10 +- + Doc/tools/extensions/pyspecific.py | 25 +++++ + Doc/tutorial/classes.rst | 2 + Doc/tutorial/controlflow.rst | 2 + Doc/tutorial/inputoutput.rst | 6 - + Doc/tutorial/modules.rst | 4 + Doc/tutorial/stdlib.rst | 2 + 71 files changed, 457 insertions(+), 427 deletions(-) + +--- a/Doc/c-api/bytearray.rst ++++ b/Doc/c-api/bytearray.rst +@@ -5,7 +5,7 @@ + Byte Array Objects + ------------------ -+# Allow translation of index directives -+gettext_additional_targets = [ -+ 'index', -+] -+ - # Options for HTML output - # ----------------------- - -Index: Python-3.10.13/Doc/tools/extensions/pyspecific.py -=================================================================== ---- Python-3.10.13.orig/Doc/tools/extensions/pyspecific.py -+++ Python-3.10.13/Doc/tools/extensions/pyspecific.py -@@ -623,6 +623,30 @@ def process_audit_events(app, doctree, f - node.replace_self(table) +-.. index:: object: bytearray ++.. index:: pair: object; bytearray -+def patch_pairindextypes(app) -> None: -+ if app.builder.name != 'gettext': -+ return -+ -+ # allow translating deprecated index entries -+ try: -+ from sphinx.domains.python import pairindextypes -+ except ImportError: -+ pass -+ else: -+ # Sphinx checks if a 'pair' type entry on an index directive is one of -+ # the Sphinx-translated pairindextypes values. As we intend to move -+ # away from this, we need Sphinx to believe that these values don't -+ # exist, by deleting them when using the gettext builder. -+ -+ pairindextypes.pop('module', None) -+ pairindextypes.pop('keyword', None) -+ pairindextypes.pop('operator', None) -+ pairindextypes.pop('object', None) -+ pairindextypes.pop('exception', None) -+ pairindextypes.pop('statement', None) -+ pairindextypes.pop('builtin', None) -+ -+ - def setup(app): - app.add_role('issue', issue_role) - app.add_role('gh', gh_issue_role) -@@ -645,6 +669,7 @@ def setup(app): - app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod) - app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod) - app.add_directive('miscnews', MiscNews) -+ app.connect('builder-inited', patch_pairindextypes) - app.connect('doctree-resolved', process_audit_events) - app.connect('env-merge-info', audit_events_merge) - app.connect('env-purge-doc', audit_events_purge) -Index: Python-3.10.13/Doc/c-api/exceptions.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/exceptions.rst -+++ Python-3.10.13/Doc/c-api/exceptions.rst + .. c:type:: PyByteArrayObject +--- a/Doc/c-api/bytes.rst ++++ b/Doc/c-api/bytes.rst +@@ -8,7 +8,7 @@ Bytes Objects + These functions raise :exc:`TypeError` when expecting a bytes parameter and + called with a non-bytes parameter. + +-.. index:: object: bytes ++.. index:: pair: object; bytes + + + .. c:type:: PyBytesObject +--- a/Doc/c-api/capsule.rst ++++ b/Doc/c-api/capsule.rst +@@ -5,7 +5,7 @@ + Capsules + -------- + +-.. index:: object: Capsule ++.. index:: pair: object; Capsule + + Refer to :ref:`using-capsules` for more information on using these objects. + +--- a/Doc/c-api/complex.rst ++++ b/Doc/c-api/complex.rst +@@ -5,7 +5,7 @@ + Complex Number Objects + ---------------------- + +-.. index:: object: complex number ++.. index:: pair: object; complex number + + Python's complex number objects are implemented as two distinct types when + viewed from the C API: one is the Python object exposed to Python programs, and +--- a/Doc/c-api/concrete.rst ++++ b/Doc/c-api/concrete.rst +@@ -40,7 +40,7 @@ This section describes Python type objec + Numeric Objects + =============== + +-.. index:: object: numeric ++.. index:: pair: object; numeric + + .. toctree:: + +@@ -55,7 +55,7 @@ Numeric Objects + Sequence Objects + ================ + +-.. index:: object: sequence ++.. index:: pair: object; sequence + + Generic operations on sequence objects were discussed in the previous chapter; + this section deals with the specific kinds of sequence objects that are +@@ -77,7 +77,7 @@ intrinsic to the Python language. + Container Objects + ================= + +-.. index:: object: mapping ++.. index:: pair: object; mapping + + .. toctree:: + +--- a/Doc/c-api/dict.rst ++++ b/Doc/c-api/dict.rst +@@ -5,7 +5,7 @@ + Dictionary Objects + ------------------ + +-.. index:: object: dictionary ++.. index:: pair: object; dictionary + + + .. c:type:: PyDictObject +@@ -154,7 +154,7 @@ Dictionary Objects + + .. c:function:: Py_ssize_t PyDict_Size(PyObject *p) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the number of items in the dictionary. This is equivalent to + ``len(p)`` on a dictionary. +--- a/Doc/c-api/exceptions.rst ++++ b/Doc/c-api/exceptions.rst @@ -503,7 +503,7 @@ Signal Handling .. c:function:: int PyErr_CheckSignals() @@ -88,10 +194,61 @@ Index: Python-3.10.13/Doc/c-api/exceptions.rst single: KeyboardInterrupt (built-in exception) Simulate the effect of a signal arriving. The next time -Index: Python-3.10.13/Doc/c-api/init.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/init.rst -+++ Python-3.10.13/Doc/c-api/init.rst +--- a/Doc/c-api/file.rst ++++ b/Doc/c-api/file.rst +@@ -5,7 +5,7 @@ + File Objects + ------------ + +-.. index:: object: file ++.. index:: pair: object; file + + These APIs are a minimal emulation of the Python 2 C API for built-in file + objects, which used to rely on the buffered I/O (:c:expr:`FILE*`) support +--- a/Doc/c-api/float.rst ++++ b/Doc/c-api/float.rst +@@ -5,7 +5,7 @@ + Floating Point Objects + ---------------------- + +-.. index:: object: floating point ++.. index:: pair: object; floating point + + + .. c:type:: PyFloatObject +--- a/Doc/c-api/function.rst ++++ b/Doc/c-api/function.rst +@@ -5,7 +5,7 @@ + Function Objects + ---------------- + +-.. index:: object: function ++.. index:: pair: object; function + + There are a few functions specific to Python functions. + +--- a/Doc/c-api/import.rst ++++ b/Doc/c-api/import.rst +@@ -41,7 +41,7 @@ Importing Modules + + .. c:function:: PyObject* PyImport_ImportModuleEx(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist) + +- .. index:: builtin: __import__ ++ .. index:: pair: built-in function; __import__ + + Import a module. This is best described by referring to the built-in Python + function :func:`__import__`. +@@ -120,7 +120,7 @@ Importing Modules + + .. c:function:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co) + +- .. index:: builtin: compile ++ .. index:: pair: built-in function; compile + + Given a module name (possibly of the form ``package.module``) and a code object + read from a Python bytecode file or obtained from the built-in function +--- a/Doc/c-api/init.rst ++++ b/Doc/c-api/init.rst @@ -233,9 +233,9 @@ Initializing and finalizing the interpre single: PyEval_InitThreads() single: modules (in module sys) @@ -127,10 +284,8 @@ Index: Python-3.10.13/Doc/c-api/init.rst single: stdout (in module sys) single: stderr (in module sys) single: stdin (in module sys) -Index: Python-3.10.13/Doc/c-api/intro.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/intro.rst -+++ Python-3.10.13/Doc/c-api/intro.rst +--- a/Doc/c-api/intro.rst ++++ b/Doc/c-api/intro.rst @@ -226,7 +226,7 @@ complete listing. Objects, Types and Reference Counts =================================== @@ -153,10 +308,355 @@ Index: Python-3.10.13/Doc/c-api/intro.rst triple: module; search; path single: path (in module sys) -Index: Python-3.10.13/Doc/library/_thread.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/_thread.rst -+++ Python-3.10.13/Doc/library/_thread.rst +--- a/Doc/c-api/list.rst ++++ b/Doc/c-api/list.rst +@@ -5,7 +5,7 @@ + List Objects + ------------ + +-.. index:: object: list ++.. index:: pair: object; list + + + .. c:type:: PyListObject +@@ -45,7 +45,7 @@ List Objects + + .. c:function:: Py_ssize_t PyList_Size(PyObject *list) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the length of the list object in *list*; this is equivalent to + ``len(list)`` on a list object. +@@ -138,7 +138,7 @@ List Objects + + .. c:function:: PyObject* PyList_AsTuple(PyObject *list) + +- .. index:: builtin: tuple ++ .. index:: pair: built-in function; tuple + + Return a new tuple object containing the contents of *list*; equivalent to + ``tuple(list)``. +--- a/Doc/c-api/long.rst ++++ b/Doc/c-api/long.rst +@@ -5,8 +5,8 @@ + Integer Objects + --------------- + +-.. index:: object: long integer +- object: integer ++.. index:: pair: object; long integer ++ pair: object; integer + + All integers are implemented as "long" integer objects of arbitrary size. + +--- a/Doc/c-api/mapping.rst ++++ b/Doc/c-api/mapping.rst +@@ -20,7 +20,7 @@ See also :c:func:`PyObject_GetItem`, :c: + .. c:function:: Py_ssize_t PyMapping_Size(PyObject *o) + Py_ssize_t PyMapping_Length(PyObject *o) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Returns the number of keys in object *o* on success, and ``-1`` on failure. + This is equivalent to the Python expression ``len(o)``. +--- a/Doc/c-api/memoryview.rst ++++ b/Doc/c-api/memoryview.rst +@@ -3,7 +3,7 @@ + .. _memoryview-objects: + + .. index:: +- object: memoryview ++ pair: object; memoryview + + MemoryView objects + ------------------ +--- a/Doc/c-api/method.rst ++++ b/Doc/c-api/method.rst +@@ -5,7 +5,7 @@ + Instance Method Objects + ----------------------- + +-.. index:: object: instancemethod ++.. index:: pair: object; instancemethod + + An instance method is a wrapper for a :c:data:`PyCFunction` and the new way + to bind a :c:data:`PyCFunction` to a class object. It replaces the former call +@@ -47,7 +47,7 @@ to bind a :c:data:`PyCFunction` to a cla + Method Objects + -------------- + +-.. index:: object: method ++.. index:: pair: object; method + + Methods are bound function objects. Methods are always bound to an instance of + a user-defined class. Unbound methods (methods bound to a class object) are +--- a/Doc/c-api/module.rst ++++ b/Doc/c-api/module.rst +@@ -5,7 +5,7 @@ + Module Objects + -------------- + +-.. index:: object: module ++.. index:: pair: object; module + + + .. c:var:: PyTypeObject PyModule_Type +--- a/Doc/c-api/none.rst ++++ b/Doc/c-api/none.rst +@@ -5,7 +5,7 @@ + The ``None`` Object + ------------------- + +-.. index:: object: None ++.. index:: pair: object; None + + Note that the :c:type:`PyTypeObject` for ``None`` is not directly exposed in the + Python/C API. Since ``None`` is a singleton, testing for object identity (using +--- a/Doc/c-api/number.rst ++++ b/Doc/c-api/number.rst +@@ -64,7 +64,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Divmod(PyObject *o1, PyObject *o2) + +- .. index:: builtin: divmod ++ .. index:: pair: built-in function; divmod + + See the built-in function :func:`divmod`. Returns ``NULL`` on failure. This is + the equivalent of the Python expression ``divmod(o1, o2)``. +@@ -72,7 +72,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Power(PyObject *o1, PyObject *o2, PyObject *o3) + +- .. index:: builtin: pow ++ .. index:: pair: built-in function; pow + + See the built-in function :func:`pow`. Returns ``NULL`` on failure. This is the + equivalent of the Python expression ``pow(o1, o2, o3)``, where *o3* is optional. +@@ -94,7 +94,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Absolute(PyObject *o) + +- .. index:: builtin: abs ++ .. index:: pair: built-in function; abs + + Returns the absolute value of *o*, or ``NULL`` on failure. This is the equivalent + of the Python expression ``abs(o)``. +@@ -192,7 +192,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_InPlacePower(PyObject *o1, PyObject *o2, PyObject *o3) + +- .. index:: builtin: pow ++ .. index:: pair: built-in function; pow + + See the built-in function :func:`pow`. Returns ``NULL`` on failure. The operation + is done *in-place* when *o1* supports it. This is the equivalent of the Python +@@ -238,7 +238,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Long(PyObject *o) + +- .. index:: builtin: int ++ .. index:: pair: built-in function; int + + Returns the *o* converted to an integer object on success, or ``NULL`` on + failure. This is the equivalent of the Python expression ``int(o)``. +@@ -246,7 +246,7 @@ Number Protocol + + .. c:function:: PyObject* PyNumber_Float(PyObject *o) + +- .. index:: builtin: float ++ .. index:: pair: built-in function; float + + Returns the *o* converted to a float object on success, or ``NULL`` on failure. + This is the equivalent of the Python expression ``float(o)``. +--- a/Doc/c-api/object.rst ++++ b/Doc/c-api/object.rst +@@ -172,7 +172,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_Repr(PyObject *o) + +- .. index:: builtin: repr ++ .. index:: pair: built-in function; repr + + Compute a string representation of object *o*. Returns the string + representation on success, ``NULL`` on failure. This is the equivalent of the +@@ -184,7 +184,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_ASCII(PyObject *o) + +- .. index:: builtin: ascii ++ .. index:: pair: built-in function; ascii + + As :c:func:`PyObject_Repr`, compute a string representation of object *o*, but + escape the non-ASCII characters in the string returned by +@@ -209,7 +209,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_Bytes(PyObject *o) + +- .. index:: builtin: bytes ++ .. index:: pair: built-in function; bytes + + Compute a bytes representation of object *o*. ``NULL`` is returned on + failure and a bytes object on success. This is equivalent to the Python +@@ -260,7 +260,7 @@ Object Protocol + + .. c:function:: Py_hash_t PyObject_Hash(PyObject *o) + +- .. index:: builtin: hash ++ .. index:: pair: built-in function; hash + + Compute and return the hash value of an object *o*. On failure, return ``-1``. + This is the equivalent of the Python expression ``hash(o)``. +@@ -294,7 +294,7 @@ Object Protocol + + .. c:function:: PyObject* PyObject_Type(PyObject *o) + +- .. index:: builtin: type ++ .. index:: pair: built-in function; type + + When *o* is non-``NULL``, returns a type object corresponding to the object type + of object *o*. On failure, raises :exc:`SystemError` and returns ``NULL``. This +@@ -315,7 +315,7 @@ Object Protocol + .. c:function:: Py_ssize_t PyObject_Size(PyObject *o) + Py_ssize_t PyObject_Length(PyObject *o) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the length of object *o*. If the object *o* provides either the sequence + and mapping protocols, the sequence length is returned. On error, ``-1`` is +--- a/Doc/c-api/sequence.rst ++++ b/Doc/c-api/sequence.rst +@@ -18,7 +18,7 @@ Sequence Protocol + .. c:function:: Py_ssize_t PySequence_Size(PyObject *o) + Py_ssize_t PySequence_Length(PyObject *o) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Returns the number of objects in sequence *o* on success, and ``-1`` on + failure. This is equivalent to the Python expression ``len(o)``. +@@ -120,7 +120,7 @@ Sequence Protocol + + .. c:function:: PyObject* PySequence_Tuple(PyObject *o) + +- .. index:: builtin: tuple ++ .. index:: pair: built-in function; tuple + + Return a tuple object with the same contents as the sequence or iterable *o*, + or ``NULL`` on failure. If *o* is a tuple, a new reference will be returned, +--- a/Doc/c-api/set.rst ++++ b/Doc/c-api/set.rst +@@ -9,8 +9,8 @@ Set Objects + + + .. index:: +- object: set +- object: frozenset ++ pair: object; set ++ pair: object; frozenset + + This section details the public API for :class:`set` and :class:`frozenset` + objects. Any functionality not listed below is best accessed using either +@@ -107,7 +107,7 @@ or :class:`frozenset` or instances of th + + .. c:function:: Py_ssize_t PySet_Size(PyObject *anyset) + +- .. index:: builtin: len ++ .. index:: pair: built-in function; len + + Return the length of a :class:`set` or :class:`frozenset` object. Equivalent to + ``len(anyset)``. Raises a :exc:`PyExc_SystemError` if *anyset* is not a +--- a/Doc/c-api/structures.rst ++++ b/Doc/c-api/structures.rst +@@ -351,7 +351,7 @@ method. + + .. data:: METH_CLASS + +- .. index:: builtin: classmethod ++ .. index:: pair: built-in function; classmethod + + The method will be passed the type object as the first parameter rather + than an instance of the type. This is used to create *class methods*, +@@ -361,7 +361,7 @@ method. + + .. data:: METH_STATIC + +- .. index:: builtin: staticmethod ++ .. index:: pair: built-in function; staticmethod + + The method will be passed ``NULL`` as the first parameter rather than an + instance of the type. This is used to create *static methods*, similar to +--- a/Doc/c-api/tuple.rst ++++ b/Doc/c-api/tuple.rst +@@ -5,7 +5,7 @@ + Tuple Objects + ------------- + +-.. index:: object: tuple ++.. index:: pair: object; tuple + + + .. c:type:: PyTupleObject +--- a/Doc/c-api/type.rst ++++ b/Doc/c-api/type.rst +@@ -5,7 +5,7 @@ + Type Objects + ------------ + +-.. index:: object: type ++.. index:: pair: object; type + + + .. c:type:: PyTypeObject +--- a/Doc/c-api/typeobj.rst ++++ b/Doc/c-api/typeobj.rst +@@ -803,7 +803,7 @@ and :c:type:`PyType_Type` effectively ac + + .. c:member:: reprfunc PyTypeObject.tp_repr + +- .. index:: builtin: repr ++ .. index:: pair: built-in function; repr + + An optional pointer to a function that implements the built-in function + :func:`repr`. +@@ -868,7 +868,7 @@ and :c:type:`PyType_Type` effectively ac + + .. c:member:: hashfunc PyTypeObject.tp_hash + +- .. index:: builtin: hash ++ .. index:: pair: built-in function; hash + + An optional pointer to a function that implements the built-in function + :func:`hash`. +--- a/Doc/conf.py ++++ b/Doc/conf.py +@@ -61,6 +61,11 @@ smartquotes_excludes = { + # Avoid a warning with Sphinx >= 2.0 + master_doc = 'contents' + ++# Allow translation of index directives ++gettext_additional_targets = [ ++ 'index', ++] ++ + # Options for HTML output + # ----------------------- + +--- a/Doc/extending/newtypes.rst ++++ b/Doc/extending/newtypes.rst +@@ -149,7 +149,7 @@ done. This can be done using the :c:fun + + .. index:: + single: string; object representation +- builtin: repr ++ pair: built-in function; repr + + Object Presentation + ------------------- +--- a/Doc/library/_thread.rst ++++ b/Doc/library/_thread.rst @@ -204,7 +204,7 @@ In addition to these methods, lock objec **Caveats:** @@ -166,10 +666,23 @@ Index: Python-3.10.13/Doc/library/_thread.rst * Threads interact strangely with interrupts: the :exc:`KeyboardInterrupt` exception will be received by an arbitrary thread. (When the :mod:`signal` -Index: Python-3.10.13/Doc/library/cmath.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/cmath.rst -+++ Python-3.10.13/Doc/library/cmath.rst +--- a/Doc/library/binascii.rst ++++ b/Doc/library/binascii.rst +@@ -6,9 +6,9 @@ + representations. + + .. index:: +- module: uu +- module: base64 +- module: binhex ++ pair: module; uu ++ pair: module; base64 ++ pair: module; binhex + + -------------- + +--- a/Doc/library/cmath.rst ++++ b/Doc/library/cmath.rst @@ -301,7 +301,7 @@ Constants .. versionadded:: 3.6 @@ -179,10 +692,8 @@ Index: Python-3.10.13/Doc/library/cmath.rst Note that the selection of functions is similar, but not identical, to that in module :mod:`math`. The reason for having two modules is that some users aren't -Index: Python-3.10.13/Doc/library/copy.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/copy.rst -+++ Python-3.10.13/Doc/library/copy.rst +--- a/Doc/library/copy.rst ++++ b/Doc/library/copy.rst @@ -68,7 +68,7 @@ Shallow copies of dictionaries can be ma of lists by assigning a slice of the entire list, for example, ``copied_list = original_list[:]``. @@ -192,10 +703,8 @@ Index: Python-3.10.13/Doc/library/copy.rst Classes can use the same interfaces to control copying that they use to control pickling. See the description of module :mod:`pickle` for information on these -Index: Python-3.10.13/Doc/library/copyreg.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/copyreg.rst -+++ Python-3.10.13/Doc/library/copyreg.rst +--- a/Doc/library/copyreg.rst ++++ b/Doc/library/copyreg.rst @@ -7,8 +7,8 @@ **Source code:** :source:`Lib/copyreg.py` @@ -207,10 +716,19 @@ Index: Python-3.10.13/Doc/library/copyreg.rst -------------- -Index: Python-3.10.13/Doc/library/exceptions.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/exceptions.rst -+++ Python-3.10.13/Doc/library/exceptions.rst +--- a/Doc/library/dis.rst ++++ b/Doc/library/dis.rst +@@ -1207,7 +1207,7 @@ All of the following opcodes use their a + + .. opcode:: BUILD_SLICE (argc) + +- .. index:: builtin: slice ++ .. index:: pair: built-in function; slice + + Pushes a slice object on the stack. *argc* must be 2 or 3. If it is 2, + ``slice(TOS1, TOS)`` is pushed; if it is 3, ``slice(TOS2, TOS1, TOS)`` is +--- a/Doc/library/exceptions.rst ++++ b/Doc/library/exceptions.rst @@ -4,8 +4,8 @@ Built-in Exceptions =================== @@ -249,10 +767,8 @@ Index: Python-3.10.13/Doc/library/exceptions.rst This exception is raised when a system function returns a system-related error, including I/O failures such as "file not found" or "disk full" -Index: Python-3.10.13/Doc/library/fnmatch.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/fnmatch.rst -+++ Python-3.10.13/Doc/library/fnmatch.rst +--- a/Doc/library/fnmatch.rst ++++ b/Doc/library/fnmatch.rst @@ -8,7 +8,7 @@ .. index:: single: filenames; wildcard expansion @@ -271,10 +787,8 @@ Index: Python-3.10.13/Doc/library/fnmatch.rst Note that the filename separator (``'/'`` on Unix) is *not* special to this module. See module :mod:`glob` for pathname expansion (:mod:`glob` uses -Index: Python-3.10.13/Doc/library/functions.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/functions.rst -+++ Python-3.10.13/Doc/library/functions.rst +--- a/Doc/library/functions.rst ++++ b/Doc/library/functions.rst @@ -548,7 +548,7 @@ are always available. They are listed h Raises an :ref:`auditing event ` ``exec`` with the code object as the argument. Code compilation events may also be raised. @@ -313,10 +827,8 @@ Index: Python-3.10.13/Doc/library/functions.rst .. note:: -Index: Python-3.10.13/Doc/library/http.client.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/http.client.rst -+++ Python-3.10.13/Doc/library/http.client.rst +--- a/Doc/library/http.client.rst ++++ b/Doc/library/http.client.rst @@ -10,7 +10,7 @@ pair: HTTP; protocol single: HTTP; http.client (standard module) @@ -326,10 +838,19 @@ Index: Python-3.10.13/Doc/library/http.client.rst -------------- -Index: Python-3.10.13/Doc/library/internet.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/internet.rst -+++ Python-3.10.13/Doc/library/internet.rst +--- a/Doc/library/imp.rst ++++ b/Doc/library/imp.rst +@@ -10,7 +10,7 @@ + .. deprecated:: 3.4 + The :mod:`imp` module is deprecated in favor of :mod:`importlib`. + +-.. index:: statement: import ++.. index:: pair: statement; import + + -------------- + +--- a/Doc/library/internet.rst ++++ b/Doc/library/internet.rst @@ -9,7 +9,7 @@ Internet Protocols and Support single: Internet single: World Wide Web @@ -339,10 +860,8 @@ Index: Python-3.10.13/Doc/library/internet.rst The modules described in this chapter implement internet protocols and support for related technology. They are all implemented in Python. Most of these -Index: Python-3.10.13/Doc/library/locale.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/locale.rst -+++ Python-3.10.13/Doc/library/locale.rst +--- a/Doc/library/locale.rst ++++ b/Doc/library/locale.rst @@ -16,7 +16,7 @@ functionality. The POSIX locale mechanis certain cultural issues in an application, without requiring the programmer to know all the specifics of each country where the software is executed. @@ -361,10 +880,8 @@ Index: Python-3.10.13/Doc/library/locale.rst Locale category for the character type functions. Depending on the settings of this category, the functions of module :mod:`string` dealing with case change -Index: Python-3.10.13/Doc/library/marshal.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/marshal.rst -+++ Python-3.10.13/Doc/library/marshal.rst +--- a/Doc/library/marshal.rst ++++ b/Doc/library/marshal.rst @@ -15,8 +15,8 @@ undocumented on purpose; it may change b rarely does). [#]_ @@ -376,10 +893,8 @@ Index: Python-3.10.13/Doc/library/marshal.rst This is not a general "persistence" module. For general persistence and transfer of Python objects through RPC calls, see the modules :mod:`pickle` and -Index: Python-3.10.13/Doc/library/os.path.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/os.path.rst -+++ Python-3.10.13/Doc/library/os.path.rst +--- a/Doc/library/os.path.rst ++++ b/Doc/library/os.path.rst @@ -159,7 +159,7 @@ the :mod:`glob` module.) On Unix and Windows, return the argument with an initial component of ``~`` or ``~user`` replaced by that *user*'s home directory. @@ -389,10 +904,8 @@ Index: Python-3.10.13/Doc/library/os.path.rst On Unix, an initial ``~`` is replaced by the environment variable :envvar:`HOME` if it is set; otherwise the current user's home directory is looked up in the -Index: Python-3.10.13/Doc/library/os.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/os.rst -+++ Python-3.10.13/Doc/library/os.rst +--- a/Doc/library/os.rst ++++ b/Doc/library/os.rst @@ -1136,7 +1136,7 @@ or `the MSDN None: ++ if app.builder.name != 'gettext': ++ return ++ ++ # allow translating deprecated index entries ++ try: ++ from sphinx.domains.python import pairindextypes ++ except ImportError: ++ pass ++ else: ++ # Sphinx checks if a 'pair' type entry on an index directive is one of ++ # the Sphinx-translated pairindextypes values. As we intend to move ++ # away from this, we need Sphinx to believe that these values don't ++ # exist, by deleting them when using the gettext builder. ++ ++ pairindextypes.pop('module', None) ++ pairindextypes.pop('keyword', None) ++ pairindextypes.pop('operator', None) ++ pairindextypes.pop('object', None) ++ pairindextypes.pop('exception', None) ++ pairindextypes.pop('statement', None) ++ pairindextypes.pop('builtin', None) ++ ++ + def setup(app): + app.add_role('issue', issue_role) + app.add_role('gh', gh_issue_role) +@@ -645,6 +669,7 @@ def setup(app): + app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod) + app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod) + app.add_directive('miscnews', MiscNews) ++ app.connect('builder-inited', patch_pairindextypes) + app.connect('doctree-resolved', process_audit_events) + app.connect('env-merge-info', audit_events_merge) + app.connect('env-purge-doc', audit_events_purge) +--- a/Doc/tutorial/classes.rst ++++ b/Doc/tutorial/classes.rst @@ -344,7 +344,7 @@ list objects have methods called append, However, in the following discussion, we'll use the term method exclusively to mean methods of class instance objects, unless explicitly stated otherwise.) @@ -2781,10 +2983,8 @@ Index: Python-3.10.13/Doc/tutorial/classes.rst Valid method names of an instance object depend on its class. By definition, all attributes of a class that are function objects define corresponding -Index: Python-3.10.13/Doc/tutorial/controlflow.rst -=================================================================== ---- Python-3.10.13.orig/Doc/tutorial/controlflow.rst -+++ Python-3.10.13/Doc/tutorial/controlflow.rst +--- a/Doc/tutorial/controlflow.rst ++++ b/Doc/tutorial/controlflow.rst @@ -46,7 +46,7 @@ details see :ref:`tut-match`. ========================== @@ -2794,288 +2994,50 @@ Index: Python-3.10.13/Doc/tutorial/controlflow.rst The :keyword:`for` statement in Python differs a bit from what you may be used to in C or Pascal. Rather than always iterating over an arithmetic progression -Index: Python-3.10.13/Doc/c-api/import.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/import.rst -+++ Python-3.10.13/Doc/c-api/import.rst -@@ -41,7 +41,7 @@ Importing Modules - - .. c:function:: PyObject* PyImport_ImportModuleEx(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist) - -- .. index:: builtin: __import__ -+ .. index:: pair: built-in function; __import__ - - Import a module. This is best described by referring to the built-in Python - function :func:`__import__`. -@@ -120,7 +120,7 @@ Importing Modules - - .. c:function:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co) - -- .. index:: builtin: compile -+ .. index:: pair: built-in function; compile - - Given a module name (possibly of the form ``package.module``) and a code object - read from a Python bytecode file or obtained from the built-in function -Index: Python-3.10.13/Doc/c-api/mapping.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/mapping.rst -+++ Python-3.10.13/Doc/c-api/mapping.rst -@@ -20,7 +20,7 @@ See also :c:func:`PyObject_GetItem`, :c: - .. c:function:: Py_ssize_t PyMapping_Size(PyObject *o) - Py_ssize_t PyMapping_Length(PyObject *o) - -- .. index:: builtin: len -+ .. index:: pair: built-in function; len - - Returns the number of keys in object *o* on success, and ``-1`` on failure. - This is equivalent to the Python expression ``len(o)``. -Index: Python-3.10.13/Doc/c-api/number.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/number.rst -+++ Python-3.10.13/Doc/c-api/number.rst -@@ -64,7 +64,7 @@ Number Protocol - - .. c:function:: PyObject* PyNumber_Divmod(PyObject *o1, PyObject *o2) - -- .. index:: builtin: divmod -+ .. index:: pair: built-in function; divmod - - See the built-in function :func:`divmod`. Returns ``NULL`` on failure. This is - the equivalent of the Python expression ``divmod(o1, o2)``. -@@ -72,7 +72,7 @@ Number Protocol - - .. c:function:: PyObject* PyNumber_Power(PyObject *o1, PyObject *o2, PyObject *o3) - -- .. index:: builtin: pow -+ .. index:: pair: built-in function; pow - - See the built-in function :func:`pow`. Returns ``NULL`` on failure. This is the - equivalent of the Python expression ``pow(o1, o2, o3)``, where *o3* is optional. -@@ -94,7 +94,7 @@ Number Protocol - - .. c:function:: PyObject* PyNumber_Absolute(PyObject *o) - -- .. index:: builtin: abs -+ .. index:: pair: built-in function; abs - - Returns the absolute value of *o*, or ``NULL`` on failure. This is the equivalent - of the Python expression ``abs(o)``. -@@ -192,7 +192,7 @@ Number Protocol - - .. c:function:: PyObject* PyNumber_InPlacePower(PyObject *o1, PyObject *o2, PyObject *o3) - -- .. index:: builtin: pow -+ .. index:: pair: built-in function; pow - - See the built-in function :func:`pow`. Returns ``NULL`` on failure. The operation - is done *in-place* when *o1* supports it. This is the equivalent of the Python -@@ -238,7 +238,7 @@ Number Protocol - - .. c:function:: PyObject* PyNumber_Long(PyObject *o) - -- .. index:: builtin: int -+ .. index:: pair: built-in function; int - - Returns the *o* converted to an integer object on success, or ``NULL`` on - failure. This is the equivalent of the Python expression ``int(o)``. -@@ -246,7 +246,7 @@ Number Protocol - - .. c:function:: PyObject* PyNumber_Float(PyObject *o) - -- .. index:: builtin: float -+ .. index:: pair: built-in function; float - - Returns the *o* converted to a float object on success, or ``NULL`` on failure. - This is the equivalent of the Python expression ``float(o)``. -Index: Python-3.10.13/Doc/c-api/object.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/object.rst -+++ Python-3.10.13/Doc/c-api/object.rst -@@ -172,7 +172,7 @@ Object Protocol - - .. c:function:: PyObject* PyObject_Repr(PyObject *o) - -- .. index:: builtin: repr -+ .. index:: pair: built-in function; repr - - Compute a string representation of object *o*. Returns the string - representation on success, ``NULL`` on failure. This is the equivalent of the -@@ -184,7 +184,7 @@ Object Protocol - - .. c:function:: PyObject* PyObject_ASCII(PyObject *o) - -- .. index:: builtin: ascii -+ .. index:: pair: built-in function; ascii - - As :c:func:`PyObject_Repr`, compute a string representation of object *o*, but - escape the non-ASCII characters in the string returned by -@@ -209,7 +209,7 @@ Object Protocol - - .. c:function:: PyObject* PyObject_Bytes(PyObject *o) - -- .. index:: builtin: bytes -+ .. index:: pair: built-in function; bytes - - Compute a bytes representation of object *o*. ``NULL`` is returned on - failure and a bytes object on success. This is equivalent to the Python -@@ -260,7 +260,7 @@ Object Protocol - - .. c:function:: Py_hash_t PyObject_Hash(PyObject *o) - -- .. index:: builtin: hash -+ .. index:: pair: built-in function; hash - - Compute and return the hash value of an object *o*. On failure, return ``-1``. - This is the equivalent of the Python expression ``hash(o)``. -@@ -294,7 +294,7 @@ Object Protocol - - .. c:function:: PyObject* PyObject_Type(PyObject *o) - -- .. index:: builtin: type -+ .. index:: pair: built-in function; type - - When *o* is non-``NULL``, returns a type object corresponding to the object type - of object *o*. On failure, raises :exc:`SystemError` and returns ``NULL``. This -@@ -315,7 +315,7 @@ Object Protocol - .. c:function:: Py_ssize_t PyObject_Size(PyObject *o) - Py_ssize_t PyObject_Length(PyObject *o) - -- .. index:: builtin: len -+ .. index:: pair: built-in function; len - - Return the length of object *o*. If the object *o* provides either the sequence - and mapping protocols, the sequence length is returned. On error, ``-1`` is -Index: Python-3.10.13/Doc/c-api/sequence.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/sequence.rst -+++ Python-3.10.13/Doc/c-api/sequence.rst -@@ -18,7 +18,7 @@ Sequence Protocol - .. c:function:: Py_ssize_t PySequence_Size(PyObject *o) - Py_ssize_t PySequence_Length(PyObject *o) - -- .. index:: builtin: len -+ .. index:: pair: built-in function; len - - Returns the number of objects in sequence *o* on success, and ``-1`` on - failure. This is equivalent to the Python expression ``len(o)``. -@@ -120,7 +120,7 @@ Sequence Protocol - - .. c:function:: PyObject* PySequence_Tuple(PyObject *o) - -- .. index:: builtin: tuple -+ .. index:: pair: built-in function; tuple - - Return a tuple object with the same contents as the sequence or iterable *o*, - or ``NULL`` on failure. If *o* is a tuple, a new reference will be returned, -Index: Python-3.10.13/Doc/c-api/structures.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/structures.rst -+++ Python-3.10.13/Doc/c-api/structures.rst -@@ -351,7 +351,7 @@ method. - - .. data:: METH_CLASS - -- .. index:: builtin: classmethod -+ .. index:: pair: built-in function; classmethod - - The method will be passed the type object as the first parameter rather - than an instance of the type. This is used to create *class methods*, -@@ -361,7 +361,7 @@ method. - - .. data:: METH_STATIC - -- .. index:: builtin: staticmethod -+ .. index:: pair: built-in function; staticmethod - - The method will be passed ``NULL`` as the first parameter rather than an - instance of the type. This is used to create *static methods*, similar to -Index: Python-3.10.13/Doc/c-api/typeobj.rst -=================================================================== ---- Python-3.10.13.orig/Doc/c-api/typeobj.rst -+++ Python-3.10.13/Doc/c-api/typeobj.rst -@@ -803,7 +803,7 @@ and :c:type:`PyType_Type` effectively ac - - .. c:member:: reprfunc PyTypeObject.tp_repr - -- .. index:: builtin: repr -+ .. index:: pair: built-in function; repr - - An optional pointer to a function that implements the built-in function - :func:`repr`. -@@ -868,7 +868,7 @@ and :c:type:`PyType_Type` effectively ac - - .. c:member:: hashfunc PyTypeObject.tp_hash - -- .. index:: builtin: hash -+ .. index:: pair: built-in function; hash - - An optional pointer to a function that implements the built-in function - :func:`hash`. -Index: Python-3.10.13/Doc/extending/newtypes.rst -=================================================================== ---- Python-3.10.13.orig/Doc/extending/newtypes.rst -+++ Python-3.10.13/Doc/extending/newtypes.rst -@@ -149,7 +149,7 @@ done. This can be done using the :c:fun +--- a/Doc/tutorial/inputoutput.rst ++++ b/Doc/tutorial/inputoutput.rst +@@ -285,8 +285,8 @@ Reading and Writing Files + ========================= .. index:: - single: string; object representation -- builtin: repr -+ pair: built-in function; repr +- builtin: open +- object: file ++ pair: built-in function; open ++ pair: object; file - Object Presentation - ------------------- -Index: Python-3.10.13/Doc/library/dis.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/dis.rst -+++ Python-3.10.13/Doc/library/dis.rst -@@ -1207,7 +1207,7 @@ All of the following opcodes use their a + :func:`open` returns a :term:`file object`, and is most commonly used with + two positional arguments and one keyword argument: +@@ -466,7 +466,7 @@ Reference for a complete guide to file o + Saving structured data with :mod:`json` + --------------------------------------- - .. opcode:: BUILD_SLICE (argc) +-.. index:: module: json ++.. index:: pair: module; json -- .. index:: builtin: slice -+ .. index:: pair: built-in function; slice + Strings can easily be written to and read from a file. Numbers take a bit more + effort, since the :meth:`read` method only returns strings, which will have to +--- a/Doc/tutorial/modules.rst ++++ b/Doc/tutorial/modules.rst +@@ -260,7 +260,7 @@ Some tips for experts: + Standard Modules + ================ - Pushes a slice object on the stack. *argc* must be 2 or 3. If it is 2, - ``slice(TOS1, TOS)`` is pushed; if it is 3, ``slice(TOS2, TOS1, TOS)`` is -Index: Python-3.10.13/Doc/library/pprint.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/pprint.rst -+++ Python-3.10.13/Doc/library/pprint.rst -@@ -171,7 +171,7 @@ The :mod:`pprint` module also provides s +-.. index:: module: sys ++.. index:: pair: module; sys - .. function:: isreadable(object) + Python comes with a library of standard modules, described in a separate + document, the Python Library Reference ("Library Reference" hereafter). Some +@@ -341,7 +341,7 @@ Without arguments, :func:`dir` lists the -- .. index:: builtin: eval -+ .. index:: pair: built-in function; eval + Note that it lists all types of names: variables, modules, functions, etc. - Determine if the formatted representation of *object* is "readable", or can be - used to reconstruct the value using :func:`eval`. This always returns ``False`` -@@ -226,7 +226,7 @@ created. +-.. index:: module: builtins ++.. index:: pair: module; builtins - .. method:: PrettyPrinter.isreadable(object) - -- .. index:: builtin: eval -+ .. index:: pair: built-in function; eval - - Determine if the formatted representation of the object is "readable," or can be - used to reconstruct the value using :func:`eval`. Note that this returns -Index: Python-3.10.13/Doc/library/types.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/types.rst -+++ Python-3.10.13/Doc/library/types.rst -@@ -146,7 +146,7 @@ Standard names are defined for the follo - - .. class:: CodeType(**kwargs) - -- .. index:: builtin: compile -+ .. index:: pair: built-in function; compile - - The type for code objects such as returned by :func:`compile`. - -Index: Python-3.10.13/Doc/tutorial/stdlib.rst -=================================================================== ---- Python-3.10.13.orig/Doc/tutorial/stdlib.rst -+++ Python-3.10.13/Doc/tutorial/stdlib.rst + :func:`dir` does not list the names of built-in functions and variables. If you + want a list of those, they are defined in the standard module +--- a/Doc/tutorial/stdlib.rst ++++ b/Doc/tutorial/stdlib.rst @@ -24,7 +24,7 @@ Be sure to use the ``import os`` style i will keep :func:`os.open` from shadowing the built-in :func:`open` function which operates much differently. @@ -3085,33 +3047,3 @@ Index: Python-3.10.13/Doc/tutorial/stdlib.rst The built-in :func:`dir` and :func:`help` functions are useful as interactive aids for working with large modules like :mod:`os`:: -Index: Python-3.10.13/Doc/library/imp.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/imp.rst -+++ Python-3.10.13/Doc/library/imp.rst -@@ -10,7 +10,7 @@ - .. deprecated:: 3.4 - The :mod:`imp` module is deprecated in favor of :mod:`importlib`. - --.. index:: statement: import -+.. index:: pair: statement; import - - -------------- - -Index: Python-3.10.13/Doc/library/binascii.rst -=================================================================== ---- Python-3.10.13.orig/Doc/library/binascii.rst -+++ Python-3.10.13/Doc/library/binascii.rst -@@ -6,9 +6,9 @@ - representations. - - .. index:: -- module: uu -- module: base64 -- module: binhex -+ pair: module; uu -+ pair: module; base64 -+ pair: module; binhex - - -------------- - diff --git a/python310.changes b/python310.changes index 797456c..c9cbc46 100644 --- a/python310.changes +++ b/python310.changes @@ -1,15 +1,23 @@ +------------------------------------------------------------------- +Mon Dec 18 16:20:58 UTC 2023 - Matej Cepl + +- Refresh CVE-2023-27043-email-parsing-errors.patch to + gh#python/cpython!111116, fixing bsc#1210638 (CVE-2023-27043). +- Thus we can remove Revert-gh105127-left-tests.patch, which is + now useless. + ------------------------------------------------------------------- Mon Sep 4 13:18:29 UTC 2023 - Daniel Garcia - Add fix-sphinx-72.patch to make it work with latest sphinx version gh#python/cpython#97950 -- Update to 3.10.13: +- Update to 3.10.13 (bsc#1214692): - gh-108310: Fixed an issue where instances of ssl.SSLSocket were vulnerable to a bypass of the TLS handshake and included protections (like certificate verification) and treating sent unencrypted data as if it were post-handshake TLS encrypted data. Security issue reported as CVE-2023-40217 by Aapo Oksman. Patch by - Gregory P. Smith. (bsc#1214692) + Gregory P. Smith. - gh-107845: tarfile.data_filter() now takes the location of symlinks into account when determining their target, so it will no longer reject some valid tarballs with diff --git a/python310.spec b/python310.spec index 720a3fb..8d20d2b 100644 --- a/python310.spec +++ b/python310.spec @@ -1,7 +1,7 @@ # -# spec file +# spec file for package python310 # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -177,9 +177,6 @@ Patch39: gh-78214-marshal_stabilize_FLAG_REF.patch # indicate the parsing error (old API), from gh#python/cpython!105127 # Patch carries a REGRESSION (gh#python/cpython#106669), so it has been also partially REVERTED Patch40: CVE-2023-27043-email-parsing-errors.patch -# PATCH-FIX-UPSTREAM Revert-gh105127-left-tests.patch bsc#1210638 mcepl@suse.com -# Partially revert previous patch -Patch41: Revert-gh105127-left-tests.patch # PATCH-FIX-UPSTREAM fix-sphinx-72.patch gh#python/cpython#97950 # This is a patch with a lot of PR combined to make the doc work with # sphinx 7.2 @@ -468,7 +465,6 @@ other applications. %patch38 -p1 %patch39 -p1 %patch40 -p1 -%patch41 -p1 %patch42 -p1 # drop Autoconf version requirement From b7a2f17f3419c16b627c1d66cf057e05a912b40e900cdf34f60df4520a436fec Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Fri, 8 Mar 2024 11:06:12 +0000 Subject: [PATCH 11/23] Set link to python310.32817 via maintenance_release request --- CVE-2023-6597-TempDir-cleaning-symlink.patch | 191 +++++++++++++++++++ libexpat260.patch | 107 +++++++++++ python310.changes | 20 ++ python310.spec | 39 ++-- 4 files changed, 341 insertions(+), 16 deletions(-) create mode 100644 CVE-2023-6597-TempDir-cleaning-symlink.patch create mode 100644 libexpat260.patch diff --git a/CVE-2023-6597-TempDir-cleaning-symlink.patch b/CVE-2023-6597-TempDir-cleaning-symlink.patch new file mode 100644 index 0000000..5d9f7f4 --- /dev/null +++ b/CVE-2023-6597-TempDir-cleaning-symlink.patch @@ -0,0 +1,191 @@ +--- + Lib/tempfile.py | 26 +- + Lib/test/test_tempfile.py | 117 +++++++++- + Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst | 2 + 3 files changed, 136 insertions(+), 9 deletions(-) + +--- a/Lib/tempfile.py ++++ b/Lib/tempfile.py +@@ -269,6 +269,22 @@ def _mkstemp_inner(dir, pre, suf, flags, + raise FileExistsError(_errno.EEXIST, + "No usable temporary file name found") + ++def _dont_follow_symlinks(func, path, *args): ++ # Pass follow_symlinks=False, unless not supported on this platform. ++ if func in _os.supports_follow_symlinks: ++ func(path, *args, follow_symlinks=False) ++ elif _os.name == 'nt' or not _os.path.islink(path): ++ func(path, *args) ++ ++def _resetperms(path): ++ try: ++ chflags = _os.chflags ++ except AttributeError: ++ pass ++ else: ++ _dont_follow_symlinks(chflags, path, 0) ++ _dont_follow_symlinks(_os.chmod, path, 0o700) ++ + + # User visible interfaces. + +@@ -827,17 +843,11 @@ class TemporaryDirectory: + def _rmtree(cls, name, ignore_errors=False): + def onerror(func, path, exc_info): + if issubclass(exc_info[0], PermissionError): +- def resetperms(path): +- try: +- _os.chflags(path, 0) +- except AttributeError: +- pass +- _os.chmod(path, 0o700) + + try: + if path != name: +- resetperms(_os.path.dirname(path)) +- resetperms(path) ++ _resetperms(_os.path.dirname(path)) ++ _resetperms(path) + + try: + _os.unlink(path) +--- a/Lib/test/test_tempfile.py ++++ b/Lib/test/test_tempfile.py +@@ -1499,6 +1499,103 @@ class TestTemporaryDirectory(BaseTestCas + "were deleted") + d2.cleanup() + ++ @os_helper.skip_unless_symlink ++ def test_cleanup_with_symlink_modes(self): ++ # cleanup() should not follow symlinks when fixing mode bits (#91133) ++ with self.do_create(recurse=0) as d2: ++ file1 = os.path.join(d2, 'file1') ++ open(file1, 'wb').close() ++ dir1 = os.path.join(d2, 'dir1') ++ os.mkdir(dir1) ++ for mode in range(8): ++ mode <<= 6 ++ with self.subTest(mode=format(mode, '03o')): ++ def test(target, target_is_directory): ++ d1 = self.do_create(recurse=0) ++ symlink = os.path.join(d1.name, 'symlink') ++ os.symlink(target, symlink, ++ target_is_directory=target_is_directory) ++ try: ++ os.chmod(symlink, mode, follow_symlinks=False) ++ except NotImplementedError: ++ pass ++ try: ++ os.chmod(symlink, mode) ++ except FileNotFoundError: ++ pass ++ os.chmod(d1.name, mode) ++ d1.cleanup() ++ self.assertFalse(os.path.exists(d1.name)) ++ ++ with self.subTest('nonexisting file'): ++ test('nonexisting', target_is_directory=False) ++ with self.subTest('nonexisting dir'): ++ test('nonexisting', target_is_directory=True) ++ ++ with self.subTest('existing file'): ++ os.chmod(file1, mode) ++ old_mode = os.stat(file1).st_mode ++ test(file1, target_is_directory=False) ++ new_mode = os.stat(file1).st_mode ++ self.assertEqual(new_mode, old_mode, ++ '%03o != %03o' % (new_mode, old_mode)) ++ ++ with self.subTest('existing dir'): ++ os.chmod(dir1, mode) ++ old_mode = os.stat(dir1).st_mode ++ test(dir1, target_is_directory=True) ++ new_mode = os.stat(dir1).st_mode ++ self.assertEqual(new_mode, old_mode, ++ '%03o != %03o' % (new_mode, old_mode)) ++ ++ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') ++ @os_helper.skip_unless_symlink ++ def test_cleanup_with_symlink_flags(self): ++ # cleanup() should not follow symlinks when fixing flags (#91133) ++ flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK ++ self.check_flags(flags) ++ ++ with self.do_create(recurse=0) as d2: ++ file1 = os.path.join(d2, 'file1') ++ open(file1, 'wb').close() ++ dir1 = os.path.join(d2, 'dir1') ++ os.mkdir(dir1) ++ def test(target, target_is_directory): ++ d1 = self.do_create(recurse=0) ++ symlink = os.path.join(d1.name, 'symlink') ++ os.symlink(target, symlink, ++ target_is_directory=target_is_directory) ++ try: ++ os.chflags(symlink, flags, follow_symlinks=False) ++ except NotImplementedError: ++ pass ++ try: ++ os.chflags(symlink, flags) ++ except FileNotFoundError: ++ pass ++ os.chflags(d1.name, flags) ++ d1.cleanup() ++ self.assertFalse(os.path.exists(d1.name)) ++ ++ with self.subTest('nonexisting file'): ++ test('nonexisting', target_is_directory=False) ++ with self.subTest('nonexisting dir'): ++ test('nonexisting', target_is_directory=True) ++ ++ with self.subTest('existing file'): ++ os.chflags(file1, flags) ++ old_flags = os.stat(file1).st_flags ++ test(file1, target_is_directory=False) ++ new_flags = os.stat(file1).st_flags ++ self.assertEqual(new_flags, old_flags) ++ ++ with self.subTest('existing dir'): ++ os.chflags(dir1, flags) ++ old_flags = os.stat(dir1).st_flags ++ test(dir1, target_is_directory=True) ++ new_flags = os.stat(dir1).st_flags ++ self.assertEqual(new_flags, old_flags) ++ + @support.cpython_only + def test_del_on_collection(self): + # A TemporaryDirectory is deleted when garbage collected +@@ -1671,9 +1768,27 @@ class TestTemporaryDirectory(BaseTestCas + d.cleanup() + self.assertFalse(os.path.exists(d.name)) + +- @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.lchflags') ++ def check_flags(self, flags): ++ # skip the test if these flags are not supported (ex: FreeBSD 13) ++ filename = os_helper.TESTFN ++ try: ++ open(filename, "w").close() ++ try: ++ os.chflags(filename, flags) ++ except OSError as exc: ++ # "OSError: [Errno 45] Operation not supported" ++ self.skipTest(f"chflags() doesn't support flags " ++ f"{flags:#b}: {exc}") ++ else: ++ os.chflags(filename, 0) ++ finally: ++ support.unlink(filename) ++ ++ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') + def test_flags(self): + flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK ++ self.check_flags(flags) ++ + d = self.do_create(recurse=3, dirs=2, files=2) + with d: + # Change files and directories flags recursively. +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst +@@ -0,0 +1,2 @@ ++Fix a bug in :class:`tempfile.TemporaryDirectory` cleanup, which now no longer ++dereferences symlinks when working around file system permission errors. diff --git a/libexpat260.patch b/libexpat260.patch new file mode 100644 index 0000000..bd4b4b3 --- /dev/null +++ b/libexpat260.patch @@ -0,0 +1,107 @@ +From f2eebf3c38eae77765247791576b437ec25ccfe2 Mon Sep 17 00:00:00 2001 +From: Serhiy Storchaka +Date: Sun, 11 Feb 2024 12:08:39 +0200 +Subject: [PATCH] gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 + (GH-115164) + +Feeding the parser by too small chunks defers parsing to prevent +CVE-2023-52425. Future versions of Expat may be more reactive. +(cherry picked from commit 4a08e7b3431cd32a0daf22a33421cd3035343dc4) + +Co-authored-by: Serhiy Storchaka +--- + Lib/test/test_xml_etree.py | 58 ++++++++++++------- + ...-02-08-14-21-28.gh-issue-115133.ycl4ko.rst | 2 + + 2 files changed, 38 insertions(+), 22 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst + +Index: Python-3.10.13/Lib/test/test_xml_etree.py +=================================================================== +--- Python-3.10.13.orig/Lib/test/test_xml_etree.py ++++ Python-3.10.13/Lib/test/test_xml_etree.py +@@ -13,6 +13,7 @@ import itertools + import operator + import os + import pickle ++import pyexpat + import sys + import textwrap + import types +@@ -120,6 +121,10 @@ ATTLIST_XML = """\ + + """ + ++fails_with_expat_2_6_0 = (unittest.expectedFailure ++ if pyexpat.version_info >= (2, 6, 0) else ++ lambda test: test) ++ + def checkwarnings(*filters, quiet=False): + def decorator(test): + def newtest(*args, **kwargs): +@@ -1396,28 +1401,37 @@ class XMLPullParserTest(unittest.TestCas + self.assertEqual([(action, elem.tag) for action, elem in events], + expected) + +- def test_simple_xml(self): +- for chunk_size in (None, 1, 5): +- with self.subTest(chunk_size=chunk_size): +- parser = ET.XMLPullParser() +- self.assert_event_tags(parser, []) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, []) +- self._feed(parser, +- "\n text\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'element')]) +- self._feed(parser, "texttail\n", chunk_size) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [ +- ('end', 'element'), +- ('end', 'empty-element'), +- ]) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'root')]) +- self.assertIsNone(parser.close()) ++ def test_simple_xml(self, chunk_size=None): ++ parser = ET.XMLPullParser() ++ self.assert_event_tags(parser, []) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, []) ++ self._feed(parser, ++ "\n text\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'element')]) ++ self._feed(parser, "texttail\n", chunk_size) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [ ++ ('end', 'element'), ++ ('end', 'empty-element'), ++ ]) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'root')]) ++ self.assertIsNone(parser.close()) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_1(self): ++ self.test_simple_xml(chunk_size=1) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_5(self): ++ self.test_simple_xml(chunk_size=5) ++ ++ def test_simple_xml_chunk_22(self): ++ self.test_simple_xml(chunk_size=22) + + def test_feed_while_iterating(self): + parser = ET.XMLPullParser() +Index: Python-3.10.13/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +=================================================================== +--- /dev/null ++++ Python-3.10.13/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +@@ -0,0 +1,2 @@ ++Fix tests for :class:`~xml.etree.ElementTree.XMLPullParser` with Expat ++2.6.0. diff --git a/python310.changes b/python310.changes index c9cbc46..34de3c2 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,23 @@ +------------------------------------------------------------------- +Fri Feb 23 01:06:42 UTC 2024 - Matej Cepl + +- (bsc#1219666, CVE-2023-6597) Add + CVE-2023-6597-TempDir-cleaning-symlink.patch (patch from + gh#python/cpython!99930) fixing symlink bug in cleanup of + tempfile.TemporaryDirectory. + +------------------------------------------------------------------- +Tue Feb 20 22:14:02 UTC 2024 - Matej Cepl + +- Remove double definition of /usr/bin/idle%%{version} in + %%files. + +------------------------------------------------------------------- +Thu Feb 15 10:29:07 UTC 2024 - Daniel Garcia + +- Add upstream patch libexpat260.patch, Fix tests for XMLPullParser + with Expat 2.6.0, gh#python/cpython#115289 + ------------------------------------------------------------------- Mon Dec 18 16:20:58 UTC 2023 - Matej Cepl diff --git a/python310.spec b/python310.spec index 8d20d2b..ce0e095 100644 --- a/python310.spec +++ b/python310.spec @@ -191,6 +191,12 @@ Patch40: CVE-2023-27043-email-parsing-errors.patch # * gh#python/cpython#104221 # * gh#python/cpython#107246 Patch42: fix-sphinx-72.patch +# PATCH-FIX-UPSTREAM libexpat260.patch gh#python/cpython#115289 +# Fix tests for XMLPullParser with Expat 2.6.0 +Patch43: libexpat260.patch +# PATCH-FIX-UPSTREAM CVE-2023-6597-TempDir-cleaning-symlink.patch bsc#1219666 mcepl@suse.com +# tempfile.TemporaryDirectory: fix symlink bug in cleanup (from gh#python/cpython!99930) +Patch44: CVE-2023-6597-TempDir-cleaning-symlink.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -449,23 +455,25 @@ other applications. %prep %setup -q -n %{tarname} -%patch02 -p1 -%patch06 -p1 -%patch07 -p1 -%patch08 -p1 -%patch09 -p1 -%patch15 -p1 -%patch29 -p1 +%patch -P 02 -p1 +%patch -P 06 -p1 +%patch -P 07 -p1 +%patch -P 08 -p1 +%patch -P 09 -p1 +%patch -P 15 -p1 +%patch -P 29 -p1 %if 0%{?sle_version} && 0%{?sle_version} <= 150300 -%patch33 -p1 -%patch34 -p1 +%patch -P 33 -p1 +%patch -P 34 -p1 %endif -%patch35 -p1 -%patch36 -p1 -%patch38 -p1 -%patch39 -p1 -%patch40 -p1 -%patch42 -p1 +%patch -P 35 -p1 +%patch -P 36 -p1 +%patch -P 38 -p1 +%patch -P 39 -p1 +%patch -P 40 -p1 +%patch -P 42 -p1 +%patch -P 43 -p1 +%patch -P 44 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -848,7 +856,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %dir %{_datadir}/icons/hicolor/32x32 %dir %{_datadir}/icons/hicolor/48x48 %dir %{_datadir}/icons/hicolor/*/apps -%attr(755, root, root) %{_bindir}/idle%{python_version} # endif for if general %endif From 0a130e2ad5f356959f050f28d433ada0574841e608ac2bd7d49626b874f677b1 Mon Sep 17 00:00:00 2001 From: Marco Strigl Date: Mon, 8 Apr 2024 11:33:49 +0000 Subject: [PATCH 12/23] Set link to python310.33187 via maintenance_release request --- CVE-2023-6597-TempDir-cleaning-symlink.patch | 191 ----- F00251-change-user-install-location.patch | 2 +- Python-3.10.13.tar.xz.asc | 16 - ...on-3.10.13.tar.xz => Python-3.10.14.tar.xz | 0 Python-3.10.14.tar.xz.asc | 16 + fix_configure_rst.patch | 14 +- libexpat260.patch | 107 --- old-libexpat.patch | 79 ++ python-3.3.0b1-localpath.patch | 8 +- python.keyring | 780 +++++++++++++++--- python310.changes | 58 ++ python310.spec | 82 +- skip-test_pyobject_freed_is_freed.patch | 21 +- 13 files changed, 897 insertions(+), 477 deletions(-) delete mode 100644 CVE-2023-6597-TempDir-cleaning-symlink.patch delete mode 100644 Python-3.10.13.tar.xz.asc rename Python-3.10.13.tar.xz => Python-3.10.14.tar.xz (100%) create mode 100644 Python-3.10.14.tar.xz.asc delete mode 100644 libexpat260.patch create mode 100644 old-libexpat.patch diff --git a/CVE-2023-6597-TempDir-cleaning-symlink.patch b/CVE-2023-6597-TempDir-cleaning-symlink.patch deleted file mode 100644 index 5d9f7f4..0000000 --- a/CVE-2023-6597-TempDir-cleaning-symlink.patch +++ /dev/null @@ -1,191 +0,0 @@ ---- - Lib/tempfile.py | 26 +- - Lib/test/test_tempfile.py | 117 +++++++++- - Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst | 2 - 3 files changed, 136 insertions(+), 9 deletions(-) - ---- a/Lib/tempfile.py -+++ b/Lib/tempfile.py -@@ -269,6 +269,22 @@ def _mkstemp_inner(dir, pre, suf, flags, - raise FileExistsError(_errno.EEXIST, - "No usable temporary file name found") - -+def _dont_follow_symlinks(func, path, *args): -+ # Pass follow_symlinks=False, unless not supported on this platform. -+ if func in _os.supports_follow_symlinks: -+ func(path, *args, follow_symlinks=False) -+ elif _os.name == 'nt' or not _os.path.islink(path): -+ func(path, *args) -+ -+def _resetperms(path): -+ try: -+ chflags = _os.chflags -+ except AttributeError: -+ pass -+ else: -+ _dont_follow_symlinks(chflags, path, 0) -+ _dont_follow_symlinks(_os.chmod, path, 0o700) -+ - - # User visible interfaces. - -@@ -827,17 +843,11 @@ class TemporaryDirectory: - def _rmtree(cls, name, ignore_errors=False): - def onerror(func, path, exc_info): - if issubclass(exc_info[0], PermissionError): -- def resetperms(path): -- try: -- _os.chflags(path, 0) -- except AttributeError: -- pass -- _os.chmod(path, 0o700) - - try: - if path != name: -- resetperms(_os.path.dirname(path)) -- resetperms(path) -+ _resetperms(_os.path.dirname(path)) -+ _resetperms(path) - - try: - _os.unlink(path) ---- a/Lib/test/test_tempfile.py -+++ b/Lib/test/test_tempfile.py -@@ -1499,6 +1499,103 @@ class TestTemporaryDirectory(BaseTestCas - "were deleted") - d2.cleanup() - -+ @os_helper.skip_unless_symlink -+ def test_cleanup_with_symlink_modes(self): -+ # cleanup() should not follow symlinks when fixing mode bits (#91133) -+ with self.do_create(recurse=0) as d2: -+ file1 = os.path.join(d2, 'file1') -+ open(file1, 'wb').close() -+ dir1 = os.path.join(d2, 'dir1') -+ os.mkdir(dir1) -+ for mode in range(8): -+ mode <<= 6 -+ with self.subTest(mode=format(mode, '03o')): -+ def test(target, target_is_directory): -+ d1 = self.do_create(recurse=0) -+ symlink = os.path.join(d1.name, 'symlink') -+ os.symlink(target, symlink, -+ target_is_directory=target_is_directory) -+ try: -+ os.chmod(symlink, mode, follow_symlinks=False) -+ except NotImplementedError: -+ pass -+ try: -+ os.chmod(symlink, mode) -+ except FileNotFoundError: -+ pass -+ os.chmod(d1.name, mode) -+ d1.cleanup() -+ self.assertFalse(os.path.exists(d1.name)) -+ -+ with self.subTest('nonexisting file'): -+ test('nonexisting', target_is_directory=False) -+ with self.subTest('nonexisting dir'): -+ test('nonexisting', target_is_directory=True) -+ -+ with self.subTest('existing file'): -+ os.chmod(file1, mode) -+ old_mode = os.stat(file1).st_mode -+ test(file1, target_is_directory=False) -+ new_mode = os.stat(file1).st_mode -+ self.assertEqual(new_mode, old_mode, -+ '%03o != %03o' % (new_mode, old_mode)) -+ -+ with self.subTest('existing dir'): -+ os.chmod(dir1, mode) -+ old_mode = os.stat(dir1).st_mode -+ test(dir1, target_is_directory=True) -+ new_mode = os.stat(dir1).st_mode -+ self.assertEqual(new_mode, old_mode, -+ '%03o != %03o' % (new_mode, old_mode)) -+ -+ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') -+ @os_helper.skip_unless_symlink -+ def test_cleanup_with_symlink_flags(self): -+ # cleanup() should not follow symlinks when fixing flags (#91133) -+ flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK -+ self.check_flags(flags) -+ -+ with self.do_create(recurse=0) as d2: -+ file1 = os.path.join(d2, 'file1') -+ open(file1, 'wb').close() -+ dir1 = os.path.join(d2, 'dir1') -+ os.mkdir(dir1) -+ def test(target, target_is_directory): -+ d1 = self.do_create(recurse=0) -+ symlink = os.path.join(d1.name, 'symlink') -+ os.symlink(target, symlink, -+ target_is_directory=target_is_directory) -+ try: -+ os.chflags(symlink, flags, follow_symlinks=False) -+ except NotImplementedError: -+ pass -+ try: -+ os.chflags(symlink, flags) -+ except FileNotFoundError: -+ pass -+ os.chflags(d1.name, flags) -+ d1.cleanup() -+ self.assertFalse(os.path.exists(d1.name)) -+ -+ with self.subTest('nonexisting file'): -+ test('nonexisting', target_is_directory=False) -+ with self.subTest('nonexisting dir'): -+ test('nonexisting', target_is_directory=True) -+ -+ with self.subTest('existing file'): -+ os.chflags(file1, flags) -+ old_flags = os.stat(file1).st_flags -+ test(file1, target_is_directory=False) -+ new_flags = os.stat(file1).st_flags -+ self.assertEqual(new_flags, old_flags) -+ -+ with self.subTest('existing dir'): -+ os.chflags(dir1, flags) -+ old_flags = os.stat(dir1).st_flags -+ test(dir1, target_is_directory=True) -+ new_flags = os.stat(dir1).st_flags -+ self.assertEqual(new_flags, old_flags) -+ - @support.cpython_only - def test_del_on_collection(self): - # A TemporaryDirectory is deleted when garbage collected -@@ -1671,9 +1768,27 @@ class TestTemporaryDirectory(BaseTestCas - d.cleanup() - self.assertFalse(os.path.exists(d.name)) - -- @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.lchflags') -+ def check_flags(self, flags): -+ # skip the test if these flags are not supported (ex: FreeBSD 13) -+ filename = os_helper.TESTFN -+ try: -+ open(filename, "w").close() -+ try: -+ os.chflags(filename, flags) -+ except OSError as exc: -+ # "OSError: [Errno 45] Operation not supported" -+ self.skipTest(f"chflags() doesn't support flags " -+ f"{flags:#b}: {exc}") -+ else: -+ os.chflags(filename, 0) -+ finally: -+ support.unlink(filename) -+ -+ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') - def test_flags(self): - flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK -+ self.check_flags(flags) -+ - d = self.do_create(recurse=3, dirs=2, files=2) - with d: - # Change files and directories flags recursively. ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst -@@ -0,0 +1,2 @@ -+Fix a bug in :class:`tempfile.TemporaryDirectory` cleanup, which now no longer -+dereferences symlinks when working around file system permission errors. diff --git a/F00251-change-user-install-location.patch b/F00251-change-user-install-location.patch index e69c5a9..9e645d4 100644 --- a/F00251-change-user-install-location.patch +++ b/F00251-change-user-install-location.patch @@ -39,7 +39,7 @@ Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe if self.exec_prefix is None: --- a/Lib/site.py +++ b/Lib/site.py -@@ -380,8 +380,15 @@ def getsitepackages(prefixes=None): +@@ -390,8 +390,15 @@ def getsitepackages(prefixes=None): return sitepackages def addsitepackages(known_paths, prefixes=None): diff --git a/Python-3.10.13.tar.xz.asc b/Python-3.10.13.tar.xz.asc deleted file mode 100644 index f5c2fa5..0000000 --- a/Python-3.10.13.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmTnVFIACgkQ/+h0BBaL -2EfANRAAng1gGOlrpC5O6dU3w37Ubbp4nqRxCgLO3jcOipafkq4PdQN0VrP8Gvbx -NGn+ELdAiBasVkgx3aZcDKCSHQT9PkMOGjNKKcv6NamMTnwp5IeVEY+uXSITxOoQ -VmLRKdnNO9PYkSfh0bB51p5+HtnbFabs6lY6NEwSg9J2ooNCChgaVEEjGJalLkKn -aCy+G9prd4KUXMonm8fqk3kj79qcEdj97CdOdEpE0EvH5oNNfKRwkD8j4nztE5mM -GeymPb9g9gY+u2Ph5bPkkNHSwgO/NVgCrAxcdJC4/tabiWBFZtmLEcEUvMmMVDdM -VAV40PHlIevzY2wm2PIZYKqqVpvyfjk3SRSfqJe7BiOMfifXpG1rYD+pB823U20c -Xzi9Z1p39406XbgOI9EE4dVWSI03kyEcnOth8pv/aYoIlSGnfH7ngST0OGoBMUZ4 -LwwOiBBgqoRJoYKERmbQ+r4hFJTkdFgFZHu9YkT5L+TwCRbVYGK6bptZGCveVn92 -j4t8gOpR+m27Sc3YWxEqp3dW3u3GWEPqtK2MDdbj3omZ00vb+Ukdq3CLLNoxP/KC -PvJsxe2coMYz0R9SbSrKujJeYeb/6P8P5QxzcqiVfWtZRc9SJ+aianaTgfcag08R -9XDTiL4thCq5uaKsYaLECaPDKMF/skQrqbbLShf5rvcvDJO783s= -=UP6h ------END PGP SIGNATURE----- diff --git a/Python-3.10.13.tar.xz b/Python-3.10.14.tar.xz similarity index 100% rename from Python-3.10.13.tar.xz rename to Python-3.10.14.tar.xz diff --git a/Python-3.10.14.tar.xz.asc b/Python-3.10.14.tar.xz.asc new file mode 100644 index 0000000..f8e92c6 --- /dev/null +++ b/Python-3.10.14.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmX6IF0ACgkQsmmV4xAl +BWj0Lw//cYVyJ6YPSr42rylEeHV3+jZNjgpNadPFlM46M9zKmx8tP95fAut+JDF4 +gwcuql/lvfbPw1eJDrX+7TiIn33yS+KZDD/J2U6v/aCbuLByOWGWt5C3BfE6U5o/ +FkCbJ2GM6e4M85dVGEIudgwoZfZByRLxA8HJ6n37UhoeXSQQTr8LHfC4w7lyXJ+D +V2JQ+Mru0NJmG2FaVA+46Rez1ynsqSQnabJsM+0Kmaqs+ziWHSpWkLMecwqrojcJ +kzEs6TBGCaPDJncSONRU2o8i4pzkeq9SsXGT03kHsfPTZqU5sJD6yIeiIJbhQfea ++hPKg5+LVNDnOeSWPzm+5Fs1WWqeVNiFi/pDG4ofVXH79ULP7hxnnDMRe/ShdWWh +gp4uchu15tqbQLwCdH7r/r/j21PjSYxJBnz+1n2Yon9hYrjT8wTPalt6TyaQ7dpL +tKG/JLC8r9hxMSFSa6QlHB/+kOu57NZccy4XoitCEQhsnqmRFX/7zCiHM3P4t1G9 +RrLWYseS9elf6OZUlvF1PbwiEuRA36pvGCHEF0NWBF4yVxShN81f05pZutZVfVFx +W2V/ACB10agGbMsR5FVJROLo0zuek/G/QJ9GSp6cB6D1xuNbL1nNFfWZ7aEzPdeu +yBZkyyY4bOqstAw2I/bOlZTfFEMwdZFG6Zg2au/mtt2qFcGIEKM= +=9/7V +-----END PGP SIGNATURE----- diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index 9a698cc..5eca1ee 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -3,10 +3,8 @@ Misc/NEWS | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) -Index: Python-3.10.12/Doc/using/configure.rst -=================================================================== ---- Python-3.10.12.orig/Doc/using/configure.rst -+++ Python-3.10.12/Doc/using/configure.rst +--- a/Doc/using/configure.rst ++++ b/Doc/using/configure.rst @@ -42,7 +42,6 @@ General Options See :data:`sys.int_info.bits_per_digit `. @@ -29,11 +27,9 @@ Index: Python-3.10.12/Doc/using/configure.rst .. cmdoption:: --enable-framework=INSTALLDIR Create a Python.framework rather than a traditional Unix install. Optional -Index: Python-3.10.12/Misc/NEWS -=================================================================== ---- Python-3.10.12.orig/Misc/NEWS -+++ Python-3.10.12/Misc/NEWS -@@ -3618,7 +3618,7 @@ C API +--- a/Misc/NEWS ++++ b/Misc/NEWS +@@ -3731,7 +3731,7 @@ C API ----- - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name diff --git a/libexpat260.patch b/libexpat260.patch deleted file mode 100644 index bd4b4b3..0000000 --- a/libexpat260.patch +++ /dev/null @@ -1,107 +0,0 @@ -From f2eebf3c38eae77765247791576b437ec25ccfe2 Mon Sep 17 00:00:00 2001 -From: Serhiy Storchaka -Date: Sun, 11 Feb 2024 12:08:39 +0200 -Subject: [PATCH] gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 - (GH-115164) - -Feeding the parser by too small chunks defers parsing to prevent -CVE-2023-52425. Future versions of Expat may be more reactive. -(cherry picked from commit 4a08e7b3431cd32a0daf22a33421cd3035343dc4) - -Co-authored-by: Serhiy Storchaka ---- - Lib/test/test_xml_etree.py | 58 ++++++++++++------- - ...-02-08-14-21-28.gh-issue-115133.ycl4ko.rst | 2 + - 2 files changed, 38 insertions(+), 22 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst - -Index: Python-3.10.13/Lib/test/test_xml_etree.py -=================================================================== ---- Python-3.10.13.orig/Lib/test/test_xml_etree.py -+++ Python-3.10.13/Lib/test/test_xml_etree.py -@@ -13,6 +13,7 @@ import itertools - import operator - import os - import pickle -+import pyexpat - import sys - import textwrap - import types -@@ -120,6 +121,10 @@ ATTLIST_XML = """\ - - """ - -+fails_with_expat_2_6_0 = (unittest.expectedFailure -+ if pyexpat.version_info >= (2, 6, 0) else -+ lambda test: test) -+ - def checkwarnings(*filters, quiet=False): - def decorator(test): - def newtest(*args, **kwargs): -@@ -1396,28 +1401,37 @@ class XMLPullParserTest(unittest.TestCas - self.assertEqual([(action, elem.tag) for action, elem in events], - expected) - -- def test_simple_xml(self): -- for chunk_size in (None, 1, 5): -- with self.subTest(chunk_size=chunk_size): -- parser = ET.XMLPullParser() -- self.assert_event_tags(parser, []) -- self._feed(parser, "\n", chunk_size) -- self.assert_event_tags(parser, []) -- self._feed(parser, -- "\n text\n", chunk_size) -- self.assert_event_tags(parser, [('end', 'element')]) -- self._feed(parser, "texttail\n", chunk_size) -- self._feed(parser, "\n", chunk_size) -- self.assert_event_tags(parser, [ -- ('end', 'element'), -- ('end', 'empty-element'), -- ]) -- self._feed(parser, "\n", chunk_size) -- self.assert_event_tags(parser, [('end', 'root')]) -- self.assertIsNone(parser.close()) -+ def test_simple_xml(self, chunk_size=None): -+ parser = ET.XMLPullParser() -+ self.assert_event_tags(parser, []) -+ self._feed(parser, "\n", chunk_size) -+ self.assert_event_tags(parser, []) -+ self._feed(parser, -+ "\n text\n", chunk_size) -+ self.assert_event_tags(parser, [('end', 'element')]) -+ self._feed(parser, "texttail\n", chunk_size) -+ self._feed(parser, "\n", chunk_size) -+ self.assert_event_tags(parser, [ -+ ('end', 'element'), -+ ('end', 'empty-element'), -+ ]) -+ self._feed(parser, "\n", chunk_size) -+ self.assert_event_tags(parser, [('end', 'root')]) -+ self.assertIsNone(parser.close()) -+ -+ @fails_with_expat_2_6_0 -+ def test_simple_xml_chunk_1(self): -+ self.test_simple_xml(chunk_size=1) -+ -+ @fails_with_expat_2_6_0 -+ def test_simple_xml_chunk_5(self): -+ self.test_simple_xml(chunk_size=5) -+ -+ def test_simple_xml_chunk_22(self): -+ self.test_simple_xml(chunk_size=22) - - def test_feed_while_iterating(self): - parser = ET.XMLPullParser() -Index: Python-3.10.13/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst -=================================================================== ---- /dev/null -+++ Python-3.10.13/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst -@@ -0,0 +1,2 @@ -+Fix tests for :class:`~xml.etree.ElementTree.XMLPullParser` with Expat -+2.6.0. diff --git a/old-libexpat.patch b/old-libexpat.patch new file mode 100644 index 0000000..d40dd51 --- /dev/null +++ b/old-libexpat.patch @@ -0,0 +1,79 @@ +--- + Lib/test/test_sax.py | 10 +++++----- + Lib/test/test_xml_etree.py | 17 ++++++++--------- + 2 files changed, 13 insertions(+), 14 deletions(-) + +--- a/Lib/test/test_sax.py ++++ b/Lib/test/test_sax.py +@@ -1215,10 +1215,9 @@ class ExpatReaderTest(XmlTestBase): + + self.assertEqual(result.getvalue(), start + b"text") + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ "Reparse deferral not defined for libexpat < 2.6.0") + def test_flush_reparse_deferral_enabled(self): +- if pyexpat.version_info < (2, 6, 0): +- self.skipTest(f'Expat {pyexpat.version_info} does not support reparse deferral') +- + result = BytesIO() + xmlgen = XMLGenerator(result) + parser = create_parser() +@@ -1240,6 +1239,8 @@ class ExpatReaderTest(XmlTestBase): + + self.assertEqual(result.getvalue(), start + b"") + ++ @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) +@@ -1249,8 +1250,7 @@ class ExpatReaderTest(XmlTestBase): + for chunk in (""): + parser.feed(chunk) + +- if pyexpat.version_info >= (2, 6, 0): +- parser._parser.SetReparseDeferralEnabled(False) ++ parser._parser.SetReparseDeferralEnabled(False) + + self.assertEqual(result.getvalue(), start) # i.e. no elements started + self.assertFalse(parser._parser.GetReparseDeferralEnabled()) +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -1623,11 +1623,9 @@ class XMLPullParserTest(unittest.TestCas + with self.assertRaises(ValueError): + ET.XMLPullParser(events=('start', 'end', 'bogus')) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ "Reparse deferral not defined for libexpat < 2.6.0") + def test_flush_reparse_deferral_enabled(self): +- if pyexpat.version_info < (2, 6, 0): +- self.skipTest(f'Expat {pyexpat.version_info} does not ' +- 'support reparse deferral') +- + parser = ET.XMLPullParser(events=('start', 'end')) + + for chunk in (""): +@@ -1648,17 +1646,18 @@ class XMLPullParserTest(unittest.TestCas + + 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')) + + for chunk in (""): + parser.feed(chunk) + +- if pyexpat.version_info >= (2, 6, 0): +- if not ET is pyET: +- self.skipTest(f'XMLParser.(Get|Set)ReparseDeferralEnabled ' +- 'methods not available in C') +- parser._parser._parser.SetReparseDeferralEnabled(False) ++ if not ET is pyET: ++ self.skipTest(f'XMLParser.(Get|Set)ReparseDeferralEnabled ' ++ 'methods not available in C') ++ parser._parser._parser.SetReparseDeferralEnabled(False) + + self.assert_event_tags(parser, []) # i.e. no elements started + if ET is pyET: diff --git a/python-3.3.0b1-localpath.patch b/python-3.3.0b1-localpath.patch index ff9a376..f527f1f 100644 --- a/python-3.3.0b1-localpath.patch +++ b/python-3.3.0b1-localpath.patch @@ -1,7 +1,11 @@ +--- + Lib/site.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + --- a/Lib/site.py +++ b/Lib/site.py -@@ -76,7 +76,7 @@ import _sitebuiltins - import io +@@ -77,7 +77,7 @@ import io + import stat # Prefixes for site-packages; add additional prefixes like /usr/local here -PREFIXES = [sys.prefix, sys.exec_prefix] diff --git a/python.keyring b/python.keyring index 747bc27..145af71 100644 --- a/python.keyring +++ b/python.keyring @@ -1,109 +1,677 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -mQINBFq+ToQBEADRYvIVtbK6owynD3j3nxwpW2KEk/p+aDvtXmc2SR2dBcZ8sFW2 -R5vEsG8d3/D3wgv5pcL3KfNNXQYUnXVbobrFUUWQYc79qIsE3MgiPf5NVOtwKPUR -i5g9YJgKvpBxkQfqp3LYGm9ZBtwo3DVLA3yn7KsazCmAgTNFJYw7ku1XxgmIzY6K -5J30DfbJiqDqj4f9GslCCCCH3qiPnuLG/HUyVLHMpbWlaiy9NI0GcaLxjJewHj9w -W2D2lydkxe5JGo7egUkV3ILcuLVSVKA35SKY27dYqfuyqp9tAzaRbjDYjsYdHA6G -BqrNrKBn/GwlFDPrVdcvN3ZSY2wMLTxWE3Axc/FweuHxFnou/80FwX7F3JD+oEQ6 -rofmcxOBCC7J98I7HZAhP9jBn88XIS2hztbLq8d6rZJZRtcz0k61VR0ddO+TrFmf -9rMYCPgCckRtVxeFIVIabrN1IzKynLFeo040h8hSGswd6YKDOVwjJY6Oa6EmVefZ -a8QSt4+M65RSzH6SEPY008F3nJUAK6MEkzTak+tFltZNrVWu8p2xd1j9nmxAwEhZ -/lgbxLqzYgaUWmfyHeZ8yVA0MhHzdiAL8nVUEdG3KecIq0RWCJLGLWWIjd6KAJl1 -yAmhRYKK/sjPDsL3elHsFACfZbyx3o5GGQNlas1FYoPLWbaNGaJtgFTF2QARAQAB -tCtQYWJsbyBHYWxpbmRvIFNhbGdhZG8gPHBhYmxvZ3NhbEBnbWFpbC5jb20+iQJO -BBMBCgA4FiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+ToQCGwMFCwkIBwMFFQoJ -CAsFFgIDAQACHgECF4AACgkQZOYo+NaEaW2bmA/+PXIap2udLoUVOHxnsIBdqYwp -sv1Aj5lfIJmNhmxPbHShwp1Jg+w4urxe+2Dj5ofKVlIo1i83bQkvnKJMDXDVuc/K -P6zqhBJ3rT4Q3qx2mzX8bIfQoJ2JHuH4lkP+I7doDcHHRyeNASyk72VdQmU4twNw -Ibn8nSNV6ThKHdoPYzVnO2rZUFcGIqH5HNsvR+B7cc1MBCHsgURYwSVhSePIFGlZ -iasdBD6QQkDSe4QWi7AcJFWFElw4kbOKJWxAWsrEk+tMXJVGRjnmL289EmPCx/vx -BqKy7Mse0yWCSRR3vB+O6TB1S5SgEyEgqlYsfGNv1qf/rfRD4KkyCbNU3LhY1Aim -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 -=6Zb4 +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 +=7KV3 -----END PGP PUBLIC KEY BLOCK----- diff --git a/python310.changes b/python310.changes index 34de3c2..0f27724 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,61 @@ +------------------------------------------------------------------- +Sun Mar 24 00:43:14 UTC 2024 - Matej Cepl + +- Add old-libexpat.patch making the test suite work with + libexpat < 2.6.0 (gh#python/cpython#117187). + +------------------------------------------------------------------- +Fri Mar 22 21:17:25 UTC 2024 - Matej Cepl + +- Because of bsc#1189495 we have to revert use of %autopatch. + +------------------------------------------------------------------- +Thu Mar 21 07:38:15 UTC 2024 - Matej Cepl + +- Update 3.10.14: + - gh-115399 & gh-115398: bundled libexpat was updated to 2.6.0 + to address CVE-2023-52425, and control of the new reparse + deferral functionality was exposed with new APIs + (bsc#1219559). + - gh-109858: zipfile is now protected from the “quoted-overlap” + zipbomb to address CVE-2024-0450. It now raises BadZipFile + when attempting to read an entry that overlaps with another + entry or central directory. (bsc#1221854) + - gh-91133: tempfile.TemporaryDirectory cleanup no longer + dereferences symlinks when working around file system + permission errors to address CVE-2023-6597 (bsc#1219666) + - gh-115197: urllib.request no longer resolves the hostname + before checking it against the system’s proxy bypass list on + macOS and Windows + - gh-81194: a crash in socket.if_indextoname() with a specific + value (UINT_MAX) was fixed. Relatedly, an integer overflow in + socket.if_indextoname() on 64-bit non-Windows platforms was + fixed + - gh-113659: .pth files with names starting with a dot or + containing the hidden file attribute are now skipped + - gh-102388: iso2022_jp_3 and iso2022_jp_2004 codecs no longer + read out of bounds + - gh-114572: ssl.SSLContext.cert_store_stats() and + ssl.SSLContext.get_ca_certs() now correctly lock access to + the certificate store, when the ssl.SSLContext is shared + across multiple threads +- Remove upstreamed patches: + - CVE-2023-6597-TempDir-cleaning-symlink.patch + - libexpat260.patch +- Readjust patches: + - F00251-change-user-install-location.patch + - fix_configure_rst.patch + - python-3.3.0b1-localpath.patch + - skip-test_pyobject_freed_is_freed.patch +- Port to %autosetup and %autopatch. + +------------------------------------------------------------------- +Wed Mar 6 14:13:58 UTC 2024 - Pedro Monreal + +- Use the system-wide crypto-policies [bsc#1211301] + * Use the system default cipher list instead of hardcoded values + * Add the --with-ssl-default-suites=openssl configure option + ------------------------------------------------------------------- Fri Feb 23 01:06:42 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index ce0e095..49ce1b7 100644 --- a/python310.spec +++ b/python310.spec @@ -103,7 +103,7 @@ Obsoletes: python39%{?1:-%{1}} %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so %bcond_without profileopt Name: %{python_pkg_name}%{psuffix} -Version: 3.10.13 +Version: 3.10.14 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -139,44 +139,45 @@ Source100: PACKAGING-NOTES # Set values of prefix and exec_prefix in distutils install command # to /usr/local if executable is /usr/bin/python* and RPM build # is not detected to make pip and distutils install into separate location -Patch02: F00251-change-user-install-location.patch +Patch01: F00251-change-user-install-location.patch # PATCH-FEATURE-UPSTREAM distutils-reproducible-compile.patch gh#python/cpython#8057 mcepl@suse.com # Improve reproduceability -Patch06: distutils-reproducible-compile.patch +Patch02: distutils-reproducible-compile.patch # support finding packages in /usr/local, install to /usr/local by default -Patch07: python-3.3.0b1-localpath.patch +Patch03: python-3.3.0b1-localpath.patch # replace DATE, TIME and COMPILER by fixed definitions to aid reproducible builds -Patch08: python-3.3.0b1-fix_date_time_compiler.patch +Patch04: python-3.3.0b1-fix_date_time_compiler.patch # POSIX_FADV_WILLNEED throws EINVAL. Use a different constant in test -Patch09: python-3.3.0b1-test-posix_fadvise.patch +Patch05: python-3.3.0b1-test-posix_fadvise.patch # Raise timeout value for test_subprocess -Patch15: subprocess-raise-timeout.patch +Patch06: subprocess-raise-timeout.patch # PATCH-FEATURE-UPSTREAM bpo-31046_ensurepip_honours_prefix.patch bpo#31046 mcepl@suse.com # ensurepip should honour the value of $(prefix) -Patch29: bpo-31046_ensurepip_honours_prefix.patch +Patch07: bpo-31046_ensurepip_honours_prefix.patch # PATCH-FIX-SLE no-skipif-doctests.patch jsc#SLE-13738 mcepl@suse.com # SLE-15 version of Sphinx doesn't know about skipif directive in doctests. -Patch33: no-skipif-doctests.patch +Patch11: no-skipif-doctests.patch + # PATCH-FIX-SLE skip-test_pyobject_freed_is_freed.patch mcepl@suse.com # skip a test failing on SLE-15 -Patch34: skip-test_pyobject_freed_is_freed.patch +Patch15: skip-test_pyobject_freed_is_freed.patch # PATCH-FIX-SLE fix_configure_rst.patch bpo#43774 mcepl@suse.com # remove duplicate link targets and make documentation with old Sphinx in SLE -Patch35: fix_configure_rst.patch +Patch16: fix_configure_rst.patch # PATCH-FIX-UPSTREAM bpo-46811 gh#python/cpython#7da97f61816f mcepl@suse.com # NOTE: SUSE version of expat 2.4.4 is patched in SUSE for CVE-2022-25236 -Patch36: support-expat-CVE-2022-25236-patched.patch +Patch17: support-expat-CVE-2022-25236-patched.patch # PATCH-FIX-UPSTREAM bpo-37596-make-set-marshalling.patch bsc#1211765 mcepl@suse.com # Make `set` and `frozenset` marshalling deterministic -Patch38: bpo-37596-make-set-marshalling.patch +Patch18: bpo-37596-make-set-marshalling.patch # PATCH-FIX-UPSTREAM gh-78214-marshal_stabilize_FLAG_REF.patch bsc#1213463 mcepl@suse.com # marshal: Stabilize FLAG_REF usage -Patch39: gh-78214-marshal_stabilize_FLAG_REF.patch +Patch19: gh-78214-marshal_stabilize_FLAG_REF.patch # PATCH-FIX-UPSTREAM CVE-2023-27043-email-parsing-errors.patch bsc#1210638 mcepl@suse.com # Detect email address parsing errors and return empty tuple to # indicate the parsing error (old API), from gh#python/cpython!105127 # Patch carries a REGRESSION (gh#python/cpython#106669), so it has been also partially REVERTED -Patch40: CVE-2023-27043-email-parsing-errors.patch +Patch20: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM fix-sphinx-72.patch gh#python/cpython#97950 # This is a patch with a lot of PR combined to make the doc work with # sphinx 7.2 @@ -190,13 +191,10 @@ Patch40: CVE-2023-27043-email-parsing-errors.patch # * gh#python/cpython#104163 # * gh#python/cpython#104221 # * gh#python/cpython#107246 -Patch42: fix-sphinx-72.patch -# PATCH-FIX-UPSTREAM libexpat260.patch gh#python/cpython#115289 -# Fix tests for XMLPullParser with Expat 2.6.0 -Patch43: libexpat260.patch -# PATCH-FIX-UPSTREAM CVE-2023-6597-TempDir-cleaning-symlink.patch bsc#1219666 mcepl@suse.com -# tempfile.TemporaryDirectory: fix symlink bug in cleanup (from gh#python/cpython!99930) -Patch44: CVE-2023-6597-TempDir-cleaning-symlink.patch +Patch21: fix-sphinx-72.patch +# PATCH-FIX-UPSTREAM old-libexpat.patch gh#python/cpython#117187 mcepl@suse.com +# Make the test suite work with libexpat < 2.6.0 +Patch22: old-libexpat.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -455,25 +453,26 @@ other applications. %prep %setup -q -n %{tarname} -%patch -P 02 -p1 -%patch -P 06 -p1 -%patch -P 07 -p1 -%patch -P 08 -p1 -%patch -P 09 -p1 -%patch -P 15 -p1 -%patch -P 29 -p1 +%patch -p1 -P 01 +%patch -p1 -P 02 +%patch -p1 -P 03 +%patch -p1 -P 04 +%patch -p1 -P 05 +%patch -p1 -P 06 +%patch -p1 -P 07 + %if 0%{?sle_version} && 0%{?sle_version} <= 150300 -%patch -P 33 -p1 -%patch -P 34 -p1 +%patch -P 11 -p1 %endif -%patch -P 35 -p1 -%patch -P 36 -p1 -%patch -P 38 -p1 -%patch -P 39 -p1 -%patch -P 40 -p1 -%patch -P 42 -p1 -%patch -P 43 -p1 -%patch -P 44 -p1 + +%patch -p1 -P 15 +%patch -p1 -P 16 +%patch -p1 -P 17 +%patch -p1 -P 18 +%patch -p1 -P 19 +%patch -p1 -P 20 +%patch -p1 -P 21 +%patch -p1 -P 22 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -543,6 +542,9 @@ export CFLAGS="%{optflags} -IVendor/" --with-system-ffi \ --with-system-expat \ --with-lto \ +%if 0%{?suse_version} >= 1550 || 0%{?sle_version} >= 150400 + --with-ssl-default-suites=openssl \ +%endif %if %{with profileopt} --enable-optimizations \ %endif @@ -574,6 +576,8 @@ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH \ %endif %check +export SUSE_VERSION="0%{?suse_version}" +export SLE_VERSION="0%{?sle_version}" %if %{with general} # exclude test_gdb -- it doesn't run in buildservice anyway, and fails on missing debuginfos # when you install gdb into your test env diff --git a/skip-test_pyobject_freed_is_freed.patch b/skip-test_pyobject_freed_is_freed.patch index 7be01a7..b9a65cb 100644 --- a/skip-test_pyobject_freed_is_freed.patch +++ b/skip-test_pyobject_freed_is_freed.patch @@ -1,14 +1,23 @@ --- - Lib/test/test_capi.py | 1 + - 1 file changed, 1 insertion(+) + Lib/test/test_capi/test_misc.py | 3 +++ + 1 file changed, 3 insertions(+) ---- a/Lib/test/test_capi.py -+++ b/Lib/test/test_capi.py -@@ -941,6 +941,7 @@ class PyMemDebugTests(unittest.TestCase) +--- a/Lib/test/test_capi/test_misc.py ++++ b/Lib/test/test_capi/test_misc.py +@@ -38,6 +38,8 @@ Py_DEBUG = hasattr(sys, 'gettotalrefcoun + def decode_stderr(err): + return err.decode('utf-8', 'replace').replace('\r', '') + ++# Which version of the SLE distro we build on? ++SLE_VERSION = int(os.environ.get('SLE_VERSION', '0'), 10) + + def testfunction(self): + """some doc""" +@@ -967,6 +969,7 @@ class PyMemDebugTests(unittest.TestCase) def test_pyobject_forbidden_bytes_is_freed(self): self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed') -+ @unittest.skip('Failing on Leap 15.*') ++ @unittest.skipIf(0 < SLE_VERSION < 150300, 'Failing on Leap 15.*') def test_pyobject_freed_is_freed(self): self.check_pyobject_is_freed('check_pyobject_freed_is_freed') From ee08452a4b766bdba95533cc8b17ab61ba3bb42f6d1acb558f5baeaa6ffbca6e Mon Sep 17 00:00:00 2001 From: Ruediger Oertel Date: Mon, 20 May 2024 18:07:56 +0000 Subject: [PATCH 13/23] Set link to python310.33868 via maintenance_release request --- CVE-2023-52425-libexpat-2.6.0-backport.patch | 57 ++ Python-3.10.14.tar.xz.asc | 26 +- old-libexpat.patch | 79 -- python.keyring | 780 +++---------------- python310.changes | 8 + python310.spec | 4 +- 6 files changed, 186 insertions(+), 768 deletions(-) create mode 100644 CVE-2023-52425-libexpat-2.6.0-backport.patch delete mode 100644 old-libexpat.patch diff --git a/CVE-2023-52425-libexpat-2.6.0-backport.patch b/CVE-2023-52425-libexpat-2.6.0-backport.patch new file mode 100644 index 0000000..334b7b8 --- /dev/null +++ b/CVE-2023-52425-libexpat-2.6.0-backport.patch @@ -0,0 +1,57 @@ +--- + Lib/test/test_pyexpat.py | 4 ++++ + Lib/test/test_sax.py | 3 +++ + Lib/test/test_xml_etree.py | 7 +++++++ + 3 files changed, 14 insertions(+) + +--- a/Lib/test/test_pyexpat.py ++++ b/Lib/test/test_pyexpat.py +@@ -766,6 +766,10 @@ class ReparseDeferralTest(unittest.TestC + self.assertEqual(started, ['doc']) + + def test_reparse_deferral_disabled(self): ++ if expat.version_info < (2, 6, 0): ++ self.skipTest(f'Expat {expat.version_info} does not ' ++ 'support reparse deferral') ++ + started = [] + + def start_element(name, _): +--- a/Lib/test/test_sax.py ++++ b/Lib/test/test_sax.py +@@ -1240,6 +1240,9 @@ class ExpatReaderTest(XmlTestBase): + + self.assertEqual(result.getvalue(), start + b"") + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Expat {pyexpat.version_info} does not ' ++ 'support reparse deferral') + def test_flush_reparse_deferral_disabled(self): + result = BytesIO() + xmlgen = XMLGenerator(result) +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -1420,9 +1420,13 @@ class XMLPullParserTest(unittest.TestCas + self.assert_event_tags(parser, [('end', 'root')]) + self.assertIsNone(parser.close()) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Fail with patched version of Expat {pyexpat.version_info}') + def test_simple_xml_chunk_1(self): + self.test_simple_xml(chunk_size=1, flush=True) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Fail with patched version of Expat {pyexpat.version_info}') + def test_simple_xml_chunk_5(self): + self.test_simple_xml(chunk_size=5, flush=True) + +@@ -1648,6 +1652,9 @@ class XMLPullParserTest(unittest.TestCas + + self.assert_event_tags(parser, [('end', 'doc')]) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Expat {pyexpat.version_info} does not ' ++ 'support reparse deferral') + def test_flush_reparse_deferral_disabled(self): + parser = ET.XMLPullParser(events=('start', 'end')) + diff --git a/Python-3.10.14.tar.xz.asc b/Python-3.10.14.tar.xz.asc index f8e92c6..b087178 100644 --- a/Python-3.10.14.tar.xz.asc +++ b/Python-3.10.14.tar.xz.asc @@ -1,16 +1,16 @@ -----BEGIN PGP SIGNATURE----- -iQIzBAABCAAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmX6IF0ACgkQsmmV4xAl -BWj0Lw//cYVyJ6YPSr42rylEeHV3+jZNjgpNadPFlM46M9zKmx8tP95fAut+JDF4 -gwcuql/lvfbPw1eJDrX+7TiIn33yS+KZDD/J2U6v/aCbuLByOWGWt5C3BfE6U5o/ -FkCbJ2GM6e4M85dVGEIudgwoZfZByRLxA8HJ6n37UhoeXSQQTr8LHfC4w7lyXJ+D -V2JQ+Mru0NJmG2FaVA+46Rez1ynsqSQnabJsM+0Kmaqs+ziWHSpWkLMecwqrojcJ -kzEs6TBGCaPDJncSONRU2o8i4pzkeq9SsXGT03kHsfPTZqU5sJD6yIeiIJbhQfea -+hPKg5+LVNDnOeSWPzm+5Fs1WWqeVNiFi/pDG4ofVXH79ULP7hxnnDMRe/ShdWWh -gp4uchu15tqbQLwCdH7r/r/j21PjSYxJBnz+1n2Yon9hYrjT8wTPalt6TyaQ7dpL -tKG/JLC8r9hxMSFSa6QlHB/+kOu57NZccy4XoitCEQhsnqmRFX/7zCiHM3P4t1G9 -RrLWYseS9elf6OZUlvF1PbwiEuRA36pvGCHEF0NWBF4yVxShN81f05pZutZVfVFx -W2V/ACB10agGbMsR5FVJROLo0zuek/G/QJ9GSp6cB6D1xuNbL1nNFfWZ7aEzPdeu -yBZkyyY4bOqstAw2I/bOlZTfFEMwdZFG6Zg2au/mtt2qFcGIEKM= -=9/7V +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmX76AkACgkQ/+h0BBaL +2EeKvhAAuN+7X3iFv8tYwUTbKJT9x9fLsADI5wOn5j8xuDiXQCOMzsqqyB1RSdEd +tbCQXg9XJj1bVHc4DY337vUix9jvFcTqbQqlzUm/peX4buY2bKkZu2quti1iWSJf +IN26jDYO2TobPvGdvNiH2Hceqe1dc5tU7iYEfaLR5ImgO4aGgK6x4DiLdmFqo2bk +ZZWZLkXbwenrSdLVmUZLP5Gg2dsfMkbfFpydau9Zk3RVl6mVYATwzJaY9K5otC0K +7kc+nKPwkTxKAjndbznjsVrWK0Xcr4hrlMHs4Re2Nrdqa2mVd1jAAFO5xETJJtd7 +YqL6mQuJ9wQfEEq2QWz1hEi67l8g8VeEgzYQOjZ6pTxwYYt0YDfKBjRtRCWuJ11c +w6Q+pniGcgIHAMkQGjZds88CwAdIiyG7IAIT2ovW+xVxH/JqLPHeRsHMKYx4DPqL +2y23Tchw+gBUvmbwCdObXWL1eq5R3Xz3ikkdX/I6zknmEvgPTi5N59C1IQqh0W/6 +8uMrHOdELz9I5Fd+zGTJ8iyh/wrecMiIx+HOsBTYv/FYbMVnQUshUBOiD70geUb5 +uSeHyxl/P7VK/0phbxOznU4oDot2fHPmZRK3q+K67J9L16q7pEou1AJAw8E7ed5C +Ywf+y2tdxsuqChQK/OA6uuqW6rXjZPuCoG5Bn6YIEuU769LsHcY= +=1PBR -----END PGP SIGNATURE----- diff --git a/old-libexpat.patch b/old-libexpat.patch deleted file mode 100644 index d40dd51..0000000 --- a/old-libexpat.patch +++ /dev/null @@ -1,79 +0,0 @@ ---- - Lib/test/test_sax.py | 10 +++++----- - Lib/test/test_xml_etree.py | 17 ++++++++--------- - 2 files changed, 13 insertions(+), 14 deletions(-) - ---- a/Lib/test/test_sax.py -+++ b/Lib/test/test_sax.py -@@ -1215,10 +1215,9 @@ class ExpatReaderTest(XmlTestBase): - - self.assertEqual(result.getvalue(), start + b"text") - -+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), -+ "Reparse deferral not defined for libexpat < 2.6.0") - def test_flush_reparse_deferral_enabled(self): -- if pyexpat.version_info < (2, 6, 0): -- self.skipTest(f'Expat {pyexpat.version_info} does not support reparse deferral') -- - result = BytesIO() - xmlgen = XMLGenerator(result) - parser = create_parser() -@@ -1240,6 +1239,8 @@ class ExpatReaderTest(XmlTestBase): - - self.assertEqual(result.getvalue(), start + b"") - -+ @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) -@@ -1249,8 +1250,7 @@ class ExpatReaderTest(XmlTestBase): - for chunk in (""): - parser.feed(chunk) - -- if pyexpat.version_info >= (2, 6, 0): -- parser._parser.SetReparseDeferralEnabled(False) -+ parser._parser.SetReparseDeferralEnabled(False) - - self.assertEqual(result.getvalue(), start) # i.e. no elements started - self.assertFalse(parser._parser.GetReparseDeferralEnabled()) ---- a/Lib/test/test_xml_etree.py -+++ b/Lib/test/test_xml_etree.py -@@ -1623,11 +1623,9 @@ class XMLPullParserTest(unittest.TestCas - with self.assertRaises(ValueError): - ET.XMLPullParser(events=('start', 'end', 'bogus')) - -+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), -+ "Reparse deferral not defined for libexpat < 2.6.0") - def test_flush_reparse_deferral_enabled(self): -- if pyexpat.version_info < (2, 6, 0): -- self.skipTest(f'Expat {pyexpat.version_info} does not ' -- 'support reparse deferral') -- - parser = ET.XMLPullParser(events=('start', 'end')) - - for chunk in (""): -@@ -1648,17 +1646,18 @@ class XMLPullParserTest(unittest.TestCas - - 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')) - - for chunk in (""): - parser.feed(chunk) - -- if pyexpat.version_info >= (2, 6, 0): -- if not ET is pyET: -- self.skipTest(f'XMLParser.(Get|Set)ReparseDeferralEnabled ' -- 'methods not available in C') -- parser._parser._parser.SetReparseDeferralEnabled(False) -+ if not ET is pyET: -+ self.skipTest(f'XMLParser.(Get|Set)ReparseDeferralEnabled ' -+ 'methods not available in C') -+ parser._parser._parser.SetReparseDeferralEnabled(False) - - self.assert_event_tags(parser, []) # i.e. no elements started - if ET is pyET: diff --git a/python.keyring b/python.keyring index 145af71..747bc27 100644 --- a/python.keyring +++ b/python.keyring @@ -1,677 +1,109 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -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 -=7KV3 +mQINBFq+ToQBEADRYvIVtbK6owynD3j3nxwpW2KEk/p+aDvtXmc2SR2dBcZ8sFW2 +R5vEsG8d3/D3wgv5pcL3KfNNXQYUnXVbobrFUUWQYc79qIsE3MgiPf5NVOtwKPUR +i5g9YJgKvpBxkQfqp3LYGm9ZBtwo3DVLA3yn7KsazCmAgTNFJYw7ku1XxgmIzY6K +5J30DfbJiqDqj4f9GslCCCCH3qiPnuLG/HUyVLHMpbWlaiy9NI0GcaLxjJewHj9w +W2D2lydkxe5JGo7egUkV3ILcuLVSVKA35SKY27dYqfuyqp9tAzaRbjDYjsYdHA6G +BqrNrKBn/GwlFDPrVdcvN3ZSY2wMLTxWE3Axc/FweuHxFnou/80FwX7F3JD+oEQ6 +rofmcxOBCC7J98I7HZAhP9jBn88XIS2hztbLq8d6rZJZRtcz0k61VR0ddO+TrFmf +9rMYCPgCckRtVxeFIVIabrN1IzKynLFeo040h8hSGswd6YKDOVwjJY6Oa6EmVefZ +a8QSt4+M65RSzH6SEPY008F3nJUAK6MEkzTak+tFltZNrVWu8p2xd1j9nmxAwEhZ +/lgbxLqzYgaUWmfyHeZ8yVA0MhHzdiAL8nVUEdG3KecIq0RWCJLGLWWIjd6KAJl1 +yAmhRYKK/sjPDsL3elHsFACfZbyx3o5GGQNlas1FYoPLWbaNGaJtgFTF2QARAQAB +tCtQYWJsbyBHYWxpbmRvIFNhbGdhZG8gPHBhYmxvZ3NhbEBnbWFpbC5jb20+iQJO +BBMBCgA4FiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+ToQCGwMFCwkIBwMFFQoJ +CAsFFgIDAQACHgECF4AACgkQZOYo+NaEaW2bmA/+PXIap2udLoUVOHxnsIBdqYwp +sv1Aj5lfIJmNhmxPbHShwp1Jg+w4urxe+2Dj5ofKVlIo1i83bQkvnKJMDXDVuc/K +P6zqhBJ3rT4Q3qx2mzX8bIfQoJ2JHuH4lkP+I7doDcHHRyeNASyk72VdQmU4twNw +Ibn8nSNV6ThKHdoPYzVnO2rZUFcGIqH5HNsvR+B7cc1MBCHsgURYwSVhSePIFGlZ +iasdBD6QQkDSe4QWi7AcJFWFElw4kbOKJWxAWsrEk+tMXJVGRjnmL289EmPCx/vx +BqKy7Mse0yWCSRR3vB+O6TB1S5SgEyEgqlYsfGNv1qf/rfRD4KkyCbNU3LhY1Aim +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 +=6Zb4 -----END PGP PUBLIC KEY BLOCK----- diff --git a/python310.changes b/python310.changes index 0f27724..d93c3be 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Fri Apr 19 08:37:04 UTC 2024 - Matej Cepl + +- Add CVE-2023-52425-libexpat-2.6.0-backport.patch to fix tests with + patched libexpat below 2.6.0 that doesn't update the version number, + just in SLE. +- Drop old-libexpat.patch, which is replaced. + ------------------------------------------------------------------- Sun Mar 24 00:43:14 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index 49ce1b7..202491b 100644 --- a/python310.spec +++ b/python310.spec @@ -192,9 +192,9 @@ Patch20: CVE-2023-27043-email-parsing-errors.patch # * gh#python/cpython#104221 # * gh#python/cpython#107246 Patch21: fix-sphinx-72.patch -# PATCH-FIX-UPSTREAM old-libexpat.patch gh#python/cpython#117187 mcepl@suse.com +# PATCH-FIX-UPSTREAM CVE-2023-52425-libexpat-2.6.0-backport.patch gh#python/cpython#117187 mcepl@suse.com # Make the test suite work with libexpat < 2.6.0 -Patch22: old-libexpat.patch +Patch22: CVE-2023-52425-libexpat-2.6.0-backport.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes From cbf1db613667317b47a50e871995ff807ae9f881079975542cb2ea176420c03b Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Mon, 1 Jul 2024 22:33:34 +0000 Subject: [PATCH 14/23] Set link to python310.34503 via maintenance_release request --- CVE-2024-4032-private-IP-addrs.patch | 376 +++++++++++++++++++++++++++ python310.changes | 11 +- python310.spec | 4 + 3 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 CVE-2024-4032-private-IP-addrs.patch diff --git a/CVE-2024-4032-private-IP-addrs.patch b/CVE-2024-4032-private-IP-addrs.patch new file mode 100644 index 0000000..7aa5e10 --- /dev/null +++ b/CVE-2024-4032-private-IP-addrs.patch @@ -0,0 +1,376 @@ +From 0740166e60b8cdae9448220beb28721f0126ee03 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Wed, 24 Apr 2024 14:29:30 +0200 +Subject: [PATCH 1/2] gh-113171: gh-65056: Fix "private" (non-global) IP + address ranges (GH-113179) (GH-113186) (GH-118177) + +* GH-113171: Fix "private" (non-global) IP address ranges (GH-113179) + +The _private_networks variables, used by various is_private +implementations, were missing some ranges and at the same time had +overly strict ranges (where there are more specific ranges considered +globally reachable by the IANA registries). + +This patch updates the ranges with what was missing or otherwise +incorrect. + +100.64.0.0/10 is left alone, for now, as it's been made special in [1]. + +The _address_exclude_many() call returns 8 networks for IPv4, 121 +networks for IPv6. + +[1] https://github.com/python/cpython/issues/61602 + +* GH-65056: Improve the IP address' is_global/is_private documentation (GH-113186) + +It wasn't clear what the semantics of is_global/is_private are and, when +one gets to the bottom of it, it's not quite so simple (hence the +exceptions listed). + +(cherry picked from commit 2a4cbf17af19a01d942f9579342f77c39fbd23c4) +(cherry picked from commit 40d75c2b7f5c67e254d0a025e0f2e2c7ada7f69f) + +--------- + +(cherry picked from commit f86b17ac511e68192ba71f27e752321a3252cee3) + +Co-authored-by: Jakub Stasiak +--- + Doc/library/ipaddress.rst | 43 +++- + Doc/whatsnew/3.10.rst | 9 + Lib/ipaddress.py | 99 +++++++--- + Lib/test/test_ipaddress.py | 52 +++++ + Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst | 9 + 5 files changed, 187 insertions(+), 25 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst + +--- a/Doc/library/ipaddress.rst ++++ b/Doc/library/ipaddress.rst +@@ -188,18 +188,53 @@ write code that handles both IP versions + + .. attribute:: is_private + +- ``True`` if the address is allocated for private networks. See ++ ``True`` if the address is defined as not globally reachable by + iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ +- (for IPv6). ++ (for IPv6) with the following exceptions: ++ ++ * ``is_private`` is ``False`` for the shared address space (``100.64.0.0/10``) ++ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the ++ semantics of the underlying IPv4 addresses and the following condition holds ++ (see :attr:`IPv6Address.ipv4_mapped`):: ++ ++ address.is_private == address.ipv4_mapped.is_private ++ ++ ``is_private`` has value opposite to :attr:`is_global`, except for the shared address space ++ (``100.64.0.0/10`` range) where they are both ``False``. ++ ++ .. versionchanged:: 3.10.15 ++ ++ Fixed some false positives and false negatives. ++ ++ * ``192.0.0.0/24`` is considered private with the exception of ``192.0.0.9/32`` and ++ ``192.0.0.10/32`` (previously: only the ``192.0.0.0/29`` sub-range was considered private). ++ * ``64:ff9b:1::/48`` is considered private. ++ * ``2002::/16`` is considered private. ++ * There are exceptions within ``2001::/23`` (otherwise considered private): ``2001:1::1/128``, ++ ``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``. ++ The exceptions are not considered private. + + .. attribute:: is_global + +- ``True`` if the address is allocated for public networks. See ++ ``True`` if the address is defined as globally reachable by + iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ +- (for IPv6). ++ (for IPv6) with the following exception: ++ ++ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the ++ semantics of the underlying IPv4 addresses and the following condition holds ++ (see :attr:`IPv6Address.ipv4_mapped`):: ++ ++ address.is_global == address.ipv4_mapped.is_global ++ ++ ``is_global`` has value opposite to :attr:`is_private`, except for the shared address space ++ (``100.64.0.0/10`` range) where they are both ``False``. + + .. versionadded:: 3.4 + ++ .. versionchanged:: 3.10.15 ++ ++ Fixed some false positives and false negatives, see :attr:`is_private` for details. ++ + .. attribute:: is_unspecified + + ``True`` if the address is unspecified. See :RFC:`5735` (for IPv4) +--- a/Doc/whatsnew/3.10.rst ++++ b/Doc/whatsnew/3.10.rst +@@ -2348,3 +2348,12 @@ tarfile + :exc:`DeprecationWarning`. + In Python 3.14, the default will switch to ``'data'``. + (Contributed by Petr Viktorin in :pep:`706`.) ++ ++Notable changes in 3.10.15 ++========================== ++ ++ipaddress ++--------- ++ ++* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``, ++ ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``. +--- a/Lib/ipaddress.py ++++ b/Lib/ipaddress.py +@@ -1323,18 +1323,41 @@ class IPv4Address(_BaseV4, _BaseAddress) + @property + @functools.lru_cache() + def is_private(self): +- """Test if this address is allocated for private networks. +- +- Returns: +- A boolean, True if the address is reserved per +- iana-ipv4-special-registry. +- +- """ +- return any(self in net for net in self._constants._private_networks) ++ """``True`` if the address is defined as not globally reachable by ++ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ ++ (for IPv6) with the following exceptions: ++ ++ * ``is_private`` is ``False`` for ``100.64.0.0/10`` ++ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the ++ semantics of the underlying IPv4 addresses and the following condition holds ++ (see :attr:`IPv6Address.ipv4_mapped`):: ++ ++ address.is_private == address.ipv4_mapped.is_private ++ ++ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10`` ++ IPv4 range where they are both ``False``. ++ """ ++ return ( ++ any(self in net for net in self._constants._private_networks) ++ and all(self not in net for net in self._constants._private_networks_exceptions) ++ ) + + @property + @functools.lru_cache() + def is_global(self): ++ """``True`` if the address is defined as globally reachable by ++ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ ++ (for IPv6) with the following exception: ++ ++ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the ++ semantics of the underlying IPv4 addresses and the following condition holds ++ (see :attr:`IPv6Address.ipv4_mapped`):: ++ ++ address.is_global == address.ipv4_mapped.is_global ++ ++ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10`` ++ IPv4 range where they are both ``False``. ++ """ + return self not in self._constants._public_network and not self.is_private + + @property +@@ -1538,13 +1561,15 @@ class _IPv4Constants: + + _public_network = IPv4Network('100.64.0.0/10') + ++ # Not globally reachable address blocks listed on ++ # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + _private_networks = [ + IPv4Network('0.0.0.0/8'), + IPv4Network('10.0.0.0/8'), + IPv4Network('127.0.0.0/8'), + IPv4Network('169.254.0.0/16'), + IPv4Network('172.16.0.0/12'), +- IPv4Network('192.0.0.0/29'), ++ IPv4Network('192.0.0.0/24'), + IPv4Network('192.0.0.170/31'), + IPv4Network('192.0.2.0/24'), + IPv4Network('192.168.0.0/16'), +@@ -1555,6 +1580,11 @@ class _IPv4Constants: + IPv4Network('255.255.255.255/32'), + ] + ++ _private_networks_exceptions = [ ++ IPv4Network('192.0.0.9/32'), ++ IPv4Network('192.0.0.10/32'), ++ ] ++ + _reserved_network = IPv4Network('240.0.0.0/4') + + _unspecified_address = IPv4Address('0.0.0.0') +@@ -1996,27 +2026,42 @@ class IPv6Address(_BaseV6, _BaseAddress) + @property + @functools.lru_cache() + def is_private(self): +- """Test if this address is allocated for private networks. ++ """``True`` if the address is defined as not globally reachable by ++ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ ++ (for IPv6) with the following exceptions: ++ ++ * ``is_private`` is ``False`` for ``100.64.0.0/10`` ++ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the ++ semantics of the underlying IPv4 addresses and the following condition holds ++ (see :attr:`IPv6Address.ipv4_mapped`):: + +- Returns: +- A boolean, True if the address is reserved per +- iana-ipv6-special-registry, or is ipv4_mapped and is +- reserved in the iana-ipv4-special-registry. ++ address.is_private == address.ipv4_mapped.is_private + ++ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10`` ++ IPv4 range where they are both ``False``. + """ + ipv4_mapped = self.ipv4_mapped + if ipv4_mapped is not None: + return ipv4_mapped.is_private +- return any(self in net for net in self._constants._private_networks) ++ return ( ++ any(self in net for net in self._constants._private_networks) ++ and all(self not in net for net in self._constants._private_networks_exceptions) ++ ) + + @property + def is_global(self): +- """Test if this address is allocated for public networks. ++ """``True`` if the address is defined as globally reachable by ++ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ ++ (for IPv6) with the following exception: ++ ++ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the ++ semantics of the underlying IPv4 addresses and the following condition holds ++ (see :attr:`IPv6Address.ipv4_mapped`):: + +- Returns: +- A boolean, true if the address is not reserved per +- iana-ipv6-special-registry. ++ address.is_global == address.ipv4_mapped.is_global + ++ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10`` ++ IPv4 range where they are both ``False``. + """ + return not self.is_private + +@@ -2257,19 +2302,31 @@ class _IPv6Constants: + + _multicast_network = IPv6Network('ff00::/8') + ++ # Not globally reachable address blocks listed on ++ # https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + _private_networks = [ + IPv6Network('::1/128'), + IPv6Network('::/128'), + IPv6Network('::ffff:0:0/96'), ++ IPv6Network('64:ff9b:1::/48'), + IPv6Network('100::/64'), + IPv6Network('2001::/23'), +- IPv6Network('2001:2::/48'), + IPv6Network('2001:db8::/32'), +- IPv6Network('2001:10::/28'), ++ # IANA says N/A, let's consider it not globally reachable to be safe ++ IPv6Network('2002::/16'), + IPv6Network('fc00::/7'), + IPv6Network('fe80::/10'), + ] + ++ _private_networks_exceptions = [ ++ IPv6Network('2001:1::1/128'), ++ IPv6Network('2001:1::2/128'), ++ IPv6Network('2001:3::/32'), ++ IPv6Network('2001:4:112::/48'), ++ IPv6Network('2001:20::/28'), ++ IPv6Network('2001:30::/28'), ++ ] ++ + _reserved_networks = [ + IPv6Network('::/8'), IPv6Network('100::/8'), + IPv6Network('200::/7'), IPv6Network('400::/6'), +--- a/Lib/test/test_ipaddress.py ++++ b/Lib/test/test_ipaddress.py +@@ -2263,6 +2263,10 @@ class IpaddrUnitTest(unittest.TestCase): + self.assertEqual(True, ipaddress.ip_address( + '172.31.255.255').is_private) + self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private) ++ self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global) ++ self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global) ++ self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global) ++ self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global) + + self.assertEqual(True, + ipaddress.ip_address('169.254.100.200').is_link_local) +@@ -2278,6 +2282,40 @@ class IpaddrUnitTest(unittest.TestCase): + self.assertEqual(False, ipaddress.ip_address('128.0.0.0').is_loopback) + self.assertEqual(True, ipaddress.ip_network('0.0.0.0').is_unspecified) + ++ def testPrivateNetworks(self): ++ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/0").is_private) ++ self.assertEqual(False, ipaddress.ip_network("1.0.0.0/8").is_private) ++ ++ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/8").is_private) ++ self.assertEqual(True, ipaddress.ip_network("10.0.0.0/8").is_private) ++ self.assertEqual(True, ipaddress.ip_network("127.0.0.0/8").is_private) ++ self.assertEqual(True, ipaddress.ip_network("169.254.0.0/16").is_private) ++ self.assertEqual(True, ipaddress.ip_network("172.16.0.0/12").is_private) ++ self.assertEqual(True, ipaddress.ip_network("192.0.0.0/29").is_private) ++ self.assertEqual(False, ipaddress.ip_network("192.0.0.9/32").is_private) ++ self.assertEqual(True, ipaddress.ip_network("192.0.0.170/31").is_private) ++ self.assertEqual(True, ipaddress.ip_network("192.0.2.0/24").is_private) ++ self.assertEqual(True, ipaddress.ip_network("192.168.0.0/16").is_private) ++ self.assertEqual(True, ipaddress.ip_network("198.18.0.0/15").is_private) ++ self.assertEqual(True, ipaddress.ip_network("198.51.100.0/24").is_private) ++ self.assertEqual(True, ipaddress.ip_network("203.0.113.0/24").is_private) ++ self.assertEqual(True, ipaddress.ip_network("240.0.0.0/4").is_private) ++ self.assertEqual(True, ipaddress.ip_network("255.255.255.255/32").is_private) ++ ++ self.assertEqual(False, ipaddress.ip_network("::/0").is_private) ++ self.assertEqual(False, ipaddress.ip_network("::ff/128").is_private) ++ ++ self.assertEqual(True, ipaddress.ip_network("::1/128").is_private) ++ self.assertEqual(True, ipaddress.ip_network("::/128").is_private) ++ self.assertEqual(True, ipaddress.ip_network("::ffff:0:0/96").is_private) ++ self.assertEqual(True, ipaddress.ip_network("100::/64").is_private) ++ self.assertEqual(True, ipaddress.ip_network("2001:2::/48").is_private) ++ self.assertEqual(False, ipaddress.ip_network("2001:3::/48").is_private) ++ self.assertEqual(True, ipaddress.ip_network("2001:db8::/32").is_private) ++ self.assertEqual(True, ipaddress.ip_network("2001:10::/28").is_private) ++ self.assertEqual(True, ipaddress.ip_network("fc00::/7").is_private) ++ self.assertEqual(True, ipaddress.ip_network("fe80::/10").is_private) ++ + def testReservedIpv6(self): + + self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast) +@@ -2351,6 +2389,20 @@ class IpaddrUnitTest(unittest.TestCase): + self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified) + self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified) + ++ self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global) ++ self.assertFalse(ipaddress.ip_address('2001::').is_global) ++ self.assertTrue(ipaddress.ip_address('2001:1::1').is_global) ++ self.assertTrue(ipaddress.ip_address('2001:1::2').is_global) ++ self.assertFalse(ipaddress.ip_address('2001:2::').is_global) ++ self.assertTrue(ipaddress.ip_address('2001:3::').is_global) ++ self.assertFalse(ipaddress.ip_address('2001:4::').is_global) ++ self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global) ++ self.assertFalse(ipaddress.ip_address('2001:10::').is_global) ++ self.assertTrue(ipaddress.ip_address('2001:20::').is_global) ++ self.assertTrue(ipaddress.ip_address('2001:30::').is_global) ++ self.assertFalse(ipaddress.ip_address('2001:40::').is_global) ++ self.assertFalse(ipaddress.ip_address('2002::').is_global) ++ + # some generic IETF reserved addresses + self.assertEqual(True, ipaddress.ip_address('100::').is_reserved) + self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved) +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst +@@ -0,0 +1,9 @@ ++Fixed various false positives and false negatives in ++ ++* :attr:`ipaddress.IPv4Address.is_private` (see these docs for details) ++* :attr:`ipaddress.IPv4Address.is_global` ++* :attr:`ipaddress.IPv6Address.is_private` ++* :attr:`ipaddress.IPv6Address.is_global` ++ ++Also in the corresponding :class:`ipaddress.IPv4Network` and :class:`ipaddress.IPv6Network` ++attributes. diff --git a/python310.changes b/python310.changes index d93c3be..9caeb17 100644 --- a/python310.changes +++ b/python310.changes @@ -1,10 +1,17 @@ +------------------------------------------------------------------- +Tue Jun 25 21:57:40 UTC 2024 - Matej Cepl + +- Add CVE-2024-4032-private-IP-addrs.patch to fix bsc#1226448 + (CVE-2024-4032) rearranging definition of private v global IP + addresses. + ------------------------------------------------------------------- Fri Apr 19 08:37:04 UTC 2024 - Matej Cepl - Add CVE-2023-52425-libexpat-2.6.0-backport.patch to fix tests with patched libexpat below 2.6.0 that doesn't update the version number, just in SLE. -- Drop old-libexpat.patch, which is replaced. +- Remove old-libexpat.patch, of course. ------------------------------------------------------------------- Sun Mar 24 00:43:14 UTC 2024 - Matej Cepl @@ -46,7 +53,7 @@ Thu Mar 21 07:38:15 UTC 2024 - Matej Cepl - gh-114572: ssl.SSLContext.cert_store_stats() and ssl.SSLContext.get_ca_certs() now correctly lock access to the certificate store, when the ssl.SSLContext is shared - across multiple threads + across multiple threads (bsc#1226447, CVE-2024-0397) - Remove upstreamed patches: - CVE-2023-6597-TempDir-cleaning-symlink.patch - libexpat260.patch diff --git a/python310.spec b/python310.spec index 202491b..ca80042 100644 --- a/python310.spec +++ b/python310.spec @@ -195,6 +195,9 @@ Patch21: fix-sphinx-72.patch # PATCH-FIX-UPSTREAM CVE-2023-52425-libexpat-2.6.0-backport.patch gh#python/cpython#117187 mcepl@suse.com # Make the test suite work with libexpat < 2.6.0 Patch22: CVE-2023-52425-libexpat-2.6.0-backport.patch +# PATCH-FIX-UPSTREAM CVE-2024-4032-private-IP-addrs.patch bsc#1226448 mcepl@suse.com +# rearrange definition of private v global IP addresses +Patch23: CVE-2024-4032-private-IP-addrs.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -473,6 +476,7 @@ other applications. %patch -p1 -P 20 %patch -p1 -P 21 %patch -p1 -P 22 +%patch -p1 -P 23 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac From 5bf1e2a905fa6a55ef11a001d1fa858c788fcc0371a127cd8149b9dccf7de52e Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Tue, 20 Aug 2024 07:16:19 +0000 Subject: [PATCH 15/23] Set link to python310.35265 via maintenance_release request --- CVE-2024-6923-email-hdr-inject.patch | 335 ++++++++++++++++++++++ F00251-change-user-install-location.patch | 137 ++++++++- bso1227999-reproducible-builds.patch | 37 +++ python310.changes | 32 +++ python310.spec | 28 +- 5 files changed, 551 insertions(+), 18 deletions(-) create mode 100644 CVE-2024-6923-email-hdr-inject.patch create mode 100644 bso1227999-reproducible-builds.patch diff --git a/CVE-2024-6923-email-hdr-inject.patch b/CVE-2024-6923-email-hdr-inject.patch new file mode 100644 index 0000000..fe6cde5 --- /dev/null +++ b/CVE-2024-6923-email-hdr-inject.patch @@ -0,0 +1,335 @@ +From ff3629b3cedf5e50a7a5f567283778fe5539a11e Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Wed, 31 Jul 2024 00:19:48 +0200 +Subject: [PATCH] [3.10] gh-121650: Encode newlines in headers, and verify + headers are sound (GH-122233) + +Per RFC 2047: + +> [...] these encoding schemes allow the +> encoding of arbitrary octet values, mail readers that implement this +> decoding should also ensure that display of the decoded data on the +> recipient's terminal will not cause unwanted side-effects + +It seems that the "quoted-word" scheme is a valid way to include +a newline character in a header value, just like we already allow +undecodable bytes or control characters. +They do need to be properly quoted when serialized to text, though. + +This should fail for custom fold() implementations that aren't careful +about newlines. + +(cherry picked from commit 097633981879b3c9de9a1dd120d3aa585ecc2384) + +Co-authored-by: Petr Viktorin +Co-authored-by: Bas Bloemsaat +Co-authored-by: Serhiy Storchaka +--- + Doc/library/email.errors.rst | 6 + Doc/library/email.policy.rst | 18 ++ + Doc/whatsnew/3.10.rst | 12 + + Lib/email/_header_value_parser.py | 12 + + Lib/email/_policybase.py | 8 + + Lib/email/errors.py | 4 + Lib/email/generator.py | 13 +- + Lib/test/test_email/test_generator.py | 62 ++++++++++ + Lib/test/test_email/test_policy.py | 26 ++++ + Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst | 5 + 10 files changed, 162 insertions(+), 4 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst + +--- a/Doc/library/email.errors.rst ++++ b/Doc/library/email.errors.rst +@@ -59,6 +59,12 @@ The following exception classes are defi + :class:`~email.mime.image.MIMEImage`). + + ++.. exception:: HeaderWriteError() ++ ++ Raised when an error occurs when the :mod:`~email.generator` outputs ++ headers. ++ ++ + Here is the list of the defects that the :class:`~email.parser.FeedParser` + can find while parsing messages. Note that the defects are added to the message + where the problem was found, so for example, if a message nested inside a +--- a/Doc/library/email.policy.rst ++++ b/Doc/library/email.policy.rst +@@ -229,6 +229,24 @@ added matters. To illustrate:: + + .. versionadded:: 3.6 + ++ ++ .. attribute:: verify_generated_headers ++ ++ If ``True`` (the default), the generator will raise ++ :exc:`~email.errors.HeaderWriteError` instead of writing a header ++ that is improperly folded or delimited, such that it would ++ be parsed as multiple headers or joined with adjacent data. ++ Such headers can be generated by custom header classes or bugs ++ in the ``email`` module. ++ ++ As it's a security feature, this defaults to ``True`` even in the ++ :class:`~email.policy.Compat32` policy. ++ For backwards compatible, but unsafe, behavior, it must be set to ++ ``False`` explicitly. ++ ++ .. versionadded:: 3.10.15 ++ ++ + The following :class:`Policy` method is intended to be called by code using + the email library to create policy instances with custom settings: + +--- a/Doc/whatsnew/3.10.rst ++++ b/Doc/whatsnew/3.10.rst +@@ -2357,3 +2357,15 @@ ipaddress + + * Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``, + ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``. ++ ++email ++----- ++ ++* Headers with embedded newlines are now quoted on output. ++ ++ The :mod:`~email.generator` will now refuse to serialize (write) headers ++ that are improperly folded or delimited, such that they would be parsed as ++ multiple headers or joined with adjacent data. ++ If you need to turn this safety feature off, ++ set :attr:`~email.policy.Policy.verify_generated_headers`. ++ (Contributed by Bas Bloemsaat and Petr Viktorin in :gh:`121650`.) +--- a/Lib/email/_header_value_parser.py ++++ b/Lib/email/_header_value_parser.py +@@ -92,6 +92,8 @@ TOKEN_ENDS = TSPECIALS | WSP + ASPECIALS = TSPECIALS | set("*'%") + ATTRIBUTE_ENDS = ASPECIALS | WSP + EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%') ++NLSET = {'\n', '\r'} ++SPECIALSNL = SPECIALS | NLSET + + def quote_string(value): + return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' +@@ -2778,9 +2780,13 @@ def _refold_parse_tree(parse_tree, *, po + wrap_as_ew_blocked -= 1 + continue + tstr = str(part) +- if part.token_type == 'ptext' and set(tstr) & SPECIALS: +- # Encode if tstr contains special characters. +- want_encoding = True ++ if not want_encoding: ++ if part.token_type == 'ptext': ++ # Encode if tstr contains special characters. ++ want_encoding = not SPECIALSNL.isdisjoint(tstr) ++ else: ++ # Encode if tstr contains newlines. ++ want_encoding = not NLSET.isdisjoint(tstr) + try: + tstr.encode(encoding) + charset = encoding +--- a/Lib/email/_policybase.py ++++ b/Lib/email/_policybase.py +@@ -157,6 +157,13 @@ class Policy(_PolicyBase, metaclass=abc. + message_factory -- the class to use to create new message objects. + If the value is None, the default is Message. + ++ verify_generated_headers ++ -- if true, the generator verifies that each header ++ they are properly folded, so that a parser won't ++ treat it as multiple headers, start-of-body, or ++ part of another header. ++ This is a check against custom Header & fold() ++ implementations. + """ + + raise_on_defect = False +@@ -165,6 +172,7 @@ class Policy(_PolicyBase, metaclass=abc. + max_line_length = 78 + mangle_from_ = False + message_factory = None ++ verify_generated_headers = True + + def handle_defect(self, obj, defect): + """Based on policy, either raise defect or call register_defect. +--- a/Lib/email/errors.py ++++ b/Lib/email/errors.py +@@ -29,6 +29,10 @@ class CharsetError(MessageError): + """An illegal charset was given.""" + + ++class HeaderWriteError(MessageError): ++ """Error while writing headers.""" ++ ++ + # These are parsing defects which the parser was able to work around. + class MessageDefect(ValueError): + """Base class for a message defect.""" +--- a/Lib/email/generator.py ++++ b/Lib/email/generator.py +@@ -14,12 +14,14 @@ import random + from copy import deepcopy + from io import StringIO, BytesIO + from email.utils import _has_surrogates ++from email.errors import HeaderWriteError + + UNDERSCORE = '_' + NL = '\n' # XXX: no longer used by the code below. + + NLCRE = re.compile(r'\r\n|\r|\n') + fcre = re.compile(r'^From ', re.MULTILINE) ++NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]') + + + +@@ -223,7 +225,16 @@ class Generator: + + def _write_headers(self, msg): + for h, v in msg.raw_items(): +- self.write(self.policy.fold(h, v)) ++ folded = self.policy.fold(h, v) ++ if self.policy.verify_generated_headers: ++ linesep = self.policy.linesep ++ if not folded.endswith(self.policy.linesep): ++ raise HeaderWriteError( ++ f'folded header does not end with {linesep!r}: {folded!r}') ++ if NEWLINE_WITHOUT_FWSP.search(folded.removesuffix(linesep)): ++ raise HeaderWriteError( ++ f'folded header contains newline: {folded!r}') ++ self.write(folded) + # A blank line always separates headers from body + self.write(self._NL) + +--- a/Lib/test/test_email/test_generator.py ++++ b/Lib/test/test_email/test_generator.py +@@ -6,6 +6,7 @@ from email.message import EmailMessage + from email.generator import Generator, BytesGenerator + from email.headerregistry import Address + from email import policy ++import email.errors + from test.test_email import TestEmailBase, parameterize + + +@@ -216,6 +217,44 @@ class TestGeneratorBase: + g.flatten(msg) + self.assertEqual(s.getvalue(), self.typ(expected)) + ++ def test_keep_encoded_newlines(self): ++ msg = self.msgmaker(self.typ(textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com ++ ++ None ++ """))) ++ expected = textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com ++ ++ None ++ """) ++ s = self.ioclass() ++ g = self.genclass(s, policy=self.policy.clone(max_line_length=80)) ++ g.flatten(msg) ++ self.assertEqual(s.getvalue(), self.typ(expected)) ++ ++ def test_keep_long_encoded_newlines(self): ++ msg = self.msgmaker(self.typ(textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com ++ ++ None ++ """))) ++ expected = textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject ++ =?utf-8?q?=0A?=Bcc: ++ injection@example.com ++ ++ None ++ """) ++ s = self.ioclass() ++ g = self.genclass(s, policy=self.policy.clone(max_line_length=30)) ++ g.flatten(msg) ++ self.assertEqual(s.getvalue(), self.typ(expected)) ++ + + class TestGenerator(TestGeneratorBase, TestEmailBase): + +@@ -224,6 +263,29 @@ class TestGenerator(TestGeneratorBase, T + ioclass = io.StringIO + typ = str + ++ def test_verify_generated_headers(self): ++ """gh-121650: by default the generator prevents header injection""" ++ class LiteralHeader(str): ++ name = 'Header' ++ def fold(self, **kwargs): ++ return self ++ ++ for text in ( ++ 'Value\r\nBad Injection\r\n', ++ 'NoNewLine' ++ ): ++ with self.subTest(text=text): ++ message = message_from_string( ++ "Header: Value\r\n\r\nBody", ++ policy=self.policy, ++ ) ++ ++ del message['Header'] ++ message['Header'] = LiteralHeader(text) ++ ++ with self.assertRaises(email.errors.HeaderWriteError): ++ message.as_string() ++ + + class TestBytesGenerator(TestGeneratorBase, TestEmailBase): + +--- a/Lib/test/test_email/test_policy.py ++++ b/Lib/test/test_email/test_policy.py +@@ -26,6 +26,7 @@ class PolicyAPITests(unittest.TestCase): + 'raise_on_defect': False, + 'mangle_from_': True, + 'message_factory': None, ++ 'verify_generated_headers': True, + } + # These default values are the ones set on email.policy.default. + # If any of these defaults change, the docs must be updated. +@@ -277,6 +278,31 @@ class PolicyAPITests(unittest.TestCase): + with self.assertRaises(email.errors.HeaderParseError): + policy.fold("Subject", subject) + ++ def test_verify_generated_headers(self): ++ """Turning protection off allows header injection""" ++ policy = email.policy.default.clone(verify_generated_headers=False) ++ for text in ( ++ 'Header: Value\r\nBad: Injection\r\n', ++ 'Header: NoNewLine' ++ ): ++ with self.subTest(text=text): ++ message = email.message_from_string( ++ "Header: Value\r\n\r\nBody", ++ policy=policy, ++ ) ++ class LiteralHeader(str): ++ name = 'Header' ++ def fold(self, **kwargs): ++ return self ++ ++ del message['Header'] ++ message['Header'] = LiteralHeader(text) ++ ++ self.assertEqual( ++ message.as_string(), ++ f"{text}\nBody", ++ ) ++ + # XXX: Need subclassing tests. + # For adding subclassed objects, make sure the usual rules apply (subclass + # wins), but that the order still works (right overrides left). +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst +@@ -0,0 +1,5 @@ ++:mod:`email` headers with embedded newlines are now quoted on output. The ++:mod:`~email.generator` will now refuse to serialize (write) headers that ++are unsafely folded or delimited; see ++:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas ++Bloemsaat and Petr Viktorin in :gh:`121650`.) diff --git a/F00251-change-user-install-location.patch b/F00251-change-user-install-location.patch index 9e645d4..b9efb30 100644 --- a/F00251-change-user-install-location.patch +++ b/F00251-change-user-install-location.patch @@ -13,8 +13,10 @@ Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe Lib/site.py | 9 ++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) ---- a/Lib/distutils/command/install.py -+++ b/Lib/distutils/command/install.py +Index: Python-3.10.14/Lib/distutils/command/install.py +=================================================================== +--- Python-3.10.14.orig/Lib/distutils/command/install.py ++++ Python-3.10.14/Lib/distutils/command/install.py @@ -441,8 +441,19 @@ class install(Command): raise DistutilsOptionError( "must not supply exec-prefix without prefix") @@ -37,8 +39,10 @@ Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe else: if self.exec_prefix is None: ---- a/Lib/site.py -+++ b/Lib/site.py +Index: Python-3.10.14/Lib/site.py +=================================================================== +--- Python-3.10.14.orig/Lib/site.py ++++ Python-3.10.14/Lib/site.py @@ -390,8 +390,15 @@ def getsitepackages(prefixes=None): return sitepackages @@ -56,3 +60,128 @@ Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe for sitedir in getsitepackages(prefixes): if os.path.isdir(sitedir): addsitedir(sitedir, known_paths) +Index: Python-3.10.14/Lib/sysconfig.py +=================================================================== +--- Python-3.10.14.orig/Lib/sysconfig.py ++++ Python-3.10.14/Lib/sysconfig.py +@@ -117,6 +117,19 @@ if _HAS_USER_BASE: + }, + } + ++# This is used by distutils.command.install in the stdlib ++# as well as pypa/distutils (e.g. bundled in setuptools). ++# The self.prefix value is set to sys.prefix + /local/ ++# if neither RPM build nor virtual environment is ++# detected to make distutils install packages ++# into the separate location. ++# https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++if (not (hasattr(sys, 'real_prefix') or ++ sys.prefix != sys.base_prefix) and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ _prefix_addition = '/local' ++ ++ + _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', + 'scripts', 'data') + +@@ -136,6 +149,16 @@ _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+) + _findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)" + _findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}" + ++# For a brief period of time in the Fedora 36 life cycle, ++# this installation scheme existed and was documented in the release notes. ++# For backwards compatibility, we keep it here (at least on 3.10 and 3.11). ++_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix'] ++ ++# For a brief period of time in the Fedora 36 life cycle, ++# this installation scheme existed and was documented in the release notes. ++# For backwards compatibility, we keep it here (at least on 3.10 and 3.11). ++_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix'] ++ + + def _safe_realpath(path): + try: +@@ -211,11 +234,39 @@ def _extend_dict(target_dict, other_dict + target_dict[key] = value + + ++_CONFIG_VARS_LOCAL = None ++ ++ ++def _config_vars_local(): ++ # This function returns the config vars with prefixes amended to /usr/local ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ global _CONFIG_VARS_LOCAL ++ if _CONFIG_VARS_LOCAL is None: ++ _CONFIG_VARS_LOCAL = dict(get_config_vars()) ++ _CONFIG_VARS_LOCAL['base'] = '/usr/local' ++ _CONFIG_VARS_LOCAL['platbase'] = '/usr/local' ++ return _CONFIG_VARS_LOCAL ++ ++ + def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} +- _extend_dict(vars, get_config_vars()) ++ ++ # when we are not in a virtual environment or an RPM build ++ # we change '/usr' to '/usr/local' ++ # to avoid surprises, we explicitly check for the /usr/ prefix ++ # Python virtual environments have different prefixes ++ # we only do this for posix_prefix, not to mangle the venv scheme ++ # posix_prefix is used by sudo pip install ++ # we only change the defaults here, so explicit --prefix will take precedence ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ if (scheme == 'posix_prefix' and ++ _PREFIX == '/usr' and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ _extend_dict(vars, _config_vars_local()) ++ else: ++ _extend_dict(vars, get_config_vars()) + + for key, value in _INSTALL_SCHEMES[scheme].items(): + if os.name in ('posix', 'nt'): +Index: Python-3.10.14/Lib/test/test_sysconfig.py +=================================================================== +--- Python-3.10.14.orig/Lib/test/test_sysconfig.py ++++ Python-3.10.14/Lib/test/test_sysconfig.py +@@ -105,8 +105,19 @@ class TestSysConfig(unittest.TestCase): + for scheme in _INSTALL_SCHEMES: + for name in _INSTALL_SCHEMES[scheme]: + expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars) ++ tested = get_path(name, scheme) ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ if tested.startswith('/usr/local'): ++ # /usr/local should only be used in posix_prefix ++ self.assertEqual(scheme, 'posix_prefix') ++ # Fedora CI runs tests for venv and virtualenv that check for other prefixes ++ self.assertEqual(sys.prefix, '/usr') ++ # When building the RPM of Python, %check runs this with RPM_BUILD_ROOT set ++ # Fedora CI runs this with RPM_BUILD_ROOT unset ++ self.assertNotIn('RPM_BUILD_ROOT', os.environ) ++ tested = tested.replace('/usr/local', '/usr') + self.assertEqual( +- os.path.normpath(get_path(name, scheme)), ++ os.path.normpath(tested), + os.path.normpath(expected), + ) + +@@ -263,7 +274,7 @@ class TestSysConfig(unittest.TestCase): + self.assertTrue(os.path.isfile(config_h), config_h) + + def test_get_scheme_names(self): +- wanted = ['nt', 'posix_home', 'posix_prefix'] ++ wanted = ['nt', 'posix_home', 'posix_prefix', 'rpm_prefix'] + if HAS_USER_BASE: + wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) + self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) +@@ -274,6 +285,8 @@ class TestSysConfig(unittest.TestCase): + cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" + self.assertEqual(py.call_real(*cmd), py.call_link(*cmd)) + ++ @unittest.skipIf('RPM_BUILD_ROOT' not in os.environ, ++ "Test doesn't expect Fedora's paths") + def test_user_similar(self): + # Issue #8759: make sure the posix scheme for the users + # is similar to the global posix_prefix one diff --git a/bso1227999-reproducible-builds.patch b/bso1227999-reproducible-builds.patch new file mode 100644 index 0000000..1b674a7 --- /dev/null +++ b/bso1227999-reproducible-builds.patch @@ -0,0 +1,37 @@ +From ac2b8869724d7a57d9b5efbdce2f20423214e8bb Mon Sep 17 00:00:00 2001 +From: "Bernhard M. Wiedemann" +Date: Tue, 16 Jul 2024 21:39:33 +0200 +Subject: [PATCH] Allow to override build date with SOURCE_DATE_EPOCH + +to make builds reproducible. +See https://reproducible-builds.org/ for why this is good +and https://reproducible-builds.org/specs/source-date-epoch/ +for the definition of this variable. +--- + Doc/conf.py | 3 ++- + Doc/library/functions.rst | 2 +- + 2 files changed, 3 insertions(+), 2 deletions(-) + +--- a/Doc/conf.py ++++ b/Doc/conf.py +@@ -89,7 +89,8 @@ html_short_title = '%s Documentation' % + + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, + # using the given strftime format. +-html_last_updated_fmt = '%b %d, %Y' ++html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) ++html_last_updated_fmt = time.strftime('%b %d, %Y (%H:%M UTC)', time.gmtime(html_time)) + + # Path to find HTML templates. + templates_path = ['tools/templates'] +--- a/Doc/library/functions.rst ++++ b/Doc/library/functions.rst +@@ -1320,7 +1320,7 @@ are always available. They are listed h + (where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`, + and :mod:`shutil`. + +- .. audit-event:: open file,mode,flags open ++ .. audit-event:: open path,mode,flags open + + The ``mode`` and ``flags`` arguments may have been modified or inferred from + the original call. diff --git a/python310.changes b/python310.changes index 9caeb17..815fac7 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,35 @@ +------------------------------------------------------------------- +Wed Aug 7 13:40:44 UTC 2024 - Matej Cepl + +- Add CVE-2024-6923-email-hdr-inject.patch to prevent email + header injection due to unquoted newlines (bsc#1228780, + CVE-2024-6923). +- Adding bso1227999-reproducible-builds.patch fixing bsc#1227999 + adding reproducibility patches from gh#python/cpython!121872 + and gh#python/cpython!121883. +- %{profileopt} variable is set according to the variable + %{do_profiling} (bsc#1227999) +- Update bluez-devel-vendor.tar.xz + +------------------------------------------------------------------- +Mon Jul 22 21:20:55 UTC 2024 - Matej Cepl + +- Remove %suse_update_desktop_file macro as it is not useful any + more. + +------------------------------------------------------------------- +Mon Jul 15 12:14:57 UTC 2024 - Matej Cepl + +- Stop using %%defattr, it seems to be breaking proper executable + attributes on /usr/bin/ scripts (bsc#1227378). + +------------------------------------------------------------------- +Tue Jul 2 10:31:26 UTC 2024 - Daniel Garcia + +- Update F00251-change-user-install-location.patch to make pip and + modern tools install directly in /usr/local when used by the user. + bsc#1225660 + ------------------------------------------------------------------- Tue Jun 25 21:57:40 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index ca80042..45f3fda 100644 --- a/python310.spec +++ b/python310.spec @@ -36,6 +36,12 @@ %bcond_without general %endif +%if 0%{?do_profiling} +%bcond_without profileopt +%else +%bcond_with profileopt +%endif + %define python_pkg_name python310 %if "%{python_pkg_name}" == "%{primary_python}" %define primary_interpreter 1 @@ -101,7 +107,6 @@ Obsoletes: python39%{?1:-%{1}} # pyexpat.cpython-35m-armv7-linux-gnueabihf # _md5.cpython-38m-x86_64-linux-gnu.so %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so -%bcond_without profileopt Name: %{python_pkg_name}%{psuffix} Version: 3.10.14 Release: 0 @@ -198,6 +203,12 @@ Patch22: CVE-2023-52425-libexpat-2.6.0-backport.patch # PATCH-FIX-UPSTREAM CVE-2024-4032-private-IP-addrs.patch bsc#1226448 mcepl@suse.com # rearrange definition of private v global IP addresses Patch23: CVE-2024-4032-private-IP-addrs.patch +# PATCH-FIX-UPSTREAM bso1227999-reproducible-builds.patch bsc#1227999 mcepl@suse.com +# reproducibility patches +Patch24: bso1227999-reproducible-builds.patch +# PATCH-FIX-UPSTREAM CVE-2024-6923-email-hdr-inject.patch bsc#1228780 mcepl@suse.com +# prevent email header injection, patch from gh#python/cpython!122608 +Patch25: CVE-2024-6923-email-hdr-inject.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -243,7 +254,6 @@ BuildRequires: gettext BuildRequires: readline-devel BuildRequires: sqlite-devel BuildRequires: timezone -BuildRequires: update-desktop-files BuildRequires: pkgconfig(ncurses) BuildRequires: pkgconfig(tk) BuildRequires: pkgconfig(x11) @@ -477,6 +487,8 @@ other applications. %patch -p1 -P 21 %patch -p1 -P 22 %patch -p1 -P 23 +%patch -p1 -P 24 +%patch -p1 -P 25 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -708,7 +720,6 @@ done cp %{SOURCE19} idle%{python_version}.desktop sed -i -e 's:idle3:idle%{python_version}:g' idle%{python_version}.desktop install -m 644 -D -t %{buildroot}%{_datadir}/applications idle%{python_version}.desktop -%suse_update_desktop_file idle%{python_version} cp %{SOURCE20} idle%{python_version}.appdata.xml sed -i -e 's:idle3.desktop:idle%{python_version}.desktop:g' idle%{python_version}.appdata.xml @@ -816,25 +827,21 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %if %{with general} %files -n %{python_pkg_name}-tk -%defattr(644, root, root, 755) %{sitedir}/tkinter %exclude %{sitedir}/tkinter/test %{dynlib _tkinter} %files -n %{python_pkg_name}-curses -%defattr(644, root, root, 755) %{sitedir}/curses %{dynlib _curses} %{dynlib _curses_panel} %files -n %{python_pkg_name}-dbm -%defattr(644, root, root, 755) %{sitedir}/dbm %{dynlib _dbm} %{dynlib _gdbm} %files -n %{python_pkg_name} -%defattr(644, root, root, 755) %dir %{sitedir} %dir %{sitedir}/lib-dynload %{sitedir}/sqlite3 @@ -846,7 +853,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %endif %files -n %{python_pkg_name}-idle -%defattr(644, root, root, 755) %{sitedir}/idlelib %dir %{_sysconfdir}/idle%{python_version} %config %{_sysconfdir}/idle%{python_version}/* @@ -884,11 +890,9 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %postun -n libpython%{so_version} -p /sbin/ldconfig %files -n libpython%{so_version} -%defattr(644, root,root) %{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor} %files -n %{python_pkg_name}-tools -%defattr(644, root, root, 755) %{sitedir}/turtledemo %if %{primary_interpreter} %{_bindir}/2to3 @@ -897,7 +901,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %doc %{_docdir}/%{name}/Tools %files -n %{python_pkg_name}-devel -%defattr(644, root, root, 755) %{_libdir}/libpython%{python_abi}.so %if %{primary_interpreter} %{_libdir}/libpython3.so @@ -905,7 +908,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %{_libdir}/pkgconfig/* %{_includedir}/python%{python_abi} %{sitedir}/config-%{python_abi}-* -%defattr(755, root, root) %{_bindir}/python%{python_abi}-config %if %{primary_interpreter} %{_bindir}/python3-config @@ -918,7 +920,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %{_datadir}/gdb/auto-load/%{_libdir}/libpython%{python_abi}.so.%{so_major}.%{so_minor}-gdb.py %files -n %{python_pkg_name}-testsuite -%defattr(644, root, root, 755) %{sitedir}/test %{sitedir}/*/test %{sitedir}/*/tests @@ -935,7 +936,6 @@ echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-impo %dir %{sitedir}/tkinter %files -n %{python_pkg_name}-base -%defattr(644, root, root, 755) # docs %dir %{_docdir}/%{name} %doc %{_docdir}/%{name}/README.rst From e629f4ae28179b6938685b24d915bb247f24f3560580fbd89190f4a245e0966a Mon Sep 17 00:00:00 2001 From: Ruediger Oertel Date: Fri, 20 Sep 2024 15:12:39 +0000 Subject: [PATCH 16/23] Set link to python310.35771 via maintenance_release request --- CVE-2023-27043-email-parsing-errors.patch | 461 ------------------ CVE-2023-52425-libexpat-2.6.0-backport.patch | 2 +- CVE-2024-4032-private-IP-addrs.patch | 376 -------------- Python-3.10.14.tar.xz.asc | 16 - ...on-3.10.14.tar.xz => Python-3.10.15.tar.xz | 0 Python-3.10.15.tar.xz.asc | 16 + fix-sphinx-72.patch | 2 +- fix_configure_rst.patch | 2 +- gh120226-fix-sendfile-test-kernel-610.patch | 35 ++ python310.changes | 96 ++++ python310.spec | 24 +- sphinx-802.patch | 21 + 12 files changed, 180 insertions(+), 871 deletions(-) delete mode 100644 CVE-2023-27043-email-parsing-errors.patch delete mode 100644 CVE-2024-4032-private-IP-addrs.patch delete mode 100644 Python-3.10.14.tar.xz.asc rename Python-3.10.14.tar.xz => Python-3.10.15.tar.xz (100%) create mode 100644 Python-3.10.15.tar.xz.asc create mode 100644 gh120226-fix-sendfile-test-kernel-610.patch create mode 100644 sphinx-802.patch diff --git a/CVE-2023-27043-email-parsing-errors.patch b/CVE-2023-27043-email-parsing-errors.patch deleted file mode 100644 index df77785..0000000 --- a/CVE-2023-27043-email-parsing-errors.patch +++ /dev/null @@ -1,461 +0,0 @@ ---- - Doc/library/email.utils.rst | 19 - - Lib/email/utils.py | 151 +++++++- - Lib/test/test_email/test_email.py | 187 +++++++++- - Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 - 4 files changed, 344 insertions(+), 21 deletions(-) - ---- 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 `. Here's a simple -- example that gets all the recipients of a message:: -+ :meth:`Message.get_all `. -+ -+ If *strict* is true, use a strict parser which rejects malformed inputs. -+ -+ Here's a simple example that gets all the recipients of a message:: - - from email.utils import getaddresses - -@@ -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) - ---- a/Lib/email/utils.py -+++ b/Lib/email/utils.py -@@ -48,6 +48,7 @@ TICK = "'" - specialsre = re.compile(r'[][\\()<>@,:;".]') - escapesre = re.compile(r'[\\"]') - -+ - def _has_surrogates(s): - """Return True if s contains surrogate-escaped binary data.""" - # This check is based on the fact that unless there are surrogates, utf8 -@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'): - return address - - -+def _iter_escaped_chars(addr): -+ pos = 0 -+ escape = False -+ for pos, ch in enumerate(addr): -+ if escape: -+ yield (pos, '\\' + ch) -+ escape = False -+ elif ch == '\\': -+ escape = True -+ else: -+ yield (pos, ch) -+ if escape: -+ yield (pos, '\\') -+ -+ -+def _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 - --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 start < len(addr): -+ result.append(addr[start:]) -+ -+ return ''.join(result) -+ -+ -+supports_strict_parsing = True -+ -+def getaddresses(fieldvalues, *, strict=True): -+ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. -+ -+ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in -+ its place. -+ -+ If strict is true, use a strict parser which rejects malformed inputs. -+ """ -+ -+ # If strict is true, if the resulting list of parsed addresses is greater -+ # than the number of fieldvalues in the input list, a parsing error has -+ # occurred and consequently a list containing a single empty 2-tuple [('', -+ # '')] is returned in its place. This is done to avoid invalid output. -+ # -+ # Malformed input: getaddresses(['alice@example.com ']) -+ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')] -+ # Safe output: [('', '')] -+ -+ if not strict: -+ all = COMMASPACE.join(str(v) for v in fieldvalues) -+ a = _AddressList(all) -+ return a.addresslist -+ -+ fieldvalues = [str(v) for v in fieldvalues] -+ fieldvalues = _pre_parse_validation(fieldvalues) -+ addr = COMMASPACE.join(fieldvalues) -+ a = _AddressList(addr) -+ result = _post_parse_validation(a.addresslist) -+ -+ # Treat output as invalid if the number of addresses is not equal to the -+ # expected number of addresses. -+ n = 0 -+ for v in fieldvalues: -+ # When a comma is used in the Real Name part it is not a deliminator. -+ # So strip those out before counting the commas. -+ v = _strip_quoted_realnames(v) -+ # Expected number of addresses: 1 + number of commas -+ n += 1 + v.count(',') -+ if len(result) != n: -+ return [('', '')] -+ -+ return result -+ -+ -+def _check_parenthesis(addr): -+ # Ignore parenthesis in quoted real names. -+ addr = _strip_quoted_realnames(addr) -+ -+ opens = 0 -+ for pos, ch in _iter_escaped_chars(addr): -+ if ch == '(': -+ opens += 1 -+ elif ch == ')': -+ opens -= 1 -+ if opens < 0: -+ return False -+ return (opens == 0) -+ -+ -+def _pre_parse_validation(email_header_fields): -+ accepted_values = [] -+ for v in email_header_fields: -+ if not _check_parenthesis(v): -+ v = "('', '')" -+ accepted_values.append(v) -+ -+ return accepted_values -+ -+ -+def _post_parse_validation(parsed_email_header_tuples): -+ accepted_values = [] -+ # The parser would have parsed a correctly formatted domain-literal -+ # The existence of an [ after parsing indicates a parsing failure -+ for v in parsed_email_header_tuples: -+ if '[' in v[1]: -+ v = ('', '') -+ accepted_values.append(v) -+ -+ return accepted_values - - - def _format_timetuple_and_zone(timetuple, zone): -@@ -205,16 +321,33 @@ def parsedate_to_datetime(data): - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) - - --def parseaddr(addr): -+def parseaddr(addr, *, strict=True): - """ - Parse addr into its constituent realname and email address parts. - - Return a tuple of realname and email address, unless the parse fails, in - which case return a 2-tuple of ('', ''). -+ -+ If strict is True, use a strict parser which rejects malformed inputs. - """ -- addrs = _AddressList(addr).addresslist -- if not addrs: -- return '', '' -+ if not strict: -+ addrs = _AddressList(addr).addresslist -+ if not addrs: -+ return ('', '') -+ return addrs[0] -+ -+ if isinstance(addr, list): -+ addr = addr[0] -+ -+ if not isinstance(addr, str): -+ return ('', '') -+ -+ addr = _pre_parse_validation([addr])[0] -+ addrs = _post_parse_validation(_AddressList(addr).addresslist) -+ -+ if not addrs or len(addrs) > 1: -+ return ('', '') -+ - return addrs[0] - - ---- a/Lib/test/test_email/test_email.py -+++ b/Lib/test/test_email/test_email.py -@@ -16,6 +16,7 @@ from unittest.mock import patch - - import email - import email.policy -+import email.utils - - from email.charset import Charset - from email.generator import Generator, DecodedGenerator, BytesGenerator -@@ -3288,15 +3289,137 @@ Foo - [('Al Person', 'aperson@dom.ain'), - ('Bud Person', 'bperson@dom.ain')]) - -+ def test_parsing_errors(self): -+ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056""" -+ alice = 'alice@example.org' -+ bob = 'bob@example.com' -+ empty = ('', '') -+ -+ # Test utils.getaddresses() and utils.parseaddr() on malformed email -+ # addresses: default behavior (strict=True) rejects malformed address, -+ # and strict=False which tolerates malformed address. -+ for invalid_separator, expected_non_strict in ( -+ ('(', [(f'<{bob}>', alice)]), -+ (')', [('', alice), empty, ('', bob)]), -+ ('<', [('', alice), empty, ('', bob), empty]), -+ ('>', [('', alice), empty, ('', bob)]), -+ ('[', [('', f'{alice}[<{bob}>]')]), -+ (']', [('', alice), empty, ('', bob)]), -+ ('@', [empty, empty, ('', bob)]), -+ (';', [('', alice), empty, ('', bob)]), -+ (':', [('', alice), ('', bob)]), -+ ('.', [('', alice + '.'), ('', bob)]), -+ ('"', [('', alice), ('', f'<{bob}>')]), -+ ): -+ address = f'{alice}{invalid_separator}<{bob}>' -+ with self.subTest(address=address): -+ self.assertEqual(utils.getaddresses([address]), -+ [empty]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ expected_non_strict) -+ -+ self.assertEqual(utils.parseaddr([address]), -+ empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Comma (',') is treated differently depending on strict parameter. -+ # Comma without quotes. -+ address = f'{alice},<{bob}>' -+ self.assertEqual(utils.getaddresses([address]), -+ [('', alice), ('', bob)]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('', alice), ('', bob)]) -+ self.assertEqual(utils.parseaddr([address]), -+ empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Real name between quotes containing comma. -+ address = '"Alice, alice@example.org" ' -+ expected_strict = ('Alice, alice@example.org', 'bob@example.com') -+ self.assertEqual(utils.getaddresses([address]), [expected_strict]) -+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) -+ self.assertEqual(utils.parseaddr([address]), expected_strict) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Valid parenthesis in comments. -+ address = 'alice@example.org (Alice)' -+ expected_strict = ('Alice', 'alice@example.org') -+ self.assertEqual(utils.getaddresses([address]), [expected_strict]) -+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) -+ self.assertEqual(utils.parseaddr([address]), expected_strict) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Invalid parenthesis in comments. -+ address = 'alice@example.org )Alice(' -+ self.assertEqual(utils.getaddresses([address]), [empty]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')]) -+ self.assertEqual(utils.parseaddr([address]), empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Two addresses with quotes separated by comma. -+ address = '"Jane Doe" , "John Doe" ' -+ self.assertEqual(utils.getaddresses([address]), -+ [('Jane Doe', 'jane@example.net'), -+ ('John Doe', 'john@example.net')]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('Jane Doe', 'jane@example.net'), -+ ('John Doe', 'john@example.net')]) -+ self.assertEqual(utils.parseaddr([address]), empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Test email.utils.supports_strict_parsing attribute -+ self.assertEqual(email.utils.supports_strict_parsing, True) -+ - def test_getaddresses_nasty(self): -- eq = self.assertEqual -- eq(utils.getaddresses(['foo: ;']), [('', '')]) -- eq(utils.getaddresses( -- ['[]*-- =~$']), -- [('', ''), ('', ''), ('', '*--')]) -- eq(utils.getaddresses( -- ['foo: ;', '"Jason R. Mastaler" ']), -- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) -+ for addresses, expected in ( -+ (['"Sürname, Firstname" '], -+ [('Sürname, Firstname', 'to@example.com')]), -+ -+ (['foo: ;'], -+ [('', '')]), -+ -+ (['foo: ;', '"Jason R. Mastaler" '], -+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]), -+ -+ ([r'Pete(A nice \) chap) '], -+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]), -+ -+ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'], -+ [('', '')]), -+ -+ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'], -+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]), -+ -+ (['John Doe '], -+ [('John Doe (comment)', 'jdoe@machine.example')]), -+ -+ (['"Mary Smith: Personal Account" '], -+ [('Mary Smith: Personal Account', 'smith@home.example')]), -+ -+ (['Undisclosed recipients:;'], -+ [('', '')]), -+ -+ ([r', "Giant; \"Big\" Box" '], -+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]), -+ ): -+ with self.subTest(addresses=addresses): -+ self.assertEqual(utils.getaddresses(addresses), -+ expected) -+ self.assertEqual(utils.getaddresses(addresses, strict=False), -+ expected) -+ -+ addresses = ['[]*-- =~$'] -+ self.assertEqual(utils.getaddresses(addresses), -+ [('', '')]) -+ self.assertEqual(utils.getaddresses(addresses, strict=False), -+ [('', ''), ('', ''), ('', '*--')]) - - def test_getaddresses_embedded_comment(self): - """Test proper handling of a nested comment""" -@@ -3485,6 +3608,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" , "John Doe" ', -+ ' , ') -+ check(r'"Jane \"Doe\"." ', -+ ' ') -+ -+ # special cases -+ check(r'before"name"after', 'beforeafter') -+ check(r'before"name"', 'before') -+ check(r'b"name"', 'b') # single char -+ check(r'"name"after', 'after') -+ check(r'"name"a', 'a') # single char -+ check(r'"name"', '') -+ -+ # no change -+ for addr in ( -+ 'Jane Doe , John Doe ', -+ 'lone " quote', -+ ): -+ self.assertEqual(utils._strip_quoted_realnames(addr), addr) -+ -+ -+ def test_check_parenthesis(self): -+ addr = 'alice@example.net' -+ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice(')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)')) -+ -+ # Ignore real name between quotes -+ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}')) -+ - - # Test the iterator/generators - class TestIterators(TestEmailBase): ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst -@@ -0,0 +1,8 @@ -+:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now -+return ``('', '')`` 2-tuples in more situations where invalid email -+addresses are encountered instead of potentially inaccurate values. Add -+optional *strict* parameter to these two functions: use ``strict=False`` to -+get the old behavior, accept malformed inputs. -+``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check -+if the *strict* paramater is available. Patch by Thomas Dwyer and Victor -+Stinner to improve the CVE-2023-27043 fix. diff --git a/CVE-2023-52425-libexpat-2.6.0-backport.patch b/CVE-2023-52425-libexpat-2.6.0-backport.patch index 334b7b8..25e068f 100644 --- a/CVE-2023-52425-libexpat-2.6.0-backport.patch +++ b/CVE-2023-52425-libexpat-2.6.0-backport.patch @@ -45,7 +45,7 @@ def test_simple_xml_chunk_5(self): self.test_simple_xml(chunk_size=5, flush=True) -@@ -1648,6 +1652,9 @@ class XMLPullParserTest(unittest.TestCas +@@ -1647,6 +1651,9 @@ class XMLPullParserTest(unittest.TestCas self.assert_event_tags(parser, [('end', 'doc')]) diff --git a/CVE-2024-4032-private-IP-addrs.patch b/CVE-2024-4032-private-IP-addrs.patch deleted file mode 100644 index 7aa5e10..0000000 --- a/CVE-2024-4032-private-IP-addrs.patch +++ /dev/null @@ -1,376 +0,0 @@ -From 0740166e60b8cdae9448220beb28721f0126ee03 Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Wed, 24 Apr 2024 14:29:30 +0200 -Subject: [PATCH 1/2] gh-113171: gh-65056: Fix "private" (non-global) IP - address ranges (GH-113179) (GH-113186) (GH-118177) - -* GH-113171: Fix "private" (non-global) IP address ranges (GH-113179) - -The _private_networks variables, used by various is_private -implementations, were missing some ranges and at the same time had -overly strict ranges (where there are more specific ranges considered -globally reachable by the IANA registries). - -This patch updates the ranges with what was missing or otherwise -incorrect. - -100.64.0.0/10 is left alone, for now, as it's been made special in [1]. - -The _address_exclude_many() call returns 8 networks for IPv4, 121 -networks for IPv6. - -[1] https://github.com/python/cpython/issues/61602 - -* GH-65056: Improve the IP address' is_global/is_private documentation (GH-113186) - -It wasn't clear what the semantics of is_global/is_private are and, when -one gets to the bottom of it, it's not quite so simple (hence the -exceptions listed). - -(cherry picked from commit 2a4cbf17af19a01d942f9579342f77c39fbd23c4) -(cherry picked from commit 40d75c2b7f5c67e254d0a025e0f2e2c7ada7f69f) - ---------- - -(cherry picked from commit f86b17ac511e68192ba71f27e752321a3252cee3) - -Co-authored-by: Jakub Stasiak ---- - Doc/library/ipaddress.rst | 43 +++- - Doc/whatsnew/3.10.rst | 9 - Lib/ipaddress.py | 99 +++++++--- - Lib/test/test_ipaddress.py | 52 +++++ - Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst | 9 - 5 files changed, 187 insertions(+), 25 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst - ---- a/Doc/library/ipaddress.rst -+++ b/Doc/library/ipaddress.rst -@@ -188,18 +188,53 @@ write code that handles both IP versions - - .. attribute:: is_private - -- ``True`` if the address is allocated for private networks. See -+ ``True`` if the address is defined as not globally reachable by - iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ -- (for IPv6). -+ (for IPv6) with the following exceptions: -+ -+ * ``is_private`` is ``False`` for the shared address space (``100.64.0.0/10``) -+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the -+ semantics of the underlying IPv4 addresses and the following condition holds -+ (see :attr:`IPv6Address.ipv4_mapped`):: -+ -+ address.is_private == address.ipv4_mapped.is_private -+ -+ ``is_private`` has value opposite to :attr:`is_global`, except for the shared address space -+ (``100.64.0.0/10`` range) where they are both ``False``. -+ -+ .. versionchanged:: 3.10.15 -+ -+ Fixed some false positives and false negatives. -+ -+ * ``192.0.0.0/24`` is considered private with the exception of ``192.0.0.9/32`` and -+ ``192.0.0.10/32`` (previously: only the ``192.0.0.0/29`` sub-range was considered private). -+ * ``64:ff9b:1::/48`` is considered private. -+ * ``2002::/16`` is considered private. -+ * There are exceptions within ``2001::/23`` (otherwise considered private): ``2001:1::1/128``, -+ ``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``. -+ The exceptions are not considered private. - - .. attribute:: is_global - -- ``True`` if the address is allocated for public networks. See -+ ``True`` if the address is defined as globally reachable by - iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ -- (for IPv6). -+ (for IPv6) with the following exception: -+ -+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the -+ semantics of the underlying IPv4 addresses and the following condition holds -+ (see :attr:`IPv6Address.ipv4_mapped`):: -+ -+ address.is_global == address.ipv4_mapped.is_global -+ -+ ``is_global`` has value opposite to :attr:`is_private`, except for the shared address space -+ (``100.64.0.0/10`` range) where they are both ``False``. - - .. versionadded:: 3.4 - -+ .. versionchanged:: 3.10.15 -+ -+ Fixed some false positives and false negatives, see :attr:`is_private` for details. -+ - .. attribute:: is_unspecified - - ``True`` if the address is unspecified. See :RFC:`5735` (for IPv4) ---- a/Doc/whatsnew/3.10.rst -+++ b/Doc/whatsnew/3.10.rst -@@ -2348,3 +2348,12 @@ tarfile - :exc:`DeprecationWarning`. - In Python 3.14, the default will switch to ``'data'``. - (Contributed by Petr Viktorin in :pep:`706`.) -+ -+Notable changes in 3.10.15 -+========================== -+ -+ipaddress -+--------- -+ -+* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``, -+ ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``. ---- a/Lib/ipaddress.py -+++ b/Lib/ipaddress.py -@@ -1323,18 +1323,41 @@ class IPv4Address(_BaseV4, _BaseAddress) - @property - @functools.lru_cache() - def is_private(self): -- """Test if this address is allocated for private networks. -- -- Returns: -- A boolean, True if the address is reserved per -- iana-ipv4-special-registry. -- -- """ -- return any(self in net for net in self._constants._private_networks) -+ """``True`` if the address is defined as not globally reachable by -+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ -+ (for IPv6) with the following exceptions: -+ -+ * ``is_private`` is ``False`` for ``100.64.0.0/10`` -+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the -+ semantics of the underlying IPv4 addresses and the following condition holds -+ (see :attr:`IPv6Address.ipv4_mapped`):: -+ -+ address.is_private == address.ipv4_mapped.is_private -+ -+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10`` -+ IPv4 range where they are both ``False``. -+ """ -+ return ( -+ any(self in net for net in self._constants._private_networks) -+ and all(self not in net for net in self._constants._private_networks_exceptions) -+ ) - - @property - @functools.lru_cache() - def is_global(self): -+ """``True`` if the address is defined as globally reachable by -+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ -+ (for IPv6) with the following exception: -+ -+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the -+ semantics of the underlying IPv4 addresses and the following condition holds -+ (see :attr:`IPv6Address.ipv4_mapped`):: -+ -+ address.is_global == address.ipv4_mapped.is_global -+ -+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10`` -+ IPv4 range where they are both ``False``. -+ """ - return self not in self._constants._public_network and not self.is_private - - @property -@@ -1538,13 +1561,15 @@ class _IPv4Constants: - - _public_network = IPv4Network('100.64.0.0/10') - -+ # Not globally reachable address blocks listed on -+ # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - _private_networks = [ - IPv4Network('0.0.0.0/8'), - IPv4Network('10.0.0.0/8'), - IPv4Network('127.0.0.0/8'), - IPv4Network('169.254.0.0/16'), - IPv4Network('172.16.0.0/12'), -- IPv4Network('192.0.0.0/29'), -+ IPv4Network('192.0.0.0/24'), - IPv4Network('192.0.0.170/31'), - IPv4Network('192.0.2.0/24'), - IPv4Network('192.168.0.0/16'), -@@ -1555,6 +1580,11 @@ class _IPv4Constants: - IPv4Network('255.255.255.255/32'), - ] - -+ _private_networks_exceptions = [ -+ IPv4Network('192.0.0.9/32'), -+ IPv4Network('192.0.0.10/32'), -+ ] -+ - _reserved_network = IPv4Network('240.0.0.0/4') - - _unspecified_address = IPv4Address('0.0.0.0') -@@ -1996,27 +2026,42 @@ class IPv6Address(_BaseV6, _BaseAddress) - @property - @functools.lru_cache() - def is_private(self): -- """Test if this address is allocated for private networks. -+ """``True`` if the address is defined as not globally reachable by -+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ -+ (for IPv6) with the following exceptions: -+ -+ * ``is_private`` is ``False`` for ``100.64.0.0/10`` -+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the -+ semantics of the underlying IPv4 addresses and the following condition holds -+ (see :attr:`IPv6Address.ipv4_mapped`):: - -- Returns: -- A boolean, True if the address is reserved per -- iana-ipv6-special-registry, or is ipv4_mapped and is -- reserved in the iana-ipv4-special-registry. -+ address.is_private == address.ipv4_mapped.is_private - -+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10`` -+ IPv4 range where they are both ``False``. - """ - ipv4_mapped = self.ipv4_mapped - if ipv4_mapped is not None: - return ipv4_mapped.is_private -- return any(self in net for net in self._constants._private_networks) -+ return ( -+ any(self in net for net in self._constants._private_networks) -+ and all(self not in net for net in self._constants._private_networks_exceptions) -+ ) - - @property - def is_global(self): -- """Test if this address is allocated for public networks. -+ """``True`` if the address is defined as globally reachable by -+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ -+ (for IPv6) with the following exception: -+ -+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the -+ semantics of the underlying IPv4 addresses and the following condition holds -+ (see :attr:`IPv6Address.ipv4_mapped`):: - -- Returns: -- A boolean, true if the address is not reserved per -- iana-ipv6-special-registry. -+ address.is_global == address.ipv4_mapped.is_global - -+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10`` -+ IPv4 range where they are both ``False``. - """ - return not self.is_private - -@@ -2257,19 +2302,31 @@ class _IPv6Constants: - - _multicast_network = IPv6Network('ff00::/8') - -+ # Not globally reachable address blocks listed on -+ # https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - _private_networks = [ - IPv6Network('::1/128'), - IPv6Network('::/128'), - IPv6Network('::ffff:0:0/96'), -+ IPv6Network('64:ff9b:1::/48'), - IPv6Network('100::/64'), - IPv6Network('2001::/23'), -- IPv6Network('2001:2::/48'), - IPv6Network('2001:db8::/32'), -- IPv6Network('2001:10::/28'), -+ # IANA says N/A, let's consider it not globally reachable to be safe -+ IPv6Network('2002::/16'), - IPv6Network('fc00::/7'), - IPv6Network('fe80::/10'), - ] - -+ _private_networks_exceptions = [ -+ IPv6Network('2001:1::1/128'), -+ IPv6Network('2001:1::2/128'), -+ IPv6Network('2001:3::/32'), -+ IPv6Network('2001:4:112::/48'), -+ IPv6Network('2001:20::/28'), -+ IPv6Network('2001:30::/28'), -+ ] -+ - _reserved_networks = [ - IPv6Network('::/8'), IPv6Network('100::/8'), - IPv6Network('200::/7'), IPv6Network('400::/6'), ---- a/Lib/test/test_ipaddress.py -+++ b/Lib/test/test_ipaddress.py -@@ -2263,6 +2263,10 @@ class IpaddrUnitTest(unittest.TestCase): - self.assertEqual(True, ipaddress.ip_address( - '172.31.255.255').is_private) - self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private) -+ self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global) -+ self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global) -+ self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global) -+ self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global) - - self.assertEqual(True, - ipaddress.ip_address('169.254.100.200').is_link_local) -@@ -2278,6 +2282,40 @@ class IpaddrUnitTest(unittest.TestCase): - self.assertEqual(False, ipaddress.ip_address('128.0.0.0').is_loopback) - self.assertEqual(True, ipaddress.ip_network('0.0.0.0').is_unspecified) - -+ def testPrivateNetworks(self): -+ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/0").is_private) -+ self.assertEqual(False, ipaddress.ip_network("1.0.0.0/8").is_private) -+ -+ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/8").is_private) -+ self.assertEqual(True, ipaddress.ip_network("10.0.0.0/8").is_private) -+ self.assertEqual(True, ipaddress.ip_network("127.0.0.0/8").is_private) -+ self.assertEqual(True, ipaddress.ip_network("169.254.0.0/16").is_private) -+ self.assertEqual(True, ipaddress.ip_network("172.16.0.0/12").is_private) -+ self.assertEqual(True, ipaddress.ip_network("192.0.0.0/29").is_private) -+ self.assertEqual(False, ipaddress.ip_network("192.0.0.9/32").is_private) -+ self.assertEqual(True, ipaddress.ip_network("192.0.0.170/31").is_private) -+ self.assertEqual(True, ipaddress.ip_network("192.0.2.0/24").is_private) -+ self.assertEqual(True, ipaddress.ip_network("192.168.0.0/16").is_private) -+ self.assertEqual(True, ipaddress.ip_network("198.18.0.0/15").is_private) -+ self.assertEqual(True, ipaddress.ip_network("198.51.100.0/24").is_private) -+ self.assertEqual(True, ipaddress.ip_network("203.0.113.0/24").is_private) -+ self.assertEqual(True, ipaddress.ip_network("240.0.0.0/4").is_private) -+ self.assertEqual(True, ipaddress.ip_network("255.255.255.255/32").is_private) -+ -+ self.assertEqual(False, ipaddress.ip_network("::/0").is_private) -+ self.assertEqual(False, ipaddress.ip_network("::ff/128").is_private) -+ -+ self.assertEqual(True, ipaddress.ip_network("::1/128").is_private) -+ self.assertEqual(True, ipaddress.ip_network("::/128").is_private) -+ self.assertEqual(True, ipaddress.ip_network("::ffff:0:0/96").is_private) -+ self.assertEqual(True, ipaddress.ip_network("100::/64").is_private) -+ self.assertEqual(True, ipaddress.ip_network("2001:2::/48").is_private) -+ self.assertEqual(False, ipaddress.ip_network("2001:3::/48").is_private) -+ self.assertEqual(True, ipaddress.ip_network("2001:db8::/32").is_private) -+ self.assertEqual(True, ipaddress.ip_network("2001:10::/28").is_private) -+ self.assertEqual(True, ipaddress.ip_network("fc00::/7").is_private) -+ self.assertEqual(True, ipaddress.ip_network("fe80::/10").is_private) -+ - def testReservedIpv6(self): - - self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast) -@@ -2351,6 +2389,20 @@ class IpaddrUnitTest(unittest.TestCase): - self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified) - self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified) - -+ self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global) -+ self.assertFalse(ipaddress.ip_address('2001::').is_global) -+ self.assertTrue(ipaddress.ip_address('2001:1::1').is_global) -+ self.assertTrue(ipaddress.ip_address('2001:1::2').is_global) -+ self.assertFalse(ipaddress.ip_address('2001:2::').is_global) -+ self.assertTrue(ipaddress.ip_address('2001:3::').is_global) -+ self.assertFalse(ipaddress.ip_address('2001:4::').is_global) -+ self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global) -+ self.assertFalse(ipaddress.ip_address('2001:10::').is_global) -+ self.assertTrue(ipaddress.ip_address('2001:20::').is_global) -+ self.assertTrue(ipaddress.ip_address('2001:30::').is_global) -+ self.assertFalse(ipaddress.ip_address('2001:40::').is_global) -+ self.assertFalse(ipaddress.ip_address('2002::').is_global) -+ - # some generic IETF reserved addresses - self.assertEqual(True, ipaddress.ip_address('100::').is_reserved) - self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved) ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst -@@ -0,0 +1,9 @@ -+Fixed various false positives and false negatives in -+ -+* :attr:`ipaddress.IPv4Address.is_private` (see these docs for details) -+* :attr:`ipaddress.IPv4Address.is_global` -+* :attr:`ipaddress.IPv6Address.is_private` -+* :attr:`ipaddress.IPv6Address.is_global` -+ -+Also in the corresponding :class:`ipaddress.IPv4Network` and :class:`ipaddress.IPv6Network` -+attributes. diff --git a/Python-3.10.14.tar.xz.asc b/Python-3.10.14.tar.xz.asc deleted file mode 100644 index b087178..0000000 --- a/Python-3.10.14.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmX76AkACgkQ/+h0BBaL -2EeKvhAAuN+7X3iFv8tYwUTbKJT9x9fLsADI5wOn5j8xuDiXQCOMzsqqyB1RSdEd -tbCQXg9XJj1bVHc4DY337vUix9jvFcTqbQqlzUm/peX4buY2bKkZu2quti1iWSJf -IN26jDYO2TobPvGdvNiH2Hceqe1dc5tU7iYEfaLR5ImgO4aGgK6x4DiLdmFqo2bk -ZZWZLkXbwenrSdLVmUZLP5Gg2dsfMkbfFpydau9Zk3RVl6mVYATwzJaY9K5otC0K -7kc+nKPwkTxKAjndbznjsVrWK0Xcr4hrlMHs4Re2Nrdqa2mVd1jAAFO5xETJJtd7 -YqL6mQuJ9wQfEEq2QWz1hEi67l8g8VeEgzYQOjZ6pTxwYYt0YDfKBjRtRCWuJ11c -w6Q+pniGcgIHAMkQGjZds88CwAdIiyG7IAIT2ovW+xVxH/JqLPHeRsHMKYx4DPqL -2y23Tchw+gBUvmbwCdObXWL1eq5R3Xz3ikkdX/I6zknmEvgPTi5N59C1IQqh0W/6 -8uMrHOdELz9I5Fd+zGTJ8iyh/wrecMiIx+HOsBTYv/FYbMVnQUshUBOiD70geUb5 -uSeHyxl/P7VK/0phbxOznU4oDot2fHPmZRK3q+K67J9L16q7pEou1AJAw8E7ed5C -Ywf+y2tdxsuqChQK/OA6uuqW6rXjZPuCoG5Bn6YIEuU769LsHcY= -=1PBR ------END PGP SIGNATURE----- diff --git a/Python-3.10.14.tar.xz b/Python-3.10.15.tar.xz similarity index 100% rename from Python-3.10.14.tar.xz rename to Python-3.10.15.tar.xz diff --git a/Python-3.10.15.tar.xz.asc b/Python-3.10.15.tar.xz.asc new file mode 100644 index 0000000..315072a --- /dev/null +++ b/Python-3.10.15.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmbboS8ACgkQ/+h0BBaL +2Ecc7BAAmdd+jqs4mNpJg58HgOnLIx3hVBrzn1kyI9AkbxfFGGfm3Gg9Exa/dIph +m1Bt8FogUqOxFnEsFBFTgxh49TCDiUDFzTWYWcrbhtodGFywCmr+0ha6CuEcuuFa +hL0qV7sIJRoVzcdPU6pHh4OcDtdLR0Ws27WiMilrpquw/sWztIiueASZn/kehToD +XM1RTcFtaJeO++cp2tECXRrTU79lzsdpRY/DOyUWWJmLFv0GdrKi4bszKhcYK8x7 +qKleGklFf6AzhGT1A91cRyQ6AEcD3Vnp1Or+agJUwxA0hVuyw6cEmf0+VONqwDMe +M/5bz8xgt6kopfz48mrTJhHg24+6wt6b4kQgwrtUoyucgb+k7ThzwgCj+Wg/Z0Pz +/S+M1hF7I0Ot/PFA3LH5QJADM7nsw5+Rkl68HqQp7s8O9RddPHpCILDIM/AUkUu+ +Xn/1MgPdhhTnA5elyZ2DDDtETUugNu5RILrIRoKonHsZtOQOpOERzUdbzEHCuLv5 +AunaLPWrvxXtEJUKLmyOUfYoI35Gw3/gHYyKTSmo4C1SMYUjke++N7c6vbsvroRG +aUQa/TdAf71zz/r6lHg0vYt+D5FlmFJzB8gCmt6ewKJAO82ls3rr0XjmD1w58sXV +kuwy+53MopEaI1I4D6qIMq/XxNnU2Q63sqKaai8Emx1Yw28Csvw= +=f/SR +-----END PGP SIGNATURE----- diff --git a/fix-sphinx-72.patch b/fix-sphinx-72.patch index dc3d130..68701fe 100644 --- a/fix-sphinx-72.patch +++ b/fix-sphinx-72.patch @@ -915,7 +915,7 @@ Open a new pseudo-terminal pair. Return a pair of file descriptors ``(master, slave)`` for the pty and the tty, respectively. The new file -@@ -2637,7 +2637,7 @@ features: +@@ -2644,7 +2644,7 @@ features: possible and call :func:`lstat` on the result. This does not apply to dangling symlinks or junction points, which will raise the usual exceptions. diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index 5eca1ee..46067d1 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -29,7 +29,7 @@ Create a Python.framework rather than a traditional Unix install. Optional --- a/Misc/NEWS +++ b/Misc/NEWS -@@ -3731,7 +3731,7 @@ C API +@@ -3810,7 +3810,7 @@ C API ----- - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name diff --git a/gh120226-fix-sendfile-test-kernel-610.patch b/gh120226-fix-sendfile-test-kernel-610.patch new file mode 100644 index 0000000..aebd6b6 --- /dev/null +++ b/gh120226-fix-sendfile-test-kernel-610.patch @@ -0,0 +1,35 @@ +From 1b3f6523a5c83323cdc44031b33a1c062e5dc698 Mon Sep 17 00:00:00 2001 +From: Xi Ruoyao +Date: Fri, 7 Jun 2024 23:51:32 +0800 +Subject: [PATCH] gh-120226: Fix + test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10 + (GH-120227) + +The worst case is that the kernel buffers 17 pages with a page size of 64k. +(cherry picked from commit a7584245661102a5768c643fbd7db8395fd3c90e) + +Co-authored-by: Xi Ruoyao +--- + Lib/test/test_asyncio/test_sendfile.py | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +--- a/Lib/test/test_asyncio/test_sendfile.py ++++ b/Lib/test/test_asyncio/test_sendfile.py +@@ -93,13 +93,10 @@ class MyProto(asyncio.Protocol): + + class SendfileBase: + +- # 256 KiB plus small unaligned to buffer chunk +- # Newer versions of Windows seems to have increased its internal +- # buffer and tries to send as much of the data as it can as it +- # has some form of buffering for this which is less than 256KiB +- # on newer server versions and Windows 11. +- # So DATA should be larger than 256 KiB to make this test reliable. +- DATA = b"x" * (1024 * 256 + 1) ++ # Linux >= 6.10 seems buffering up to 17 pages of data. ++ # So DATA should be large enough to make this test reliable even with a ++ # 64 KiB page configuration. ++ DATA = b"x" * (1024 * 17 * 64 + 1) + # Reduce socket buffer size to test on relative small data sets. + BUF_SIZE = 4 * 1024 # 4 KiB + diff --git a/python310.changes b/python310.changes index 815fac7..20e1f0c 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,99 @@ +------------------------------------------------------------------- +Mon Sep 9 13:41:07 UTC 2024 - Matej Cepl + +- Update to 3.10.15: + - Tests + - gh-112769: The tests now correctly compare zlib version + when :const:`zlib.ZLIB_RUNTIME_VERSION` contains + non-integer suffixes. For example zlib-ng defines the + version as ``1.3.0.zlib-ng``. + - gh-117187: Fix XML tests for vanilla Expat <2.6.0. + - gh-100454: Fix SSL tests CI for OpenSSL 3.1+ + - Security + - gh-123678: Upgrade libexpat to 2.6.3 + - gh-121957: Fixed missing audit events around interactive + use of Python, now also properly firing for ``python -i``, + as well as for ``python -m asyncio``. The event in question + is ``cpython.run_stdin``. + - gh-122133: Authenticate the socket connection for the + ``socket.socketpair()`` fallback on platforms where + ``AF_UNIX`` is not available like Windows. Patch by + Gregory P. Smith and Seth Larson + . Reported by Ellie + - gh-121285: Remove backtracking from tarfile header + parsing for ``hdrcharset``, PAX, and GNU sparse headers + (bsc#1230227, CVE-2024-6232). + - gh-118486: :func:`os.mkdir` on Windows now accepts + *mode* of ``0o700`` to restrict the new directory to + the current user. This fixes CVE-2024-4030 affecting + :func:`tempfile.mkdtemp` in scenarios where the base + temporary directory is more permissive than the default. + - gh-116741: Update bundled libexpat to 2.6.2 + - Library + - gh-123693: Use platform-agnostic behavior when computing + ``zipfile.Path.name``. + - gh-123270: Applied a more surgical fix for malformed + payloads in :class:`zipfile.Path` causing infinite loops + (gh-122905) without breaking contents using legitimate + characters (bsc#1229704, CVE-2024-8088). + - gh-123067: Fix quadratic complexity in parsing ``"``-quoted + cookie values with backslashes by :mod:`http.cookies` + (bsc#1229596, CVE-2024-7592). + - gh-122905: :class:`zipfile.Path` objects now sanitize names + from the zipfile. + - gh-121650: :mod:`email` headers with embedded newlines are + now quoted on output. The :mod:`~email.generator` will now + refuse to serialize (write) headers that are unsafely folded + or delimited; see :attr:`~email.policy.Policy.verify_generated_headers`. + (Contributed by Bas Bloemsaat and Petr Viktorin in + gh-121650.; CVE-2024-6923, bsc#1228780). + - gh-113171: Fixed various false positives and false negatives in + * :attr:`ipaddress.IPv4Address.is_private` (see these docs for details) + * :attr:`ipaddress.IPv4Address.is_global` + * :attr:`ipaddress.IPv6Address.is_private` + * :attr:`ipaddress.IPv6Address.is_global` + Also in the corresponding :class:`ipaddress.IPv4Network` and + :class:`ipaddress.IPv6Network` attributes. + Fixes bsc#1226448 (CVE-2024-4032). + - gh-102988: :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 (bsc#1210638). + - gh-67693: Fix :func:`urllib.parse.urlunparse` and + :func:`urllib.parse.urlunsplit` for URIs with path starting with multiple + slashes and no authority. Based on patch by Ashwin Ramaswami. + - Core and Builtins + - gh-112275: A deadlock involving ``pystate.c``'s + ``HEAD_LOCK`` in ``posixmodule.c`` at fork is now + fixed. Patch by ChuBoning based on previous Python 3.12 fix + by Victor Stinner. +- Remove upstreamed patches: + - CVE-2023-27043-email-parsing-errors.patch + - CVE-2024-4032-private-IP-addrs.patch + - CVE-2024-6923-email-hdr-inject.patch + - CVE-2024-8088-inf-loop-zipfile_Path.patch +- Add sphinx-802.patch to overcome working both with the most + recent and older Sphinx versions. + +------------------------------------------------------------------- +Mon Sep 2 09:44:26 UTC 2024 - Matej Cepl + +- Add gh120226-fix-sendfile-test-kernel-610.patch to avoid + failing test_sendfile_close_peer_in_the_middle_of_receiving + tests on Linux >= 6.10 (GH-120227). + +------------------------------------------------------------------- +Wed Aug 28 16:54:34 UTC 2024 - Matej Cepl + +- Add CVE-2024-8088-inf-loop-zipfile_Path.patch to prevent + malformed payload to cause infinite loops in zipfile.Path + (bsc#1229704, CVE-2024-8088). + ------------------------------------------------------------------- Wed Aug 7 13:40:44 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index 45f3fda..48d2192 100644 --- a/python310.spec +++ b/python310.spec @@ -108,7 +108,7 @@ Obsoletes: python39%{?1:-%{1}} # _md5.cpython-38m-x86_64-linux-gnu.so %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so Name: %{python_pkg_name}%{psuffix} -Version: 3.10.14 +Version: 3.10.15 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -178,11 +178,6 @@ Patch18: bpo-37596-make-set-marshalling.patch # PATCH-FIX-UPSTREAM gh-78214-marshal_stabilize_FLAG_REF.patch bsc#1213463 mcepl@suse.com # marshal: Stabilize FLAG_REF usage Patch19: gh-78214-marshal_stabilize_FLAG_REF.patch -# PATCH-FIX-UPSTREAM CVE-2023-27043-email-parsing-errors.patch bsc#1210638 mcepl@suse.com -# Detect email address parsing errors and return empty tuple to -# indicate the parsing error (old API), from gh#python/cpython!105127 -# Patch carries a REGRESSION (gh#python/cpython#106669), so it has been also partially REVERTED -Patch20: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM fix-sphinx-72.patch gh#python/cpython#97950 # This is a patch with a lot of PR combined to make the doc work with # sphinx 7.2 @@ -200,15 +195,15 @@ Patch21: fix-sphinx-72.patch # PATCH-FIX-UPSTREAM CVE-2023-52425-libexpat-2.6.0-backport.patch gh#python/cpython#117187 mcepl@suse.com # Make the test suite work with libexpat < 2.6.0 Patch22: CVE-2023-52425-libexpat-2.6.0-backport.patch -# PATCH-FIX-UPSTREAM CVE-2024-4032-private-IP-addrs.patch bsc#1226448 mcepl@suse.com -# rearrange definition of private v global IP addresses -Patch23: CVE-2024-4032-private-IP-addrs.patch # PATCH-FIX-UPSTREAM bso1227999-reproducible-builds.patch bsc#1227999 mcepl@suse.com # reproducibility patches Patch24: bso1227999-reproducible-builds.patch -# PATCH-FIX-UPSTREAM CVE-2024-6923-email-hdr-inject.patch bsc#1228780 mcepl@suse.com -# prevent email header injection, patch from gh#python/cpython!122608 -Patch25: CVE-2024-6923-email-hdr-inject.patch +# PATCH-FIX-UPSTREAM gh120226-fix-sendfile-test-kernel-610.patch gh#python/cpython#120226 mcepl@suse.com +# Fix test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10 (GH-120227) +Patch27: gh120226-fix-sendfile-test-kernel-610.patch +# PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com +# status_iterator method moved between the Sphinx versions +Patch28: sphinx-802.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -483,12 +478,11 @@ other applications. %patch -p1 -P 17 %patch -p1 -P 18 %patch -p1 -P 19 -%patch -p1 -P 20 %patch -p1 -P 21 %patch -p1 -P 22 -%patch -p1 -P 23 %patch -p1 -P 24 -%patch -p1 -P 25 +%patch -p1 -P 27 +%patch -p1 -P 28 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac diff --git a/sphinx-802.patch b/sphinx-802.patch new file mode 100644 index 0000000..c4600b7 --- /dev/null +++ b/sphinx-802.patch @@ -0,0 +1,21 @@ +--- + Doc/tools/extensions/pyspecific.py | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/Doc/tools/extensions/pyspecific.py ++++ b/Doc/tools/extensions/pyspecific.py +@@ -27,7 +27,13 @@ try: + except ImportError: + from sphinx.environment import NoUri + from sphinx.locale import _ as sphinx_gettext +-from sphinx.util import status_iterator, logging ++try: ++ from sphinx.util.display import status_iterator ++except ImportError: ++ # This method was moved into sphinx.util.display in Sphinx 6.1.0. Before ++ # that it resided in sphinx.util. ++ from sphinx.util import status_iterator ++from sphinx.util import logging + from sphinx.util.nodes import split_explicit_title + from sphinx.writers.text import TextWriter, TextTranslator + from sphinx.writers.latex import LaTeXTranslator From e9291e95e786bfd388c6aaed9a4b848ac4d061339e2c93919ea27f43b36f31ee Mon Sep 17 00:00:00 2001 From: Marco Strigl Date: Wed, 6 Nov 2024 10:14:01 +0000 Subject: [PATCH 17/23] Set link to python310.36315 via maintenance_release request --- CVE-2024-6923-email-hdr-inject.patch | 335 ------------------------- CVE-2024-9287-venv_path_unquoted.patch | 296 ++++++++++++++++++++++ python310.changes | 19 ++ python310.spec | 9 + 4 files changed, 324 insertions(+), 335 deletions(-) delete mode 100644 CVE-2024-6923-email-hdr-inject.patch create mode 100644 CVE-2024-9287-venv_path_unquoted.patch diff --git a/CVE-2024-6923-email-hdr-inject.patch b/CVE-2024-6923-email-hdr-inject.patch deleted file mode 100644 index fe6cde5..0000000 --- a/CVE-2024-6923-email-hdr-inject.patch +++ /dev/null @@ -1,335 +0,0 @@ -From ff3629b3cedf5e50a7a5f567283778fe5539a11e Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Wed, 31 Jul 2024 00:19:48 +0200 -Subject: [PATCH] [3.10] gh-121650: Encode newlines in headers, and verify - headers are sound (GH-122233) - -Per RFC 2047: - -> [...] these encoding schemes allow the -> encoding of arbitrary octet values, mail readers that implement this -> decoding should also ensure that display of the decoded data on the -> recipient's terminal will not cause unwanted side-effects - -It seems that the "quoted-word" scheme is a valid way to include -a newline character in a header value, just like we already allow -undecodable bytes or control characters. -They do need to be properly quoted when serialized to text, though. - -This should fail for custom fold() implementations that aren't careful -about newlines. - -(cherry picked from commit 097633981879b3c9de9a1dd120d3aa585ecc2384) - -Co-authored-by: Petr Viktorin -Co-authored-by: Bas Bloemsaat -Co-authored-by: Serhiy Storchaka ---- - Doc/library/email.errors.rst | 6 - Doc/library/email.policy.rst | 18 ++ - Doc/whatsnew/3.10.rst | 12 + - Lib/email/_header_value_parser.py | 12 + - Lib/email/_policybase.py | 8 + - Lib/email/errors.py | 4 - Lib/email/generator.py | 13 +- - Lib/test/test_email/test_generator.py | 62 ++++++++++ - Lib/test/test_email/test_policy.py | 26 ++++ - Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst | 5 - 10 files changed, 162 insertions(+), 4 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst - ---- a/Doc/library/email.errors.rst -+++ b/Doc/library/email.errors.rst -@@ -59,6 +59,12 @@ The following exception classes are defi - :class:`~email.mime.image.MIMEImage`). - - -+.. exception:: HeaderWriteError() -+ -+ Raised when an error occurs when the :mod:`~email.generator` outputs -+ headers. -+ -+ - Here is the list of the defects that the :class:`~email.parser.FeedParser` - can find while parsing messages. Note that the defects are added to the message - where the problem was found, so for example, if a message nested inside a ---- a/Doc/library/email.policy.rst -+++ b/Doc/library/email.policy.rst -@@ -229,6 +229,24 @@ added matters. To illustrate:: - - .. versionadded:: 3.6 - -+ -+ .. attribute:: verify_generated_headers -+ -+ If ``True`` (the default), the generator will raise -+ :exc:`~email.errors.HeaderWriteError` instead of writing a header -+ that is improperly folded or delimited, such that it would -+ be parsed as multiple headers or joined with adjacent data. -+ Such headers can be generated by custom header classes or bugs -+ in the ``email`` module. -+ -+ As it's a security feature, this defaults to ``True`` even in the -+ :class:`~email.policy.Compat32` policy. -+ For backwards compatible, but unsafe, behavior, it must be set to -+ ``False`` explicitly. -+ -+ .. versionadded:: 3.10.15 -+ -+ - The following :class:`Policy` method is intended to be called by code using - the email library to create policy instances with custom settings: - ---- a/Doc/whatsnew/3.10.rst -+++ b/Doc/whatsnew/3.10.rst -@@ -2357,3 +2357,15 @@ ipaddress - - * Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``, - ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``. -+ -+email -+----- -+ -+* Headers with embedded newlines are now quoted on output. -+ -+ The :mod:`~email.generator` will now refuse to serialize (write) headers -+ that are improperly folded or delimited, such that they would be parsed as -+ multiple headers or joined with adjacent data. -+ If you need to turn this safety feature off, -+ set :attr:`~email.policy.Policy.verify_generated_headers`. -+ (Contributed by Bas Bloemsaat and Petr Viktorin in :gh:`121650`.) ---- a/Lib/email/_header_value_parser.py -+++ b/Lib/email/_header_value_parser.py -@@ -92,6 +92,8 @@ TOKEN_ENDS = TSPECIALS | WSP - ASPECIALS = TSPECIALS | set("*'%") - ATTRIBUTE_ENDS = ASPECIALS | WSP - EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%') -+NLSET = {'\n', '\r'} -+SPECIALSNL = SPECIALS | NLSET - - def quote_string(value): - return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' -@@ -2778,9 +2780,13 @@ def _refold_parse_tree(parse_tree, *, po - wrap_as_ew_blocked -= 1 - continue - tstr = str(part) -- if part.token_type == 'ptext' and set(tstr) & SPECIALS: -- # Encode if tstr contains special characters. -- want_encoding = True -+ if not want_encoding: -+ if part.token_type == 'ptext': -+ # Encode if tstr contains special characters. -+ want_encoding = not SPECIALSNL.isdisjoint(tstr) -+ else: -+ # Encode if tstr contains newlines. -+ want_encoding = not NLSET.isdisjoint(tstr) - try: - tstr.encode(encoding) - charset = encoding ---- a/Lib/email/_policybase.py -+++ b/Lib/email/_policybase.py -@@ -157,6 +157,13 @@ class Policy(_PolicyBase, metaclass=abc. - message_factory -- the class to use to create new message objects. - If the value is None, the default is Message. - -+ verify_generated_headers -+ -- if true, the generator verifies that each header -+ they are properly folded, so that a parser won't -+ treat it as multiple headers, start-of-body, or -+ part of another header. -+ This is a check against custom Header & fold() -+ implementations. - """ - - raise_on_defect = False -@@ -165,6 +172,7 @@ class Policy(_PolicyBase, metaclass=abc. - max_line_length = 78 - mangle_from_ = False - message_factory = None -+ verify_generated_headers = True - - def handle_defect(self, obj, defect): - """Based on policy, either raise defect or call register_defect. ---- a/Lib/email/errors.py -+++ b/Lib/email/errors.py -@@ -29,6 +29,10 @@ class CharsetError(MessageError): - """An illegal charset was given.""" - - -+class HeaderWriteError(MessageError): -+ """Error while writing headers.""" -+ -+ - # These are parsing defects which the parser was able to work around. - class MessageDefect(ValueError): - """Base class for a message defect.""" ---- a/Lib/email/generator.py -+++ b/Lib/email/generator.py -@@ -14,12 +14,14 @@ import random - from copy import deepcopy - from io import StringIO, BytesIO - from email.utils import _has_surrogates -+from email.errors import HeaderWriteError - - UNDERSCORE = '_' - NL = '\n' # XXX: no longer used by the code below. - - NLCRE = re.compile(r'\r\n|\r|\n') - fcre = re.compile(r'^From ', re.MULTILINE) -+NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]') - - - -@@ -223,7 +225,16 @@ class Generator: - - def _write_headers(self, msg): - for h, v in msg.raw_items(): -- self.write(self.policy.fold(h, v)) -+ folded = self.policy.fold(h, v) -+ if self.policy.verify_generated_headers: -+ linesep = self.policy.linesep -+ if not folded.endswith(self.policy.linesep): -+ raise HeaderWriteError( -+ f'folded header does not end with {linesep!r}: {folded!r}') -+ if NEWLINE_WITHOUT_FWSP.search(folded.removesuffix(linesep)): -+ raise HeaderWriteError( -+ f'folded header contains newline: {folded!r}') -+ self.write(folded) - # A blank line always separates headers from body - self.write(self._NL) - ---- a/Lib/test/test_email/test_generator.py -+++ b/Lib/test/test_email/test_generator.py -@@ -6,6 +6,7 @@ from email.message import EmailMessage - from email.generator import Generator, BytesGenerator - from email.headerregistry import Address - from email import policy -+import email.errors - from test.test_email import TestEmailBase, parameterize - - -@@ -216,6 +217,44 @@ class TestGeneratorBase: - g.flatten(msg) - self.assertEqual(s.getvalue(), self.typ(expected)) - -+ def test_keep_encoded_newlines(self): -+ msg = self.msgmaker(self.typ(textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com -+ -+ None -+ """))) -+ expected = textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com -+ -+ None -+ """) -+ s = self.ioclass() -+ g = self.genclass(s, policy=self.policy.clone(max_line_length=80)) -+ g.flatten(msg) -+ self.assertEqual(s.getvalue(), self.typ(expected)) -+ -+ def test_keep_long_encoded_newlines(self): -+ msg = self.msgmaker(self.typ(textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com -+ -+ None -+ """))) -+ expected = textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject -+ =?utf-8?q?=0A?=Bcc: -+ injection@example.com -+ -+ None -+ """) -+ s = self.ioclass() -+ g = self.genclass(s, policy=self.policy.clone(max_line_length=30)) -+ g.flatten(msg) -+ self.assertEqual(s.getvalue(), self.typ(expected)) -+ - - class TestGenerator(TestGeneratorBase, TestEmailBase): - -@@ -224,6 +263,29 @@ class TestGenerator(TestGeneratorBase, T - ioclass = io.StringIO - typ = str - -+ def test_verify_generated_headers(self): -+ """gh-121650: by default the generator prevents header injection""" -+ class LiteralHeader(str): -+ name = 'Header' -+ def fold(self, **kwargs): -+ return self -+ -+ for text in ( -+ 'Value\r\nBad Injection\r\n', -+ 'NoNewLine' -+ ): -+ with self.subTest(text=text): -+ message = message_from_string( -+ "Header: Value\r\n\r\nBody", -+ policy=self.policy, -+ ) -+ -+ del message['Header'] -+ message['Header'] = LiteralHeader(text) -+ -+ with self.assertRaises(email.errors.HeaderWriteError): -+ message.as_string() -+ - - class TestBytesGenerator(TestGeneratorBase, TestEmailBase): - ---- a/Lib/test/test_email/test_policy.py -+++ b/Lib/test/test_email/test_policy.py -@@ -26,6 +26,7 @@ class PolicyAPITests(unittest.TestCase): - 'raise_on_defect': False, - 'mangle_from_': True, - 'message_factory': None, -+ 'verify_generated_headers': True, - } - # These default values are the ones set on email.policy.default. - # If any of these defaults change, the docs must be updated. -@@ -277,6 +278,31 @@ class PolicyAPITests(unittest.TestCase): - with self.assertRaises(email.errors.HeaderParseError): - policy.fold("Subject", subject) - -+ def test_verify_generated_headers(self): -+ """Turning protection off allows header injection""" -+ policy = email.policy.default.clone(verify_generated_headers=False) -+ for text in ( -+ 'Header: Value\r\nBad: Injection\r\n', -+ 'Header: NoNewLine' -+ ): -+ with self.subTest(text=text): -+ message = email.message_from_string( -+ "Header: Value\r\n\r\nBody", -+ policy=policy, -+ ) -+ class LiteralHeader(str): -+ name = 'Header' -+ def fold(self, **kwargs): -+ return self -+ -+ del message['Header'] -+ message['Header'] = LiteralHeader(text) -+ -+ self.assertEqual( -+ message.as_string(), -+ f"{text}\nBody", -+ ) -+ - # XXX: Need subclassing tests. - # For adding subclassed objects, make sure the usual rules apply (subclass - # wins), but that the order still works (right overrides left). ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst -@@ -0,0 +1,5 @@ -+:mod:`email` headers with embedded newlines are now quoted on output. The -+:mod:`~email.generator` will now refuse to serialize (write) headers that -+are unsafely folded or delimited; see -+:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas -+Bloemsaat and Petr Viktorin in :gh:`121650`.) diff --git a/CVE-2024-9287-venv_path_unquoted.patch b/CVE-2024-9287-venv_path_unquoted.patch new file mode 100644 index 0000000..93f30e1 --- /dev/null +++ b/CVE-2024-9287-venv_path_unquoted.patch @@ -0,0 +1,296 @@ +From 21139b45039a72e8346bdc32d498345ef174ba92 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Fri, 1 Nov 2024 14:11:47 +0100 +Subject: [PATCH] [3.11] gh-124651: Quote template strings in `venv` activation + scripts (GH-124712) (GH-126185) (#126269) + +(cherry picked from commit ae961ae94bf19c8f8c7fbea3d1c25cc55ce8ae97) +--- + Lib/test/test_venv.py | 81 ++++++++++ + Lib/venv/__init__.py | 42 ++++- + Lib/venv/scripts/common/activate | 8 + Lib/venv/scripts/nt/activate.bat | 6 + Lib/venv/scripts/posix/activate.csh | 8 + Lib/venv/scripts/posix/activate.fish | 8 + Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst | 1 + 7 files changed, 134 insertions(+), 20 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst + +--- a/Lib/test/test_venv.py ++++ b/Lib/test/test_venv.py +@@ -15,6 +15,7 @@ import struct + import subprocess + import sys + import tempfile ++import shlex + from test.support import (captured_stdout, captured_stderr, requires_zlib, + skip_if_broken_multiprocessing_synchronize) + from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) +@@ -85,6 +86,10 @@ class BaseTest(unittest.TestCase): + result = f.read() + return result + ++ def assertEndsWith(self, string, tail): ++ if not string.endswith(tail): ++ self.fail(f"String {string!r} does not end with {tail!r}") ++ + class BasicTest(BaseTest): + """Test venv module functionality.""" + +@@ -342,6 +347,82 @@ class BasicTest(BaseTest): + 'import sys; print(sys.executable)']) + self.assertEqual(out.strip(), envpy.encode()) + ++ # gh-124651: test quoted strings ++ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') ++ def test_special_chars_bash(self): ++ """ ++ Test that the template strings are quoted properly (bash) ++ """ ++ rmtree(self.env_dir) ++ bash = shutil.which('bash') ++ if bash is None: ++ self.skipTest('bash required for this test') ++ env_name = '"\';&&$e|\'"' ++ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) ++ builder = venv.EnvBuilder(clear=True) ++ builder.create(env_dir) ++ activate = os.path.join(env_dir, self.bindir, 'activate') ++ test_script = os.path.join(self.env_dir, 'test_special_chars.sh') ++ with open(test_script, "w") as f: ++ f.write(f'source {shlex.quote(activate)}\n' ++ 'python -c \'import sys; print(sys.executable)\'\n' ++ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' ++ 'deactivate\n') ++ out, err = check_output([bash, test_script]) ++ lines = out.splitlines() ++ self.assertTrue(env_name.encode() in lines[0]) ++ self.assertEndsWith(lines[1], env_name.encode()) ++ ++ # gh-124651: test quoted strings ++ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') ++ def test_special_chars_csh(self): ++ """ ++ Test that the template strings are quoted properly (csh) ++ """ ++ rmtree(self.env_dir) ++ csh = shutil.which('tcsh') or shutil.which('csh') ++ if csh is None: ++ self.skipTest('csh required for this test') ++ env_name = '"\';&&$e|\'"' ++ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) ++ builder = venv.EnvBuilder(clear=True) ++ builder.create(env_dir) ++ activate = os.path.join(env_dir, self.bindir, 'activate.csh') ++ test_script = os.path.join(self.env_dir, 'test_special_chars.csh') ++ with open(test_script, "w") as f: ++ f.write(f'source {shlex.quote(activate)}\n' ++ 'python -c \'import sys; print(sys.executable)\'\n' ++ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' ++ 'deactivate\n') ++ out, err = check_output([csh, test_script]) ++ lines = out.splitlines() ++ self.assertTrue(env_name.encode() in lines[0]) ++ self.assertEndsWith(lines[1], env_name.encode()) ++ ++ # gh-124651: test quoted strings on Windows ++ @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') ++ def test_special_chars_windows(self): ++ """ ++ Test that the template strings are quoted properly on Windows ++ """ ++ rmtree(self.env_dir) ++ env_name = "'&&^$e" ++ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) ++ builder = venv.EnvBuilder(clear=True) ++ builder.create(env_dir) ++ activate = os.path.join(env_dir, self.bindir, 'activate.bat') ++ test_batch = os.path.join(self.env_dir, 'test_special_chars.bat') ++ with open(test_batch, "w") as f: ++ f.write('@echo off\n' ++ f'"{activate}" & ' ++ f'{self.exe} -c "import sys; print(sys.executable)" & ' ++ f'{self.exe} -c "import os; print(os.environ[\'VIRTUAL_ENV\'])" & ' ++ 'deactivate') ++ out, err = check_output([test_batch]) ++ lines = out.splitlines() ++ self.assertTrue(env_name.encode() in lines[0]) ++ self.assertEndsWith(lines[1], env_name.encode()) ++ + @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') + def test_unicode_in_batch_file(self): + """ +--- a/Lib/venv/__init__.py ++++ b/Lib/venv/__init__.py +@@ -11,6 +11,7 @@ import subprocess + import sys + import sysconfig + import types ++import shlex + + + CORE_VENV_DEPS = ('pip', 'setuptools') +@@ -364,11 +365,41 @@ class EnvBuilder: + :param context: The information for the environment creation request + being processed. + """ +- text = text.replace('__VENV_DIR__', context.env_dir) +- text = text.replace('__VENV_NAME__', context.env_name) +- text = text.replace('__VENV_PROMPT__', context.prompt) +- text = text.replace('__VENV_BIN_NAME__', context.bin_name) +- text = text.replace('__VENV_PYTHON__', context.env_exe) ++ replacements = { ++ '__VENV_DIR__': context.env_dir, ++ '__VENV_NAME__': context.env_name, ++ '__VENV_PROMPT__': context.prompt, ++ '__VENV_BIN_NAME__': context.bin_name, ++ '__VENV_PYTHON__': context.env_exe, ++ } ++ ++ def quote_ps1(s): ++ """ ++ This should satisfy PowerShell quoting rules [1], unless the quoted ++ string is passed directly to Windows native commands [2]. ++ [1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules ++ [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters ++ """ ++ s = s.replace("'", "''") ++ return f"'{s}'" ++ ++ def quote_bat(s): ++ return s ++ ++ # gh-124651: need to quote the template strings properly ++ quote = shlex.quote ++ script_path = context.script_path ++ if script_path.endswith('.ps1'): ++ quote = quote_ps1 ++ elif script_path.endswith('.bat'): ++ quote = quote_bat ++ else: ++ # fallbacks to POSIX shell compliant quote ++ quote = shlex.quote ++ ++ replacements = {key: quote(s) for key, s in replacements.items()} ++ for key, quoted in replacements.items(): ++ text = text.replace(key, quoted) + return text + + def install_scripts(self, context, path): +@@ -408,6 +439,7 @@ class EnvBuilder: + with open(srcfile, 'rb') as f: + data = f.read() + if not srcfile.endswith(('.exe', '.pdb')): ++ context.script_path = srcfile + try: + data = data.decode('utf-8') + data = self.replace_variables(data, context) +--- a/Lib/venv/scripts/common/activate ++++ b/Lib/venv/scripts/common/activate +@@ -38,11 +38,11 @@ deactivate () { + # unset irrelevant variables + deactivate nondestructive + +-VIRTUAL_ENV="__VENV_DIR__" ++VIRTUAL_ENV=__VENV_DIR__ + export VIRTUAL_ENV + + _OLD_VIRTUAL_PATH="$PATH" +-PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH" ++PATH="$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH" + export PATH + + # unset PYTHONHOME if set +@@ -55,9 +55,9 @@ fi + + if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" +- PS1="__VENV_PROMPT__${PS1:-}" ++ PS1=__VENV_PROMPT__"${PS1:-}" + export PS1 +- VIRTUAL_ENV_PROMPT="__VENV_PROMPT__" ++ VIRTUAL_ENV_PROMPT=__VENV_PROMPT__ + export VIRTUAL_ENV_PROMPT + fi + +--- a/Lib/venv/scripts/nt/activate.bat ++++ b/Lib/venv/scripts/nt/activate.bat +@@ -8,7 +8,7 @@ if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" 65001 > nul + ) + +-set VIRTUAL_ENV=__VENV_DIR__ ++set "VIRTUAL_ENV=__VENV_DIR__" + + if not defined PROMPT set PROMPT=$P$G + +@@ -24,8 +24,8 @@ set PYTHONHOME= + if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% + if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% + +-set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH% +-set VIRTUAL_ENV_PROMPT=__VENV_PROMPT__ ++set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%" ++set "VIRTUAL_ENV_PROMPT=__VENV_PROMPT__" + + :END + if defined _OLD_CODEPAGE ( +--- a/Lib/venv/scripts/posix/activate.csh ++++ b/Lib/venv/scripts/posix/activate.csh +@@ -8,17 +8,17 @@ alias deactivate 'test $?_OLD_VIRTUAL_PA + # Unset irrelevant variables. + deactivate nondestructive + +-setenv VIRTUAL_ENV "__VENV_DIR__" ++setenv VIRTUAL_ENV __VENV_DIR__ + + set _OLD_VIRTUAL_PATH="$PATH" +-setenv PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH" ++setenv PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH" + + + set _OLD_VIRTUAL_PROMPT="$prompt" + + if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then +- set prompt = "__VENV_PROMPT__$prompt" +- setenv VIRTUAL_ENV_PROMPT "__VENV_PROMPT__" ++ set prompt = __VENV_PROMPT__"$prompt" ++ setenv VIRTUAL_ENV_PROMPT __VENV_PROMPT__ + endif + + alias pydoc python -m pydoc +--- a/Lib/venv/scripts/posix/activate.fish ++++ b/Lib/venv/scripts/posix/activate.fish +@@ -33,10 +33,10 @@ end + # Unset irrelevant variables. + deactivate nondestructive + +-set -gx VIRTUAL_ENV "__VENV_DIR__" ++set -gx VIRTUAL_ENV __VENV_DIR__ + + set -gx _OLD_VIRTUAL_PATH $PATH +-set -gx PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__" $PATH ++set -gx PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__ $PATH + + # Unset PYTHONHOME if set. + if set -q PYTHONHOME +@@ -56,7 +56,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. +- printf "%s%s%s" (set_color 4B8BBE) "__VENV_PROMPT__" (set_color normal) ++ printf "%s%s%s" (set_color 4B8BBE) __VENV_PROMPT__ (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . +@@ -65,5 +65,5 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +- set -gx VIRTUAL_ENV_PROMPT "__VENV_PROMPT__" ++ set -gx VIRTUAL_ENV_PROMPT __VENV_PROMPT__ + end +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst +@@ -0,0 +1 @@ ++Properly quote template strings in :mod:`venv` activation scripts. diff --git a/python310.changes b/python310.changes index 20e1f0c..385f82c 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,22 @@ +------------------------------------------------------------------- +Fri Nov 1 21:38:45 UTC 2024 - Matej Cepl + +- Update CVE-2024-9287-venv_path_unquoted.patch according to the + upstream PR gh#python/cpython!126301. + +------------------------------------------------------------------- +Thu Oct 24 16:09:00 UTC 2024 - Matej Cepl + +- Add CVE-2024-9287-venv_path_unquoted.patch to properly quote + path names provided when creating a virtual environment + (bsc#1232241, CVE-2024-9287) + +------------------------------------------------------------------- +Wed Oct 2 16:18:29 UTC 2024 - Matej Cepl + +- Drop .pyc files from docdir for reproducible builds + (bsc#1230906). + ------------------------------------------------------------------- Mon Sep 9 13:41:07 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index 48d2192..fe74b4e 100644 --- a/python310.spec +++ b/python310.spec @@ -204,6 +204,9 @@ Patch27: gh120226-fix-sendfile-test-kernel-610.patch # PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com # status_iterator method moved between the Sphinx versions Patch28: sphinx-802.patch +# PATCH-FIX-UPSTREAM CVE-2024-9287-venv_path_unquoted.patch gh#python/cpython#124651 mcepl@suse.com +# venv should properly quote path names provided when creating a venv +Patch29: CVE-2024-9287-venv_path_unquoted.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -483,6 +486,7 @@ other applications. %patch -p1 -P 24 %patch -p1 -P 27 %patch -p1 -P 28 +%patch -p1 -P 29 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -819,6 +823,11 @@ LD_LIBRARY_PATH=. ./python -O -c "from py_compile import compile; compile('$FAIL echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-import-failed-hooks.pth %endif +# For the purposes of reproducibility, it is necessary to eliminate any *.pyc files inside documentation dirs +if [ -d %{buildroot}%{_defaultdocdir} ] ; then +find %{buildroot}%{_defaultdocdir} -type f -name \*.pyc -ls -exec rm -vf '{}' \; +fi + %if %{with general} %files -n %{python_pkg_name}-tk %{sitedir}/tkinter From def81b6d2fdbbfb0a9c6530f4595102f6c584d673e648e3722d7f4c3380e4549 Mon Sep 17 00:00:00 2001 From: Marcus Rueckert Date: Tue, 3 Dec 2024 11:12:42 +0000 Subject: [PATCH 18/23] Set link to python310.36641 via maintenance_release request --- CVE-2024-11168-validation-IPv6-addrs.patch | 97 +++++++++++++ python310.changes | 21 +++ python310.spec | 13 +- fix-sphinx-72.patch => sphinx-72.patch | 155 +++++++++++---------- 4 files changed, 213 insertions(+), 73 deletions(-) create mode 100644 CVE-2024-11168-validation-IPv6-addrs.patch rename fix-sphinx-72.patch => sphinx-72.patch (95%) diff --git a/CVE-2024-11168-validation-IPv6-addrs.patch b/CVE-2024-11168-validation-IPv6-addrs.patch new file mode 100644 index 0000000..7f1d4fb --- /dev/null +++ b/CVE-2024-11168-validation-IPv6-addrs.patch @@ -0,0 +1,97 @@ +From 37bc08c699f48461be5e000b2da9212237a1ca0f Mon Sep 17 00:00:00 2001 +From: JohnJamesUtley +Date: Tue, 25 Apr 2023 16:01:03 -0400 +Subject: [PATCH 1/4] Adds checks to ensure that bracketed hosts found by + urlsplit are of IPv6 or IPvFuture format + +--- + Lib/test/test_urlparse.py | 26 ++++++++++ + Lib/urllib/parse.py | 16 +++++- + Misc/NEWS.d/next/Library/2023-04-26-09-54-25.gh-issue-103848.aDSnpR.rst | 2 + 3 files changed, 43 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Library/2023-04-26-09-54-25.gh-issue-103848.aDSnpR.rst + +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -1138,6 +1138,32 @@ class UrlParseTestCase(unittest.TestCase + self.assertEqual(p2.scheme, 'tel') + self.assertEqual(p2.path, '+31641044153') + ++ def test_invalid_bracketed_hosts(self): ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[192.0.2.146]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[important.com:8000]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123r.IP]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v12ae]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v.IP]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123.]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path') ++ ++ def test_splitting_bracketed_hosts(self): ++ p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query') ++ self.assertEqual(p1.hostname, 'v6a.ip') ++ self.assertEqual(p1.username, 'user') ++ self.assertEqual(p1.path, '/path') ++ p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query') ++ self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test') ++ self.assertEqual(p2.username, 'user') ++ self.assertEqual(p2.path, '/path') ++ p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query') ++ self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test') ++ self.assertEqual(p3.username, 'user') ++ self.assertEqual(p3.path, '/path') ++ + def test_port_casting_failure_message(self): + message = "Port could not be cast to integer value as 'oracle'" + p1 = urllib.parse.urlparse('http://Server=sde; Service=sde:oracle') +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -36,6 +36,7 @@ import sys + import types + import collections + import warnings ++import ipaddress + + __all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag", + "urlsplit", "urlunsplit", "urlencode", "parse_qs", +@@ -441,6 +442,17 @@ def _checknetloc(netloc): + raise ValueError("netloc '" + netloc + "' contains invalid " + + "characters under NFKC normalization") + ++# Valid bracketed hosts are defined in ++# https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ ++def _check_bracketed_host(hostname): ++ if hostname.startswith('v'): ++ if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", hostname): ++ raise ValueError(f"IPvFuture address is invalid") ++ else: ++ ip = ipaddress.ip_address(hostname) # Throws Value Error if not IPv6 or IPv4 ++ if isinstance(ip, ipaddress.IPv4Address): ++ raise ValueError(f"An IPv4 address cannot be in brackets") ++ + def urlsplit(url, scheme='', allow_fragments=True): + """Parse a URL into 5 components: + :///?# +@@ -487,12 +499,14 @@ def urlsplit(url, scheme='', allow_fragm + break + else: + scheme, url = url[:i].lower(), url[i+1:] +- + if url[:2] == '//': + netloc, url = _splitnetloc(url, 2) + if (('[' in netloc and ']' not in netloc) or + (']' in netloc and '[' not in netloc)): + raise ValueError("Invalid IPv6 URL") ++ if '[' in netloc and ']' in netloc: ++ bracketed_host = netloc.partition('[')[2].partition(']')[0] ++ _check_bracketed_host(bracketed_host) + if allow_fragments and '#' in url: + url, fragment = url.split('#', 1) + if '?' in url: +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2023-04-26-09-54-25.gh-issue-103848.aDSnpR.rst +@@ -0,0 +1,2 @@ ++Add checks to ensure that ``[`` bracketed ``]`` hosts found by ++:func:`urllib.parse.urlsplit` are of IPv6 or IPvFuture format. diff --git a/python310.changes b/python310.changes index 385f82c..41ecc0a 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,24 @@ +------------------------------------------------------------------- +Thu Nov 14 07:06:20 UTC 2024 - Matej Cepl + +- Remove -IVendor/ from python-config boo#1231795 +- Apply sphinx-72.patch only conditionally for non-SLE-15 builds. + +------------------------------------------------------------------- +Wed Nov 13 13:25:01 UTC 2024 - Matej Cepl + +- Add CVE-2024-11168-validation-IPv6-addrs.patch + fixing bsc#1233307 (CVE-2024-11168, + gh#python/cpython#103848): Improper validation of IPv6 and + IPvFuture addresses. + +------------------------------------------------------------------- +Mon Nov 4 21:49:20 UTC 2024 - Matej Cepl + +- Update sphinx-72.patch to include renaming :noindex: option to + :no-index: in Sphinx 7.2 (bsc#1232750). +- While renaming drop fix-sphinx-72.patch. + ------------------------------------------------------------------- Fri Nov 1 21:38:45 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index fe74b4e..4ec7a9b 100644 --- a/python310.spec +++ b/python310.spec @@ -191,7 +191,7 @@ Patch19: gh-78214-marshal_stabilize_FLAG_REF.patch # * gh#python/cpython#104163 # * gh#python/cpython#104221 # * gh#python/cpython#107246 -Patch21: fix-sphinx-72.patch +Patch21: sphinx-72.patch # PATCH-FIX-UPSTREAM CVE-2023-52425-libexpat-2.6.0-backport.patch gh#python/cpython#117187 mcepl@suse.com # Make the test suite work with libexpat < 2.6.0 Patch22: CVE-2023-52425-libexpat-2.6.0-backport.patch @@ -207,6 +207,9 @@ Patch28: sphinx-802.patch # PATCH-FIX-UPSTREAM CVE-2024-9287-venv_path_unquoted.patch gh#python/cpython#124651 mcepl@suse.com # venv should properly quote path names provided when creating a venv Patch29: CVE-2024-9287-venv_path_unquoted.patch +# PATCH-FIX-UPSTREAM CVE-2024-11168-validation-IPv6-addrs.patch bsc#1233307 mcepl@suse.com +# improve validation of IPv6 and IPvFuture addresses in urlparse and urlsplit +Patch30: CVE-2024-11168-validation-IPv6-addrs.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -481,12 +484,17 @@ other applications. %patch -p1 -P 17 %patch -p1 -P 18 %patch -p1 -P 19 + +%if ! 0%{?sle_version} || 0%{?sle_version} >= 160000 %patch -p1 -P 21 +%endif + %patch -p1 -P 22 %patch -p1 -P 24 %patch -p1 -P 27 %patch -p1 -P 28 %patch -p1 -P 29 +%patch -p1 -P 30 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -795,6 +803,9 @@ install -m 755 -D Tools/gdb/libpython.py %{buildroot}%{_datadir}/gdb/auto-load/% # install devel files to /config #cp Makefile Makefile.pre.in Makefile.pre $RPM_BUILD_ROOT%{sitedir}/config-%{python_abi}/ +# Remove -IVendor/ from python-config boo#1231795 +sed -i 's/-IVendor\///' %{buildroot}%{_bindir}/python%{python_abi}-config + # RPM macros %if %{primary_interpreter} mkdir -p %{buildroot}%{_rpmconfigdir}/macros.d/ diff --git a/fix-sphinx-72.patch b/sphinx-72.patch similarity index 95% rename from fix-sphinx-72.patch rename to sphinx-72.patch index 68701fe..39de7e1 100644 --- a/fix-sphinx-72.patch +++ b/sphinx-72.patch @@ -1,76 +1,77 @@ --- - Doc/c-api/bytearray.rst | 2 - Doc/c-api/bytes.rst | 2 - Doc/c-api/capsule.rst | 2 - Doc/c-api/complex.rst | 2 - Doc/c-api/concrete.rst | 6 - - Doc/c-api/dict.rst | 4 - Doc/c-api/exceptions.rst | 6 - - Doc/c-api/file.rst | 2 - Doc/c-api/float.rst | 2 - Doc/c-api/function.rst | 2 - Doc/c-api/import.rst | 4 - Doc/c-api/init.rst | 14 +-- - Doc/c-api/intro.rst | 8 - - Doc/c-api/list.rst | 6 - - Doc/c-api/long.rst | 4 - Doc/c-api/mapping.rst | 2 - Doc/c-api/memoryview.rst | 2 - Doc/c-api/method.rst | 4 - Doc/c-api/module.rst | 2 - Doc/c-api/none.rst | 2 - Doc/c-api/number.rst | 12 +- - Doc/c-api/object.rst | 12 +- - Doc/c-api/sequence.rst | 4 - Doc/c-api/set.rst | 6 - - Doc/c-api/structures.rst | 4 - Doc/c-api/tuple.rst | 2 - Doc/c-api/type.rst | 2 - Doc/c-api/typeobj.rst | 4 - Doc/conf.py | 5 + - Doc/extending/newtypes.rst | 2 - Doc/library/_thread.rst | 2 - Doc/library/binascii.rst | 6 - - Doc/library/cmath.rst | 2 - Doc/library/copy.rst | 2 - Doc/library/copyreg.rst | 4 - Doc/library/dis.rst | 2 - Doc/library/exceptions.rst | 10 +- - Doc/library/fnmatch.rst | 4 - Doc/library/functions.rst | 10 +- - Doc/library/http.client.rst | 2 - Doc/library/imp.rst | 2 - Doc/library/internet.rst | 2 - Doc/library/locale.rst | 4 - Doc/library/marshal.rst | 4 - Doc/library/os.path.rst | 2 - Doc/library/os.rst | 4 - Doc/library/pdb.rst | 4 - Doc/library/posix.rst | 2 - Doc/library/pprint.rst | 4 - Doc/library/pwd.rst | 2 - Doc/library/pyexpat.rst | 2 - Doc/library/runpy.rst | 4 - Doc/library/shelve.rst | 6 - - Doc/library/site.rst | 6 - - Doc/library/socket.rst | 4 - Doc/library/stdtypes.rst | 146 ++++++++++++++++---------------- - Doc/library/sys.rst | 2 - Doc/library/traceback.rst | 2 - Doc/library/types.rst | 2 - Doc/reference/compound_stmts.rst | 90 +++++++++---------- - Doc/reference/datamodel.rst | 154 +++++++++++++++++----------------- - Doc/reference/executionmodel.rst | 2 - Doc/reference/expressions.rst | 134 ++++++++++++++--------------- - Doc/reference/simple_stmts.rst | 74 ++++++++-------- - Doc/reference/toplevel_components.rst | 10 +- - Doc/tools/extensions/pyspecific.py | 25 +++++ - Doc/tutorial/classes.rst | 2 - Doc/tutorial/controlflow.rst | 2 - Doc/tutorial/inputoutput.rst | 6 - - Doc/tutorial/modules.rst | 4 - Doc/tutorial/stdlib.rst | 2 - 71 files changed, 457 insertions(+), 427 deletions(-) + Doc/c-api/bytearray.rst | 2 + Doc/c-api/bytes.rst | 2 + Doc/c-api/capsule.rst | 2 + Doc/c-api/complex.rst | 2 + Doc/c-api/concrete.rst | 6 - + Doc/c-api/dict.rst | 4 + Doc/c-api/exceptions.rst | 6 - + Doc/c-api/file.rst | 2 + Doc/c-api/float.rst | 2 + Doc/c-api/function.rst | 2 + Doc/c-api/import.rst | 4 + Doc/c-api/init.rst | 14 +-- + Doc/c-api/intro.rst | 8 - + Doc/c-api/list.rst | 6 - + Doc/c-api/long.rst | 4 + Doc/c-api/mapping.rst | 2 + Doc/c-api/memoryview.rst | 2 + Doc/c-api/method.rst | 4 + Doc/c-api/module.rst | 2 + Doc/c-api/none.rst | 2 + Doc/c-api/number.rst | 12 +- + Doc/c-api/object.rst | 12 +- + Doc/c-api/sequence.rst | 4 + Doc/c-api/set.rst | 6 - + Doc/c-api/structures.rst | 4 + Doc/c-api/tuple.rst | 2 + Doc/c-api/type.rst | 2 + Doc/c-api/typeobj.rst | 4 + Doc/conf.py | 5 + + Doc/extending/newtypes.rst | 2 + Doc/library/_thread.rst | 2 + Doc/library/binascii.rst | 6 - + Doc/library/cmath.rst | 2 + Doc/library/copy.rst | 2 + Doc/library/copyreg.rst | 4 + Doc/library/dis.rst | 2 + Doc/library/email.compat32-message.rst | 1 + Doc/library/exceptions.rst | 10 +- + Doc/library/fnmatch.rst | 4 + Doc/library/functions.rst | 10 +- + Doc/library/http.client.rst | 2 + Doc/library/imp.rst | 2 + Doc/library/internet.rst | 2 + Doc/library/locale.rst | 4 + Doc/library/marshal.rst | 4 + Doc/library/os.path.rst | 2 + Doc/library/os.rst | 4 + Doc/library/pdb.rst | 4 + Doc/library/posix.rst | 2 + Doc/library/pprint.rst | 4 + Doc/library/pwd.rst | 2 + Doc/library/pyexpat.rst | 2 + Doc/library/runpy.rst | 4 + Doc/library/shelve.rst | 6 - + Doc/library/site.rst | 6 - + Doc/library/socket.rst | 4 + Doc/library/stdtypes.rst | 146 +++++++++++++++---------------- + Doc/library/sys.rst | 2 + Doc/library/traceback.rst | 2 + Doc/library/types.rst | 2 + Doc/reference/compound_stmts.rst | 90 +++++++++---------- + Doc/reference/datamodel.rst | 154 ++++++++++++++++----------------- + Doc/reference/executionmodel.rst | 2 + Doc/reference/expressions.rst | 134 ++++++++++++++-------------- + Doc/reference/simple_stmts.rst | 74 +++++++-------- + Doc/reference/toplevel_components.rst | 10 +- + Doc/tools/extensions/pyspecific.py | 25 +++++ + Doc/tutorial/classes.rst | 2 + Doc/tutorial/controlflow.rst | 2 + Doc/tutorial/inputoutput.rst | 6 - + Doc/tutorial/modules.rst | 4 + Doc/tutorial/stdlib.rst | 2 + 72 files changed, 458 insertions(+), 427 deletions(-) --- a/Doc/c-api/bytearray.rst +++ b/Doc/c-api/bytearray.rst @@ -727,6 +728,16 @@ Pushes a slice object on the stack. *argc* must be 2 or 3. If it is 2, ``slice(TOS1, TOS)`` is pushed; if it is 3, ``slice(TOS2, TOS1, TOS)`` is +--- a/Doc/library/email.compat32-message.rst ++++ b/Doc/library/email.compat32-message.rst +@@ -7,6 +7,7 @@ + :synopsis: The base class representing email messages in a fashion + backward compatible with Python 3.2 + :noindex: ++ :no-index: + + + The :class:`Message` class is very similar to the --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -4,8 +4,8 @@ Built-in Exceptions From e7a6e8ed69b61f5c38394f7b2b41ad4ad721ca57f3e5961618d34dc1aba569ef Mon Sep 17 00:00:00 2001 From: Marco Strigl Date: Thu, 9 Jan 2025 15:37:04 +0000 Subject: [PATCH 19/23] Set link to python310.36903 via maintenance_release request --- CVE-2024-11168-validation-IPv6-addrs.patch | 97 ------ CVE-2024-9287-venv_path_unquoted.patch | 296 ------------------ Python-3.10.15.tar.xz.asc | 16 - ...on-3.10.15.tar.xz => Python-3.10.16.tar.xz | 0 Python-3.10.16.tar.xz.sigstore | 1 + python310.changes | 30 ++ python310.spec | 12 +- 7 files changed, 33 insertions(+), 419 deletions(-) delete mode 100644 CVE-2024-11168-validation-IPv6-addrs.patch delete mode 100644 CVE-2024-9287-venv_path_unquoted.patch delete mode 100644 Python-3.10.15.tar.xz.asc rename Python-3.10.15.tar.xz => Python-3.10.16.tar.xz (100%) create mode 100644 Python-3.10.16.tar.xz.sigstore diff --git a/CVE-2024-11168-validation-IPv6-addrs.patch b/CVE-2024-11168-validation-IPv6-addrs.patch deleted file mode 100644 index 7f1d4fb..0000000 --- a/CVE-2024-11168-validation-IPv6-addrs.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 37bc08c699f48461be5e000b2da9212237a1ca0f Mon Sep 17 00:00:00 2001 -From: JohnJamesUtley -Date: Tue, 25 Apr 2023 16:01:03 -0400 -Subject: [PATCH 1/4] Adds checks to ensure that bracketed hosts found by - urlsplit are of IPv6 or IPvFuture format - ---- - Lib/test/test_urlparse.py | 26 ++++++++++ - Lib/urllib/parse.py | 16 +++++- - Misc/NEWS.d/next/Library/2023-04-26-09-54-25.gh-issue-103848.aDSnpR.rst | 2 - 3 files changed, 43 insertions(+), 1 deletion(-) - create mode 100644 Misc/NEWS.d/next/Library/2023-04-26-09-54-25.gh-issue-103848.aDSnpR.rst - ---- a/Lib/test/test_urlparse.py -+++ b/Lib/test/test_urlparse.py -@@ -1138,6 +1138,32 @@ class UrlParseTestCase(unittest.TestCase - self.assertEqual(p2.scheme, 'tel') - self.assertEqual(p2.path, '+31641044153') - -+ def test_invalid_bracketed_hosts(self): -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[192.0.2.146]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[important.com:8000]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123r.IP]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v12ae]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v.IP]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123.]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path') -+ -+ def test_splitting_bracketed_hosts(self): -+ p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query') -+ self.assertEqual(p1.hostname, 'v6a.ip') -+ self.assertEqual(p1.username, 'user') -+ self.assertEqual(p1.path, '/path') -+ p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query') -+ self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test') -+ self.assertEqual(p2.username, 'user') -+ self.assertEqual(p2.path, '/path') -+ p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query') -+ self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test') -+ self.assertEqual(p3.username, 'user') -+ self.assertEqual(p3.path, '/path') -+ - def test_port_casting_failure_message(self): - message = "Port could not be cast to integer value as 'oracle'" - p1 = urllib.parse.urlparse('http://Server=sde; Service=sde:oracle') ---- a/Lib/urllib/parse.py -+++ b/Lib/urllib/parse.py -@@ -36,6 +36,7 @@ import sys - import types - import collections - import warnings -+import ipaddress - - __all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag", - "urlsplit", "urlunsplit", "urlencode", "parse_qs", -@@ -441,6 +442,17 @@ def _checknetloc(netloc): - raise ValueError("netloc '" + netloc + "' contains invalid " + - "characters under NFKC normalization") - -+# Valid bracketed hosts are defined in -+# https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ -+def _check_bracketed_host(hostname): -+ if hostname.startswith('v'): -+ if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", hostname): -+ raise ValueError(f"IPvFuture address is invalid") -+ else: -+ ip = ipaddress.ip_address(hostname) # Throws Value Error if not IPv6 or IPv4 -+ if isinstance(ip, ipaddress.IPv4Address): -+ raise ValueError(f"An IPv4 address cannot be in brackets") -+ - def urlsplit(url, scheme='', allow_fragments=True): - """Parse a URL into 5 components: - :///?# -@@ -487,12 +499,14 @@ def urlsplit(url, scheme='', allow_fragm - break - else: - scheme, url = url[:i].lower(), url[i+1:] -- - if url[:2] == '//': - netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") -+ if '[' in netloc and ']' in netloc: -+ bracketed_host = netloc.partition('[')[2].partition(']')[0] -+ _check_bracketed_host(bracketed_host) - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2023-04-26-09-54-25.gh-issue-103848.aDSnpR.rst -@@ -0,0 +1,2 @@ -+Add checks to ensure that ``[`` bracketed ``]`` hosts found by -+:func:`urllib.parse.urlsplit` are of IPv6 or IPvFuture format. diff --git a/CVE-2024-9287-venv_path_unquoted.patch b/CVE-2024-9287-venv_path_unquoted.patch deleted file mode 100644 index 93f30e1..0000000 --- a/CVE-2024-9287-venv_path_unquoted.patch +++ /dev/null @@ -1,296 +0,0 @@ -From 21139b45039a72e8346bdc32d498345ef174ba92 Mon Sep 17 00:00:00 2001 -From: Victor Stinner -Date: Fri, 1 Nov 2024 14:11:47 +0100 -Subject: [PATCH] [3.11] gh-124651: Quote template strings in `venv` activation - scripts (GH-124712) (GH-126185) (#126269) - -(cherry picked from commit ae961ae94bf19c8f8c7fbea3d1c25cc55ce8ae97) ---- - Lib/test/test_venv.py | 81 ++++++++++ - Lib/venv/__init__.py | 42 ++++- - Lib/venv/scripts/common/activate | 8 - Lib/venv/scripts/nt/activate.bat | 6 - Lib/venv/scripts/posix/activate.csh | 8 - Lib/venv/scripts/posix/activate.fish | 8 - Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst | 1 - 7 files changed, 134 insertions(+), 20 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst - ---- a/Lib/test/test_venv.py -+++ b/Lib/test/test_venv.py -@@ -15,6 +15,7 @@ import struct - import subprocess - import sys - import tempfile -+import shlex - from test.support import (captured_stdout, captured_stderr, requires_zlib, - skip_if_broken_multiprocessing_synchronize) - from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) -@@ -85,6 +86,10 @@ class BaseTest(unittest.TestCase): - result = f.read() - return result - -+ def assertEndsWith(self, string, tail): -+ if not string.endswith(tail): -+ self.fail(f"String {string!r} does not end with {tail!r}") -+ - class BasicTest(BaseTest): - """Test venv module functionality.""" - -@@ -342,6 +347,82 @@ class BasicTest(BaseTest): - 'import sys; print(sys.executable)']) - self.assertEqual(out.strip(), envpy.encode()) - -+ # gh-124651: test quoted strings -+ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') -+ def test_special_chars_bash(self): -+ """ -+ Test that the template strings are quoted properly (bash) -+ """ -+ rmtree(self.env_dir) -+ bash = shutil.which('bash') -+ if bash is None: -+ self.skipTest('bash required for this test') -+ env_name = '"\';&&$e|\'"' -+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) -+ builder = venv.EnvBuilder(clear=True) -+ builder.create(env_dir) -+ activate = os.path.join(env_dir, self.bindir, 'activate') -+ test_script = os.path.join(self.env_dir, 'test_special_chars.sh') -+ with open(test_script, "w") as f: -+ f.write(f'source {shlex.quote(activate)}\n' -+ 'python -c \'import sys; print(sys.executable)\'\n' -+ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' -+ 'deactivate\n') -+ out, err = check_output([bash, test_script]) -+ lines = out.splitlines() -+ self.assertTrue(env_name.encode() in lines[0]) -+ self.assertEndsWith(lines[1], env_name.encode()) -+ -+ # gh-124651: test quoted strings -+ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') -+ def test_special_chars_csh(self): -+ """ -+ Test that the template strings are quoted properly (csh) -+ """ -+ rmtree(self.env_dir) -+ csh = shutil.which('tcsh') or shutil.which('csh') -+ if csh is None: -+ self.skipTest('csh required for this test') -+ env_name = '"\';&&$e|\'"' -+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) -+ builder = venv.EnvBuilder(clear=True) -+ builder.create(env_dir) -+ activate = os.path.join(env_dir, self.bindir, 'activate.csh') -+ test_script = os.path.join(self.env_dir, 'test_special_chars.csh') -+ with open(test_script, "w") as f: -+ f.write(f'source {shlex.quote(activate)}\n' -+ 'python -c \'import sys; print(sys.executable)\'\n' -+ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' -+ 'deactivate\n') -+ out, err = check_output([csh, test_script]) -+ lines = out.splitlines() -+ self.assertTrue(env_name.encode() in lines[0]) -+ self.assertEndsWith(lines[1], env_name.encode()) -+ -+ # gh-124651: test quoted strings on Windows -+ @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') -+ def test_special_chars_windows(self): -+ """ -+ Test that the template strings are quoted properly on Windows -+ """ -+ rmtree(self.env_dir) -+ env_name = "'&&^$e" -+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) -+ builder = venv.EnvBuilder(clear=True) -+ builder.create(env_dir) -+ activate = os.path.join(env_dir, self.bindir, 'activate.bat') -+ test_batch = os.path.join(self.env_dir, 'test_special_chars.bat') -+ with open(test_batch, "w") as f: -+ f.write('@echo off\n' -+ f'"{activate}" & ' -+ f'{self.exe} -c "import sys; print(sys.executable)" & ' -+ f'{self.exe} -c "import os; print(os.environ[\'VIRTUAL_ENV\'])" & ' -+ 'deactivate') -+ out, err = check_output([test_batch]) -+ lines = out.splitlines() -+ self.assertTrue(env_name.encode() in lines[0]) -+ self.assertEndsWith(lines[1], env_name.encode()) -+ - @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') - def test_unicode_in_batch_file(self): - """ ---- a/Lib/venv/__init__.py -+++ b/Lib/venv/__init__.py -@@ -11,6 +11,7 @@ import subprocess - import sys - import sysconfig - import types -+import shlex - - - CORE_VENV_DEPS = ('pip', 'setuptools') -@@ -364,11 +365,41 @@ class EnvBuilder: - :param context: The information for the environment creation request - being processed. - """ -- text = text.replace('__VENV_DIR__', context.env_dir) -- text = text.replace('__VENV_NAME__', context.env_name) -- text = text.replace('__VENV_PROMPT__', context.prompt) -- text = text.replace('__VENV_BIN_NAME__', context.bin_name) -- text = text.replace('__VENV_PYTHON__', context.env_exe) -+ replacements = { -+ '__VENV_DIR__': context.env_dir, -+ '__VENV_NAME__': context.env_name, -+ '__VENV_PROMPT__': context.prompt, -+ '__VENV_BIN_NAME__': context.bin_name, -+ '__VENV_PYTHON__': context.env_exe, -+ } -+ -+ def quote_ps1(s): -+ """ -+ This should satisfy PowerShell quoting rules [1], unless the quoted -+ string is passed directly to Windows native commands [2]. -+ [1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules -+ [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters -+ """ -+ s = s.replace("'", "''") -+ return f"'{s}'" -+ -+ def quote_bat(s): -+ return s -+ -+ # gh-124651: need to quote the template strings properly -+ quote = shlex.quote -+ script_path = context.script_path -+ if script_path.endswith('.ps1'): -+ quote = quote_ps1 -+ elif script_path.endswith('.bat'): -+ quote = quote_bat -+ else: -+ # fallbacks to POSIX shell compliant quote -+ quote = shlex.quote -+ -+ replacements = {key: quote(s) for key, s in replacements.items()} -+ for key, quoted in replacements.items(): -+ text = text.replace(key, quoted) - return text - - def install_scripts(self, context, path): -@@ -408,6 +439,7 @@ class EnvBuilder: - with open(srcfile, 'rb') as f: - data = f.read() - if not srcfile.endswith(('.exe', '.pdb')): -+ context.script_path = srcfile - try: - data = data.decode('utf-8') - data = self.replace_variables(data, context) ---- a/Lib/venv/scripts/common/activate -+++ b/Lib/venv/scripts/common/activate -@@ -38,11 +38,11 @@ deactivate () { - # unset irrelevant variables - deactivate nondestructive - --VIRTUAL_ENV="__VENV_DIR__" -+VIRTUAL_ENV=__VENV_DIR__ - export VIRTUAL_ENV - - _OLD_VIRTUAL_PATH="$PATH" --PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH" -+PATH="$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH" - export PATH - - # unset PYTHONHOME if set -@@ -55,9 +55,9 @@ fi - - if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then - _OLD_VIRTUAL_PS1="${PS1:-}" -- PS1="__VENV_PROMPT__${PS1:-}" -+ PS1=__VENV_PROMPT__"${PS1:-}" - export PS1 -- VIRTUAL_ENV_PROMPT="__VENV_PROMPT__" -+ VIRTUAL_ENV_PROMPT=__VENV_PROMPT__ - export VIRTUAL_ENV_PROMPT - fi - ---- a/Lib/venv/scripts/nt/activate.bat -+++ b/Lib/venv/scripts/nt/activate.bat -@@ -8,7 +8,7 @@ if defined _OLD_CODEPAGE ( - "%SystemRoot%\System32\chcp.com" 65001 > nul - ) - --set VIRTUAL_ENV=__VENV_DIR__ -+set "VIRTUAL_ENV=__VENV_DIR__" - - if not defined PROMPT set PROMPT=$P$G - -@@ -24,8 +24,8 @@ set PYTHONHOME= - if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% - if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% - --set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH% --set VIRTUAL_ENV_PROMPT=__VENV_PROMPT__ -+set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%" -+set "VIRTUAL_ENV_PROMPT=__VENV_PROMPT__" - - :END - if defined _OLD_CODEPAGE ( ---- a/Lib/venv/scripts/posix/activate.csh -+++ b/Lib/venv/scripts/posix/activate.csh -@@ -8,17 +8,17 @@ alias deactivate 'test $?_OLD_VIRTUAL_PA - # Unset irrelevant variables. - deactivate nondestructive - --setenv VIRTUAL_ENV "__VENV_DIR__" -+setenv VIRTUAL_ENV __VENV_DIR__ - - set _OLD_VIRTUAL_PATH="$PATH" --setenv PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH" -+setenv PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH" - - - set _OLD_VIRTUAL_PROMPT="$prompt" - - if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then -- set prompt = "__VENV_PROMPT__$prompt" -- setenv VIRTUAL_ENV_PROMPT "__VENV_PROMPT__" -+ set prompt = __VENV_PROMPT__"$prompt" -+ setenv VIRTUAL_ENV_PROMPT __VENV_PROMPT__ - endif - - alias pydoc python -m pydoc ---- a/Lib/venv/scripts/posix/activate.fish -+++ b/Lib/venv/scripts/posix/activate.fish -@@ -33,10 +33,10 @@ end - # Unset irrelevant variables. - deactivate nondestructive - --set -gx VIRTUAL_ENV "__VENV_DIR__" -+set -gx VIRTUAL_ENV __VENV_DIR__ - - set -gx _OLD_VIRTUAL_PATH $PATH --set -gx PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__" $PATH -+set -gx PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__ $PATH - - # Unset PYTHONHOME if set. - if set -q PYTHONHOME -@@ -56,7 +56,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - set -l old_status $status - - # Output the venv prompt; color taken from the blue of the Python logo. -- printf "%s%s%s" (set_color 4B8BBE) "__VENV_PROMPT__" (set_color normal) -+ printf "%s%s%s" (set_color 4B8BBE) __VENV_PROMPT__ (set_color normal) - - # Restore the return status of the previous command. - echo "exit $old_status" | . -@@ -65,5 +65,5 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - end - - set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" -- set -gx VIRTUAL_ENV_PROMPT "__VENV_PROMPT__" -+ set -gx VIRTUAL_ENV_PROMPT __VENV_PROMPT__ - end ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst -@@ -0,0 +1 @@ -+Properly quote template strings in :mod:`venv` activation scripts. diff --git a/Python-3.10.15.tar.xz.asc b/Python-3.10.15.tar.xz.asc deleted file mode 100644 index 315072a..0000000 --- a/Python-3.10.15.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEz9yiRbEEPPKl+Xhl/+h0BBaL2EcFAmbboS8ACgkQ/+h0BBaL -2Ecc7BAAmdd+jqs4mNpJg58HgOnLIx3hVBrzn1kyI9AkbxfFGGfm3Gg9Exa/dIph -m1Bt8FogUqOxFnEsFBFTgxh49TCDiUDFzTWYWcrbhtodGFywCmr+0ha6CuEcuuFa -hL0qV7sIJRoVzcdPU6pHh4OcDtdLR0Ws27WiMilrpquw/sWztIiueASZn/kehToD -XM1RTcFtaJeO++cp2tECXRrTU79lzsdpRY/DOyUWWJmLFv0GdrKi4bszKhcYK8x7 -qKleGklFf6AzhGT1A91cRyQ6AEcD3Vnp1Or+agJUwxA0hVuyw6cEmf0+VONqwDMe -M/5bz8xgt6kopfz48mrTJhHg24+6wt6b4kQgwrtUoyucgb+k7ThzwgCj+Wg/Z0Pz -/S+M1hF7I0Ot/PFA3LH5QJADM7nsw5+Rkl68HqQp7s8O9RddPHpCILDIM/AUkUu+ -Xn/1MgPdhhTnA5elyZ2DDDtETUugNu5RILrIRoKonHsZtOQOpOERzUdbzEHCuLv5 -AunaLPWrvxXtEJUKLmyOUfYoI35Gw3/gHYyKTSmo4C1SMYUjke++N7c6vbsvroRG -aUQa/TdAf71zz/r6lHg0vYt+D5FlmFJzB8gCmt6ewKJAO82ls3rr0XjmD1w58sXV -kuwy+53MopEaI1I4D6qIMq/XxNnU2Q63sqKaai8Emx1Yw28Csvw= -=f/SR ------END PGP SIGNATURE----- diff --git a/Python-3.10.15.tar.xz b/Python-3.10.16.tar.xz similarity index 100% rename from Python-3.10.15.tar.xz rename to Python-3.10.16.tar.xz diff --git a/Python-3.10.16.tar.xz.sigstore b/Python-3.10.16.tar.xz.sigstore new file mode 100644 index 0000000..b0fe16f --- /dev/null +++ b/Python-3.10.16.tar.xz.sigstore @@ -0,0 +1 @@ +{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIICzDCCAlKgAwIBAgIUH4wr+RBSLHxmoLeVWQFWtLrOickwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQxMjAzMTgzMzQ0WhcNMjQxMjAzMTg0MzQ0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDu7neWfjmWCoWs4toOwMe5ZS5m/Qod/IAYGdai04Qjx+fJ2JKIML8OXa376wmCRg+1mE5N75M5g55sjeH6AW5KOCAXEwggFtMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVxcoODLOP6ce+ffWUlBZ8dQpAtAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wIgYDVR0RAQH/BBgwFoEUcGFibG9nc2FsQHB5dGhvbi5vcmcwKQYKKwYBBAGDvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgEHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGTjcy6WQAABAMARjBEAiAWKkpOltF6fngTPFPiPrjo6Mnx7DvjTjhy+I9C8a1E8AIgDFHJXkaNO/cRHZCEycDJfdcJrMlDEI+iTAc9GbhJreMwCgYIKoZIzj0EAwMDaAAwZQIwRlcLZ+KTrMXkSOqoeZkRtIMzrMhiRvjoMfcYt7d3zX+t+fDtaywsYIu0WSWNtwLuAjEAk1LM0s1c13zn/l9B9I8YMDJxCiUY2ed5AK523TIy75cmH+IYO7XODOD86YSkytvS"}, "tlogEntries": [{"logIndex": "153123526", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1733250825", "inclusionPromise": {"signedEntryTimestamp": "MEUCIBb+3OGEfIJgweBH+795X/kmenmW5L6lzTaW5mU9DN++AiEAni2MKnETeAsGhc8u0W/Y5AuhYKd14TdRvoUw/bWhzjs="}, "inclusionProof": {"logIndex": "31219264", "rootHash": "EEDRbQekcBvIu2A3f37wAtzpj3Tu+lPYLi9AUyS4FBY=", "treeSize": "31219265", "hashes": ["jy1RZw1zMvGOhV5pYK21mUnw/3hfyXoogDNhzfMT8uA=", "t7CZ1TCAQBidKeIL1f3M7Y3VwBYB2DQeG1Sp8X8Mepc=", "LIvgEWJ5UP1rLp6WPJ2TzjrHAa5MpLpXOdj/yoZvLcM=", "XjayhjKU3shP7q7lhmhKDv3Vpi4gJgAPCu0KlEzc9Qo=", "go1dmexQYS5etu69upRRX7IFvuA0rIcT9aYjMstmPIU=", "AYwr74Bm2w383UnS7DdbZUUAhusq28JoxKpWrQ7OvGQ=", "u+yWmGIR6sAH32wiSy22mz1Yf+jfPdBTjFbyRISuTZw=", "3eFC7Gp4fWecybDOAw9uUTrM1xB7YRYRAGsfYkiQbV8=", "1uKk2qjOliHMiTk906jrchP8mXWsRG8apaU1sa0lfh0=", "oOecFfN3YqDOkbijS/ej1WF5Da/Gt/AZNhbwE9uoOE8=", "4lUF0YOu9XkIDXKXA0wMSzd6VeDY3TZAgmoOeWmS2+Y=", "gf+9m552B3PnkWnO0o4KdVvjcT3WVHLrCbf1DoVYKFw="], "checkpoint": {"envelope": "rekor.sigstore.dev - 1193050959916656506\n31219265\nEEDRbQekcBvIu2A3f37wAtzpj3Tu+lPYLi9AUyS4FBY=\n\n\u2014 rekor.sigstore.dev wNI9ajBFAiAnUUia2onArhzOpQclqAm9wBFu32/qoYagpd3PkWeELgIhAPUWvc2y6UP8V2I/ABP9HtsQi208X3nuSI8xunycnmZl\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiZmIyNDk2MDk5OTAyMjA0OTFhMWI5Mjg1MGEwNzEzNWVkMDgzMWU0MTczOGNmNjgxZDYzY2YwMWIyYThmYmQxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQyYW4xbTUvSWl4clZsYVlpcUMxQmpuamc3eG55MTBxVWw5WHhIM2hJSkNRSWhBS1l4YzRNeTNYTndscEdEU25QTTBjU1gxM3ljMGNnN3BTVVZCS2RrOHZMaiIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjZSRU5EUVd4TFowRjNTVUpCWjBsVlNEUjNjaXRTUWxOTVNIaHRiMHhsVmxkUlJsZDBUSEpQYVdOcmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFJlRTFxUVhwTlZHZDZUWHBSTUZkb1kwNU5hbEY0VFdwQmVrMVVaekJOZWxFd1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZFZFRkdVpWZG1hbTFYUTI5WGN6UjBiMDkzVFdVMVdsTTFiUzlSYjJRdlNVRlpSMlFLWVdrd05GRnFlQ3RtU2pKS1MwbE5URGhQV0dFek56WjNiVU5TWnlzeGJVVTFUamMxVFRWbk5UVnphbVZJTmtGWE5VdFBRMEZZUlhkblowWjBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXZUdOdkNrOUVURTlRTm1ObEsyWm1WMVZzUWxvNFpGRndRWFJCZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsbldVUldVakJTUVZGSUwwSkNaM2RHYjBWVlkwZEdhV0pIT1c1ak1rWnpVVWhDTldSSGFIWmlhVFYyWTIxamQwdFJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FWRlJZbUZJVWpCalNFMDJUSGs1YUZreVRuWmtWelV3WTNrMWJtSXlPVzVpUjFWMVdUSTVkRTFEYzBkRGFYTkhRVkZSUW1jM09IZEJVV2RGQ2toUmQySmhTRkl3WTBoTk5reDVPV2haTWs1MlpGYzFNR041Tlc1aU1qbHVZa2RWZFZreU9YUk5TVWRLUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJYzBVS1pWRkNNMEZJVlVFelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIxUnFZM2syVjFGQlFRcENRVTFCVW1wQ1JVRnBRVmRMYTNCUGJIUkdObVp1WjFSUVJsQnBVSEpxYnpaTmJuZzNSSFpxVkdwb2VTdEpPVU00WVRGRk9FRkpaMFJHU0VwWWEyRk9Dazh2WTFKSVdrTkZlV05FU21aa1kwcHlUV3hFUlVrcmFWUkJZemxIWW1oS2NtVk5kME5uV1VsTGIxcEplbW93UlVGM1RVUmhRVUYzV2xGSmQxSnNZMHdLV2l0TFZISk5XR3RUVDNGdlpWcHJVblJKVFhweVRXaHBVblpxYjAxbVkxbDBOMlF6ZWxncmRDdG1SSFJoZVhkeldVbDFNRmRUVjA1MGQweDFRV3BGUVFwck1VeE5NSE14WXpFemVtNHZiRGxDT1VrNFdVMUVTbmhEYVZWWk1tVmtOVUZMTlRJelZFbDVOelZqYlVnclNWbFBOMWhQUkU5RU9EWlpVMnQ1ZEhaVENpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19"}]}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "v7JJYJmQIgSRobkoUKBxNe0IMeQXOM9oHWPPAbKo+9E="}, "signature": "MEYCIQD2an1m5/IixrVlaYiqC1Bjnjg7xny10qUl9XxH3hIJCQIhAKYxc4My3XNwlpGDSnPM0cSX13yc0cg7pSUVBKdk8vLj"}} diff --git a/python310.changes b/python310.changes index 41ecc0a..1571a67 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,33 @@ +------------------------------------------------------------------- +Wed Dec 4 21:23:20 UTC 2024 - Matej Cepl + +- Update to 3.10.16: + - Tests + - gh-125041: Re-enable skipped tests for zlib on the + s390x architecture: only skip checks of the compressed + bytes, which can be different between zlib’s software + implementation and the hardware-accelerated implementation. + - gh-109396: Fix test_socket.test_hmac_sha1() in FIPS + mode. Use a longer key: FIPS mode requires at least of at + least 112 bits. The previous key was only 32 bits. Patch by + Victor Stinner. + - Security + - gh-126623: Upgrade libexpat to 2.6.4 + - gh-122792: Changed IPv4-mapped ipaddress.IPv6Address to + consistently use the mapped IPv4 address value for deciding + properties. Properties which have their behavior fixed are + is_multicast, is_reserved, is_link_local, is_global, and + is_unspecified (bsc#1233307, CVE-2024-11168). + - Library + - gh-124651: Properly quote template strings in venv + activation scripts (bsc#1232241, CVE-2024-9287). + - gh-103848: Add checks to ensure that [ bracketed ] hosts + found by urllib.parse.urlsplit() are of IPv6 or IPvFuture + format. +- Removed upstreamed patches: + - CVE-2024-9287-venv_path_unquoted.patch + - CVE-2024-11168-validation-IPv6-addrs.patch + ------------------------------------------------------------------- Thu Nov 14 07:06:20 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index 4ec7a9b..a84a6f7 100644 --- a/python310.spec +++ b/python310.spec @@ -108,13 +108,13 @@ Obsoletes: python39%{?1:-%{1}} # _md5.cpython-38m-x86_64-linux-gnu.so %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so Name: %{python_pkg_name}%{psuffix} -Version: 3.10.15 +Version: 3.10.16 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 URL: https://www.python.org/ Source0: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz -Source1: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.asc +Source1: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.sigstore Source2: baselibs.conf Source3: README.SUSE Source7: macros.python3 @@ -204,12 +204,6 @@ Patch27: gh120226-fix-sendfile-test-kernel-610.patch # PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com # status_iterator method moved between the Sphinx versions Patch28: sphinx-802.patch -# PATCH-FIX-UPSTREAM CVE-2024-9287-venv_path_unquoted.patch gh#python/cpython#124651 mcepl@suse.com -# venv should properly quote path names provided when creating a venv -Patch29: CVE-2024-9287-venv_path_unquoted.patch -# PATCH-FIX-UPSTREAM CVE-2024-11168-validation-IPv6-addrs.patch bsc#1233307 mcepl@suse.com -# improve validation of IPv6 and IPvFuture addresses in urlparse and urlsplit -Patch30: CVE-2024-11168-validation-IPv6-addrs.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -493,8 +487,6 @@ other applications. %patch -p1 -P 24 %patch -p1 -P 27 %patch -p1 -P 28 -%patch -p1 -P 29 -%patch -p1 -P 30 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac From f4287b83049384879f34d3cfd59e401044aa30978db52d45cbb00c779619d023 Mon Sep 17 00:00:00 2001 From: Marcus Rueckert Date: Mon, 10 Feb 2025 13:55:43 +0000 Subject: [PATCH 20/23] Set link to python310.37367 via maintenance_release request --- CVE-2025-0938-sq-brackets-domain-names.patch | 127 +++++++++++++++++++ python310.changes | 7 + python310.spec | 6 +- 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 CVE-2025-0938-sq-brackets-domain-names.patch diff --git a/CVE-2025-0938-sq-brackets-domain-names.patch b/CVE-2025-0938-sq-brackets-domain-names.patch new file mode 100644 index 0000000..fc07748 --- /dev/null +++ b/CVE-2025-0938-sq-brackets-domain-names.patch @@ -0,0 +1,127 @@ +From d91e2c740890837edafaee24d68112b776cda9c5 Mon Sep 17 00:00:00 2001 +From: Seth Michael Larson +Date: Fri, 31 Jan 2025 11:41:34 -0600 +Subject: [PATCH] gh-105704: Disallow square brackets (`[` and `]`) in domain + names for parsed URLs (GH-129418) + +* gh-105704: Disallow square brackets ( and ) in domain names for parsed URLs + +* Use Sphinx references + +Co-authored-by: Peter Bierma + +* Add mismatched bracket test cases, fix news format + +* Add more test coverage for ports + +--------- + +(cherry picked from commit d89a5f6a6e65511a5f6e0618c4c30a7aa5aba56a) + +Co-authored-by: Seth Michael Larson +Co-authored-by: Peter Bierma +--- + Lib/test/test_urlparse.py | 37 +++++++++- + Lib/urllib/parse.py | 20 ++++- + Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst | 4 + + 3 files changed, 58 insertions(+), 3 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst + +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -1149,16 +1149,51 @@ class UrlParseTestCase(unittest.TestCase + self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a1') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a1') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:1a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:1a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@prefix.[v6a.ip]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@[v6a.ip].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip[') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip[suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[suffix') + + def test_splitting_bracketed_hosts(self): +- p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query') ++ p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]:1234/path?query') + self.assertEqual(p1.hostname, 'v6a.ip') + self.assertEqual(p1.username, 'user') + self.assertEqual(p1.path, '/path') ++ self.assertEqual(p1.port, 1234) + p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query') + self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test') + self.assertEqual(p2.username, 'user') + self.assertEqual(p2.path, '/path') ++ self.assertIs(p2.port, None) + p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query') + self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test') + self.assertEqual(p3.username, 'user') +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -442,6 +442,23 @@ def _checknetloc(netloc): + raise ValueError("netloc '" + netloc + "' contains invalid " + + "characters under NFKC normalization") + ++def _check_bracketed_netloc(netloc): ++ # Note that this function must mirror the splitting ++ # done in NetlocResultMixins._hostinfo(). ++ hostname_and_port = netloc.rpartition('@')[2] ++ before_bracket, have_open_br, bracketed = hostname_and_port.partition('[') ++ if have_open_br: ++ # No data is allowed before a bracket. ++ if before_bracket: ++ raise ValueError("Invalid IPv6 URL") ++ hostname, _, port = bracketed.partition(']') ++ # No data is allowed after the bracket but before the port delimiter. ++ if port and not port.startswith(":"): ++ raise ValueError("Invalid IPv6 URL") ++ else: ++ hostname, _, port = hostname_and_port.partition(':') ++ _check_bracketed_host(hostname) ++ + # Valid bracketed hosts are defined in + # https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ + def _check_bracketed_host(hostname): +@@ -505,8 +522,7 @@ def urlsplit(url, scheme='', allow_fragm + (']' in netloc and '[' not in netloc)): + raise ValueError("Invalid IPv6 URL") + if '[' in netloc and ']' in netloc: +- bracketed_host = netloc.partition('[')[2].partition(']')[0] +- _check_bracketed_host(bracketed_host) ++ _check_bracketed_netloc(netloc) + if allow_fragments and '#' in url: + url, fragment = url.split('#', 1) + if '?' in url: +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst +@@ -0,0 +1,4 @@ ++When using :func:`urllib.parse.urlsplit` and :func:`urllib.parse.urlparse` host ++parsing would not reject domain names containing square brackets (``[`` and ++``]``). Square brackets are only valid for IPv6 and IPvFuture hosts according to ++`RFC 3986 Section 3.2.2 `__. diff --git a/python310.changes b/python310.changes index 1571a67..3a37b16 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Feb 4 14:43:13 UTC 2025 - Matej Cepl + +- Add CVE-2025-0938-sq-brackets-domain-names.patch which + disallows square brackets ([ and ]) in domain names for parsed + URLs (bsc#1236705, CVE-2025-0938, gh#python/cpython#105704) + ------------------------------------------------------------------- Wed Dec 4 21:23:20 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index a84a6f7..25b79e2 100644 --- a/python310.spec +++ b/python310.spec @@ -1,7 +1,7 @@ # # spec file for package python310 # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -204,6 +204,9 @@ Patch27: gh120226-fix-sendfile-test-kernel-610.patch # PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com # status_iterator method moved between the Sphinx versions Patch28: sphinx-802.patch +# PATCH-FIX-UPSTREAM CVE-2025-0938-sq-brackets-domain-names.patch bsc#1236705 mcepl@suse.com +# functions `urllib.parse.urlsplit` and `urlparse` accept domain names including square brackets +Patch29: CVE-2025-0938-sq-brackets-domain-names.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -487,6 +490,7 @@ other applications. %patch -p1 -P 24 %patch -p1 -P 27 %patch -p1 -P 28 +%patch -p1 -P 29 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac From d5f0b9c278da45ee5ae51b1ba8028a61e5d9a21446b99ec59b3dfaab907aa2de Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Fri, 20 Jun 2025 12:40:02 +0000 Subject: [PATCH 21/23] Set link to python310.38050 via maintenance_release request --- CVE-2025-0938-sq-brackets-domain-names.patch | 127 ------------------ Python-3.10.16.tar.xz.sigstore | 1 - ...on-3.10.16.tar.xz => Python-3.10.18.tar.xz | 0 Python-3.10.18.tar.xz.sigstore | 1 + fix_configure_rst.patch | 2 +- python-3.3.0b1-test-posix_fadvise.patch | 15 --- python310.changes | 106 ++++++++++++++- python310.spec | 35 +---- sphinx-72.patch | 4 +- sphinx-802.patch | 2 +- 10 files changed, 116 insertions(+), 177 deletions(-) delete mode 100644 CVE-2025-0938-sq-brackets-domain-names.patch delete mode 100644 Python-3.10.16.tar.xz.sigstore rename Python-3.10.16.tar.xz => Python-3.10.18.tar.xz (100%) create mode 100644 Python-3.10.18.tar.xz.sigstore delete mode 100644 python-3.3.0b1-test-posix_fadvise.patch diff --git a/CVE-2025-0938-sq-brackets-domain-names.patch b/CVE-2025-0938-sq-brackets-domain-names.patch deleted file mode 100644 index fc07748..0000000 --- a/CVE-2025-0938-sq-brackets-domain-names.patch +++ /dev/null @@ -1,127 +0,0 @@ -From d91e2c740890837edafaee24d68112b776cda9c5 Mon Sep 17 00:00:00 2001 -From: Seth Michael Larson -Date: Fri, 31 Jan 2025 11:41:34 -0600 -Subject: [PATCH] gh-105704: Disallow square brackets (`[` and `]`) in domain - names for parsed URLs (GH-129418) - -* gh-105704: Disallow square brackets ( and ) in domain names for parsed URLs - -* Use Sphinx references - -Co-authored-by: Peter Bierma - -* Add mismatched bracket test cases, fix news format - -* Add more test coverage for ports - ---------- - -(cherry picked from commit d89a5f6a6e65511a5f6e0618c4c30a7aa5aba56a) - -Co-authored-by: Seth Michael Larson -Co-authored-by: Peter Bierma ---- - Lib/test/test_urlparse.py | 37 +++++++++- - Lib/urllib/parse.py | 20 ++++- - Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst | 4 + - 3 files changed, 58 insertions(+), 3 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst - ---- a/Lib/test/test_urlparse.py -+++ b/Lib/test/test_urlparse.py -@@ -1149,16 +1149,51 @@ class UrlParseTestCase(unittest.TestCase - self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query') - self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query') - self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a1') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a1') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:1a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:1a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@prefix.[v6a.ip]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@[v6a.ip].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip[') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip[suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[suffix') - - def test_splitting_bracketed_hosts(self): -- p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query') -+ p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]:1234/path?query') - self.assertEqual(p1.hostname, 'v6a.ip') - self.assertEqual(p1.username, 'user') - self.assertEqual(p1.path, '/path') -+ self.assertEqual(p1.port, 1234) - p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query') - self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test') - self.assertEqual(p2.username, 'user') - self.assertEqual(p2.path, '/path') -+ self.assertIs(p2.port, None) - p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query') - self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test') - self.assertEqual(p3.username, 'user') ---- a/Lib/urllib/parse.py -+++ b/Lib/urllib/parse.py -@@ -442,6 +442,23 @@ def _checknetloc(netloc): - raise ValueError("netloc '" + netloc + "' contains invalid " + - "characters under NFKC normalization") - -+def _check_bracketed_netloc(netloc): -+ # Note that this function must mirror the splitting -+ # done in NetlocResultMixins._hostinfo(). -+ hostname_and_port = netloc.rpartition('@')[2] -+ before_bracket, have_open_br, bracketed = hostname_and_port.partition('[') -+ if have_open_br: -+ # No data is allowed before a bracket. -+ if before_bracket: -+ raise ValueError("Invalid IPv6 URL") -+ hostname, _, port = bracketed.partition(']') -+ # No data is allowed after the bracket but before the port delimiter. -+ if port and not port.startswith(":"): -+ raise ValueError("Invalid IPv6 URL") -+ else: -+ hostname, _, port = hostname_and_port.partition(':') -+ _check_bracketed_host(hostname) -+ - # Valid bracketed hosts are defined in - # https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ - def _check_bracketed_host(hostname): -@@ -505,8 +522,7 @@ def urlsplit(url, scheme='', allow_fragm - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if '[' in netloc and ']' in netloc: -- bracketed_host = netloc.partition('[')[2].partition(']')[0] -- _check_bracketed_host(bracketed_host) -+ _check_bracketed_netloc(netloc) - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst -@@ -0,0 +1,4 @@ -+When using :func:`urllib.parse.urlsplit` and :func:`urllib.parse.urlparse` host -+parsing would not reject domain names containing square brackets (``[`` and -+``]``). Square brackets are only valid for IPv6 and IPvFuture hosts according to -+`RFC 3986 Section 3.2.2 `__. diff --git a/Python-3.10.16.tar.xz.sigstore b/Python-3.10.16.tar.xz.sigstore deleted file mode 100644 index b0fe16f..0000000 --- a/Python-3.10.16.tar.xz.sigstore +++ /dev/null @@ -1 +0,0 @@ -{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIICzDCCAlKgAwIBAgIUH4wr+RBSLHxmoLeVWQFWtLrOickwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQxMjAzMTgzMzQ0WhcNMjQxMjAzMTg0MzQ0WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDu7neWfjmWCoWs4toOwMe5ZS5m/Qod/IAYGdai04Qjx+fJ2JKIML8OXa376wmCRg+1mE5N75M5g55sjeH6AW5KOCAXEwggFtMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUVxcoODLOP6ce+ffWUlBZ8dQpAtAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wIgYDVR0RAQH/BBgwFoEUcGFibG9nc2FsQHB5dGhvbi5vcmcwKQYKKwYBBAGDvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgEHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGTjcy6WQAABAMARjBEAiAWKkpOltF6fngTPFPiPrjo6Mnx7DvjTjhy+I9C8a1E8AIgDFHJXkaNO/cRHZCEycDJfdcJrMlDEI+iTAc9GbhJreMwCgYIKoZIzj0EAwMDaAAwZQIwRlcLZ+KTrMXkSOqoeZkRtIMzrMhiRvjoMfcYt7d3zX+t+fDtaywsYIu0WSWNtwLuAjEAk1LM0s1c13zn/l9B9I8YMDJxCiUY2ed5AK523TIy75cmH+IYO7XODOD86YSkytvS"}, "tlogEntries": [{"logIndex": "153123526", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1733250825", "inclusionPromise": {"signedEntryTimestamp": "MEUCIBb+3OGEfIJgweBH+795X/kmenmW5L6lzTaW5mU9DN++AiEAni2MKnETeAsGhc8u0W/Y5AuhYKd14TdRvoUw/bWhzjs="}, "inclusionProof": {"logIndex": "31219264", "rootHash": "EEDRbQekcBvIu2A3f37wAtzpj3Tu+lPYLi9AUyS4FBY=", "treeSize": "31219265", "hashes": ["jy1RZw1zMvGOhV5pYK21mUnw/3hfyXoogDNhzfMT8uA=", "t7CZ1TCAQBidKeIL1f3M7Y3VwBYB2DQeG1Sp8X8Mepc=", "LIvgEWJ5UP1rLp6WPJ2TzjrHAa5MpLpXOdj/yoZvLcM=", "XjayhjKU3shP7q7lhmhKDv3Vpi4gJgAPCu0KlEzc9Qo=", "go1dmexQYS5etu69upRRX7IFvuA0rIcT9aYjMstmPIU=", "AYwr74Bm2w383UnS7DdbZUUAhusq28JoxKpWrQ7OvGQ=", "u+yWmGIR6sAH32wiSy22mz1Yf+jfPdBTjFbyRISuTZw=", "3eFC7Gp4fWecybDOAw9uUTrM1xB7YRYRAGsfYkiQbV8=", "1uKk2qjOliHMiTk906jrchP8mXWsRG8apaU1sa0lfh0=", "oOecFfN3YqDOkbijS/ej1WF5Da/Gt/AZNhbwE9uoOE8=", "4lUF0YOu9XkIDXKXA0wMSzd6VeDY3TZAgmoOeWmS2+Y=", "gf+9m552B3PnkWnO0o4KdVvjcT3WVHLrCbf1DoVYKFw="], "checkpoint": {"envelope": "rekor.sigstore.dev - 1193050959916656506\n31219265\nEEDRbQekcBvIu2A3f37wAtzpj3Tu+lPYLi9AUyS4FBY=\n\n\u2014 rekor.sigstore.dev wNI9ajBFAiAnUUia2onArhzOpQclqAm9wBFu32/qoYagpd3PkWeELgIhAPUWvc2y6UP8V2I/ABP9HtsQi208X3nuSI8xunycnmZl\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiZmIyNDk2MDk5OTAyMjA0OTFhMWI5Mjg1MGEwNzEzNWVkMDgzMWU0MTczOGNmNjgxZDYzY2YwMWIyYThmYmQxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQyYW4xbTUvSWl4clZsYVlpcUMxQmpuamc3eG55MTBxVWw5WHhIM2hJSkNRSWhBS1l4YzRNeTNYTndscEdEU25QTTBjU1gxM3ljMGNnN3BTVVZCS2RrOHZMaiIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjZSRU5EUVd4TFowRjNTVUpCWjBsVlNEUjNjaXRTUWxOTVNIaHRiMHhsVmxkUlJsZDBUSEpQYVdOcmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFJlRTFxUVhwTlZHZDZUWHBSTUZkb1kwNU5hbEY0VFdwQmVrMVVaekJOZWxFd1YycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZFZFRkdVpWZG1hbTFYUTI5WGN6UjBiMDkzVFdVMVdsTTFiUzlSYjJRdlNVRlpSMlFLWVdrd05GRnFlQ3RtU2pKS1MwbE5URGhQV0dFek56WjNiVU5TWnlzeGJVVTFUamMxVFRWbk5UVnphbVZJTmtGWE5VdFBRMEZZUlhkblowWjBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZXZUdOdkNrOUVURTlRTm1ObEsyWm1WMVZzUWxvNFpGRndRWFJCZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsbldVUldVakJTUVZGSUwwSkNaM2RHYjBWVlkwZEdhV0pIT1c1ak1rWnpVVWhDTldSSGFIWmlhVFYyWTIxamQwdFJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FWRlJZbUZJVWpCalNFMDJUSGs1YUZreVRuWmtWelV3WTNrMWJtSXlPVzVpUjFWMVdUSTVkRTFEYzBkRGFYTkhRVkZSUW1jM09IZEJVV2RGQ2toUmQySmhTRkl3WTBoTk5reDVPV2haTWs1MlpGYzFNR041Tlc1aU1qbHVZa2RWZFZreU9YUk5TVWRLUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJYzBVS1pWRkNNMEZJVlVFelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIxUnFZM2syVjFGQlFRcENRVTFCVW1wQ1JVRnBRVmRMYTNCUGJIUkdObVp1WjFSUVJsQnBVSEpxYnpaTmJuZzNSSFpxVkdwb2VTdEpPVU00WVRGRk9FRkpaMFJHU0VwWWEyRk9Dazh2WTFKSVdrTkZlV05FU21aa1kwcHlUV3hFUlVrcmFWUkJZemxIWW1oS2NtVk5kME5uV1VsTGIxcEplbW93UlVGM1RVUmhRVUYzV2xGSmQxSnNZMHdLV2l0TFZISk5XR3RUVDNGdlpWcHJVblJKVFhweVRXaHBVblpxYjAxbVkxbDBOMlF6ZWxncmRDdG1SSFJoZVhkeldVbDFNRmRUVjA1MGQweDFRV3BGUVFwck1VeE5NSE14WXpFemVtNHZiRGxDT1VrNFdVMUVTbmhEYVZWWk1tVmtOVUZMTlRJelZFbDVOelZqYlVnclNWbFBOMWhQUkU5RU9EWlpVMnQ1ZEhaVENpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19"}]}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "v7JJYJmQIgSRobkoUKBxNe0IMeQXOM9oHWPPAbKo+9E="}, "signature": "MEYCIQD2an1m5/IixrVlaYiqC1Bjnjg7xny10qUl9XxH3hIJCQIhAKYxc4My3XNwlpGDSnPM0cSX13yc0cg7pSUVBKdk8vLj"}} diff --git a/Python-3.10.16.tar.xz b/Python-3.10.18.tar.xz similarity index 100% rename from Python-3.10.16.tar.xz rename to Python-3.10.18.tar.xz diff --git a/Python-3.10.18.tar.xz.sigstore b/Python-3.10.18.tar.xz.sigstore new file mode 100644 index 0000000..367da25 --- /dev/null +++ b/Python-3.10.18.tar.xz.sigstore @@ -0,0 +1 @@ +{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIICzDCCAlOgAwIBAgIUPPj8WFgcPzKI0f+pcaxc3TPA/yswCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUwNjAzMTg1ODI4WhcNMjUwNjAzMTkwODI4WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4NqgXmbx5LR/ICb4FcCE0psZ0nEPhnhmk36DOselqJIUA54Dj0SKcFUP3wbmqi9h9bo7ZqbQ5HTZR0ditz16KKOCAXIwggFuMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU91EDPL5bqZeofhdNBEBL98Ga+MEwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wIgYDVR0RAQH/BBgwFoEUcGFibG9nc2FsQHB5dGhvbi5vcmcwKQYKKwYBBAGDvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgEHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGXNyjEogAABAMARzBFAiBenFTetr0d9XKM2FxuhUPv7YJVxt4F5reAl9D4IOX2JwIhAOULSuk4H79udCf1i4QEIPCrs3oEV3u+VpJ9GTC2Y+/mMAoGCCqGSM49BAMDA2cAMGQCMGGrDPAeA+zuXcHOmXGsqPX274/sjglPqkuXX8wI1JwDUoT5mN7ZZpAJX8XAQQAixwIwfk+a9VmWcY0I1UuAu+NuRRkNQCofemUeEuzhWyK1Yn5G+DcuXVf7lNlwC6awM0mb"}, "tlogEntries": [{"logIndex": "228919874", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1748977108", "inclusionPromise": {"signedEntryTimestamp": "MEUCIBlZWCgOoVzS+qS/YuAKZFDbO/cyOLxdRdDRFQVrjbfzAiEAvs5r5K/zZEJJE5bh/OFQPTex7Hj9OzJ8SQtvX2bATVk="}, "inclusionProof": {"logIndex": "107015612", "rootHash": "SCbCVVGttK0oajRjYxDAulUQEOL1nRy9KG6JbVMfe88=", "treeSize": "107015615", "hashes": ["1jQZ0U0iztYR47T7VqVQL/XmQ1kOJ9VGJAlEV7S12Rk=", "nzFzeKP8TFHjIx2Mf5E5RHDnftKz14VXcXGOv6TjP2U=", "rYdthRwegf6R59Jn79y56HZlg/HgWGq2ThwABpgzIx8=", "itoQl5XnazIM1MaE0xAHfTyJuXWLosPRHBX6LOyQZqw=", "m4vF5qDZ/VpszDAF6BpkLLL9mJUrMqTnDGBzbP1+mAA=", "aPbPdtUCj7gO3JjjsuOf+HO0RD3cth0ZCf7GBkev2jA=", "F1AHv3JuUWYZTcWLZFEo2qDYsdVdytSXRmZu4tASPAk=", "jhwwkSRTGiVvY/O6FZ9c4ASOhW4Uktv0K324Xmy4V/k=", "m/WZEVH9CTs0KJcGZIdK4CVc1WENSbb9gjFrdzj5kYI=", "MRAzh2spHQbvIBIISBBvo0zc0n73qn6TIJ5ur8P4K/8=", "tuZDuieL5jtYIFu4Miyh8eBdvmmkGD1LcMTrLs7j/ZE=", "kbUClUnkU0UJZhuQHiwkFFDXdat+8DImNUFMe+bn0Q4=", "WdQbyoFfuhZe2IciO+mhgtPi9ev0pSFpkBr7XCWylqQ=", "uEJFtwcGQJMd9kjQhkXb7gl2WD3WMElCc15uDFvFGxs=", "VdOKzpQhJlpXgijzXANf/hNlje1G/N1kUuVnKNskkso=", "mta5fH/gFwxJ/0fT8yGpn3sFCY0G1RY555Iflm0LInM=", "7v8qPHNDLerpduaMx06eb/MwgoQwczTn/cYGKX/9wZ4="], "checkpoint": {"envelope": "rekor.sigstore.dev - 1193050959916656506\n107015615\nSCbCVVGttK0oajRjYxDAulUQEOL1nRy9KG6JbVMfe88=\n\n\u2014 rekor.sigstore.dev wNI9ajBEAiAC5UJXNKStXDw/L3DlSxscdhpQvVI3Ann4US9sFgT0mAIgJw8zaZc5WMKv1tIRBEUSw1lTjeEO69ypVTwS3MGF7MQ=\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJhZTY2NWJjNjc4YWJkOWFiNmE2ZTE1NzNkMjQ4MTYyNWE1MzcxOWJjNTE3ZTlhNjM0ZWQyYjlmZWZhZTM4MTdmIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJQ3RudW1SZHlqbUVZaHV3MlpNTWdBZFpBeXYvcjVnSjhHbnFUWTBlTS9yNUFpQklQM2pGQWN6TXFhY0N5RE9kMkFKU0VyNmEwSTQxcnRwUHJCRUJMc1ArU0E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjZSRU5EUVd4UFowRjNTVUpCWjBsVlVGQnFPRmRHWjJOUWVrdEpNR1lyY0dOaGVHTXpWRkJCTDNsemQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFZkMDVxUVhwTlZHY3hUMFJKTkZkb1kwNU5hbFYzVG1wQmVrMVVhM2RQUkVrMFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVUwVG5GbldHMWllRFZNVWk5SlEySTBSbU5EUlRCd2Mxb3dia1ZRYUc1b2JXc3pOa1FLVDNObGJIRktTVlZCTlRSRWFqQlRTMk5HVlZBemQySnRjV2s1YURsaWJ6ZGFjV0pSTlVoVVdsSXdaR2wwZWpFMlMwdFBRMEZZU1hkblowWjFUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlU1TVVWRUNsQk1OV0p4V21WdlptaGtUa0pGUWt3NU9FZGhLMDFGZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsbldVUldVakJTUVZGSUwwSkNaM2RHYjBWVlkwZEdhV0pIT1c1ak1rWnpVVWhDTldSSGFIWmlhVFYyWTIxamQwdFJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FWRlJZbUZJVWpCalNFMDJUSGs1YUZreVRuWmtWelV3WTNrMWJtSXlPVzVpUjFWMVdUSTVkRTFEYzBkRGFYTkhRVkZSUW1jM09IZEJVV2RGQ2toUmQySmhTRkl3WTBoTk5reDVPV2haTWs1MlpGYzFNR041Tlc1aU1qbHVZa2RWZFZreU9YUk5TVWRMUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJZDBVS1pXZENORUZJV1VFelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIxaE9lV3BGYjJkQlFRcENRVTFCVW5wQ1JrRnBRbVZ1UmxSbGRISXdaRGxZUzAweVJuaDFhRlZRZGpkWlNsWjRkRFJHTlhKbFFXdzVSRFJKVDFneVNuZEphRUZQVlV4VGRXczBDa2czT1hWa1EyWXhhVFJSUlVsUVEzSnpNMjlGVmpOMUsxWndTamxIVkVNeVdTc3ZiVTFCYjBkRFEzRkhVMDAwT1VKQlRVUkJNbU5CVFVkUlEwMUhSM0lLUkZCQlpVRXJlblZZWTBoUGJWaEhjM0ZRV0RJM05DOXphbWRzVUhGcmRWaFlPSGRKTVVwM1JGVnZWRFZ0VGpkYVduQkJTbGc0V0VGUlVVRnBlSGRKZHdwbWF5dGhPVlp0VjJOWk1Fa3hWWFZCZFN0T2RWSlNhMDVSUTI5bVpXMVZaVVYxZW1oWGVVc3hXVzQxUnl0RVkzVllWbVkzYkU1c2QwTTJZWGROTUcxaUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19"}], "timestampVerificationData": {}}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "rmZbxnir2atqbhVz0kgWJaU3GbxRfppjTtK5/vrjgX8="}, "signature": "MEQCICtnumRdyjmEYhuw2ZMMgAdZAyv/r5gJ8GnqTY0eM/r5AiBIP3jFAczMqacCyDOd2AJSEr6a0I41rtpPrBEBLsP+SA=="}} diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index 46067d1..7c52e72 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -29,7 +29,7 @@ Create a Python.framework rather than a traditional Unix install. Optional --- a/Misc/NEWS +++ b/Misc/NEWS -@@ -3810,7 +3810,7 @@ C API +@@ -3942,7 +3942,7 @@ C API ----- - bpo-43795: The list in :ref:`stable-abi-list` now shows the public name diff --git a/python-3.3.0b1-test-posix_fadvise.patch b/python-3.3.0b1-test-posix_fadvise.patch deleted file mode 100644 index 763441e..0000000 --- a/python-3.3.0b1-test-posix_fadvise.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- - Lib/test/test_posix.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/Lib/test/test_posix.py -+++ b/Lib/test/test_posix.py -@@ -425,7 +425,7 @@ class PosixTester(unittest.TestCase): - def test_posix_fadvise(self): - fd = os.open(os_helper.TESTFN, os.O_RDONLY) - try: -- posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED) -+ posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_RANDOM) - finally: - os.close(fd) - diff --git a/python310.changes b/python310.changes index 3a37b16..8747d27 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,107 @@ +------------------------------------------------------------------- +Mon Jun 9 16:53:24 UTC 2025 - Matej Cepl + +- Update to 3.10.18: + - Security + - gh-135034: Fixes multiple issues that allowed tarfile + extraction filters (filter="data" and filter="tar") to be + bypassed using crafted symlinks and hard links. + Addresses CVE-2024-12718 (bsc#1244056), CVE-2025-4138 + (bsc#1244059), CVE-2025-4330 (bsc#1244060), and + CVE-2025-4517 (bsc#1244032). + - gh-133767: Fix use-after-free in the “unicode-escape” + decoder with a non-“strict” error handler (CVE-2025-4516, + bsc#1243273). + - gh-128840: Short-circuit the processing of long IPv6 + addresses early in ipaddress to prevent excessive memory + consumption and a minor denial-of-service. + - Library + - gh-128840: Fix parsing long IPv6 addresses with embedded + IPv4 address. + - gh-134062: ipaddress: fix collisions in __hash__() for + IPv4Network and IPv6Network objects. + - gh-123409: Fix ipaddress.IPv6Address.reverse_pointer output + according to RFC 3596, §2.5. Patch by Bénédikt Tran. + - bpo-43633: Improve the textual representation of + IPv4-mapped IPv6 addresses (RFC 4291 Sections 2.2, 2.5.5.2) + in ipaddress. Patch by Oleksandr Pavliuk. +- Remove upstreamed patches: + - gh-126572-test_ssl-no-stop-ThreadedEchoServer-OSError.patch + - CVE-2025-4516-DecodeError-handler.patch + +------------------------------------------------------------------- +Thu May 22 13:01:17 UTC 2025 - Matej Cepl + +- Add CVE-2025-4516-DecodeError-handler.patch fixing + CVE-2025-4516 (bsc#1243273) blocking DecodeError handling + vulnerability, which could lead to DoS. + +------------------------------------------------------------------- +Sat May 17 10:02:27 UTC 2025 - Matej Cepl + +- Use extended %autopatch. + +------------------------------------------------------------------- +Sat May 10 11:38:22 UTC 2025 - Matej Cepl + +- Remove python-3.3.0b1-test-posix_fadvise.patch (not needed + since kernel 3.6-rc1) + +------------------------------------------------------------------- +Fri Apr 11 08:12:14 UTC 2025 - Matej Cepl + +- Update to 3.10.17: + - gh-131809: Update bundled libexpat to 2.7.1 + - gh-131261: Upgrade to libexpat 2.7.0 + - gh-105704: When using urllib.parse.urlsplit() and + urllib.parse.urlparse() host parsing would not reject domain + names containing square brackets ([ and ]). Square brackets + are only valid for IPv6 and IPvFuture hosts according to RFC + 3986 Section 3.2.2 (bsc#1236705, CVE-2025-0938, + gh#python/cpython#105704). + - gh-121284: Fix bug in the folding of rfc2047 encoded-words + when flattening an email message using a modern email + policy. Previously when an encoded-word was too long for + a line, it would be decoded, split across lines, and + re-encoded. But commas and other special characters in the + original text could be left unencoded and unquoted. This + could theoretically be used to spoof header lines using a + carefully constructed encoded-word if the resulting rendered + email was transmitted or re-parsed. + - gh-80222: Fix bug in the folding of quoted strings + when flattening an email message using a modern email + policy. Previously when a quoted string was folded so that + it spanned more than one line, the surrounding quotes and + internal escapes would be omitted. This could theoretically + be used to spoof header lines using a carefully constructed + quoted string if the resulting rendered email was transmitted + or re-parsed. + - gh-119511: Fix a potential denial of service in the imaplib + module. When connecting to a malicious server, it could + cause an arbitrary amount of memory to be allocated. On many + systems this is harmless as unused virtual memory is only + a mapping, but if this hit a virtual address size limit + it could lead to a MemoryError or other process crash. On + unusual systems or builds where all allocated memory is + touched and backed by actual ram or storage it could’ve + consumed resources doing so until similarly crashing. + - gh-127257: In ssl, system call failures that OpenSSL reports + using ERR_LIB_SYS are now raised as OSError. + - gh-121277: Writers of CPython’s documentation can now use + next as the version for the versionchanged, versionadded, + deprecated directives. +- Add gh-126572-test_ssl-no-stop-ThreadedEchoServer-OSError.patch + which makes test_ssl not to stop ThreadedEchoServer on OSError, + which makes test_ssl pass with OpenSSL 3.5 (bsc#1241067, + gh#python/cpython!126572) +- Remote upstreamed patch: + - CVE-2025-0938-sq-brackets-domain-names.patch + +------------------------------------------------------------------- +Mon Mar 10 15:44:31 UTC 2025 - Bernhard Wiedemann + +- Skip PGO with %want_reproducible_builds (bsc#1239210) + ------------------------------------------------------------------- Tue Feb 4 14:43:13 UTC 2025 - Matej Cepl @@ -288,7 +392,7 @@ Fri Feb 23 01:06:42 UTC 2024 - Matej Cepl Tue Feb 20 22:14:02 UTC 2024 - Matej Cepl - Remove double definition of /usr/bin/idle%%{version} in - %%files. + %%files. ------------------------------------------------------------------- Thu Feb 15 10:29:07 UTC 2024 - Daniel Garcia diff --git a/python310.spec b/python310.spec index 25b79e2..4b97c71 100644 --- a/python310.spec +++ b/python310.spec @@ -36,7 +36,7 @@ %bcond_without general %endif -%if 0%{?do_profiling} +%if 0%{?do_profiling} && !0%{?want_reproducible_builds} %bcond_without profileopt %else %bcond_with profileopt @@ -108,7 +108,7 @@ Obsoletes: python39%{?1:-%{1}} # _md5.cpython-38m-x86_64-linux-gnu.so %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so Name: %{python_pkg_name}%{psuffix} -Version: 3.10.16 +Version: 3.10.18 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -152,8 +152,6 @@ Patch02: distutils-reproducible-compile.patch Patch03: python-3.3.0b1-localpath.patch # replace DATE, TIME and COMPILER by fixed definitions to aid reproducible builds Patch04: python-3.3.0b1-fix_date_time_compiler.patch -# POSIX_FADV_WILLNEED throws EINVAL. Use a different constant in test -Patch05: python-3.3.0b1-test-posix_fadvise.patch # Raise timeout value for test_subprocess Patch06: subprocess-raise-timeout.patch # PATCH-FEATURE-UPSTREAM bpo-31046_ensurepip_honours_prefix.patch bpo#31046 mcepl@suse.com @@ -204,9 +202,6 @@ Patch27: gh120226-fix-sendfile-test-kernel-610.patch # PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com # status_iterator method moved between the Sphinx versions Patch28: sphinx-802.patch -# PATCH-FIX-UPSTREAM CVE-2025-0938-sq-brackets-domain-names.patch bsc#1236705 mcepl@suse.com -# functions `urllib.parse.urlsplit` and `urlparse` accept domain names including square brackets -Patch29: CVE-2025-0938-sq-brackets-domain-names.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -464,33 +459,15 @@ other applications. %prep %setup -q -n %{tarname} -%patch -p1 -P 01 -%patch -p1 -P 02 -%patch -p1 -P 03 -%patch -p1 -P 04 -%patch -p1 -P 05 -%patch -p1 -P 06 -%patch -p1 -P 07 - +%autopatch -p1 -M 07 %if 0%{?sle_version} && 0%{?sle_version} <= 150300 -%patch -P 11 -p1 +%patch -p1 -P 11 %endif - -%patch -p1 -P 15 -%patch -p1 -P 16 -%patch -p1 -P 17 -%patch -p1 -P 18 -%patch -p1 -P 19 - +%autopatch -p1 -m 12 -M 20 %if ! 0%{?sle_version} || 0%{?sle_version} >= 160000 %patch -p1 -P 21 %endif - -%patch -p1 -P 22 -%patch -p1 -P 24 -%patch -p1 -P 27 -%patch -p1 -P 28 -%patch -p1 -P 29 +%autopatch -p1 -m 22 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac diff --git a/sphinx-72.patch b/sphinx-72.patch index 39de7e1..8cd8457 100644 --- a/sphinx-72.patch +++ b/sphinx-72.patch @@ -2944,7 +2944,7 @@ string argument to :func:`eval` must have the following form: --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py -@@ -623,6 +623,30 @@ def process_audit_events(app, doctree, f +@@ -644,6 +644,30 @@ def process_audit_events(app, doctree, f node.replace_self(table) @@ -2975,7 +2975,7 @@ def setup(app): app.add_role('issue', issue_role) app.add_role('gh', gh_issue_role) -@@ -645,6 +669,7 @@ def setup(app): +@@ -670,6 +694,7 @@ def setup(app): app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod) app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod) app.add_directive('miscnews', MiscNews) diff --git a/sphinx-802.patch b/sphinx-802.patch index c4600b7..faac604 100644 --- a/sphinx-802.patch +++ b/sphinx-802.patch @@ -4,7 +4,7 @@ --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py -@@ -27,7 +27,13 @@ try: +@@ -28,7 +28,13 @@ try: except ImportError: from sphinx.environment import NoUri from sphinx.locale import _ as sphinx_gettext From a11a97d317bbf3d92a2348b84a8357a61d996a4180957bd6c98dfccc5e09abad Mon Sep 17 00:00:00 2001 From: Ruediger Oertel Date: Fri, 1 Aug 2025 15:14:24 +0000 Subject: [PATCH 22/23] Set link to python310.39543 via maintenance_release request --- CVE-2025-6069-quad-complex-HTMLParser.patch | 238 ++++++++++++++++++++ python310.changes | 14 +- python310.spec | 3 + 3 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 CVE-2025-6069-quad-complex-HTMLParser.patch diff --git a/CVE-2025-6069-quad-complex-HTMLParser.patch b/CVE-2025-6069-quad-complex-HTMLParser.patch new file mode 100644 index 0000000..4cfe513 --- /dev/null +++ b/CVE-2025-6069-quad-complex-HTMLParser.patch @@ -0,0 +1,238 @@ +From ec28625203b5dd4b4447dc4bb512898294cd6d0c Mon Sep 17 00:00:00 2001 +From: Serhiy Storchaka +Date: Fri, 13 Jun 2025 19:57:48 +0300 +Subject: [PATCH] [3.10] gh-135462: Fix quadratic complexity in processing + special input in HTMLParser (GH-135464) + +End-of-file errors are now handled according to the HTML5 specs -- +comments and declarations are automatically closed, tags are ignored. +(cherry picked from commit 6eb6c5dbfb528bd07d77b60fd71fd05d81d45c41) + +Co-authored-by: Serhiy Storchaka +--- + Lib/html/parser.py | 41 +++- + Lib/test/test_htmlparser.py | 95 ++++++++-- + Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst | 4 + 3 files changed, 117 insertions(+), 23 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst + +Index: Python-3.10.18/Lib/html/parser.py +=================================================================== +--- Python-3.10.18.orig/Lib/html/parser.py 2025-07-02 18:00:32.520419724 +0200 ++++ Python-3.10.18/Lib/html/parser.py 2025-07-02 18:00:42.616018075 +0200 +@@ -25,6 +25,7 @@ + charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]') + + starttagopen = re.compile('<[a-zA-Z]') ++endtagopen = re.compile('') + commentclose = re.compile(r'--\s*>') + # Note: +@@ -176,7 +177,7 @@ + k = self.parse_pi(i) + elif startswith("', i + 1) +- if k < 0: +- k = rawdata.find('<', i + 1) +- if k < 0: +- k = i + 1 ++ if starttagopen.match(rawdata, i): # < + letter ++ pass ++ elif startswith("'), +- ('comment', '/img'), +- ('endtag', 'html<')]) ++ ('data', '\n')]) + + def test_starttag_junk_chars(self): ++ self._run_check("<", [('data', '<')]) ++ self._run_check("<>", [('data', '<>')]) ++ self._run_check("< >", [('data', '< >')]) ++ self._run_check("< ", [('data', '< ')]) + self._run_check("", []) ++ self._run_check("<$>", [('data', '<$>')]) + self._run_check("", [('comment', '$')]) + self._run_check("", [('endtag', 'a')]) ++ self._run_check("", [('starttag', 'a", [('endtag', 'a'", [('data', "'", []) ++ self._run_check("", [('starttag', 'a$b', [])]) + self._run_check("", [('startendtag', 'a$b', [])]) + self._run_check("", [('starttag', 'a$b', [])]) + self._run_check("", [('startendtag', 'a$b', [])]) ++ self._run_check("", [('endtag', 'a$b')]) + + def test_slashes_in_starttag(self): + self._run_check('', [('startendtag', 'a', [('foo', 'var')])]) +@@ -537,13 +545,56 @@ + for html, expected in data: + self._run_check(html, expected) + +- def test_broken_comments(self): +- html = ('' ++ def test_eof_in_comments(self): ++ data = [ ++ ('', [('comment', '-!>')]), ++ ('' + '' + '' + '') + expected = [ ++ ('comment', 'ELEMENT br EMPTY'), + ('comment', ' not really a comment '), + ('comment', ' not a comment either --'), + ('comment', ' -- close enough --'), +@@ -598,6 +649,26 @@ + ('endtag', 'a'), ('data', ' bar & baz')] + ) + ++ @support.requires_resource('cpu') ++ def test_eof_no_quadratic_complexity(self): ++ # Each of these examples used to take about an hour. ++ # Now they take a fraction of a second. ++ def check(source): ++ parser = html.parser.HTMLParser() ++ parser.feed(source) ++ parser.close() ++ n = 120_000 ++ check(" + +- Add CVE-2025-6069-quad-complex-HTMLParser.patch to avoid worst + case quadratic complexity when processing certain crafted + malformed inputs with HTMLParser (CVE-2025-6069, bsc#1244705). + ------------------------------------------------------------------- Mon Jun 9 16:53:24 UTC 2025 - Matej Cepl - Update to 3.10.18: - Security - gh-135034: Fixes multiple issues that allowed tarfile - extraction filters (filter="data" and filter="tar") to be - bypassed using crafted symlinks and hard links. + extraction filters (filter="data" and filter="tar") + to be bypassed using crafted symlinks and hard links. Addresses CVE-2024-12718 (bsc#1244056), CVE-2025-4138 (bsc#1244059), CVE-2025-4330 (bsc#1244060), and - CVE-2025-4517 (bsc#1244032). + CVE-2025-4517 (bsc#1244032). Also addresses CVE-2025-4435 + (gh#135034, bsc#1244061). - gh-133767: Fix use-after-free in the “unicode-escape” decoder with a non-“strict” error handler (CVE-2025-4516, bsc#1243273). diff --git a/python310.spec b/python310.spec index 4b97c71..bcfbdf7 100644 --- a/python310.spec +++ b/python310.spec @@ -202,6 +202,9 @@ Patch27: gh120226-fix-sendfile-test-kernel-610.patch # PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com # status_iterator method moved between the Sphinx versions Patch28: sphinx-802.patch +# PATCH-FIX-UPSTREAM CVE-2025-6069-quad-complex-HTMLParser.patch bsc#1244705 mcepl@suse.com +# avoid quadratic complexity when processing malformed inputs with HTMLParser +Patch29: CVE-2025-6069-quad-complex-HTMLParser.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes From 8ecfc047b32bcfc0e7d519a20daac05c25de855a13a73b75f8f6322bd6f9f523 Mon Sep 17 00:00:00 2001 From: Marcus Rueckert Date: Thu, 21 Aug 2025 11:47:40 +0000 Subject: [PATCH 23/23] Set link to python310.40011 via maintenance_release request --- CVE-2025-8194-tarfile-no-neg-offsets.patch | 212 +++++++++++++++++++++ python310.changes | 7 + python310.spec | 3 + 3 files changed, 222 insertions(+) create mode 100644 CVE-2025-8194-tarfile-no-neg-offsets.patch diff --git a/CVE-2025-8194-tarfile-no-neg-offsets.patch b/CVE-2025-8194-tarfile-no-neg-offsets.patch new file mode 100644 index 0000000..fea1b69 --- /dev/null +++ b/CVE-2025-8194-tarfile-no-neg-offsets.patch @@ -0,0 +1,212 @@ +From 898ac93eeeabfaffbc008dc3201e17cb39c1a957 Mon Sep 17 00:00:00 2001 +From: Alexander Urieles +Date: Mon, 28 Jul 2025 17:37:26 +0200 +Subject: [PATCH] [3.10] gh-130577: tarfile now validates archives to ensure + member offsets are non-negative (GH-137027) (cherry picked from commit + 7040aa54f14676938970e10c5f74ea93cd56aa38) + +Co-authored-by: Alexander Urieles +Co-authored-by: Gregory P. Smith +--- + Lib/tarfile.py | 3 + Lib/test/test_tarfile.py | 156 ++++++++++ + Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3 + 3 files changed, 162 insertions(+) + create mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst + +Index: Python-3.10.18/Lib/tarfile.py +=================================================================== +--- Python-3.10.18.orig/Lib/tarfile.py 2025-08-02 17:52:24.521273582 +0200 ++++ Python-3.10.18/Lib/tarfile.py 2025-08-02 17:52:28.444044748 +0200 +@@ -1612,6 +1612,9 @@ + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ ++ # Only non-negative offsets are allowed ++ if count < 0: ++ raise InvalidHeaderError("invalid offset") + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 +Index: Python-3.10.18/Lib/test/test_tarfile.py +=================================================================== +--- Python-3.10.18.orig/Lib/test/test_tarfile.py 2025-08-02 17:52:25.849390293 +0200 ++++ Python-3.10.18/Lib/test/test_tarfile.py 2025-08-02 17:52:39.623989523 +0200 +@@ -49,6 +49,7 @@ + xzname = os.path.join(TEMPDIR, "testtar.tar.xz") + tmpname = os.path.join(TEMPDIR, "tmp.tar") + dotlessname = os.path.join(TEMPDIR, "testtar") ++SPACE = b" " + + sha256_regtype = ( + "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce" +@@ -4273,6 +4274,161 @@ + self.expect_exception(TypeError) # errorlevel is not int + + ++class OffsetValidationTests(unittest.TestCase): ++ tarname = tmpname ++ invalid_posix_header = ( ++ # name: 100 bytes ++ tarfile.NUL * tarfile.LENGTH_NAME ++ # mode, space, null terminator: 8 bytes ++ + b"000755" + SPACE + tarfile.NUL ++ # uid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # gid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # size, space: 12 bytes ++ + b"\xff" * 11 + SPACE ++ # mtime, space: 12 bytes ++ + tarfile.NUL * 11 + SPACE ++ # chksum: 8 bytes ++ + b"0011407" + tarfile.NUL ++ # type: 1 byte ++ + tarfile.REGTYPE ++ # linkname: 100 bytes ++ + tarfile.NUL * tarfile.LENGTH_LINK ++ # magic: 6 bytes, version: 2 bytes ++ + tarfile.POSIX_MAGIC ++ # uname: 32 bytes ++ + tarfile.NUL * 32 ++ # gname: 32 bytes ++ + tarfile.NUL * 32 ++ # devmajor, space, null terminator: 8 bytes ++ + tarfile.NUL * 6 + SPACE + tarfile.NUL ++ # devminor, space, null terminator: 8 bytes ++ + tarfile.NUL * 6 + SPACE + tarfile.NUL ++ # prefix: 155 bytes ++ + tarfile.NUL * tarfile.LENGTH_PREFIX ++ # padding: 12 bytes ++ + tarfile.NUL * 12 ++ ) ++ invalid_gnu_header = ( ++ # name: 100 bytes ++ tarfile.NUL * tarfile.LENGTH_NAME ++ # mode, null terminator: 8 bytes ++ + b"0000755" + tarfile.NUL ++ # uid, null terminator: 8 bytes ++ + b"0000001" + tarfile.NUL ++ # gid, space, null terminator: 8 bytes ++ + b"0000001" + tarfile.NUL ++ # size, space: 12 bytes ++ + b"\xff" * 11 + SPACE ++ # mtime, space: 12 bytes ++ + tarfile.NUL * 11 + SPACE ++ # chksum: 8 bytes ++ + b"0011327" + tarfile.NUL ++ # type: 1 byte ++ + tarfile.REGTYPE ++ # linkname: 100 bytes ++ + tarfile.NUL * tarfile.LENGTH_LINK ++ # magic: 8 bytes ++ + tarfile.GNU_MAGIC ++ # uname: 32 bytes ++ + tarfile.NUL * 32 ++ # gname: 32 bytes ++ + tarfile.NUL * 32 ++ # devmajor, null terminator: 8 bytes ++ + tarfile.NUL * 8 ++ # devminor, null terminator: 8 bytes ++ + tarfile.NUL * 8 ++ # padding: 167 bytes ++ + tarfile.NUL * 167 ++ ) ++ invalid_v7_header = ( ++ # name: 100 bytes ++ tarfile.NUL * tarfile.LENGTH_NAME ++ # mode, space, null terminator: 8 bytes ++ + b"000755" + SPACE + tarfile.NUL ++ # uid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # gid, space, null terminator: 8 bytes ++ + b"000001" + SPACE + tarfile.NUL ++ # size, space: 12 bytes ++ + b"\xff" * 11 + SPACE ++ # mtime, space: 12 bytes ++ + tarfile.NUL * 11 + SPACE ++ # chksum: 8 bytes ++ + b"0010070" + tarfile.NUL ++ # type: 1 byte ++ + tarfile.REGTYPE ++ # linkname: 100 bytes ++ + tarfile.NUL * tarfile.LENGTH_LINK ++ # padding: 255 bytes ++ + tarfile.NUL * 255 ++ ) ++ valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT) ++ data_block = b"\xff" * tarfile.BLOCKSIZE ++ ++ def _write_buffer(self, buffer): ++ with open(self.tarname, "wb") as f: ++ f.write(buffer) ++ ++ def _get_members(self, ignore_zeros=None): ++ with open(self.tarname, "rb") as f: ++ with tarfile.open( ++ mode="r", fileobj=f, ignore_zeros=ignore_zeros ++ ) as tar: ++ return tar.getmembers() ++ ++ def _assert_raises_read_error_exception(self): ++ with self.assertRaisesRegex( ++ tarfile.ReadError, "file could not be opened successfully" ++ ): ++ self._get_members() ++ ++ def test_invalid_offset_header_validations(self): ++ for tar_format, invalid_header in ( ++ ("posix", self.invalid_posix_header), ++ ("gnu", self.invalid_gnu_header), ++ ("v7", self.invalid_v7_header), ++ ): ++ with self.subTest(format=tar_format): ++ self._write_buffer(invalid_header) ++ self._assert_raises_read_error_exception() ++ ++ def test_early_stop_at_invalid_offset_header(self): ++ buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header ++ self._write_buffer(buffer) ++ members = self._get_members() ++ self.assertEqual(len(members), 1) ++ self.assertEqual(members[0].name, "filename") ++ self.assertEqual(members[0].offset, 0) ++ ++ def test_ignore_invalid_archive(self): ++ # 3 invalid headers with their respective data ++ buffer = (self.invalid_gnu_header + self.data_block) * 3 ++ self._write_buffer(buffer) ++ members = self._get_members(ignore_zeros=True) ++ self.assertEqual(len(members), 0) ++ ++ def test_ignore_invalid_offset_headers(self): ++ for first_block, second_block, expected_offset in ( ++ ( ++ (self.valid_gnu_header), ++ (self.invalid_gnu_header + self.data_block), ++ 0, ++ ), ++ ( ++ (self.invalid_gnu_header + self.data_block), ++ (self.valid_gnu_header), ++ 1024, ++ ), ++ ): ++ self._write_buffer(first_block + second_block) ++ members = self._get_members(ignore_zeros=True) ++ self.assertEqual(len(members), 1) ++ self.assertEqual(members[0].name, "filename") ++ self.assertEqual(members[0].offset, expected_offset) ++ ++ + def setUpModule(): + os_helper.unlink(TEMPDIR) + os.makedirs(TEMPDIR) +Index: Python-3.10.18/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ Python-3.10.18/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst 2025-08-02 17:52:28.446021271 +0200 +@@ -0,0 +1,3 @@ ++:mod:`tarfile` now validates archives to ensure member offsets are ++non-negative. (Contributed by Alexander Enrique Urieles Nieto in ++:gh:`130577`.) diff --git a/python310.changes b/python310.changes index fae8d74..c787f3e 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Aug 1 20:09:24 UTC 2025 - Matej Cepl + +- Add CVE-2025-8194-tarfile-no-neg-offsets.patch which now + validates archives to ensure member offsets are non-negative + (gh#python/cpython#130577, CVE-2025-8194, bsc#1247249). + ------------------------------------------------------------------- Wed Jul 2 14:47:20 UTC 2025 - Matej Cepl diff --git a/python310.spec b/python310.spec index bcfbdf7..01626db 100644 --- a/python310.spec +++ b/python310.spec @@ -205,6 +205,9 @@ Patch28: sphinx-802.patch # PATCH-FIX-UPSTREAM CVE-2025-6069-quad-complex-HTMLParser.patch bsc#1244705 mcepl@suse.com # avoid quadratic complexity when processing malformed inputs with HTMLParser Patch29: CVE-2025-6069-quad-complex-HTMLParser.patch +# PATCH-FIX-UPSTREAM CVE-2025-8194-tarfile-no-neg-offsets.patch bsc#1247249 mcepl@suse.com +# tarfile now validates archives to ensure member offsets are non-negative +Patch30: CVE-2025-8194-tarfile-no-neg-offsets.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes