Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
python-virtualenv (20.25.1+ds-1deepin1) unstable; urgency=medium

* Fix CVE-2024-53899: Command injection in activation scripts.
Properly quote string placeholders in activation script templates
to mitigate potential command injection.
Upstream: https://github.com/pypa/virtualenv/pull/2771

-- deepin-ci-robot <packages@deepin.org> Wed, 07 May 2026 23:15:00 +0800

python-virtualenv (20.25.1+ds-1) unstable; urgency=medium

* New upstream point release.
Expand Down
328 changes: 328 additions & 0 deletions debian/patches/CVE-2024-53899.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/bash/activate.sh
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/bash/activate.sh
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/bash/activate.sh
@@ -45,18 +45,18 @@ deactivate () {
# unset irrelevant variables
deactivate nondestructive

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

_OLD_VIRTUAL_PATH="$PATH"
-PATH="$VIRTUAL_ENV/__BIN_NAME__:$PATH"
+PATH="$VIRTUAL_ENV/"__BIN_NAME__":$PATH"
export PATH

-if [ "x__VIRTUAL_PROMPT__" != x ] ; then
- VIRTUAL_ENV_PROMPT="__VIRTUAL_PROMPT__"
+if [ "x"__VIRTUAL_PROMPT__ != x ] ; then
+ VIRTUAL_ENV_PROMPT=__VIRTUAL_PROMPT__
else
VIRTUAL_ENV_PROMPT=$(basename "$VIRTUAL_ENV")
fi
@@ -84,4 +84,4 @@ pydoc () {
# The hash command must be called to get it to forget past
# commands. Without forgetting past commands the $PATH changes
# we made may not be respected
-hash -r 2>/dev/null
+hash -r 2>/dev/null || true
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/batch/__init__.py
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/batch/__init__.py
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/batch/__init__.py
@@ -15,6 +15,10 @@ class BatchActivator(ViaTemplateActivato
yield "deactivate.bat"
yield "pydoc.bat"

+ @staticmethod
+ def quote(string):
+ return string
+
def instantiate_template(self, replacements, template, creator):
# ensure the text has all newlines as \r\n - required by batch
base = super().instantiate_template(replacements, template, creator)
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/cshell/activate.csh
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/cshell/activate.csh
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/cshell/activate.csh
@@ -10,15 +10,15 @@ alias deactivate 'test $?_OLD_VIRTUAL_PA
# Unset irrelevant variables.
deactivate nondestructive

-setenv VIRTUAL_ENV '__VIRTUAL_ENV__'
+setenv VIRTUAL_ENV __VIRTUAL_ENV__

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



-if ('__VIRTUAL_PROMPT__' != "") then
- setenv VIRTUAL_ENV_PROMPT '__VIRTUAL_PROMPT__'
+if (__VIRTUAL_PROMPT__ != "") then
+ setenv VIRTUAL_ENV_PROMPT __VIRTUAL_PROMPT__
else
setenv VIRTUAL_ENV_PROMPT "$VIRTUAL_ENV:t:q"
endif
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/fish/activate.fish
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/fish/activate.fish
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/fish/activate.fish
@@ -58,20 +58,20 @@ end
# Unset irrelevant variables.
deactivate nondestructive

-set -gx VIRTUAL_ENV '__VIRTUAL_ENV__'
+set -gx VIRTUAL_ENV __VIRTUAL_ENV__

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

# Prompt override provided?
# If not, just use the environment name.
-if test -n '__VIRTUAL_PROMPT__'
- set -gx VIRTUAL_ENV_PROMPT '__VIRTUAL_PROMPT__'
+if test -n __VIRTUAL_PROMPT__
+ set -gx VIRTUAL_ENV_PROMPT __VIRTUAL_PROMPT__
else
set -gx VIRTUAL_ENV_PROMPT (basename "$VIRTUAL_ENV")
end
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/nushell/activate.nu
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/nushell/activate.nu
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/nushell/activate.nu
@@ -17,7 +17,7 @@ export-env {
} | all {|i| $i == true}
}

- # Emulates a `test -z`, but btter as it handles e.g 'false'
+ # Emulates a `test -z`, but better as it handles e.g 'false'
def is-env-true [name: string] {
if (has-env $name) {
# Try to parse 'true', '0', '1', and fail if not convertible
@@ -32,8 +32,8 @@ export-env {
}
}

- let virtual_env = '__VIRTUAL_ENV__'
- let bin = '__BIN_NAME__'
+ let virtual_env = __VIRTUAL_ENV__
+ let bin = __BIN_NAME__

let is_windows = ($nu.os-info.family) == 'windows'
let path_name = (if (has-env 'Path') {
@@ -47,10 +47,10 @@ export-env {
let new_path = ($env | get $path_name | prepend $venv_path)

# If there is no default prompt, then use the env name instead
- let virtual_env_prompt = (if ('__VIRTUAL_PROMPT__' | is-empty) {
+ let virtual_env_prompt = (if (__VIRTUAL_PROMPT__ | is-empty) {
($virtual_env | path basename)
} else {
- '__VIRTUAL_PROMPT__'
+ __VIRTUAL_PROMPT__
})

let new_env = {
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/nushell/__init__.py
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/nushell/__init__.py
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/nushell/__init__.py
@@ -7,6 +7,25 @@ class NushellActivator(ViaTemplateActiva
def templates(self):
yield "activate.nu"

+ @staticmethod
+ def quote(string):
+ """
+ Nushell supports raw strings like: r###'this is a string'###.
+
+ This method finds the maximum continuous sharps in the string and then
+ quote it with an extra sharp.
+ """
+ max_sharps = 0
+ current_sharps = 0
+ for char in string:
+ if char == "#":
+ current_sharps += 1
+ max_sharps = max(current_sharps, max_sharps)
+ else:
+ current_sharps = 0
+ wrapping = "#" * (max_sharps + 1)
+ return f"r{wrapping}'{string}'{wrapping}"
+
def replacements(self, creator, dest_folder): # noqa: ARG002
return {
"__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt,
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/powershell/activate.ps1
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/powershell/activate.ps1
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/powershell/activate.ps1
@@ -37,8 +37,8 @@ deactivate -nondestructive
$VIRTUAL_ENV = $BASE_DIR
$env:VIRTUAL_ENV = $VIRTUAL_ENV

-if ("__VIRTUAL_PROMPT__" -ne "") {
- $env:VIRTUAL_ENV_PROMPT = "__VIRTUAL_PROMPT__"
+if (__VIRTUAL_PROMPT__ -ne "") {
+ $env:VIRTUAL_ENV_PROMPT = __VIRTUAL_PROMPT__
}
else {
$env:VIRTUAL_ENV_PROMPT = $( Split-Path $env:VIRTUAL_ENV -Leaf )
@@ -46,7 +46,7 @@ else {

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

-$env:PATH = "$env:VIRTUAL_ENV/__BIN_NAME____PATH_SEP__" + $env:PATH
+$env:PATH = "$env:VIRTUAL_ENV/" + __BIN_NAME__ + __PATH_SEP__ + $env:PATH
if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) {
function global:_old_virtual_prompt {
""
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/powershell/__init__.py
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/powershell/__init__.py
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/powershell/__init__.py
@@ -7,6 +7,18 @@ class PowerShellActivator(ViaTemplateAct
def templates(self):
yield "activate.ps1"

+ @staticmethod
+ def quote(string):
+ """
+ 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.core/about/about_quoting_rules
+ [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters
+ """ # noqa: D205
+ string = string.replace("'", "''")
+ return f"'{string}'"
+

__all__ = [
"PowerShellActivator",
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/python/activate_this.py
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/python/activate_this.py
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/python/activate_this.py
@@ -1,7 +1,8 @@
"""
Activate virtualenv for current interpreter:

-Use exec(open(this_file).read(), {'__file__': this_file}).
+import runpy
+runpy.run_path(this_file)

This can be used when you must use an existing Python interpreter, not the virtualenv bin/python.
""" # noqa: D415
@@ -15,22 +16,22 @@ import sys
try:
abs_file = os.path.abspath(__file__)
except NameError as exc:
- msg = "You must use exec(open(this_file).read(), {'__file__': this_file})"
+ msg = "You must use import runpy; runpy.run_path(this_file)"
raise AssertionError(msg) from exc

bin_dir = os.path.dirname(abs_file)
-base = bin_dir[: -len("__BIN_NAME__") - 1] # strip away the bin part from the __file__, plus the path separator
+base = bin_dir[: -len(__BIN_NAME__) - 1] # strip away the bin part from the __file__, plus the path separator

# prepend bin to PATH (this file is inside the bin directory)
os.environ["PATH"] = os.pathsep.join([bin_dir, *os.environ.get("PATH", "").split(os.pathsep)])
os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory
-os.environ["VIRTUAL_ENV_PROMPT"] = "__VIRTUAL_PROMPT__" or os.path.basename(base) # noqa: SIM222
+os.environ["VIRTUAL_ENV_PROMPT"] = __VIRTUAL_PROMPT__ or os.path.basename(base)

# add the virtual environments libraries to the host python import mechanism
prev_length = len(sys.path)
-for lib in "__LIB_FOLDERS__".split(os.pathsep):
+for lib in __LIB_FOLDERS__.split(os.pathsep):
path = os.path.realpath(os.path.join(bin_dir, lib))
- site.addsitedir(path.decode("utf-8") if "__DECODE_PATH__" else path)
+ site.addsitedir(path.decode("utf-8") if __DECODE_PATH__ else path)
sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]

sys.real_prefix = sys.prefix
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/python/__init__.py
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/python/__init__.py
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/python/__init__.py
@@ -10,10 +10,14 @@ class PythonActivator(ViaTemplateActivat
def templates(self):
yield "activate_this.py"

+ @staticmethod
+ def quote(string):
+ return repr(string)
+
def replacements(self, creator, dest_folder):
replacements = super().replacements(creator, dest_folder)
lib_folders = OrderedDict((os.path.relpath(str(i), str(dest_folder)), None) for i in creator.libs)
- lib_folders = os.pathsep.join(lib_folders.keys()).replace("\\", "\\\\") # escape Windows path characters
+ lib_folders = os.pathsep.join(lib_folders.keys())
replacements.update(
{
"__LIB_FOLDERS__": lib_folders,
Index: github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/via_template.py
===================================================================
--- github-python-virtualenv-CVE-2024-53899.orig/src/virtualenv/activation/via_template.py
+++ github-python-virtualenv-CVE-2024-53899/src/virtualenv/activation/via_template.py
@@ -1,6 +1,7 @@
from __future__ import annotations

import os
+import shlex
import sys
from abc import ABC, abstractmethod

@@ -21,6 +22,16 @@ class ViaTemplateActivator(Activator, AB
def templates(self):
raise NotImplementedError

+ @staticmethod
+ def quote(string):
+ """
+ Quote strings in the activation script.
+
+ :param string: the string to quote
+ :return: quoted string that works in the activation script
+ """
+ return shlex.quote(string)
+
def generate(self, creator):
dest_folder = creator.bin_dir
replacements = self.replacements(creator, dest_folder)
@@ -47,8 +58,10 @@ class ViaTemplateActivator(Activator, AB
# errors when the dest is not writable
if dest.exists():
dest.unlink()
+ # Powershell assumes Windows 1252 encoding when reading files without BOM
+ encoding = "utf-8-sig" if str(template).endswith(".ps1") else "utf-8"
# use write_bytes to avoid platform specific line normalization (\n -> \r\n)
- dest.write_bytes(text.encode("utf-8"))
+ dest.write_bytes(text.encode(encoding))
generated.append(dest)
return generated

@@ -61,7 +74,7 @@ class ViaTemplateActivator(Activator, AB
text = binary.decode("utf-8", errors="strict")
for key, value in replacements.items():
value_uni = self._repr_unicode(creator, value)
- text = text.replace(key, value_uni)
+ text = text.replace(key, self.quote(value_uni))
return text

@staticmethod
1 change: 1 addition & 0 deletions debian/patches/series
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
CVE-2024-53899.patch
debian_wheel_location.patch
debian_update_for_available_wheels.patch
disable-periodic-update.patch
Expand Down
Loading