Compare commits
No commits in common. "c9-beta" and "c10s" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
||||
SOURCES/Python-3.9.21.tar.xz
|
@ -1 +0,0 @@
|
||||
d968a953f19c6fc3bf54b5ded5c06852197ebddc SOURCES/Python-3.9.21.tar.xz
|
@ -1,30 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: David Malcolm <dmalcolm@redhat.com>
|
||||
Date: Wed, 13 Jan 2010 21:25:18 +0000
|
||||
Subject: [PATCH] 00001: Fixup distutils/unixccompiler.py to remove standard
|
||||
library path from rpath Was Patch0 in ivazquez' python3000 specfile
|
||||
|
||||
---
|
||||
Lib/distutils/unixccompiler.py | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py
|
||||
index d00c48981e..0283a28c19 100644
|
||||
--- a/Lib/distutils/unixccompiler.py
|
||||
+++ b/Lib/distutils/unixccompiler.py
|
||||
@@ -82,6 +82,15 @@ class UnixCCompiler(CCompiler):
|
||||
if sys.platform == "cygwin":
|
||||
exe_extension = ".exe"
|
||||
|
||||
+ def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
|
||||
+ """Remove standard library path from rpath"""
|
||||
+ libraries, library_dirs, runtime_library_dirs = super()._fix_lib_args(
|
||||
+ libraries, library_dirs, runtime_library_dirs)
|
||||
+ libdir = sysconfig.get_config_var('LIBDIR')
|
||||
+ if runtime_library_dirs and (libdir in runtime_library_dirs):
|
||||
+ runtime_library_dirs.remove(libdir)
|
||||
+ return libraries, library_dirs, runtime_library_dirs
|
||||
+
|
||||
def preprocess(self, source, output_file=None, macros=None,
|
||||
include_dirs=None, extra_preargs=None, extra_postargs=None):
|
||||
fixed_args = self._fix_compile_args(None, macros, include_dirs)
|
@ -1,75 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: David Malcolm <dmalcolm@redhat.com>
|
||||
Date: Mon, 18 Jan 2010 17:59:07 +0000
|
||||
Subject: [PATCH] 00111: Don't try to build a libpythonMAJOR.MINOR.a
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Downstream only: not appropriate for upstream.
|
||||
|
||||
See https://bugzilla.redhat.com/show_bug.cgi?id=556092
|
||||
|
||||
Co-authored-by: David Malcolm <dmalcolm@redhat.com>
|
||||
Co-authored-by: Bohuslav Kabrda <bkabrda@redhat.com>
|
||||
Co-authored-by: Matej Stuchlik <mstuchli@redhat.com>
|
||||
Co-authored-by: Robert Kuska <rkuska@redhat.com>
|
||||
Co-authored-by: Charalampos Stratakis <cstratak@redhat.com>
|
||||
Co-authored-by: Miro Hrončok <miro@hroncok.cz>
|
||||
---
|
||||
Makefile.pre.in | 21 ++-------------------
|
||||
1 file changed, 2 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/Makefile.pre.in b/Makefile.pre.in
|
||||
index 11230fa563..dc763e7197 100644
|
||||
--- a/Makefile.pre.in
|
||||
+++ b/Makefile.pre.in
|
||||
@@ -588,7 +588,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c
|
||||
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py --make --srcdir $(srcdir)
|
||||
|
||||
# Build the interpreter
|
||||
-$(BUILDPYTHON): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) $(EXPORTSYMS)
|
||||
+$(BUILDPYTHON): Programs/python.o $(LDLIBRARY) $(PY3LIBRARY) $(EXPORTSYMS)
|
||||
$(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS)
|
||||
|
||||
platform: $(BUILDPYTHON) pybuilddir.txt
|
||||
@@ -636,12 +636,6 @@ sharedmods: $(BUILDPYTHON) pybuilddir.txt Modules/_math.o
|
||||
_TCLTK_INCLUDES='$(TCLTK_INCLUDES)' _TCLTK_LIBS='$(TCLTK_LIBS)' \
|
||||
$(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build
|
||||
|
||||
-
|
||||
-# Build static library
|
||||
-$(LIBRARY): $(LIBRARY_OBJS)
|
||||
- -rm -f $@
|
||||
- $(AR) $(ARFLAGS) $@ $(LIBRARY_OBJS)
|
||||
-
|
||||
libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS)
|
||||
if test $(INSTSONAME) != $(LDLIBRARY); then \
|
||||
$(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \
|
||||
@@ -723,7 +717,7 @@ Makefile Modules/config.c: Makefile.pre \
|
||||
@echo "The Makefile was updated, you may need to re-run make."
|
||||
|
||||
|
||||
-Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) $(EXPORTSYMS)
|
||||
+Programs/_testembed: Programs/_testembed.o $(LDLIBRARY) $(PY3LIBRARY) $(EXPORTSYMS)
|
||||
$(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS)
|
||||
|
||||
############################################################################
|
||||
@@ -1651,17 +1645,6 @@ libainstall: @DEF_MAKE_RULE@ python-config
|
||||
else true; \
|
||||
fi; \
|
||||
done
|
||||
- @if test -d $(LIBRARY); then :; else \
|
||||
- if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \
|
||||
- if test "$(SHLIB_SUFFIX)" = .dll; then \
|
||||
- $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \
|
||||
- else \
|
||||
- $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \
|
||||
- fi; \
|
||||
- else \
|
||||
- echo Skip install of $(LIBRARY) - use make frameworkinstall; \
|
||||
- fi; \
|
||||
- fi
|
||||
$(INSTALL_DATA) Modules/config.c $(DESTDIR)$(LIBPL)/config.c
|
||||
$(INSTALL_DATA) Programs/python.o $(DESTDIR)$(LIBPL)/python.o
|
||||
$(INSTALL_DATA) $(srcdir)/Modules/config.c.in $(DESTDIR)$(LIBPL)/config.c.in
|
@ -1,78 +0,0 @@
|
||||
From 12b919396f3fd24521b5ded51e18beb55973f0ff Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
|
||||
Date: Wed, 15 Aug 2018 15:36:29 +0200
|
||||
Subject: [PATCH] 00189: Instead of bundled wheels, use our RPM packaged wheels
|
||||
|
||||
We keep them in /usr/share/python-wheels
|
||||
|
||||
Downstream only: upstream bundles
|
||||
We might eventually pursuit upstream support, but it's low prio
|
||||
---
|
||||
Lib/ensurepip/__init__.py | 37 ++++++++++++++++++++++++++-----------
|
||||
1 file changed, 26 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
|
||||
index e510cc7..5bd16a6 100644
|
||||
--- a/Lib/ensurepip/__init__.py
|
||||
+++ b/Lib/ensurepip/__init__.py
|
||||
@@ -1,3 +1,5 @@
|
||||
+import distutils.version
|
||||
+import glob
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
@@ -6,13 +8,29 @@ import tempfile
|
||||
import subprocess
|
||||
from importlib import resources
|
||||
|
||||
-from . import _bundled
|
||||
-
|
||||
|
||||
|
||||
__all__ = ["version", "bootstrap"]
|
||||
-_SETUPTOOLS_VERSION = "58.1.0"
|
||||
-_PIP_VERSION = "23.0.1"
|
||||
+
|
||||
+_WHEEL_DIR = "/usr/share/python-wheels/"
|
||||
+
|
||||
+_wheels = {}
|
||||
+
|
||||
+def _get_most_recent_wheel_version(pkg):
|
||||
+ prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg))
|
||||
+ _wheels[pkg] = {}
|
||||
+ for suffix in "-py2.py3-none-any.whl", "-py3-none-any.whl":
|
||||
+ pattern = "{}*{}".format(prefix, suffix)
|
||||
+ for path in glob.glob(pattern):
|
||||
+ version_str = path[len(prefix):-len(suffix)]
|
||||
+ _wheels[pkg][version_str] = os.path.basename(path)
|
||||
+ return str(max(_wheels[pkg], key=distutils.version.LooseVersion))
|
||||
+
|
||||
+
|
||||
+_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools")
|
||||
+
|
||||
+_PIP_VERSION = _get_most_recent_wheel_version("pip")
|
||||
+
|
||||
_PROJECTS = [
|
||||
("setuptools", _SETUPTOOLS_VERSION, "py3"),
|
||||
("pip", _PIP_VERSION, "py3"),
|
||||
@@ -101,13 +119,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
|
||||
# additional paths that need added to sys.path
|
||||
additional_paths = []
|
||||
for project, version, py_tag in _PROJECTS:
|
||||
- wheel_name = "{}-{}-{}-none-any.whl".format(project, version, py_tag)
|
||||
- whl = resources.read_binary(
|
||||
- _bundled,
|
||||
- wheel_name,
|
||||
- )
|
||||
- with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
|
||||
- fp.write(whl)
|
||||
+ wheel_name = _wheels[project][version]
|
||||
+ with open(os.path.join(_WHEEL_DIR, wheel_name), "rb") as sfp:
|
||||
+ with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
|
||||
+ fp.write(sfp.read())
|
||||
|
||||
additional_paths.append(os.path.join(tmpdir, wheel_name))
|
||||
|
||||
--
|
||||
2.35.3
|
||||
|
@ -1,110 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Cyprian <m.cyprian@gmail.com>
|
||||
Date: Mon, 26 Jun 2017 16:32:56 +0200
|
||||
Subject: [PATCH] 00251: Change user install location
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
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
|
||||
Downstream only: Reworked in Fedora 36+/Python 3.10+ to follow https://bugs.python.org/issue43976
|
||||
|
||||
pypa/distutils integration: https://github.com/pypa/distutils/pull/70
|
||||
|
||||
Also set sysconfig._PIP_USE_SYSCONFIG = False, to force pip-upgraded-pip
|
||||
to respect this patched distutils install command.
|
||||
See https://bugzilla.redhat.com/show_bug.cgi?id=2014513
|
||||
|
||||
Co-authored-by: Miro Hrončok <miro@hroncok.cz>
|
||||
---
|
||||
Lib/distutils/command/install.py | 9 +++++++--
|
||||
Lib/site.py | 9 ++++++++-
|
||||
Lib/sysconfig.py | 17 +++++++++++++++++
|
||||
3 files changed, 32 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py
|
||||
index aaa300efa9..18f01f10d4 100644
|
||||
--- a/Lib/distutils/command/install.py
|
||||
+++ b/Lib/distutils/command/install.py
|
||||
@@ -3,6 +3,7 @@
|
||||
Implements the Distutils 'install' command."""
|
||||
|
||||
import sys
|
||||
+import sysconfig
|
||||
import os
|
||||
|
||||
from distutils import log
|
||||
@@ -142,6 +143,8 @@ class install(Command):
|
||||
|
||||
negative_opt = {'no-compile' : 'compile'}
|
||||
|
||||
+ # Allow Fedora to add components to the prefix
|
||||
+ _prefix_addition = getattr(sysconfig, '_prefix_addition', '')
|
||||
|
||||
def initialize_options(self):
|
||||
"""Initializes options."""
|
||||
@@ -419,8 +422,10 @@ 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 = (
|
||||
+ os.path.normpath(sys.prefix) + self._prefix_addition)
|
||||
+ self.exec_prefix = (
|
||||
+ os.path.normpath(sys.exec_prefix) + self._prefix_addition)
|
||||
|
||||
else:
|
||||
if self.exec_prefix is None:
|
||||
diff --git a/Lib/site.py b/Lib/site.py
|
||||
index 9e617afb00..db14f715f9 100644
|
||||
--- a/Lib/site.py
|
||||
+++ b/Lib/site.py
|
||||
@@ -353,7 +353,14 @@ 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.
|
||||
+
|
||||
+ """
|
||||
+ 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/Lib/sysconfig.py b/Lib/sysconfig.py
|
||||
index e3f79bfde5..e124104876 100644
|
||||
--- a/Lib/sysconfig.py
|
||||
+++ b/Lib/sysconfig.py
|
||||
@@ -86,6 +86,23 @@ _INSTALL_SCHEMES = {
|
||||
},
|
||||
}
|
||||
|
||||
+# Force pip to use distutils paths instead of sysconfig
|
||||
+# https://github.com/pypa/pip/issues/10647
|
||||
+_PIP_USE_SYSCONFIG = False
|
||||
+
|
||||
+# 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')
|
||||
|
@ -1,54 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
|
||||
Date: Thu, 11 Jul 2019 13:44:13 +0200
|
||||
Subject: [PATCH] 00328: Restore pyc to TIMESTAMP invalidation mode as default
|
||||
in rpmbuild
|
||||
|
||||
Since Fedora 31, the $SOURCE_DATE_EPOCH is set in rpmbuild to the latest
|
||||
%changelog date. This makes Python default to the CHECKED_HASH pyc
|
||||
invalidation mode, bringing more reproducible builds traded for an import
|
||||
performance decrease. To avoid that, we don't default to CHECKED_HASH
|
||||
when $RPM_BUILD_ROOT is set (i.e. when we are building RPM packages).
|
||||
|
||||
See https://src.fedoraproject.org/rpms/redhat-rpm-config/pull-request/57#comment-27426
|
||||
Downstream only: only used when building RPM packages
|
||||
Ideally, we should talk to upstream and explain why we don't want this
|
||||
---
|
||||
Lib/py_compile.py | 3 ++-
|
||||
Lib/test/test_py_compile.py | 2 ++
|
||||
2 files changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/Lib/py_compile.py b/Lib/py_compile.py
|
||||
index a81f493731..bba3642bf2 100644
|
||||
--- a/Lib/py_compile.py
|
||||
+++ b/Lib/py_compile.py
|
||||
@@ -70,7 +70,8 @@ class PycInvalidationMode(enum.Enum):
|
||||
|
||||
|
||||
def _get_default_invalidation_mode():
|
||||
- if os.environ.get('SOURCE_DATE_EPOCH'):
|
||||
+ if (os.environ.get('SOURCE_DATE_EPOCH') and not
|
||||
+ os.environ.get('RPM_BUILD_ROOT')):
|
||||
return PycInvalidationMode.CHECKED_HASH
|
||||
else:
|
||||
return PycInvalidationMode.TIMESTAMP
|
||||
diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py
|
||||
index e6791c6916..b2d3dcf7fb 100644
|
||||
--- a/Lib/test/test_py_compile.py
|
||||
+++ b/Lib/test/test_py_compile.py
|
||||
@@ -19,6 +19,7 @@ def without_source_date_epoch(fxn):
|
||||
def wrapper(*args, **kwargs):
|
||||
with support.EnvironmentVarGuard() as env:
|
||||
env.unset('SOURCE_DATE_EPOCH')
|
||||
+ env.unset('RPM_BUILD_ROOT')
|
||||
return fxn(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
@@ -29,6 +30,7 @@ def with_source_date_epoch(fxn):
|
||||
def wrapper(*args, **kwargs):
|
||||
with support.EnvironmentVarGuard() as env:
|
||||
env['SOURCE_DATE_EPOCH'] = '123456789'
|
||||
+ env.unset('RPM_BUILD_ROOT')
|
||||
return fxn(*args, **kwargs)
|
||||
return wrapper
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,97 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lumir Balhar <lbalhar@redhat.com>
|
||||
Date: Tue, 4 Aug 2020 12:04:03 +0200
|
||||
Subject: [PATCH] 00353: Original names for architectures with different names
|
||||
downstream
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
https://fedoraproject.org/wiki/Changes/Python_Upstream_Architecture_Names
|
||||
|
||||
Pythons in RHEL/Fedora used different names for some architectures
|
||||
than upstream and other distros (for example ppc64 vs. powerpc64).
|
||||
This was patched in patch 274, now it is sedded if %with legacy_archnames.
|
||||
|
||||
That meant that an extension built with the default upstream settings
|
||||
(on other distro or as an manylinux wheel) could not been found by Python
|
||||
on RHEL/Fedora because it had a different suffix.
|
||||
This patch adds the legacy names to importlib so Python is able
|
||||
to import extensions with a legacy architecture name in its
|
||||
file name.
|
||||
It work both ways, so it support both %with and %without legacy_archnames.
|
||||
|
||||
WARNING: This patch has no effect on Python built with bootstrap
|
||||
enabled because Python/importlib_external.h is not regenerated
|
||||
and therefore Python during bootstrap contains importlib from
|
||||
upstream without this feature. It's possible to include
|
||||
Python/importlib_external.h to this patch but it'd make rebasing
|
||||
a nightmare because it's basically a binary file.
|
||||
|
||||
Co-authored-by: Miro Hrončok <miro@hroncok.cz>
|
||||
---
|
||||
Lib/importlib/_bootstrap_external.py | 40 ++++++++++++++++++++++++++--
|
||||
1 file changed, 38 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
|
||||
index fe31f437da..b5b70757f0 100644
|
||||
--- a/Lib/importlib/_bootstrap_external.py
|
||||
+++ b/Lib/importlib/_bootstrap_external.py
|
||||
@@ -1636,7 +1636,7 @@ def _get_supported_file_loaders():
|
||||
|
||||
Each item is a tuple (loader, suffixes).
|
||||
"""
|
||||
- extensions = ExtensionFileLoader, _imp.extension_suffixes()
|
||||
+ extensions = ExtensionFileLoader, _alternative_architectures(_imp.extension_suffixes())
|
||||
source = SourceFileLoader, SOURCE_SUFFIXES
|
||||
bytecode = SourcelessFileLoader, BYTECODE_SUFFIXES
|
||||
return [extensions, source, bytecode]
|
||||
@@ -1692,7 +1692,7 @@ def _setup(_bootstrap_module):
|
||||
|
||||
# Constants
|
||||
setattr(self_module, '_relax_case', _make_relax_case())
|
||||
- EXTENSION_SUFFIXES.extend(_imp.extension_suffixes())
|
||||
+ EXTENSION_SUFFIXES.extend(_alternative_architectures(_imp.extension_suffixes()))
|
||||
if builtin_os == 'nt':
|
||||
SOURCE_SUFFIXES.append('.pyw')
|
||||
if '_d.pyd' in EXTENSION_SUFFIXES:
|
||||
@@ -1705,3 +1705,39 @@ def _install(_bootstrap_module):
|
||||
supported_loaders = _get_supported_file_loaders()
|
||||
sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)])
|
||||
sys.meta_path.append(PathFinder)
|
||||
+
|
||||
+
|
||||
+_ARCH_MAP = {
|
||||
+ "-arm-linux-gnueabi.": "-arm-linux-gnueabihf.",
|
||||
+ "-armeb-linux-gnueabi.": "-armeb-linux-gnueabihf.",
|
||||
+ "-mips64-linux-gnu.": "-mips64-linux-gnuabi64.",
|
||||
+ "-mips64el-linux-gnu.": "-mips64el-linux-gnuabi64.",
|
||||
+ "-ppc-linux-gnu.": "-powerpc-linux-gnu.",
|
||||
+ "-ppc-linux-gnuspe.": "-powerpc-linux-gnuspe.",
|
||||
+ "-ppc64-linux-gnu.": "-powerpc64-linux-gnu.",
|
||||
+ "-ppc64le-linux-gnu.": "-powerpc64le-linux-gnu.",
|
||||
+ # The above, but the other way around:
|
||||
+ "-arm-linux-gnueabihf.": "-arm-linux-gnueabi.",
|
||||
+ "-armeb-linux-gnueabihf.": "-armeb-linux-gnueabi.",
|
||||
+ "-mips64-linux-gnuabi64.": "-mips64-linux-gnu.",
|
||||
+ "-mips64el-linux-gnuabi64.": "-mips64el-linux-gnu.",
|
||||
+ "-powerpc-linux-gnu.": "-ppc-linux-gnu.",
|
||||
+ "-powerpc-linux-gnuspe.": "-ppc-linux-gnuspe.",
|
||||
+ "-powerpc64-linux-gnu.": "-ppc64-linux-gnu.",
|
||||
+ "-powerpc64le-linux-gnu.": "-ppc64le-linux-gnu.",
|
||||
+}
|
||||
+
|
||||
+
|
||||
+def _alternative_architectures(suffixes):
|
||||
+ """Add a suffix with an alternative architecture name
|
||||
+ to the list of suffixes so an extension built with
|
||||
+ the default (upstream) setting is loadable with our Pythons
|
||||
+ """
|
||||
+
|
||||
+ for suffix in suffixes:
|
||||
+ for original, alternative in _ARCH_MAP.items():
|
||||
+ if original in suffix:
|
||||
+ suffixes.append(suffix.replace(original, alternative))
|
||||
+ return suffixes
|
||||
+
|
||||
+ return suffixes
|
@ -1,251 +0,0 @@
|
||||
From 8b70605b594b3831331a9340ba764ff751871612 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <encukou@gmail.com>
|
||||
Date: Mon, 6 Mar 2023 17:24:24 +0100
|
||||
Subject: [PATCH 2/2] CVE-2007-4559, PEP-706: Add filters for tarfile
|
||||
extraction (downstream)
|
||||
|
||||
Add and test RHEL-specific ways of configuring the default behavior: environment
|
||||
variable and config file.
|
||||
---
|
||||
Lib/tarfile.py | 42 +++++++++++++
|
||||
Lib/test/test_shutil.py | 3 +-
|
||||
Lib/test/test_tarfile.py | 128 ++++++++++++++++++++++++++++++++++++++-
|
||||
3 files changed, 169 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
|
||||
index b6ad7dbe2a4..dc7050b2c63 100755
|
||||
--- a/Lib/tarfile.py
|
||||
+++ b/Lib/tarfile.py
|
||||
@@ -72,6 +72,13 @@ __all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", "ReadError",
|
||||
"ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT",
|
||||
"DEFAULT_FORMAT", "open"]
|
||||
|
||||
+# If true, use the safer (but backwards-incompatible) 'tar' extraction filter,
|
||||
+# rather than 'fully_trusted', by default.
|
||||
+# The emitted warning is changed to match.
|
||||
+_RH_SAFER_DEFAULT = True
|
||||
+
|
||||
+# System-wide configuration file
|
||||
+_CONFIG_FILENAME = '/etc/python/tarfile.cfg'
|
||||
|
||||
#---------------------------------------------------------
|
||||
# tar constants
|
||||
@@ -2197,6 +2204,41 @@ class TarFile(object):
|
||||
if filter is None:
|
||||
filter = self.extraction_filter
|
||||
if filter is None:
|
||||
+ name = os.environ.get('PYTHON_TARFILE_EXTRACTION_FILTER')
|
||||
+ if name is None:
|
||||
+ try:
|
||||
+ file = bltn_open(_CONFIG_FILENAME)
|
||||
+ except FileNotFoundError:
|
||||
+ pass
|
||||
+ else:
|
||||
+ import configparser
|
||||
+ conf = configparser.ConfigParser(
|
||||
+ interpolation=None,
|
||||
+ comment_prefixes=('#', ),
|
||||
+ )
|
||||
+ with file:
|
||||
+ conf.read_file(file)
|
||||
+ name = conf.get('tarfile',
|
||||
+ 'PYTHON_TARFILE_EXTRACTION_FILTER',
|
||||
+ fallback='')
|
||||
+ if name:
|
||||
+ try:
|
||||
+ filter = _NAMED_FILTERS[name]
|
||||
+ except KeyError:
|
||||
+ raise ValueError(f"filter {filter!r} not found") from None
|
||||
+ self.extraction_filter = filter
|
||||
+ return filter
|
||||
+ if _RH_SAFER_DEFAULT:
|
||||
+ warnings.warn(
|
||||
+ 'The default behavior of tarfile extraction has been '
|
||||
+ + 'changed to disallow common exploits '
|
||||
+ + '(including CVE-2007-4559). '
|
||||
+ + 'By default, absolute/parent paths are disallowed '
|
||||
+ + 'and some mode bits are cleared. '
|
||||
+ + 'See https://access.redhat.com/articles/7004769 '
|
||||
+ + 'for more details.',
|
||||
+ RuntimeWarning)
|
||||
+ return tar_filter
|
||||
return fully_trusted_filter
|
||||
if isinstance(filter, str):
|
||||
raise TypeError(
|
||||
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
|
||||
index 9041e7aa368..1eb1116cc10 100644
|
||||
--- a/Lib/test/test_shutil.py
|
||||
+++ b/Lib/test/test_shutil.py
|
||||
@@ -1613,7 +1613,8 @@ class TestArchives(BaseTest, unittest.TestCase):
|
||||
def check_unpack_tarball(self, format):
|
||||
self.check_unpack_archive(format, filter='fully_trusted')
|
||||
self.check_unpack_archive(format, filter='data')
|
||||
- with warnings_helper.check_no_warnings(self):
|
||||
+ with warnings_helper.check_warnings(
|
||||
+ ('.*CVE-2007-4559', RuntimeWarning)):
|
||||
self.check_unpack_archive(format)
|
||||
|
||||
def test_unpack_archive_tar(self):
|
||||
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
|
||||
index a66f7efd2d6..6fd3c384b5c 100644
|
||||
--- a/Lib/test/test_tarfile.py
|
||||
+++ b/Lib/test/test_tarfile.py
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
import os
|
||||
import io
|
||||
from hashlib import sha256
|
||||
-from contextlib import contextmanager
|
||||
+from contextlib import contextmanager, ExitStack
|
||||
from random import Random
|
||||
import pathlib
|
||||
import shutil
|
||||
@@ -2929,7 +2929,11 @@ class NoneInfoExtractTests(ReadTest):
|
||||
tar = tarfile.open(tarname, mode='r', encoding="iso8859-1")
|
||||
cls.control_dir = pathlib.Path(TEMPDIR) / "extractall_ctrl"
|
||||
tar.errorlevel = 0
|
||||
- tar.extractall(cls.control_dir, filter=cls.extraction_filter)
|
||||
+ with ExitStack() as cm:
|
||||
+ if cls.extraction_filter is None:
|
||||
+ cm.enter_context(warnings.catch_warnings())
|
||||
+ warnings.simplefilter(action="ignore", category=RuntimeWarning)
|
||||
+ tar.extractall(cls.control_dir, filter=cls.extraction_filter)
|
||||
tar.close()
|
||||
cls.control_paths = set(
|
||||
p.relative_to(cls.control_dir)
|
||||
@@ -3592,7 +3596,8 @@ class TestExtractionFilters(unittest.TestCase):
|
||||
"""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 warnings_helper.check_warnings(
|
||||
+ ('.*CVE-2007-4559', RuntimeWarning)):
|
||||
with self.check_context(arc.open(), None):
|
||||
self.expect_file('foo')
|
||||
|
||||
@@ -3762,6 +3767,123 @@ class TestExtractionFilters(unittest.TestCase):
|
||||
self.expect_exception(TypeError) # errorlevel is not int
|
||||
|
||||
|
||||
+ @contextmanager
|
||||
+ def rh_config_context(self, config_lines=None):
|
||||
+ """Set up for testing various ways of overriding the default filter
|
||||
+
|
||||
+ return a triple with:
|
||||
+ - temporary directory
|
||||
+ - EnvironmentVarGuard()
|
||||
+ - a test archive for use with check_* methods below
|
||||
+
|
||||
+ If config_lines is given, write them to the config file. Otherwise
|
||||
+ the config file is missing.
|
||||
+ """
|
||||
+ tempdir = pathlib.Path(TEMPDIR) / 'tmp'
|
||||
+ configfile = tempdir / 'tarfile.cfg'
|
||||
+ with ArchiveMaker() as arc:
|
||||
+ arc.add('good')
|
||||
+ arc.add('ugly', symlink_to='/etc/passwd')
|
||||
+ arc.add('../bad')
|
||||
+ with (
|
||||
+ support.temp_dir(tempdir),
|
||||
+ support.swap_attr(tarfile, '_CONFIG_FILENAME', str(configfile)),
|
||||
+ support.EnvironmentVarGuard() as env,
|
||||
+ arc.open() as tar,
|
||||
+ ):
|
||||
+ if config_lines is not None:
|
||||
+ with configfile.open('w') as f:
|
||||
+ for line in config_lines:
|
||||
+ print(line, file=f)
|
||||
+ yield tempdir, env, tar
|
||||
+
|
||||
+ def check_rh_default_behavior(self, tar, tempdir):
|
||||
+ """Check RH default: warn and refuse to extract dangerous files."""
|
||||
+ with (
|
||||
+ warnings_helper.check_warnings(
|
||||
+ ('.*CVE-2007-4559', RuntimeWarning)),
|
||||
+ self.assertRaises(tarfile.OutsideDestinationError),
|
||||
+ ):
|
||||
+ tar.extractall(tempdir / 'outdir')
|
||||
+
|
||||
+ def check_trusted_default(self, tar, tempdir):
|
||||
+ """Check 'fully_trusted' is configured as the default filter."""
|
||||
+ with (
|
||||
+ warnings_helper.check_no_warnings(self),
|
||||
+ ):
|
||||
+ tar.extractall(tempdir / 'outdir')
|
||||
+ self.assertTrue((tempdir / 'outdir/good').exists())
|
||||
+ self.assertEqual((tempdir / 'outdir/ugly').readlink(),
|
||||
+ pathlib.Path('/etc/passwd'))
|
||||
+ self.assertTrue((tempdir / 'bad').exists())
|
||||
+
|
||||
+ def test_rh_default_no_conf(self):
|
||||
+ with self.rh_config_context() as (tempdir, env, tar):
|
||||
+ self.check_rh_default_behavior(tar, tempdir)
|
||||
+
|
||||
+ def test_rh_default_from_file(self):
|
||||
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=fully_trusted']
|
||||
+ with self.rh_config_context(lines) as (tempdir, env, tar):
|
||||
+ self.check_trusted_default(tar, tempdir)
|
||||
+
|
||||
+ def test_rh_empty_config_file(self):
|
||||
+ """Empty config file -> default behavior"""
|
||||
+ lines = []
|
||||
+ with self.rh_config_context(lines) as (tempdir, env, tar):
|
||||
+ self.check_rh_default_behavior(tar, tempdir)
|
||||
+
|
||||
+ def test_empty_config_section(self):
|
||||
+ """Empty section in config file -> default behavior"""
|
||||
+ lines = ['[tarfile]']
|
||||
+ with self.rh_config_context(lines) as (tempdir, env, tar):
|
||||
+ self.check_rh_default_behavior(tar, tempdir)
|
||||
+
|
||||
+ def test_rh_default_empty_config_option(self):
|
||||
+ """Empty option value in config file -> default behavior"""
|
||||
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=']
|
||||
+ with self.rh_config_context(lines) as (tempdir, env, tar):
|
||||
+ self.check_rh_default_behavior(tar, tempdir)
|
||||
+
|
||||
+ def test_bad_config_option(self):
|
||||
+ """Bad option value in config file -> ValueError"""
|
||||
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=unknown!']
|
||||
+ with self.rh_config_context(lines) as (tempdir, env, tar):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ tar.extractall(tempdir / 'outdir')
|
||||
+
|
||||
+ def test_default_from_envvar(self):
|
||||
+ with self.rh_config_context() as (tempdir, env, tar):
|
||||
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'fully_trusted'
|
||||
+ self.check_trusted_default(tar, tempdir)
|
||||
+
|
||||
+ def test_empty_envvar(self):
|
||||
+ """Empty env variable -> default behavior"""
|
||||
+ with self.rh_config_context() as (tempdir, env, tar):
|
||||
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = ''
|
||||
+ self.check_rh_default_behavior(tar, tempdir)
|
||||
+
|
||||
+ def test_bad_envvar(self):
|
||||
+ with self.rh_config_context() as (tempdir, env, tar):
|
||||
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'unknown!'
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ tar.extractall(tempdir / 'outdir')
|
||||
+
|
||||
+ def test_envvar_overrides_file(self):
|
||||
+ lines = ['[tarfile]', 'PYTHON_TARFILE_EXTRACTION_FILTER=data']
|
||||
+ with self.rh_config_context(lines) as (tempdir, env, tar):
|
||||
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'fully_trusted'
|
||||
+ self.check_trusted_default(tar, tempdir)
|
||||
+
|
||||
+ def test_monkeypatch_overrides_envvar(self):
|
||||
+ with self.rh_config_context(None) as (tempdir, env, tar):
|
||||
+ env['PYTHON_TARFILE_EXTRACTION_FILTER'] = 'data'
|
||||
+ with support.swap_attr(
|
||||
+ tarfile.TarFile, 'extraction_filter',
|
||||
+ staticmethod(tarfile.fully_trusted_filter)
|
||||
+ ):
|
||||
+ self.check_trusted_default(tar, tempdir)
|
||||
+
|
||||
+
|
||||
def setUpModule():
|
||||
support.unlink(TEMPDIR)
|
||||
os.makedirs(TEMPDIR)
|
||||
--
|
||||
2.40.1
|
||||
|
@ -1,248 +0,0 @@
|
||||
From 4df4fad359c280f2328b98ea9b4414f244624a58 Mon Sep 17 00:00:00 2001
|
||||
From: Lumir Balhar <lbalhar@redhat.com>
|
||||
Date: Mon, 18 Dec 2023 20:15:33 +0100
|
||||
Subject: [PATCH] Make it possible to disable strict parsing in email module
|
||||
|
||||
---
|
||||
Doc/library/email.utils.rst | 26 +++++++++++
|
||||
Lib/email/utils.py | 54 ++++++++++++++++++++++-
|
||||
Lib/test/test_email/test_email.py | 72 ++++++++++++++++++++++++++++++-
|
||||
3 files changed, 149 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst
|
||||
index d1e1898591..7aef773b5f 100644
|
||||
--- a/Doc/library/email.utils.rst
|
||||
+++ b/Doc/library/email.utils.rst
|
||||
@@ -69,6 +69,19 @@ of the new API.
|
||||
|
||||
If *strict* is true, use a strict parser which rejects malformed inputs.
|
||||
|
||||
+ The default setting for *strict* is set to ``True``, but you can override
|
||||
+ it by setting the environment variable ``PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING``
|
||||
+ to non-empty string.
|
||||
+
|
||||
+ Additionally, you can permanently set the default value for *strict* to
|
||||
+ ``False`` by creating the configuration file ``/etc/python/email.cfg``
|
||||
+ with the following content:
|
||||
+
|
||||
+ .. code-block:: ini
|
||||
+
|
||||
+ [email_addr_parsing]
|
||||
+ PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true
|
||||
+
|
||||
.. versionchanged:: 3.9.20
|
||||
Add *strict* optional parameter and reject malformed inputs by default.
|
||||
|
||||
@@ -97,6 +110,19 @@ of the new API.
|
||||
|
||||
If *strict* is true, use a strict parser which rejects malformed inputs.
|
||||
|
||||
+ The default setting for *strict* is set to ``True``, but you can override
|
||||
+ it by setting the environment variable ``PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING``
|
||||
+ to non-empty string.
|
||||
+
|
||||
+ Additionally, you can permanently set the default value for *strict* to
|
||||
+ ``False`` by creating the configuration file ``/etc/python/email.cfg``
|
||||
+ with the following content:
|
||||
+
|
||||
+ .. code-block:: ini
|
||||
+
|
||||
+ [email_addr_parsing]
|
||||
+ PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true
|
||||
+
|
||||
Here's a simple example that gets all the recipients of a message::
|
||||
|
||||
from email.utils import getaddresses
|
||||
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
|
||||
index f83b7e5d7e..b8e90ceb8e 100644
|
||||
--- a/Lib/email/utils.py
|
||||
+++ b/Lib/email/utils.py
|
||||
@@ -48,6 +48,46 @@ TICK = "'"
|
||||
specialsre = re.compile(r'[][\\()<>@,:;".]')
|
||||
escapesre = re.compile(r'[\\"]')
|
||||
|
||||
+_EMAIL_CONFIG_FILE = "/etc/python/email.cfg"
|
||||
+_cached_strict_addr_parsing = None
|
||||
+
|
||||
+
|
||||
+def _use_strict_email_parsing():
|
||||
+ """"Cache implementation for _cached_strict_addr_parsing"""
|
||||
+ global _cached_strict_addr_parsing
|
||||
+ if _cached_strict_addr_parsing is None:
|
||||
+ _cached_strict_addr_parsing = _use_strict_email_parsing_impl()
|
||||
+ return _cached_strict_addr_parsing
|
||||
+
|
||||
+
|
||||
+def _use_strict_email_parsing_impl():
|
||||
+ """Returns True if strict email parsing is not disabled by
|
||||
+ config file or env variable.
|
||||
+ """
|
||||
+ disabled = bool(os.environ.get("PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING"))
|
||||
+ if disabled:
|
||||
+ return False
|
||||
+
|
||||
+ try:
|
||||
+ file = open(_EMAIL_CONFIG_FILE)
|
||||
+ except FileNotFoundError:
|
||||
+ pass
|
||||
+ else:
|
||||
+ with file:
|
||||
+ import configparser
|
||||
+ config = configparser.ConfigParser(
|
||||
+ interpolation=None,
|
||||
+ comment_prefixes=('#', ),
|
||||
+
|
||||
+ )
|
||||
+ config.read_file(file)
|
||||
+ disabled = config.getboolean('email_addr_parsing', "PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING", fallback=None)
|
||||
+
|
||||
+ if disabled:
|
||||
+ return False
|
||||
+
|
||||
+ return True
|
||||
+
|
||||
|
||||
def _has_surrogates(s):
|
||||
"""Return True if s contains surrogate-escaped binary data."""
|
||||
@@ -149,7 +189,7 @@ def _strip_quoted_realnames(addr):
|
||||
|
||||
supports_strict_parsing = True
|
||||
|
||||
-def getaddresses(fieldvalues, *, strict=True):
|
||||
+def getaddresses(fieldvalues, *, strict=None):
|
||||
"""Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue.
|
||||
|
||||
When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in
|
||||
@@ -158,6 +198,11 @@ def getaddresses(fieldvalues, *, strict=True):
|
||||
If strict is true, use a strict parser which rejects malformed inputs.
|
||||
"""
|
||||
|
||||
+ # If default is used, it's True unless disabled
|
||||
+ # by env variable or config file.
|
||||
+ if strict == None:
|
||||
+ strict = _use_strict_email_parsing()
|
||||
+
|
||||
# If strict is true, if the resulting list of parsed addresses is greater
|
||||
# than the number of fieldvalues in the input list, a parsing error has
|
||||
# occurred and consequently a list containing a single empty 2-tuple [('',
|
||||
@@ -330,7 +375,7 @@ def parsedate_to_datetime(data):
|
||||
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
|
||||
|
||||
|
||||
-def parseaddr(addr, *, strict=True):
|
||||
+def parseaddr(addr, *, strict=None):
|
||||
"""
|
||||
Parse addr into its constituent realname and email address parts.
|
||||
|
||||
@@ -339,6 +384,11 @@ def parseaddr(addr, *, strict=True):
|
||||
|
||||
If strict is True, use a strict parser which rejects malformed inputs.
|
||||
"""
|
||||
+ # If default is used, it's True unless disabled
|
||||
+ # by env variable or config file.
|
||||
+ if strict == None:
|
||||
+ strict = _use_strict_email_parsing()
|
||||
+
|
||||
if not strict:
|
||||
addrs = _AddressList(addr).addresslist
|
||||
if not addrs:
|
||||
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
|
||||
index ce36efc1b1..05ea201b68 100644
|
||||
--- a/Lib/test/test_email/test_email.py
|
||||
+++ b/Lib/test/test_email/test_email.py
|
||||
@@ -7,6 +7,9 @@ import time
|
||||
import base64
|
||||
import unittest
|
||||
import textwrap
|
||||
+import contextlib
|
||||
+import tempfile
|
||||
+import os
|
||||
|
||||
from io import StringIO, BytesIO
|
||||
from itertools import chain
|
||||
@@ -41,7 +44,7 @@ from email import iterators
|
||||
from email import base64mime
|
||||
from email import quoprimime
|
||||
|
||||
-from test.support import unlink, start_threads
|
||||
+from test.support import unlink, start_threads, EnvironmentVarGuard, swap_attr
|
||||
from test.test_email import openfile, TestEmailBase
|
||||
|
||||
# These imports are documented to work, but we are testing them using a
|
||||
@@ -3313,6 +3316,73 @@ Foo
|
||||
# Test email.utils.supports_strict_parsing attribute
|
||||
self.assertEqual(email.utils.supports_strict_parsing, True)
|
||||
|
||||
+ def test_parsing_errors_strict_set_via_env_var(self):
|
||||
+ address = 'alice@example.org )Alice('
|
||||
+ empty = ('', '')
|
||||
+
|
||||
+ # Reset cached default value to make the function
|
||||
+ # reload the config file provided below.
|
||||
+ utils._cached_strict_addr_parsing = None
|
||||
+
|
||||
+ # Strict disabled via env variable, old behavior expected
|
||||
+ with EnvironmentVarGuard() as environ:
|
||||
+ environ["PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING"] = "1"
|
||||
+
|
||||
+ self.assertEqual(utils.getaddresses([address]),
|
||||
+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')])
|
||||
+ self.assertEqual(utils.parseaddr([address]), ('', address))
|
||||
+
|
||||
+ # Clear cache again
|
||||
+ utils._cached_strict_addr_parsing = None
|
||||
+
|
||||
+ # Default strict=True, empty result expected
|
||||
+ self.assertEqual(utils.getaddresses([address]), [empty])
|
||||
+ self.assertEqual(utils.parseaddr([address]), empty)
|
||||
+
|
||||
+ # Clear cache again
|
||||
+ utils._cached_strict_addr_parsing = None
|
||||
+
|
||||
+ # Empty string in env variable = strict parsing enabled (default)
|
||||
+ with EnvironmentVarGuard() as environ:
|
||||
+ environ["PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING"] = ""
|
||||
+
|
||||
+ # Default strict=True, empty result expected
|
||||
+ self.assertEqual(utils.getaddresses([address]), [empty])
|
||||
+ self.assertEqual(utils.parseaddr([address]), empty)
|
||||
+
|
||||
+ @contextlib.contextmanager
|
||||
+ def _email_strict_parsing_conf(self):
|
||||
+ """Context for the given email strict parsing configured in config file"""
|
||||
+ with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
+ filename = os.path.join(tmpdirname, 'conf.cfg')
|
||||
+ with swap_attr(utils, "_EMAIL_CONFIG_FILE", filename):
|
||||
+ with open(filename, 'w') as file:
|
||||
+ file.write('[email_addr_parsing]\n')
|
||||
+ file.write('PYTHON_EMAIL_DISABLE_STRICT_ADDR_PARSING = true')
|
||||
+ utils._EMAIL_CONFIG_FILE = filename
|
||||
+ yield
|
||||
+
|
||||
+ def test_parsing_errors_strict_disabled_via_config_file(self):
|
||||
+ address = 'alice@example.org )Alice('
|
||||
+ empty = ('', '')
|
||||
+
|
||||
+ # Reset cached default value to make the function
|
||||
+ # reload the config file provided below.
|
||||
+ utils._cached_strict_addr_parsing = None
|
||||
+
|
||||
+ # Strict disabled via config file, old results expected
|
||||
+ with self._email_strict_parsing_conf():
|
||||
+ self.assertEqual(utils.getaddresses([address]),
|
||||
+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')])
|
||||
+ self.assertEqual(utils.parseaddr([address]), ('', address))
|
||||
+
|
||||
+ # Clear cache again
|
||||
+ utils._cached_strict_addr_parsing = None
|
||||
+
|
||||
+ # Default strict=True, empty result expected
|
||||
+ self.assertEqual(utils.getaddresses([address]), [empty])
|
||||
+ self.assertEqual(utils.parseaddr([address]), empty)
|
||||
+
|
||||
def test_getaddresses_nasty(self):
|
||||
for addresses, expected in (
|
||||
(['"Sürname, Firstname" <to@example.com>'],
|
||||
--
|
||||
2.43.0
|
||||
|
@ -1,63 +0,0 @@
|
||||
From 60d40d7095983e0bc23a103b2050adc519dc7fe3 Mon Sep 17 00:00:00 2001
|
||||
From: Lumir Balhar <lbalhar@redhat.com>
|
||||
Date: Fri, 3 May 2024 14:17:48 +0200
|
||||
Subject: [PATCH] Expect failures in tests not working properly with expat with
|
||||
a fixed CVE in RHEL
|
||||
|
||||
---
|
||||
Lib/test/test_pyexpat.py | 1 +
|
||||
Lib/test/test_sax.py | 1 +
|
||||
Lib/test/test_xml_etree.py | 3 +++
|
||||
3 files changed, 5 insertions(+)
|
||||
|
||||
diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py
|
||||
index 43cbd27..27b1502 100644
|
||||
--- a/Lib/test/test_pyexpat.py
|
||||
+++ b/Lib/test/test_pyexpat.py
|
||||
@@ -793,6 +793,7 @@ class ReparseDeferralTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(started, ['doc'])
|
||||
|
||||
+ @unittest.expectedFailure
|
||||
def test_reparse_deferral_disabled(self):
|
||||
started = []
|
||||
|
||||
diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py
|
||||
index 9b3014a..646c92d 100644
|
||||
--- a/Lib/test/test_sax.py
|
||||
+++ b/Lib/test/test_sax.py
|
||||
@@ -1240,6 +1240,7 @@ class ExpatReaderTest(XmlTestBase):
|
||||
|
||||
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
|
||||
|
||||
+ @unittest.expectedFailure
|
||||
def test_flush_reparse_deferral_disabled(self):
|
||||
result = BytesIO()
|
||||
xmlgen = XMLGenerator(result)
|
||||
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
|
||||
index 9c382d1..62f2871 100644
|
||||
--- a/Lib/test/test_xml_etree.py
|
||||
+++ b/Lib/test/test_xml_etree.py
|
||||
@@ -1424,9 +1424,11 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
self.assert_event_tags(parser, [('end', 'root')])
|
||||
self.assertIsNone(parser.close())
|
||||
|
||||
+ @unittest.expectedFailure
|
||||
def test_simple_xml_chunk_1(self):
|
||||
self.test_simple_xml(chunk_size=1, flush=True)
|
||||
|
||||
+ @unittest.expectedFailure
|
||||
def test_simple_xml_chunk_5(self):
|
||||
self.test_simple_xml(chunk_size=5, flush=True)
|
||||
|
||||
@@ -1651,6 +1653,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
|
||||
self.assert_event_tags(parser, [('end', 'doc')])
|
||||
|
||||
+ @unittest.expectedFailure
|
||||
def test_flush_reparse_deferral_disabled(self):
|
||||
parser = ET.XMLPullParser(events=('start', 'end'))
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
@ -1,119 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Seth Michael Larson <seth@python.org>
|
||||
Date: Fri, 31 Jan 2025 11:41:34 -0600
|
||||
Subject: [PATCH] 00450: CVE-2025-0938: Disallow square brackets ([ and ]) in
|
||||
domain names for parsed URLs
|
||||
|
||||
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
|
||||
---
|
||||
Lib/test/test_urlparse.py | 37 ++++++++++++++++++-
|
||||
Lib/urllib/parse.py | 20 +++++++++-
|
||||
...-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
|
||||
|
||||
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
|
||||
index 6f7d40c212..083d08b22e 100644
|
||||
--- a/Lib/test/test_urlparse.py
|
||||
+++ b/Lib/test/test_urlparse.py
|
||||
@@ -1146,16 +1146,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')
|
||||
diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py
|
||||
index 9d37dcaa90..fb8f7f1ea8 100644
|
||||
--- a/Lib/urllib/parse.py
|
||||
+++ b/Lib/urllib/parse.py
|
||||
@@ -443,6 +443,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):
|
||||
@@ -506,8 +523,7 @@ def urlsplit(url, scheme='', allow_fragments=True):
|
||||
(']' 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:
|
||||
diff --git a/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst
|
||||
new file mode 100644
|
||||
index 0000000000..bff1bc6b0d
|
||||
--- /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 <https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2>`__.
|
@ -1,16 +0,0 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCgAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmdPScsACgkQsmmV4xAl
|
||||
BWgZtRAAiPehPRc94kRpNn4CuLw9hDFJmucXfG/Pjf9DdQkrmWAMvFS2kpigd9A0
|
||||
3QoDbgZPb8k9XtTrbpT4A0j/SYaqnLXOktXE7CEwM1vRTHbUDm62qxRSIa+RXO1d
|
||||
h/EqhF1Rpgl37I1GL3mAHew6KjIq3K/aNvJVTtKA+1xy8XpF5Dbk3feDeTucqYaM
|
||||
evtCu2SlwQXRvIbqFciMtRC2bmkNHgRVFpuxInjmp82ED0E6yZ/ecHXjb5Da7lDV
|
||||
8uRh9aEjMWY4LHTdl2tWaaerLqYZfvHSlz2xY8W5itSgOAzzJNn3dX8P6EKK5ab7
|
||||
IV85vqPX1oMcX0seZd3QlVdOxUPf1tKB7Eo7yHz3gV/KgWzFSAHSZFCwiqyfNfj8
|
||||
PibYYwtGG0+S7GJ7bZ2iCCgkqrFBoMQ8yOEDxE7Pt36JSXtZ2grtsFB8WAZqhCMa
|
||||
luIjiibGkOzzBRX/neW5RYT1HLNzi140G7XEZeRv3CXzuud66+ynLdOK7uFEr8jq
|
||||
W0t/fHbrqcSjyEo9L9VDP4Wd9VU/nwf6tb3Py6nPZKM93ZYqtlJNmzxmOyQOmn30
|
||||
bRGEQyzcYmEKMWg6zzO57In/WRytB4BgE7Dj7L876TbnJ8YoR8TVpuQ3sDKoSxwo
|
||||
gIdNR/6lcqP0HwDFtqBrcwSQ9Dew6wMz0Pp2EwX90EZLwnBvpeI=
|
||||
=taeu
|
||||
-----END PGP SIGNATURE-----
|
@ -1,55 +0,0 @@
|
||||
"""Checks if all *.pyc files have later mtime than their *.py files."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from importlib.util import cache_from_source
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
RPM_BUILD_ROOT = os.environ.get('RPM_BUILD_ROOT', '')
|
||||
|
||||
# ...cpython-3X.pyc
|
||||
# ...cpython-3X.opt-1.pyc
|
||||
# ...cpython-3X.opt-2.pyc
|
||||
LEVELS = (None, 1, 2)
|
||||
|
||||
# list of globs of test and other files that we expect not to have bytecode
|
||||
not_compiled = [
|
||||
'/usr/bin/*',
|
||||
'*/test/bad_coding.py',
|
||||
'*/test/bad_coding2.py',
|
||||
'*/test/badsyntax_*.py',
|
||||
'*/lib2to3/tests/data/bom.py',
|
||||
'*/lib2to3/tests/data/crlf.py',
|
||||
'*/lib2to3/tests/data/different_encoding.py',
|
||||
'*/lib2to3/tests/data/false_encoding.py',
|
||||
'*/lib2to3/tests/data/py2_test_grammar.py',
|
||||
'*.debug-gdb.py',
|
||||
]
|
||||
|
||||
|
||||
def bytecode_expected(path):
|
||||
path = Path(path[len(RPM_BUILD_ROOT):])
|
||||
for glob in not_compiled:
|
||||
if path.match(glob):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
failed = 0
|
||||
compiled = (path for path in sys.argv[1:] if bytecode_expected(path))
|
||||
for path in compiled:
|
||||
to_check = (cache_from_source(path, optimization=opt) for opt in LEVELS)
|
||||
f_mtime = os.path.getmtime(path)
|
||||
for pyc in to_check:
|
||||
c_mtime = os.path.getmtime(pyc)
|
||||
if c_mtime < f_mtime:
|
||||
print('Failed bytecompilation timestamps check: '
|
||||
f'Bytecode file {pyc} is older than source file {path}',
|
||||
file=sys.stderr)
|
||||
failed += 1
|
||||
|
||||
if failed:
|
||||
print(f'\n{failed} files failed bytecompilation timestamps check.',
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Copyright 2017 Zbigniew Jędrzejewski-Szmek -->
|
||||
<application>
|
||||
<id type="desktop">idle3.desktop</id>
|
||||
<name>IDLE3</name>
|
||||
<metadata_licence>CC0</metadata_licence>
|
||||
<project_license>Python-2.0</project_license>
|
||||
<summary>Python 3 Integrated Development and Learning Environment</summary>
|
||||
<description>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
IDLE is written in pure Python, and uses the tkinter GUI toolkit.
|
||||
It provides:
|
||||
</p>
|
||||
<ul>
|
||||
<li>a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,</li>
|
||||
<li>a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,</li>
|
||||
<li>search within any window, replace within editor windows, and search through multiple files (grep),</li>
|
||||
<li>a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="homepage">https://docs.python.org/3/library/idle.html</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</screenshot>
|
||||
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</screenshot>
|
||||
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</screenshot>
|
||||
</screenshots>
|
||||
<update_contact>zbyszek@in.waw.pl</update_contact>
|
||||
</application>
|
@ -1,11 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=IDLE 3
|
||||
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;
|
11542
SOURCES/pubkeys.txt
11542
SOURCES/pubkeys.txt
File diff suppressed because it is too large
Load Diff
2180
SPECS/python3.9.spec
2180
SPECS/python3.9.spec
File diff suppressed because it is too large
Load Diff
1
dead.package
Normal file
1
dead.package
Normal file
@ -0,0 +1 @@
|
||||
python3.9 package is retired on branch c10s for BAKERY-412
|
Loading…
Reference in New Issue
Block a user