From 82f86297c28f0fb73d7f7c3a4af7d5a0100033a0 Mon Sep 17 00:00:00 2001 From: Funda Wang Date: Fri, 22 Nov 2024 19:00:13 +0800 Subject: [PATCH] fix CVE-2024-9287 --- backport-CVE-2024-9287.patch | 297 +++++++++++++++++++++++++++++++++++ python3.spec | 97 ++++++------ 2 files changed, 350 insertions(+), 44 deletions(-) create mode 100644 backport-CVE-2024-9287.patch diff --git a/backport-CVE-2024-9287.patch b/backport-CVE-2024-9287.patch new file mode 100644 index 0000000..18333c0 --- /dev/null +++ b/backport-CVE-2024-9287.patch @@ -0,0 +1,297 @@ +From 633555735a023d3e4d92ba31da35b1205f9ecbd7 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Mon, 4 Nov 2024 16:16:35 +0100 +Subject: [PATCH] [3.9] gh-124651: Quote template strings in `venv` activation + scripts (GH-124712) (GH-126185) (GH-126269) (GH-126301) + +(cherry picked from commit ae961ae94bf19c8f8c7fbea3d1c25cc55ce8ae97) +--- + Lib/test/test_venv.py | 81 +++++++++++++++++++ + Lib/venv/__init__.py | 42 ++++++++-- + Lib/venv/scripts/common/activate | 6 +- + Lib/venv/scripts/nt/activate.bat | 4 +- + Lib/venv/scripts/posix/activate.csh | 6 +- + Lib/venv/scripts/posix/activate.fish | 6 +- + ...-09-28-02-03-04.gh-issue-124651.bLBGtH.rst | 1 + + 7 files changed, 130 insertions(+), 16 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst + +diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py +index 480cb29f35a6a4..871b8314b90b05 100644 +--- a/Lib/test/test_venv.py ++++ b/Lib/test/test_venv.py +@@ -14,6 +14,7 @@ + import subprocess + import sys + import tempfile ++import shlex + from test.support import (captured_stdout, captured_stderr, requires_zlib, + can_symlink, EnvironmentVarGuard, rmtree, + import_module, +@@ -85,6 +86,10 @@ def get_text_file_contents(self, *args, encoding='utf-8'): + 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 @@ def test_executable_symlinks(self): + '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): + """ +diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py +index 6f1af294ae63e3..299633117e6fbe 100644 +--- a/Lib/venv/__init__.py ++++ b/Lib/venv/__init__.py +@@ -11,6 +11,7 @@ + import sys + import sysconfig + import types ++import shlex + + + CORE_VENV_DEPS = ('pip', 'setuptools') +@@ -348,11 +349,41 @@ def replace_variables(self, text, context): + :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): +@@ -392,6 +423,7 @@ def install_scripts(self, context, path): + 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) +diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate +index 45af3536aa191d..1d116ca6eda4ed 100644 +--- a/Lib/venv/scripts/common/activate ++++ b/Lib/venv/scripts/common/activate +@@ -37,11 +37,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 +@@ -54,7 +54,7 @@ fi + + if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" +- PS1="__VENV_PROMPT__${PS1:-}" ++ PS1=__VENV_PROMPT__"${PS1:-}" + export PS1 + fi + +diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat +index af4c7e0abacb1c..5ca475a6e81879 100644 +--- a/Lib/venv/scripts/nt/activate.bat ++++ b/Lib/venv/scripts/nt/activate.bat +@@ -8,7 +8,7 @@ + "%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,7 +24,7 @@ + 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 "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%" + + :END + if defined _OLD_CODEPAGE ( +diff --git a/Lib/venv/scripts/posix/activate.csh b/Lib/venv/scripts/posix/activate.csh +index 68a0dc74e1a3c7..51301139517f10 100644 +--- a/Lib/venv/scripts/posix/activate.csh ++++ b/Lib/venv/scripts/posix/activate.csh +@@ -8,16 +8,16 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_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" ++ set prompt = __VENV_PROMPT__"$prompt" + endif + + alias pydoc python -m pydoc +diff --git a/Lib/venv/scripts/posix/activate.fish b/Lib/venv/scripts/posix/activate.fish +index 54b9ea5676b66b..62ab5312d6121b 100644 +--- a/Lib/venv/scripts/posix/activate.fish ++++ b/Lib/venv/scripts/posix/activate.fish +@@ -29,10 +29,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 +@@ -52,7 +52,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" | . +diff --git a/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst b/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst +new file mode 100644 +index 00000000000000..17fc9171390dd9 +--- /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/python3.spec b/python3.spec index 6921251..763f8b6 100644 --- a/python3.spec +++ b/python3.spec @@ -3,7 +3,7 @@ Summary: Interpreter of the Python3 programming language URL: https://www.python.org/ Version: 3.9.9 -Release: 34 +Release: 35 License: Python-2.0 %global branchversion 3.9 @@ -125,6 +125,7 @@ Patch6031: backport-CVE-2023-6597-gh-91133-tempfile.TemporaryDirectory-fix-symli Patch6032: backport-CVE-2024-0450-gh-109858-Protect-zipfile-from-quoted-overlap-zi.patch Patch6033: backport-3.9-gh-107845-Fix-symlink-handling-for-tarfile.data_.patch Patch6034: backport-CVE-2023-27043-gh-102988-Reject-malformed-addres.patch +Patch6035: backport-CVE-2024-9287.patch Patch9000: add-the-sm3-method-for-obtaining-the-salt-value.patch Patch9001: python3-Add-sw64-architecture.patch @@ -211,49 +212,50 @@ extension modules. find -name '*.exe' -print -delete rm -r Modules/expat -%patch1 -p1 -%patch111 -p1 -%patch251 -p1 -%patch6000 -p1 -%patch6001 -p1 -%patch6002 -p1 -%patch6003 -p1 -%patch6004 -p1 -%patch6005 -p1 -%patch6006 -p1 -%patch6007 -p1 -%patch6008 -p1 -%patch6009 -p1 -%patch6010 -p1 -%patch6011 -p1 -%patch6012 -p1 -%patch6013 -p1 -%patch6014 -p1 -%patch6015 -p1 -%patch6016 -p1 -%patch6017 -p1 -%patch6018 -p1 -%patch6019 -p1 -%patch6020 -p1 -%patch6021 -p1 -%patch6022 -p1 -%patch6023 -p1 -%patch6024 -p1 -%patch6025 -p1 -%patch6026 -p1 -%patch6027 -p1 -%patch6028 -p1 -%patch6029 -p1 -%patch6030 -p1 -%patch6031 -p1 -%patch6032 -p1 -%patch6033 -p1 -%patch6034 -p1 - -%patch9000 -p1 -%patch9001 -p1 -%patch9002 -p1 -%patch9003 -p1 +%patch -P1 -p1 +%patch -P111 -p1 +%patch -P251 -p1 +%patch -P6000 -p1 +%patch -P6001 -p1 +%patch -P6002 -p1 +%patch -P6003 -p1 +%patch -P6004 -p1 +%patch -P6005 -p1 +%patch -P6006 -p1 +%patch -P6007 -p1 +%patch -P6008 -p1 +%patch -P6009 -p1 +%patch -P6010 -p1 +%patch -P6011 -p1 +%patch -P6012 -p1 +%patch -P6013 -p1 +%patch -P6014 -p1 +%patch -P6015 -p1 +%patch -P6016 -p1 +%patch -P6017 -p1 +%patch -P6018 -p1 +%patch -P6019 -p1 +%patch -P6020 -p1 +%patch -P6021 -p1 +%patch -P6022 -p1 +%patch -P6023 -p1 +%patch -P6024 -p1 +%patch -P6025 -p1 +%patch -P6026 -p1 +%patch -P6027 -p1 +%patch -P6028 -p1 +%patch -P6029 -p1 +%patch -P6030 -p1 +%patch -P6031 -p1 +%patch -P6032 -p1 +%patch -P6033 -p1 +%patch -P6034 -p1 +%patch -P6035 -p1 + +%patch -P9000 -p1 +%patch -P9001 -p1 +%patch -P9002 -p1 +%patch -P9003 -p1 rm Lib/ensurepip/_bundled/*.whl rm configure pyconfig.h.in @@ -877,6 +879,13 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %{_mandir}/*/* %changelog +* Fri Nov 22 2024 Funda Wang - 3.9.9-35 +- Type:CVE +- CVE:CVE-2024-9287 +- SUG:NA +- DESC:fix CVE-2024-9287 + - Quote template strings in venv activation scripts + * Tue Nov 05 2024 GuoCe - 3.9.9-34 - Type:CVE - CVE:CVE-2023-27043 -- Gitee