Skip to content

Commit 0c6061a

Browse files
[AUTO-CHERRYPICK] python3: patch CVE-2024-9287 [Medium] - branch main (#12739)
Co-authored-by: ndubchak <144380684+ndubchak@users.noreply.github.com>
1 parent b88f679 commit 0c6061a

6 files changed

Lines changed: 336 additions & 28 deletions

File tree

SPECS/python3/CVE-2024-9287.patch

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
2+
index 480cb29..871b831 100644
3+
--- a/Lib/test/test_venv.py
4+
+++ b/Lib/test/test_venv.py
5+
@@ -14,6 +14,7 @@ import struct
6+
import subprocess
7+
import sys
8+
import tempfile
9+
+import shlex
10+
from test.support import (captured_stdout, captured_stderr, requires_zlib,
11+
can_symlink, EnvironmentVarGuard, rmtree,
12+
import_module,
13+
@@ -85,6 +86,10 @@ class BaseTest(unittest.TestCase):
14+
result = f.read()
15+
return result
16+
17+
+ def assertEndsWith(self, string, tail):
18+
+ if not string.endswith(tail):
19+
+ self.fail(f"String {string!r} does not end with {tail!r}")
20+
+
21+
class BasicTest(BaseTest):
22+
"""Test venv module functionality."""
23+
24+
@@ -342,6 +347,82 @@ class BasicTest(BaseTest):
25+
'import sys; print(sys.executable)'])
26+
self.assertEqual(out.strip(), envpy.encode())
27+
28+
+ # gh-124651: test quoted strings
29+
+ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
30+
+ def test_special_chars_bash(self):
31+
+ """
32+
+ Test that the template strings are quoted properly (bash)
33+
+ """
34+
+ rmtree(self.env_dir)
35+
+ bash = shutil.which('bash')
36+
+ if bash is None:
37+
+ self.skipTest('bash required for this test')
38+
+ env_name = '"\';&&$e|\'"'
39+
+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
40+
+ builder = venv.EnvBuilder(clear=True)
41+
+ builder.create(env_dir)
42+
+ activate = os.path.join(env_dir, self.bindir, 'activate')
43+
+ test_script = os.path.join(self.env_dir, 'test_special_chars.sh')
44+
+ with open(test_script, "w") as f:
45+
+ f.write(f'source {shlex.quote(activate)}\n'
46+
+ 'python -c \'import sys; print(sys.executable)\'\n'
47+
+ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n'
48+
+ 'deactivate\n')
49+
+ out, err = check_output([bash, test_script])
50+
+ lines = out.splitlines()
51+
+ self.assertTrue(env_name.encode() in lines[0])
52+
+ self.assertEndsWith(lines[1], env_name.encode())
53+
+
54+
+ # gh-124651: test quoted strings
55+
+ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
56+
+ def test_special_chars_csh(self):
57+
+ """
58+
+ Test that the template strings are quoted properly (csh)
59+
+ """
60+
+ rmtree(self.env_dir)
61+
+ csh = shutil.which('tcsh') or shutil.which('csh')
62+
+ if csh is None:
63+
+ self.skipTest('csh required for this test')
64+
+ env_name = '"\';&&$e|\'"'
65+
+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
66+
+ builder = venv.EnvBuilder(clear=True)
67+
+ builder.create(env_dir)
68+
+ activate = os.path.join(env_dir, self.bindir, 'activate.csh')
69+
+ test_script = os.path.join(self.env_dir, 'test_special_chars.csh')
70+
+ with open(test_script, "w") as f:
71+
+ f.write(f'source {shlex.quote(activate)}\n'
72+
+ 'python -c \'import sys; print(sys.executable)\'\n'
73+
+ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n'
74+
+ 'deactivate\n')
75+
+ out, err = check_output([csh, test_script])
76+
+ lines = out.splitlines()
77+
+ self.assertTrue(env_name.encode() in lines[0])
78+
+ self.assertEndsWith(lines[1], env_name.encode())
79+
+
80+
+ # gh-124651: test quoted strings on Windows
81+
+ @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
82+
+ def test_special_chars_windows(self):
83+
+ """
84+
+ Test that the template strings are quoted properly on Windows
85+
+ """
86+
+ rmtree(self.env_dir)
87+
+ env_name = "'&&^$e"
88+
+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
89+
+ builder = venv.EnvBuilder(clear=True)
90+
+ builder.create(env_dir)
91+
+ activate = os.path.join(env_dir, self.bindir, 'activate.bat')
92+
+ test_batch = os.path.join(self.env_dir, 'test_special_chars.bat')
93+
+ with open(test_batch, "w") as f:
94+
+ f.write('@echo off\n'
95+
+ f'"{activate}" & '
96+
+ f'{self.exe} -c "import sys; print(sys.executable)" & '
97+
+ f'{self.exe} -c "import os; print(os.environ[\'VIRTUAL_ENV\'])" & '
98+
+ 'deactivate')
99+
+ out, err = check_output([test_batch])
100+
+ lines = out.splitlines()
101+
+ self.assertTrue(env_name.encode() in lines[0])
102+
+ self.assertEndsWith(lines[1], env_name.encode())
103+
+
104+
@unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
105+
def test_unicode_in_batch_file(self):
106+
"""
107+
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
108+
index 6f1af29..2996331 100644
109+
--- a/Lib/venv/__init__.py
110+
+++ b/Lib/venv/__init__.py
111+
@@ -11,6 +11,7 @@ import subprocess
112+
import sys
113+
import sysconfig
114+
import types
115+
+import shlex
116+
117+
118+
CORE_VENV_DEPS = ('pip', 'setuptools')
119+
@@ -348,11 +349,41 @@ class EnvBuilder:
120+
:param context: The information for the environment creation request
121+
being processed.
122+
"""
123+
- text = text.replace('__VENV_DIR__', context.env_dir)
124+
- text = text.replace('__VENV_NAME__', context.env_name)
125+
- text = text.replace('__VENV_PROMPT__', context.prompt)
126+
- text = text.replace('__VENV_BIN_NAME__', context.bin_name)
127+
- text = text.replace('__VENV_PYTHON__', context.env_exe)
128+
+ replacements = {
129+
+ '__VENV_DIR__': context.env_dir,
130+
+ '__VENV_NAME__': context.env_name,
131+
+ '__VENV_PROMPT__': context.prompt,
132+
+ '__VENV_BIN_NAME__': context.bin_name,
133+
+ '__VENV_PYTHON__': context.env_exe,
134+
+ }
135+
+
136+
+ def quote_ps1(s):
137+
+ """
138+
+ This should satisfy PowerShell quoting rules [1], unless the quoted
139+
+ string is passed directly to Windows native commands [2].
140+
+ [1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules
141+
+ [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters
142+
+ """
143+
+ s = s.replace("'", "''")
144+
+ return f"'{s}'"
145+
+
146+
+ def quote_bat(s):
147+
+ return s
148+
+
149+
+ # gh-124651: need to quote the template strings properly
150+
+ quote = shlex.quote
151+
+ script_path = context.script_path
152+
+ if script_path.endswith('.ps1'):
153+
+ quote = quote_ps1
154+
+ elif script_path.endswith('.bat'):
155+
+ quote = quote_bat
156+
+ else:
157+
+ # fallbacks to POSIX shell compliant quote
158+
+ quote = shlex.quote
159+
+
160+
+ replacements = {key: quote(s) for key, s in replacements.items()}
161+
+ for key, quoted in replacements.items():
162+
+ text = text.replace(key, quoted)
163+
return text
164+
165+
def install_scripts(self, context, path):
166+
@@ -392,6 +423,7 @@ class EnvBuilder:
167+
with open(srcfile, 'rb') as f:
168+
data = f.read()
169+
if not srcfile.endswith(('.exe', '.pdb')):
170+
+ context.script_path = srcfile
171+
try:
172+
data = data.decode('utf-8')
173+
data = self.replace_variables(data, context)
174+
diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate
175+
index 45af353..1d116ca 100644
176+
--- a/Lib/venv/scripts/common/activate
177+
+++ b/Lib/venv/scripts/common/activate
178+
@@ -37,11 +37,11 @@ deactivate () {
179+
# unset irrelevant variables
180+
deactivate nondestructive
181+
182+
-VIRTUAL_ENV="__VENV_DIR__"
183+
+VIRTUAL_ENV=__VENV_DIR__
184+
export VIRTUAL_ENV
185+
186+
_OLD_VIRTUAL_PATH="$PATH"
187+
-PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
188+
+PATH="$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
189+
export PATH
190+
191+
# unset PYTHONHOME if set
192+
@@ -54,7 +54,7 @@ fi
193+
194+
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
195+
_OLD_VIRTUAL_PS1="${PS1:-}"
196+
- PS1="__VENV_PROMPT__${PS1:-}"
197+
+ PS1=__VENV_PROMPT__"${PS1:-}"
198+
export PS1
199+
fi
200+
201+
diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat
202+
index f61413e..f910aa1 100644
203+
--- a/Lib/venv/scripts/nt/activate.bat
204+
+++ b/Lib/venv/scripts/nt/activate.bat
205+
@@ -5,13 +5,13 @@ for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do (
206+
set _OLD_CODEPAGE=%%a
207+
)
208+
if defined _OLD_CODEPAGE (
209+
- "%SystemRoot%\System32\chcp.com" 65001 > nul
210+
-)
211+
-
212+
-set VIRTUAL_ENV=__VENV_DIR__
213+
-
214+
-if not defined PROMPT set PROMPT=$P$G
215+
-
216+
+ "%SystemRoot%\System32\chcp.com" 65001 > nul
217+
+)
218+
+
219+
+set "VIRTUAL_ENV=__VENV_DIR__"
220+
+
221+
+if not defined PROMPT set PROMPT=$P$G
222+
+
223+
if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT%
224+
if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%
225+
226+
@@ -21,13 +21,13 @@ set PROMPT=__VENV_PROMPT__%PROMPT%
227+
if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
228+
set PYTHONHOME=
229+
230+
-if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
231+
-if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%
232+
-
233+
-set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%
234+
-
235+
-:END
236+
-if defined _OLD_CODEPAGE (
237+
+if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
238+
+if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%
239+
+
240+
+set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%"
241+
+
242+
+:END
243+
+if defined _OLD_CODEPAGE (
244+
"%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul
245+
set _OLD_CODEPAGE=
246+
)
247+
diff --git a/Lib/venv/scripts/posix/activate.csh b/Lib/venv/scripts/posix/activate.csh
248+
index 68a0dc7..5130113 100644
249+
--- a/Lib/venv/scripts/posix/activate.csh
250+
+++ b/Lib/venv/scripts/posix/activate.csh
251+
@@ -8,16 +8,16 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
252+
# Unset irrelevant variables.
253+
deactivate nondestructive
254+
255+
-setenv VIRTUAL_ENV "__VENV_DIR__"
256+
+setenv VIRTUAL_ENV __VENV_DIR__
257+
258+
set _OLD_VIRTUAL_PATH="$PATH"
259+
-setenv PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
260+
+setenv PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
261+
262+
263+
set _OLD_VIRTUAL_PROMPT="$prompt"
264+
265+
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
266+
- set prompt = "__VENV_PROMPT__$prompt"
267+
+ set prompt = __VENV_PROMPT__"$prompt"
268+
endif
269+
270+
alias pydoc python -m pydoc
271+
diff --git a/Lib/venv/scripts/posix/activate.fish b/Lib/venv/scripts/posix/activate.fish
272+
index 54b9ea5..62ab531 100644
273+
--- a/Lib/venv/scripts/posix/activate.fish
274+
+++ b/Lib/venv/scripts/posix/activate.fish
275+
@@ -29,10 +29,10 @@ end
276+
# Unset irrelevant variables.
277+
deactivate nondestructive
278+
279+
-set -gx VIRTUAL_ENV "__VENV_DIR__"
280+
+set -gx VIRTUAL_ENV __VENV_DIR__
281+
282+
set -gx _OLD_VIRTUAL_PATH $PATH
283+
-set -gx PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__" $PATH
284+
+set -gx PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__ $PATH
285+
286+
# Unset PYTHONHOME if set.
287+
if set -q PYTHONHOME
288+
@@ -52,7 +52,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
289+
set -l old_status $status
290+
291+
# Output the venv prompt; color taken from the blue of the Python logo.
292+
- printf "%s%s%s" (set_color 4B8BBE) "__VENV_PROMPT__" (set_color normal)
293+
+ printf "%s%s%s" (set_color 4B8BBE) __VENV_PROMPT__ (set_color normal)
294+
295+
# Restore the return status of the previous command.
296+
echo "exit $old_status" | .
297+
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
298+
new file mode 100644
299+
index 0000000..17fc917
300+
--- /dev/null
301+
+++ b/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst
302+
@@ -0,0 +1 @@
303+
+Properly quote template strings in :mod:`venv` activation scripts.

SPECS/python3/python3.spec

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
Summary: A high-level scripting language
1313
Name: python3
1414
Version: 3.9.19
15-
Release: 10%{?dist}
15+
Release: 11%{?dist}
1616
License: PSF
1717
Vendor: Microsoft Corporation
1818
Distribution: Mariner
@@ -31,6 +31,7 @@ Patch7: CVE-2024-11168.patch
3131
Patch8: CVE-2024-6923.patch
3232
Patch9: CVE-2023-27043.patch
3333
Patch10: CVE-2025-0938.patch
34+
Patch11: CVE-2024-9287.patch
3435
# Patch for setuptools, resolved in 65.5.1
3536
Patch1000: CVE-2022-40897.patch
3637
Patch1001: CVE-2024-6345.patch
@@ -179,6 +180,7 @@ The test package contains all regression tests for Python as well as the modules
179180
%patch8 -p1
180181
%patch9 -p1
181182
%patch10 -p1
183+
%patch11 -p1
182184

183185
%build
184186
# Remove GCC specs and build environment linker scripts
@@ -334,10 +336,13 @@ rm -rf %{buildroot}%{_bindir}/__pycache__
334336
%{_libdir}/python%{majmin}/test/*
335337

336338
%changelog
339+
* Wed Feb 26 2025 Nadiia Dubchak <ndubchak@microsoft.com> - 3.9.19-11
340+
- Patch CVE-2024-9287
341+
337342
* Thu Feb 06 2025 Kanishk Bansal <kanbansal@microsoft.com> - 3.9.19-10
338343
- Patch CVE-2025-0938
339344

340-
* Mon Feb 03 2024 Bala <balakumaran.kannan@microsoft.com> - 3.9.19-9
345+
* Mon Feb 03 2025 Balakumaran Kannan <balakumaran.kannan@microsoft.com> - 3.9.19-9
341346
- Address CVE-2023-27043 by patching
342347

343348
* Thu Nov 28 2024 Kanishk Bansal <kanbansal@microsoft.com> - 3.9.19-8

toolkit/resources/manifests/package/pkggen_core_aarch64.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ ca-certificates-base-2.0.0-19.cm2.noarch.rpm
237237
ca-certificates-2.0.0-19.cm2.noarch.rpm
238238
dwz-0.14-2.cm2.aarch64.rpm
239239
unzip-6.0-21.cm2.aarch64.rpm
240-
python3-3.9.19-10.cm2.aarch64.rpm
241-
python3-devel-3.9.19-10.cm2.aarch64.rpm
242-
python3-libs-3.9.19-10.cm2.aarch64.rpm
243-
python3-setuptools-3.9.19-10.cm2.noarch.rpm
240+
python3-3.9.19-11.cm2.aarch64.rpm
241+
python3-devel-3.9.19-11.cm2.aarch64.rpm
242+
python3-libs-3.9.19-11.cm2.aarch64.rpm
243+
python3-setuptools-3.9.19-11.cm2.noarch.rpm
244244
python3-pygments-2.4.2-7.cm2.noarch.rpm
245245
which-2.21-8.cm2.aarch64.rpm
246246
libselinux-3.2-1.cm2.aarch64.rpm

toolkit/resources/manifests/package/pkggen_core_x86_64.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ ca-certificates-base-2.0.0-19.cm2.noarch.rpm
237237
ca-certificates-2.0.0-19.cm2.noarch.rpm
238238
dwz-0.14-2.cm2.x86_64.rpm
239239
unzip-6.0-21.cm2.x86_64.rpm
240-
python3-3.9.19-10.cm2.x86_64.rpm
241-
python3-devel-3.9.19-10.cm2.x86_64.rpm
242-
python3-libs-3.9.19-10.cm2.x86_64.rpm
243-
python3-setuptools-3.9.19-10.cm2.noarch.rpm
240+
python3-3.9.19-11.cm2.x86_64.rpm
241+
python3-devel-3.9.19-11.cm2.x86_64.rpm
242+
python3-libs-3.9.19-11.cm2.x86_64.rpm
243+
python3-setuptools-3.9.19-11.cm2.noarch.rpm
244244
python3-pygments-2.4.2-7.cm2.noarch.rpm
245245
which-2.21-8.cm2.x86_64.rpm
246246
libselinux-3.2-1.cm2.x86_64.rpm

0 commit comments

Comments
 (0)