Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc9571a5

Browse files
mceply5c4l3vstinner
committed
[CVE-2024-9287] properly quote path names provided when creating a venv
This patch properly quotes template strings in venv activationscripts. This mitigates potential command injection.Fixes: bsc#1232241 (CVE-2024-9287)Fixes: gh#python#124651Co-authored-by: y5c4l3 <y5c4l3@proton.me>Co-authored-by: Victor Stinner <vstinner@python.org>From-PR: gh#python/cpython!124712Patch:CVE-2024-9287-venv_path_unquoted.patch
1 parentbfc7d56 commitc9571a5

File tree

6 files changed

+129
-14
lines changed

6 files changed

+129
-14
lines changed

‎Lib/test/test_venv.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
importos
1010
importos.path
1111
importre
12+
importshutil
1213
importstruct
1314
importsubprocess
1415
importsys
1516
importtempfile
17+
importshlex
1618
fromtest.supportimport (captured_stdout,captured_stderr,requires_zlib,
1719
can_symlink,EnvironmentVarGuard,rmtree)
1820
importunittest
@@ -80,6 +82,10 @@ def get_text_file_contents(self, *args):
8082
result=f.read()
8183
returnresult
8284

85+
defassertEndsWith(self,string,tail):
86+
ifnotstring.endswith(tail):
87+
self.fail(f"String{string!r} does not end with{tail!r}")
88+
8389
classBasicTest(BaseTest):
8490
"""Test venv module functionality."""
8591

@@ -293,6 +299,82 @@ def test_executable_symlinks(self):
293299
'import sys; print(sys.executable)'])
294300
self.assertEqual(out.strip(),envpy.encode())
295301

302+
# gh-124651: test quoted strings
303+
@unittest.skipIf(os.name=='nt','contains invalid characters on Windows')
304+
deftest_special_chars_bash(self):
305+
"""
306+
Test that the template strings are quoted properly (bash)
307+
"""
308+
rmtree(self.env_dir)
309+
bash=shutil.which('bash')
310+
ifbashisNone:
311+
self.skipTest('bash required for this test')
312+
env_name='"\';&&$e|\'"'
313+
env_dir=os.path.join(os.path.realpath(self.env_dir),env_name)
314+
builder=venv.EnvBuilder(clear=True)
315+
builder.create(env_dir)
316+
activate=os.path.join(env_dir,self.bindir,'activate')
317+
test_script=os.path.join(self.env_dir,'test_special_chars.sh')
318+
withopen(test_script,"w")asf:
319+
f.write(f'source{shlex.quote(activate)}\n'
320+
'python -c\'import sys; print(sys.executable)\'\n'
321+
'python -c\'import os; print(os.environ["VIRTUAL_ENV"])\'\n'
322+
'deactivate\n')
323+
out,err=check_output([bash,test_script])
324+
lines=out.splitlines()
325+
self.assertTrue(env_name.encode()inlines[0])
326+
self.assertEndsWith(lines[1],env_name.encode())
327+
328+
# gh-124651: test quoted strings
329+
@unittest.skipIf(os.name=='nt','contains invalid characters on Windows')
330+
deftest_special_chars_csh(self):
331+
"""
332+
Test that the template strings are quoted properly (csh)
333+
"""
334+
rmtree(self.env_dir)
335+
csh=shutil.which('tcsh')orshutil.which('csh')
336+
ifcshisNone:
337+
self.skipTest('csh required for this test')
338+
env_name='"\';&&$e|\'"'
339+
env_dir=os.path.join(os.path.realpath(self.env_dir),env_name)
340+
builder=venv.EnvBuilder(clear=True)
341+
builder.create(env_dir)
342+
activate=os.path.join(env_dir,self.bindir,'activate.csh')
343+
test_script=os.path.join(self.env_dir,'test_special_chars.csh')
344+
withopen(test_script,"w")asf:
345+
f.write(f'source{shlex.quote(activate)}\n'
346+
'python -c\'import sys; print(sys.executable)\'\n'
347+
'python -c\'import os; print(os.environ["VIRTUAL_ENV"])\'\n'
348+
'deactivate\n')
349+
out,err=check_output([csh,test_script])
350+
lines=out.splitlines()
351+
self.assertTrue(env_name.encode()inlines[0])
352+
self.assertEndsWith(lines[1],env_name.encode())
353+
354+
# gh-124651: test quoted strings on Windows
355+
@unittest.skipUnless(os.name=='nt','only relevant on Windows')
356+
deftest_special_chars_windows(self):
357+
"""
358+
Test that the template strings are quoted properly on Windows
359+
"""
360+
rmtree(self.env_dir)
361+
env_name="'&&^$e"
362+
env_dir=os.path.join(os.path.realpath(self.env_dir),env_name)
363+
builder=venv.EnvBuilder(clear=True)
364+
builder.create(env_dir)
365+
activate=os.path.join(env_dir,self.bindir,'activate.bat')
366+
test_batch=os.path.join(self.env_dir,'test_special_chars.bat')
367+
withopen(test_batch,"w")asf:
368+
f.write('@echo off\n'
369+
f'"{activate}" & '
370+
f'{self.exe} -c "import sys; print(sys.executable)" & '
371+
f'{self.exe} -c "import os; print(os.environ[\'VIRTUAL_ENV\'])" & '
372+
'deactivate')
373+
out,err=check_output([test_batch])
374+
lines=out.splitlines()
375+
self.assertTrue(env_name.encode()inlines[0])
376+
self.assertEndsWith(lines[1],env_name.encode())
377+
296378
@unittest.skipUnless(os.name=='nt','only relevant on Windows')
297379
deftest_unicode_in_batch_file(self):
298380
"""

‎Lib/venv/__init__.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
importsubprocess
1111
importsys
1212
importtypes
13+
importshlex
1314

1415
logger=logging.getLogger(__name__)
1516

@@ -280,11 +281,41 @@ def replace_variables(self, text, context):
280281
:param context: The information for the environment creation request
281282
being processed.
282283
"""
283-
text=text.replace('__VENV_DIR__',context.env_dir)
284-
text=text.replace('__VENV_NAME__',context.env_name)
285-
text=text.replace('__VENV_PROMPT__',context.prompt)
286-
text=text.replace('__VENV_BIN_NAME__',context.bin_name)
287-
text=text.replace('__VENV_PYTHON__',context.env_exe)
284+
replacements= {
285+
'__VENV_DIR__':context.env_dir,
286+
'__VENV_NAME__':context.env_name,
287+
'__VENV_PROMPT__':context.prompt,
288+
'__VENV_BIN_NAME__':context.bin_name,
289+
'__VENV_PYTHON__':context.env_exe,
290+
}
291+
292+
defquote_ps1(s):
293+
"""
294+
This should satisfy PowerShell quoting rules [1], unless the quoted
295+
string is passed directly to Windows native commands [2].
296+
[1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules
297+
[2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters
298+
"""
299+
s=s.replace("'","''")
300+
returnf"'{s}'"
301+
302+
defquote_bat(s):
303+
returns
304+
305+
# gh-124651: need to quote the template strings properly
306+
quote=shlex.quote
307+
script_path=context.script_path
308+
ifscript_path.endswith('.ps1'):
309+
quote=quote_ps1
310+
elifscript_path.endswith('.bat'):
311+
quote=quote_bat
312+
else:
313+
# fallbacks to POSIX shell compliant quote
314+
quote=shlex.quote
315+
316+
replacements= {key:quote(s)forkey,sinreplacements.items()}
317+
forkey,quotedinreplacements.items():
318+
text=text.replace(key,quoted)
288319
returntext
289320

