Repository navigation
chore(deps): update dependency virtualenv to v21.14.13 [security] - #745
Merged
Merged
Conversation
auto-merge was automatically disabled
October 23, 2026 08:39
Pull request was closed
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
2 times, most recently
from
October 24, 2026 02:33
f14bd9e to
1dad2e4
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 26, 2026 07:30
1dad2e4 to
f72cb9a
Compare
olivermeyer
approved these changes
Oct 26, 2026
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



This PR contains the following updates:
21.14.12→21.14.13virtualenv: Bash activator leaves the --prompt value unescaped in PS1 when promptvars is off, so prompt expansion later executes it (regression in 21.14.9)
CVE-2026-115586 / GHSA-cx6f-vjhg-g3cc
More information
Details
Summary
The bash activator puts the caller-supplied prompt value into
PS1unescaped wheneverpromptvarsis off at activation time, and assumes prompt expansion can never run afterwards.promptvarsand posix mode are runtime shell state, not a property of the activation moment: re-enabling prompt expansion in the same shell (shopt -s promptvars,set -o posix) or starting an interactive child bash that inherits the exportedPS1makes bash expand the stored value and run the command substitution in it. An attacker who controls the prompt value gets command execution as the user who sourcesbin/activate.This is a regression. 21.14.8 escaped
$and the backtick unconditionally and stays inert under the same sequence. The 21.14.9 fix for GHSA-6w3r-xr62-4xcp (#3418) rewrote that block and left bash with a path that stores the raw value wheneverpromptvarsis off.Affected area
Activation scripts (activate, activate.bat, activate.csh, activate.fish, activate.nu, Activate.ps1, activate.xsh) — specifically the bash
bin/activate.Attacker-controlled input
The
--promptvalue, which the security policy already lists as a value that may come from project metadata the user did not write. It reachesvirtualenvas--prompton the command line or as thepromptargument of the API, is written intobin/activateas the literalVIRTUAL_ENV_PROMPTassignment (correctly quoted, the value arrives there as data), and the script then copies it intoPS1without escaping$or the backtick.Example: a repository or tool config supplies a prompt such as
x$(id > /tmp/ve-pwn)y; the victim creates and later activates the environment.Details
Source of the latest release:
src/virtualenv/activation/bash/activate.shlines 140-160Two separate mistakes combine:
shopt -u promptvarsat the moment ofsourcedoes not bind the shell forever:shopt -s promptvarsandset -o posixboth re-enable PS1 expansion in the same shell. The code itself testsshopt -qo posixbecause posix mode expands the prompt regardless ofpromptvars, which shows the expansion state is not fixed by the activation.$and the backtick. Bash lost the equivalent escaping in the same commit that added the zsh branch.The rendered prompt shows the regression directly:
Relevant commit:
5f48d711"fix(bash): preserve literal prompts in supported shells (#3418)", which removed the unconditional$/backtick escaping from the bash path. Advisory for that change: GHSA-6w3r-xr62-4xcp.Proof of concept
Non-destructive payload: it runs
idand writes the output to a file.python -m virtualenv --no-seed --no-periodic-update --activators bash \ --prompt 'x$(id > /tmp/ve-pwn)y' /tmp/venv-demo cat > /tmp/rc <<'EOF' shopt -u promptvars . /tmp/venv-demo/bin/activate shopt -s promptvars EOF ##### the prompt needs a tty; script(1) provides one printf 'exit\n' | script -qec 'bash --noprofile --rcfile /tmp/rc -i' /dev/null cat /tmp/ve-pwnObserved on 21.14.12:
The prompt printed as
(xy). The same script against 21.14.8 creates no file; against 21.14.9 and 21.14.12 it does.Triggers, all run in a pty, activation itself never executing anything; execution happens at the next prompt redraw after prompt expansion is enabled:
. bin/activate(activated withshopt -u promptvars)shopt -s promptvarsset -o posixexport PS1, then start an interactive childbash(itspromptvarsdefaults to on)set -o posixalready onThe third trigger needs no explicit option change: the poisoned
PS1is inherited and the child bash expands it on its first prompt.shopt -u promptvarsis a documented way to make an expensive bash prompt cheap and appears in~/.bashrcfiles for that reason; it is also the state left by some prompt wrappers. Whoever sources such an environment gets the unescaped value inPS1.Impact
Command execution as the user who sources
bin/activate, from a value the user may not have written (project metadata or tool config reaching--prompt). The value is also persisted inpyvenv.cfgand in the generated script, so it survives the activation that created it and fires later, in a different shell session, on the first prompt redraw with expansion enabled. No privilege elevation beyond the activating user.Severity: high, matching GHSA-5vjq-rrrf-7h2q for the same class of prompt-expansion command injection reached by toggling a shell option after activation. If you consider the
promptvarsstate a caller-controlled condition rather than a shell condition, this is medium; the fix is the same either way.Versions tested
virtualenv 21.14.12 and 21.14.9 (affected), virtualenv 21.14.8 (unaffected control); CPython 3.14.7; Linux 6.x (WSL2); bash 5.1.16(1)-release.
--prompthandling is platform independent; zsh/fish/csh/nu/PowerShell activators were not part of this report.Known mitigation
For a user: activate with
promptvarsleft at its default, or setVIRTUAL_ENV_DISABLE_PROMPT=1before activating, which skips the whole prompt block. Do not pass an untrusted value to--prompt.Fix in the activator: escape
$and the backtick for bash as well, in both branches, with a single backslash ($->\$, backtick ->\`,\->\\). Verified on bash 5.1.16 that aPS1built that way is inert withpromptvarson, withpromptvarsoff, and withpromptvarsoff followed by posix mode, while still rendering the literal value on screen. That is better than both current shapes: 21.14.8's four-level escaping left stray backslashes on screen, which is why the block was rewritten.For the tests,
tests/unit/activation/test_bash.py::test_bash_prompt_optionsalready parametrises("before", "after", "expected")and coversbefore="shopt -u promptvars"withafter="". The missing axis is the transition:("shopt -u promptvars", "shopt -s promptvars", ...)and("shopt -u promptvars", "set -o posix", ...), with the existingPWNEDcheck asserting no file was created.Latest release
Severity
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
pypa/virtualenv (virtualenv)
v21.14.13Compare Source
Bugfixes - 21.14.13
Stop bash running commands from
--promptor an environment name containing$(...), backticks or a backslashafter
shopt -s promptvars,set -o posix, or in a child shell that inherits an exportedPS1(GHSA-cx6f-vjhg-g3cc); reported by rozzkml. zsh now draws the prompt from
psvar[9], which is never parsedas shell syntax.
Write the
.venvredirect and the wheelRECORDwith exclusive create, so a link planted after the existencecheck cannot redirect the write. On POSIX, a read-only
.venvredirect that points at a virtualenv is replaced.(#3466)
PyPI release
Full Changelog: pypa/virtualenv@21.14.12...21.14.13
Configuration
📅 Schedule: (in timezone Europe/Berlin)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.