Skip to content

Commit f94742d

Browse files
authored
Merge pull request #1 from ActiveState/BE-5124-2024-53899
First pass at hand cherry-picking 86ddded
2 parents 5e96f89 + 7e69a41 commit f94742d

File tree

17 files changed

+106
-50
lines changed

17 files changed

+106
-50
lines changed

docs/changelog/2768.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Properly quote string placeholders in activation script templates to mitigate
2+
potential command injection - by :user:`y5c4l3`.

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ directory = "docs/changelog"
1717
title_format = false
1818
issue_format = "`#{issue} <https://github.com/pypa/virtualenv/issues/{issue}>`_"
1919
template = "docs/changelog/template.jinja2"
20+
21+
[tool.ruff]
22+
lint.per-file-ignores."src/virtualenv/activation/python/activate_this.py" = [
23+
"F821", # ignore undefined template string placeholders
24+
]

src/virtualenv/activation/bash/activate.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ deactivate () {
4444
# unset irrelevant variables
4545
deactivate nondestructive
4646

47-
VIRTUAL_ENV='__VIRTUAL_ENV__'
47+
VIRTUAL_ENV=__VIRTUAL_ENV__
4848
if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then
4949
VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV")
5050
fi
5151
export VIRTUAL_ENV
5252

5353
_OLD_VIRTUAL_PATH="$PATH"
54-
PATH="$VIRTUAL_ENV/__BIN_NAME__:$PATH"
54+
PATH="$VIRTUAL_ENV/"__BIN_NAME__":$PATH"
5555
export PATH
5656

5757
# unset PYTHONHOME if set
@@ -62,10 +62,10 @@ fi
6262

6363
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
6464
_OLD_VIRTUAL_PS1="${PS1-}"
65-
if [ "x__VIRTUAL_PROMPT__" != x ] ; then
66-
PS1="(__VIRTUAL_PROMPT__) ${PS1-}"
65+
if [ "x"__VIRTUAL_PROMPT__ != x ] ; then
66+
PS1=(__VIRTUAL_PROMPT__) ${PS1-}
6767
else
68-
PS1="(`basename \"$VIRTUAL_ENV\"`) ${PS1-}"
68+
PS1=(`basename \"$VIRTUAL_ENV\"`) ${PS1-}
6969
fi
7070
export PS1
7171
fi

src/virtualenv/activation/batch/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ def templates(self):
1717
yield Path("deactivate.bat")
1818
yield Path("pydoc.bat")
1919

20+
@staticmethod
21+
def quote(string):
22+
return string
23+
2024
def instantiate_template(self, replacements, template, creator):
2125
# ensure the text has all newlines as \r\n - required by batch
2226
base = super(BatchActivator, self).instantiate_template(replacements, template, creator)

src/virtualenv/activation/cshell/activate.csh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
1010
# Unset irrelevant variables.
1111
deactivate nondestructive
1212

13-
setenv VIRTUAL_ENV '__VIRTUAL_ENV__'
13+
setenv VIRTUAL_ENV __VIRTUAL_ENV__
1414

1515
set _OLD_VIRTUAL_PATH="$PATH:q"
16-
setenv PATH "$VIRTUAL_ENV:q/__BIN_NAME__:$PATH:q"
16+
setenv PATH "$VIRTUAL_ENV:q/"__BIN_NAME__":$PATH:q"
1717

1818

1919

20-
if ('__VIRTUAL_PROMPT__' != "") then
21-
set env_name = '(__VIRTUAL_PROMPT__) '
20+
if (__VIRTUAL_PROMPT__ != "") then
21+
set env_name = (__VIRTUAL_PROMPT__)
2222
else
2323
set env_name = '('"$VIRTUAL_ENV:t:q"') '
2424
endif

src/virtualenv/activation/fish/activate.fish

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,15 @@ end
5757
# Unset irrelevant variables.
5858
deactivate nondestructive
5959

60-
set -gx VIRTUAL_ENV '__VIRTUAL_ENV__'
60+
set -gx VIRTUAL_ENV __VIRTUAL_ENV__
6161

6262
# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling
6363
if test (echo $FISH_VERSION | head -c 1) -lt 3
6464
set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH)
6565
else
6666
set -gx _OLD_VIRTUAL_PATH $PATH
6767
end
68-
set -gx PATH "$VIRTUAL_ENV"'/__BIN_NAME__' $PATH
68+
set -gx PATH "$VIRTUAL_ENV"'/'__BIN_NAME__ $PATH
6969

