Skip to content

Commit db00eb6

Browse files
committed
python3: patch CVE-2024-9287
Taken from python/cpython#126185 which is a 3.12 backport of python/cpython#124712 Signed-off-by: Saul Paredes <[email protected]>
1 parent 38f93ab commit db00eb6

File tree

6 files changed

+368
-21
lines changed

6 files changed

+368
-21
lines changed

SPECS/python3/CVE-2024-9287.patch

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

SPECS/python3/python3.spec

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Summary: A high-level scripting language
77
Name: python3
88
Version: 3.12.3
9-
Release: 5%{?dist}
9+
Release: 6%{?dist}
1010
License: PSF
1111
Vendor: Microsoft Corporation
1212
Distribution: Azure Linux
@@ -22,6 +22,7 @@ Patch2: CVE-2024-6923.patch
2222
Patch3: CVE-2024-6232.patch
2323
Patch4: CVE-2024-8088.patch
2424
Patch5: CVE-2024-12254.patch
25+
Patch6: CVE-2024-9287.patch
2526

2627
BuildRequires: bzip2-devel
2728
BuildRequires: expat-devel >= 2.1.0
@@ -243,6 +244,9 @@ rm -rf %{buildroot}%{_bindir}/__pycache__
243244
%{_libdir}/python%{majmin}/test/*
244245

245246
%changelog
247+
* Wed Dec 18 2024 Saul Paredes <[email protected]> - 3.12.3-6
248+
- Patch CVE-2024-9287
249+
246250
* Mon Dec 10 2024 Ankita Pareek <[email protected]> - 3.12.3-5
247251
- Patch CVE-2024-12254
248252

toolkit/resources/manifests/package/pkggen_core_aarch64.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,9 @@ ca-certificates-base-3.0.0-7.azl3.noarch.rpm
240240
ca-certificates-3.0.0-7.azl3.noarch.rpm
241241
dwz-0.14-2.azl3.aarch64.rpm
242242
unzip-6.0-21.azl3.aarch64.rpm
243-
python3-3.12.3-5.azl3.aarch64.rpm
244-
python3-devel-3.12.3-5.azl3.aarch64.rpm
245-
python3-libs-3.12.3-5.azl3.aarch64.rpm
243+
python3-3.12.3-6.azl3.aarch64.rpm
244+
python3-devel-3.12.3-6.azl3.aarch64.rpm
245+
python3-libs-3.12.3-6.azl3.aarch64.rpm
246246
python3-setuptools-69.0.3-4.azl3.noarch.rpm
247247
python3-pygments-2.7.4-2.azl3.noarch.rpm
248248
which-2.21-8.azl3.aarch64.rpm

toolkit/resources/manifests/package/pkggen_core_x86_64.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,9 @@ ca-certificates-base-3.0.0-7.azl3.noarch.rpm
240240
ca-certificates-3.0.0-7.azl3.noarch.rpm
241241
dwz-0.14-2.azl3.x86_64.rpm
242242
unzip-6.0-21.azl3.x86_64.rpm
243-
python3-3.12.3-5.azl3.x86_64.rpm
244-
python3-devel-3.12.3-5.azl3.x86_64.rpm
245-
python3-libs-3.12.3-5.azl3.x86_64.rpm
243+
python3-3.12.3-6.azl3.x86_64.rpm
244+
python3-devel-3.12.3-6.azl3.x86_64.rpm
245+
python3-libs-3.12.3-6.azl3.x86_64.rpm
246246
python3-setuptools-69.0.3-4.azl3.noarch.rpm
247247
python3-pygments-2.7.4-2.azl3.noarch.rpm
248248
which-2.21-8.azl3.x86_64.rpm

0 commit comments

Comments
 (0)