290321
definstall_scripts(self,context,path):
@@ -321,6 +352,7 @@ def install_scripts(self, context, path):
321352
withopen(srcfile,'rb')asf:
322353
data=f.read()
323354
ifnotsrcfile.endswith('.exe'):
355+
context.script_path=srcfile
324356
try:
325357
data=data.decode('utf-8')
326358
data=self.replace_variables(data,context)

‎Lib/venv/scripts/common/activate

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ deactivate () {
3737
# unset irrelevant variables
3838
deactivate nondestructive
3939

40-
VIRTUAL_ENV="__VENV_DIR__"
40+
VIRTUAL_ENV=__VENV_DIR__
4141
export VIRTUAL_ENV
4242

4343
_OLD_VIRTUAL_PATH="$PATH"
44-
PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
44+
PATH="$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
4545
export PATH
4646

4747
# unset PYTHONHOME if set
@@ -55,7 +55,7 @@ fi
5555
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
5656
_OLD_VIRTUAL_PS1="${PS1:-}"
5757
if [ "x__VENV_PROMPT__" != x ] ; then
58-
PS1="__VENV_PROMPT__${PS1:-}"
58+
PS1=__VENV_PROMPT__"${PS1:-}"
5959
else
6060
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
6161
# special case for Aspen magic directories

‎Lib/venv/scripts/posix/activate.csh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
88
# Unset irrelevant variables.
99
deactivate nondestructive
1010

11-
setenv VIRTUAL_ENV"__VENV_DIR__"
11+
setenv VIRTUAL_ENV __VENV_DIR__
1212

1313
set _OLD_VIRTUAL_PATH="$PATH"
14-
setenv PATH"$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
14+
setenv PATH"$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
1515

1616

1717
set _OLD_VIRTUAL_PROMPT="$prompt"
1818

1919
if (!"$?VIRTUAL_ENV_DISABLE_PROMPT")then
2020
if ("__VENV_NAME__"!="")then
21-
set env_name ="__VENV_NAME__"
21+
set env_name = __VENV_NAME__
2222
else
2323
if (`basename"VIRTUAL_ENV"` =="__")then
2424
# special case for Aspen magic directories

‎Lib/venv/scripts/posix/activate.fish

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ end
2929
# unset irrelevant variables
3030
deactivate nondestructive
3131

32-
set-gx VIRTUAL_ENV"__VENV_DIR__"
32+
set-gx VIRTUAL_ENV __VENV_DIR__
3333

3434
set-gx _OLD_VIRTUAL_PATH$PATH
35-
set-gx PATH"$VIRTUAL_ENV/__VENV_BIN_NAME__"$PATH
35+
set-gx PATH"$VIRTUAL_ENV/"__VENV_BIN_NAME__$PATH
3636

3737
# unset PYTHONHOME if set
3838
ifset-q PYTHONHOME
@@ -53,7 +53,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
5353

5454
# Prompt override?
5555
iftest-n"__VENV_PROMPT__"
56-
printf"%s%s""__VENV_PROMPT__" (set_color normal)
56+
printf"%s%s" __VENV_PROMPT__ (set_color normal)
5757
else
5858
# ...Otherwise, prepend env
5959
set-l _checkbase (basename"$VIRTUAL_ENV")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Properly quote template strings in:mod:`venv` activation scripts.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp