Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python39 for openSUSE:Factory checked in at 2024-10-25 19:19:37 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python39 (Old) and /work/SRC/openSUSE:Factory/.python39.new.2020 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python39" Fri Oct 25 19:19:37 2024 rev:67 rq:1218097 version:3.9.20 Changes: -------- --- /work/SRC/openSUSE:Factory/python39/python39.changes 2024-09-29 18:10:50.474134779 +0200 +++ /work/SRC/openSUSE:Factory/.python39.new.2020/python39.changes 2024-10-25 19:20:29.680759399 +0200 @@ -1,0 +2,13 @@ +Thu Oct 24 16:09:00 UTC 2024 - Matej Cepl <mcepl@cepl.eu> + +- 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 <mcepl@cepl.eu> + +- Drop .pyc files from docdir for reproducible builds + (bsc#1230906). + +------------------------------------------------------------------- New: ---- CVE-2024-9287-venv_path_unquoted.patch BETA DEBUG BEGIN: New: - Add CVE-2024-9287-venv_path_unquoted.patch to properly quote path names provided when creating a virtual environment BETA DEBUG END: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python39.spec ++++++ --- /var/tmp/diff_new_pack.hA1Fcq/_old 2024-10-25 19:20:33.388914114 +0200 +++ /var/tmp/diff_new_pack.hA1Fcq/_new 2024-10-25 19:20:33.392914281 +0200 @@ -194,6 +194,9 @@ # PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com # status_iterator method moved between the Sphinx versions Patch51: 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 +Patch52: CVE-2024-9287-venv_path_unquoted.patch BuildRequires: autoconf-archive BuildRequires: automake @@ -467,6 +470,7 @@ %patch -P 48 -p1 %patch -P 50 -p1 %patch -P 51 -p1 +%patch -P 52 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -801,6 +805,11 @@ 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 ++++++ CVE-2024-9287-venv_path_unquoted.patch ++++++ From b6a3bbd155c558cdcda482629073e492437db3d0 Mon Sep 17 00:00:00 2001 From: y5c4l3 <y5c4l3@proton.me> Date: Sat, 28 Sep 2024 02:09:07 +0800 Subject: [PATCH] Quote template strings in `venv` activation scripts This patch properly quotes template strings in `venv` activation scripts. This mitigates potential command injection. Signed-off-by: y5c4l3 <y5c4l3@proton.me> --- Lib/test/test_venv.py | 81 ++++++++++ Lib/venv/__init__.py | 42 ++++- Lib/venv/scripts/common/activate | 6 Lib/venv/scripts/nt/activate.bat | 6 Lib/venv/scripts/posix/activate.csh | 6 Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst | 1 6 files changed, 128 insertions(+), 14 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 @@ -9,6 +9,7 @@ import ensurepip import os import os.path import re +import shlex import shutil import struct import subprocess @@ -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') @@ -348,11 +349,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.cor... + [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.cor... + """ + 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): @@ -393,6 +424,7 @@ class EnvBuilder: data = f.read() if not srcfile.endswith(('.exe', '.pdb')): try: + context.script_path = srcfile data = data.decode('utf-8') data = self.replace_variables(data, context) data = data.encode('utf-8') --- 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 --- 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 @@ -16,7 +16,7 @@ if defined _OLD_VIRTUAL_PROMPT set PROMP if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% set _OLD_VIRTUAL_PROMPT=%PROMPT% -set PROMPT=__VENV_PROMPT__%PROMPT% +set "PROMPT=__VENV_PROMPT__%PROMPT%" if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% set PYTHONHOME= @@ -24,7 +24,7 @@ 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 "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%" :END if defined _OLD_CODEPAGE ( --- a/Lib/venv/scripts/posix/activate.csh +++ b/Lib/venv/scripts/posix/activate.csh @@ -8,16 +8,16 @@ 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" + set prompt = __VENV_PROMPT__"$prompt" endif alias pydoc python -m pydoc --- /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.