7070
# Unset `$PYTHONHOME` if set.
7171
if set -q PYTHONHOME
@@ -87,8 +87,8 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
8787

8888
# Prompt override provided?
8989
# If not, just prepend the environment name.
90-
if test -n '__VIRTUAL_PROMPT__'
91-
printf '(%s) ' '__VIRTUAL_PROMPT__'
90+
if test -n __VIRTUAL_PROMPT__
91+
printf '(%s) ' __VIRTUAL_PROMPT__
9292
else
9393
printf '(%s) ' (basename "$VIRTUAL_ENV")
9494
end

src/virtualenv/activation/nushell/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,24 @@ def templates(self):
1313
yield Path("activate.nu")
1414
yield Path("deactivate.nu")
1515

16+
@staticmethod
17+
def quote(string):
18+
"""
19+
Nushell supports raw strings like: r###'this is a string'###.
20+
This method finds the maximum continuous sharps in the string and then
21+
quote it with an extra sharp.
22+
"""
23+
max_sharps = 0
24+
current_sharps = 0
25+
for char in string:
26+
if char == "#":
27+
current_sharps += 1
28+
max_sharps = max(current_sharps, max_sharps)
29+
else:
30+
current_sharps = 0
31+
wrapping = "#" * (max_sharps + 1)
32+
return f"r{wrapping}'{string}'{wrapping}"
33+
1634
def replacements(self, creator, dest_folder):
1735
# Due to nushell scoping, it isn't easy to create a function that will
1836
# deactivate the environment. For that reason a __DEACTIVATE_PATH__

src/virtualenv/activation/nushell/activate.nu

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ def-env activate-virtualenv [] {
99
}
1010

1111
let is-windows = ((sys).host.name | str downcase) == 'windows'
12-
let virtual-env = '__VIRTUAL_ENV__'
13-
let bin = '__BIN_NAME__'
12+
let virtual-env = __VIRTUAL_ENV__
13+
let bin = __BIN_NAME__
1414
let path-sep = '__PATH_SEP__'
1515
let path-name = if $is-windows {
1616
if (has-env 'Path') {
@@ -43,10 +43,10 @@ def-env activate-virtualenv [] {
4343
let new-path = ($old-path | prepend $venv-path | str collect $path-sep)
4444

4545
# Creating the new prompt for the session
46-
let virtual-prompt = if ('__VIRTUAL_PROMPT__' == '') {
46+
let virtual-prompt = if (__VIRTUAL_PROMPT__ == '') {
4747
$'(char lparen)($virtual-env | path basename)(char rparen) '
4848
} else {
49-
'(__VIRTUAL_PROMPT__) '
49+
(__VIRTUAL_PROMPT__)
5050
}
5151

5252
# Back up the old prompt builder

src/virtualenv/activation/powershell/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,14 @@
88
class PowerShellActivator(ViaTemplateActivator):
99
def templates(self):
1010
yield Path("activate.ps1")
11+
12+
@staticmethod
13+
def quote(string):
14+
"""
15+
This should satisfy PowerShell quoting rules [1], unless the quoted
16+
string is passed directly to Windows native commands [2].
17+
[1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules
18+
[2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters
19+
""" # noqa: D205
20+
string = string.replace("'", "''")
21+
return f"'{string}'"

src/virtualenv/activation/powershell/activate.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ $env:VIRTUAL_ENV = $VIRTUAL_ENV
3535

3636
New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH
3737

38-
$env:PATH = "$env:VIRTUAL_ENV/__BIN_NAME____PATH_SEP__" + $env:PATH
38+
$env:PATH = "$env:VIRTUAL_ENV/" + __BIN_NAME____PATH_SEP__ + $env:PATH
3939
if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) {
4040
function global:_old_virtual_prompt {
4141
""
4242
}
4343
$function:_old_virtual_prompt = $function:prompt
4444

45-
if ("__VIRTUAL_PROMPT__" -ne "") {
45+
if (__VIRTUAL_PROMPT__ -ne "") {
4646
function global:prompt {
4747
# Add the custom prefix to the existing prompt
4848
$previous_prompt_value = & $function:_old_virtual_prompt

0 commit comments

Comments
 (0)