virtualenv bash activate runs commands from an environment name with a backslash before $
CVE-2026-113725 · GHSA-vxj4-q4g9-crj7 · published 21 Oct 2026
How serious is it
- Severity: high, CVSS score 7.8 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by CVE-2026-113725?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
virtualenv(PyPI) <= 21.14.6
How to check
- Run
pip show virtualenvin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- Upgrade
virtualenvto 21.14.7 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
### Summary
The bash `activate` script escapes the environment name for only one of the two passes bash makes over `PS1`, so a
backslash in the name undoes the escaping and a name containing `\$(...)` runs as commands in the user's shell each
time bash draws the prompt.
### Affected area
`src/virtualenv/activation/bash/activate.sh`, the block that prepends the environment name to `PS1`.
### Attacker-controlled input
The environment name: the environment directory's name, or the `--prompt` or `VIRTUALENV_PROMPT` value. An attacker
needs to influence it, for example through a CI job or wrapper tool that passes a branch, project or job name through,
or a directory in a cloned repository or unpacked archive. `--prompt .` takes the current directory's name.
### Details
bash handles `PS1` in two passes each time it draws the prompt: it first decodes backslash prompt escapes, turning `\\`
into `\` and `\$` into `$` (or `#` for root), and then runs parameter expansion, command substitution and arithmetic
expansion on the result. Since 21.13.0 the script escapes backslashes, dollar signs and backticks in the name, but only
for the second pass, so the decode pass strips the escaping that a backslash in the name precedes. Releases before
21.13.0 do not escape the name at all. A bash running as root decodes `\$` to `#`, so the payload shows the wrong text
there but runs nothing. zsh does no decode pass and is not affected.
### Proof of concept
```console
$ virtualenv --prompt 'a\$(touch PWNED)' env
$ bash --norc --noprofile -c 'source env/bin/activate; printf "%s" "${PS1@P}"'
$ ls PWNED
PWNED
```
### Impact
Arbitrary command execution in the shell of a user who sources the `activate` script of an environment whose name an
attacker influenced, with that user's privileges, once per prompt draw.
### Versions tested
21.14.6 with bash 5.2.15 on Debian bookworm, as an unprivileged user and as root.
### Known mitigation
Set `VIRTUAL_ENV_DISABLE_PROMPT=1` before you source `activate`, which leaves `PS1` alone, or run `shopt -u promptvars`
if your prompt does not need expansion. Check the name of an environment you did not create before you source its
`activate`.
### Latest release
- [x] The problem reproduces on the latest virtualenv release.
21.14.6
### Patches
Fixed in 21.14.7. Under bash, `activate` now escapes backslashes, dollar signs and backticks for both passes, so the
example above runs nothing and the prompt shows the name as given. zsh keeps the single escaping it needs.
Fix: https://github.com/pypa/virtualenv/commit/c3104e02474b652832a9bbaae79f6e1696296932
### Credits
Found while running the virtualenv test suite as root inside the Anthropic OSS Scanner build image.
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/pypa/virtualenv/security/advisories/GHSA-vxj4-q4g9-crj7
- nvd.nist.gov/vuln/detail/CVE-2026-113725
- github.com/pypa/virtualenv/commit/c3104e02474b652832a9bbaae79f6e1696296932
- github.com/pypa/virtualenv/releases/tag/21.14.7
- github.com/advisories/GHSA-vxj4-q4g9-crj7
- github.com/pypa/virtualenv
Merged from GitHub Advisory Database, OSV. Last updated 25 Oct 2026. All vulnerabilities · RSS · JSON