CVE-2026-115586
virtualenv: Bash activator leaves the --prompt value unescaped in PS1 when promptvars is off, so prompt expansion later executes it (regression in 21.14.9)
virtualenv is a tool for creating isolated virtual python environments. From 21.14.9 until 21.14.13, the bash bin/activate script can copy an attacker-controlled --prompt value into PS1 without escaping dollar signs or backticks when both promptvars and POSIX mode are disabled during activation. Re-enabling promptvars, running set -o posix, or starting an interactive child bash that inherits the exported PS1 can then expand command substitution in the stored VIRTUAL_ENV_PROMPT value and execute commands as the user who activates the environment. This issue is fixed in version 21.14.13.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- High
- Privileges needed
- None
- User interaction
- Active
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.1
virtualenv: Bash activator leaves the --prompt value unescaped in PS1 when promptvars is off, so prompt expansion later executes it (regression in 21.14.9)
virtualenv is a tool for creating isolated virtual python environments. From 21.14.9 until 21.14.13, the bash bin/activate script can copy an attacker-controlled --prompt value into PS1 without escaping dollar signs or backticks when both promptvars and POSIX mode are disabled during activation. Re-enabling promptvars, running set -o posix, or starting an interactive child bash that inherits the exported PS1 can then expand command substitution in the stored VIRTUAL_ENV_PROMPT value and execute commands as the user who activates the environment. This issue is fixed in version 21.14.13.
- Record
CVE-2026-115586- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-78
- Updated
- 2026-11-02
EUVDEUVD-2026-109131
7.1
virtualenv is a tool for creating isolated virtual python environments. From 21.14.9 until 21.14.13, the bash bin/activate script can copy an attacker-controlled --prompt value into PS1 without escaping dollar signs or backticks when both promptvars and POSIX mode are disabled during activation. Re-enabling promptvars, running set -o posix, or starting an interactive child bash that inherits the exported PS1 can then expand command substitution in the stored VIRTUAL_ENV_PROMPT value and execute commands as the user who activates the environment. This issue is fixed in version 21.14.13.
- Record
EUVD-2026-109131- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-cx6f-vjhg-g3cc
7.1
virtualenv: Bash activator leaves the --prompt value unescaped in PS1 when promptvars is off, so prompt expansion later executes it (regression in 21.14.9)
Summary
The bash activator puts the caller-supplied prompt value into PS1 unescaped whenever promptvars is off at activation time, and assumes prompt expansion can never run afterwards. promptvars and 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 exported PS1 makes 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 sources bin/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 whenever promptvars is 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 --prompt value, which the security policy already lists as a value that may come from project metadata the user did not write. It reaches virtualenv as --prompt on the command line or as the prompt argument of the API, is written into bin/activate as the literal VIRTUAL_ENV_PROMPT assignment (correctly quoted, the value arrives there as data), and the script then copies it into PS1 without 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.sh lines 140-160
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] &&
{ [ -n "${BASH_VERSION-}" ] || [ -n "${ZSH_VERSION-}" ]; }; then
_OLD_VIRTUAL_PS1="${PS1-}"
# bash expands PS1 after decoding its prompt escapes when promptvars is on or in posix mode, so the prompt holds a
# reference to the variable and its value never reaches that expansion; otherwise only the decode runs and
# backslashes need doubling. ...
if [ -n "${BASH_VERSION-}" ] && { shopt -q promptvars || shopt -qo posix; }; then
PS1='(${VIRTUAL_ENV_PROMPT-}) '"${PS1-}"
else
_VIRTUAL_PROMPT="${VIRTUAL_ENV_PROMPT//\\/\\\\}"
if [ -n "${ZSH_VERSION-}" ]; then
_VIRTUAL_PROMPT="${_VIRTUAL_PROMPT//\$/\\\$}"
_VIRTUAL_PROMPT="${_VIRTUAL_PROMPT//\`/\\\`}"
_VIRTUAL_PROMPT="${_VIRTUAL_PROMPT//\%/%%}"
fi
PS1="(${_VIRTUAL_PROMPT}) ${PS1-}"
unset _VIRTUAL_PROMPT
fi
fi
Two separate mistakes combine:
- A runtime shell option is treated as a constant.
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. - The escaping that would keep the value inert in that mode is missing for bash. GHSA-5vjq-rrrf-7h2q ("activate runs commands from the environment name in zsh with PROMPT_SUBST") fixed exactly this shape for zsh, reasoning that "a user can set it after activation", by escaping
$and the backtick. Bash lost the equivalent escaping in the same commit that added the zsh branch.
The rendered prompt shows the regression directly:
21.14.8 PS1 = (x\\$(id > /tmp/ve-pwn)y) <- escaped, inert
21.14.9 PS1 = (x$(id > /tmp/ve-pwn)y) <- raw value
21.14.12 PS1 = (x$(id > /tmp/ve-pwn)y) <- raw value
Relevant commit: [5f48d711 "fix(bash): preserve literal prompts in supported shells…
- Record
GHSA-cx6f-vjhg-g3cc- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-78
- Updated
- 2026-11-02
NVD
7.1
virtualenv is a tool for creating isolated virtual python environments. From 21.14.9 until 21.14.13, the bash bin/activate script can copy an attacker-controlled --prompt value into PS1 without escaping dollar signs or backticks when both promptvars and POSIX mode are disabled during activation. Re-enabling promptvars, running set -o posix, or starting an interactive child bash that inherits the exported PS1 can then expand command substitution in the stored VIRTUAL_ENV_PROMPT value and execute commands as the user who activates the environment. This issue is fixed in version 21.14.13.
- Record
CVE-2026-115586- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-78
- Updated
- 2026-11-02
OSV
7.1
virtualenv: Bash activator leaves the --prompt value unescaped in PS1 when promptvars is off, so prompt expansion later executes it (regression in 21.14.9)
virtualenv is a tool for creating isolated virtual python environments. From 21.14.9 until 21.14.13, the bash bin/activate script can copy an attacker-controlled --prompt value into PS1 without escaping dollar signs or backticks when both promptvars and POSIX mode are disabled during activation. Re-enabling promptvars, running set -o posix, or starting an interactive child bash that inherits the exported PS1 can then expand command substitution in the stored VIRTUAL_ENV_PROMPT value and execute commands as the user who activates the environment. This issue is fixed in version 21.14.13.
- Record
CVE-2026-115586- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-78
- Updated
- 2026-11-02
OSVGHSA-cx6f-vjhg-g3cc
7.1
virtualenv: Bash activator leaves the --prompt value unescaped in PS1 when promptvars is off, so prompt expansion later executes it (regression in 21.14.9)
Summary
The bash activator puts the caller-supplied prompt value into PS1 unescaped whenever promptvars is off at activation time, and assumes prompt expansion can never run afterwards. promptvars and 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 exported PS1 makes 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 sources bin/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 whenever promptvars is 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 --prompt value, which the security policy already lists as a value that may come from project metadata the user did not write. It reaches virtualenv as --prompt on the command line or as the prompt argument of the API, is written into bin/activate as the literal VIRTUAL_ENV_PROMPT assignment (correctly quoted, the value arrives there as data), and the script then copies it into PS1 without 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.sh lines 140-160
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] &&
{ [ -n "${BASH_VERSION-}" ] || [ -n "${ZSH_VERSION-}" ]; }; then
_OLD_VIRTUAL_PS1="${PS1-}"
# bash expands PS1 after decoding its prompt escapes when promptvars is on or in posix mode, so the prompt holds a
# reference to the variable and its value never reaches that expansion; otherwise only the decode runs and
# backslashes need doubling. ...
if [ -n "${BASH_VERSION-}" ] && { shopt -q promptvars || shopt -qo posix; }; then
PS1='(${VIRTUAL_ENV_PROMPT-}) '"${PS1-}"
else
_VIRTUAL_PROMPT="${VIRTUAL_ENV_PROMPT//\\/\\\\}"
if [ -n "${ZSH_VERSION-}" ]; then
_VIRTUAL_PROMPT="${_VIRTUAL_PROMPT//\$/\\\$}"
_VIRTUAL_PROMPT="${_VIRTUAL_PROMPT//\`/\\\`}"
_VIRTUAL_PROMPT="${_VIRTUAL_PROMPT//\%/%%}"
fi
PS1="(${_VIRTUAL_PROMPT}) ${PS1-}"
unset _VIRTUAL_PROMPT
fi
fi
Two separate mistakes combine:
- A runtime shell option is treated as a constant.
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. - The escaping that would keep the value inert in that mode is missing for bash. GHSA-5vjq-rrrf-7h2q ("activate runs commands from the environment name in zsh with PROMPT_SUBST") fixed exactly this shape for zsh, reasoning that "a user can set it after activation", by escaping
$and the backtick. Bash lost the equivalent escaping in the same commit that added the zsh branch.
The rendered prompt shows the regression directly:
21.14.8 PS1 = (x\\$(id > /tmp/ve-pwn)y) <- escaped, inert
21.14.9 PS1 = (x$(id > /tmp/ve-pwn)y) <- raw value
21.14.12 PS1 = (x$(id > /tmp/ve-pwn)y) <- raw value
Relevant commit: [5f48d711 "fix(bash): preserve literal prompts in supported shells…
- Record
GHSA-cx6f-vjhg-g3cc- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-78
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-115586
7.1
virtualenv is a tool for creating isolated virtual python environments. From 21.14.9 until 21.14.13, the bash bin/activate script can copy an attacker-controlled --prompt value into PS1 without escaping dollar signs or backticks when both promptvars and POSIX mode are disabled during activation. Re-enabling promptvars, running set -o posix, or starting an interactive child bash that inherits the exported PS1 can then expand command substitution in the stored VIRTUAL_ENV_PROMPT value and execute commands as the user who activates the environment. This issue is fixed in version 21.14.13.
- Record
UBUNTU-CVE-2026-115586- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Updated
- 2026-11-02
OSVBIT-virtualenv-2026-115586 No score
virtualenv: Bash activator leaves the --prompt value unescaped in PS1 when promptvars is off, so prompt expansion later executes it (regression in 21.14.9)
virtualenv is a tool for creating isolated virtual python environments. From 21.14.9 until 21.14.13, the bash bin/activate script can copy an attacker-controlled --prompt value into PS1 without escaping dollar signs or backticks when both promptvars and POSIX mode are disabled during activation. Re-enabling promptvars, running set -o posix, or starting an interactive child bash that inherits the exported PS1 can then expand command substitution in the stored VIRTUAL_ENV_PROMPT value and execute commands as the user who activates the environment. This issue is fixed in version 21.14.13.
- Record
BIT-virtualenv-2026-115586- Updated
- 2026-11-02
OSVopenSUSE-SU-2026:12372-1 No score
python313-virtualenv-21.15.1-1.1 on GA media
These are all security issues fixed in the python313-virtualenv-21.15.1-1.1 package on the GA media of openSUSE Tumbleweed.
- Record
openSUSE-SU-2026:12372-1- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
virtualenvPyPI |
21.14.9 up to 21.14.13 | 21.14.13 |
virtualenvBitnami |
21.14.9 up to 21.14.13 | 21.14.13 |
virtualenvPyPI |
21.14.9 up to 21.14.13, plus listed versions 21.14.1021.14.1121.14.12 |
21.14.13 |
python-virtualenvUbuntu:14.04:LTS |
All versions, plus listed versions 1.10.1-11.11-11.11-21.11.4-11.11.4-1ubuntu1 |
None known |
python-virtualenvUbuntu:16.04:LTS |
All versions, plus listed versions 1.11.6+ds-114.0.5+ds-114.0.5+ds-214.0.5+ds-415.0.0+ds-1 Show 6 moreShow less15.0.0+ds-215.0.1+ds-115.0.1+ds-215.0.1+ds-315.0.1+ds-3ubuntu115.0.1+ds-3ubuntu1.1 |
None known |
python-virtualenvUbuntu:18.04:LTS |
All versions, plus listed versions 15.1.0+ds-115.1.0+ds-1.1 |
None known |
python-virtualenvUbuntu:Pro:20.04:LTS |
All versions, plus listed versions 15.1.0+ds-215.1.0+ds-2build115.1.0+ds-2ubuntu120.0.13-120.0.17-120.0.17-1ubuntu0.320.0.17-1ubuntu0.420.0.17-1ubuntu0.4+esm1 |
None known |
python-virtualenvUbuntu:Pro:22.04:LTS |
All versions, plus listed versions 20.4.6+ds-220.10.0+ds-120.12.1+ds-120.13.0+ds-120.13.0+ds-220.13.0+ds-2ubuntu0.1~esm1 |
None known |
python-virtualenvUbuntu:Pro:24.04:LTS |
All versions, plus listed versions 20.24.1+ds-120.24.6+ds-120.24.6+ds-220.25.0+ds-120.25.0+ds-220.25.0+ds-2ubuntu0.1~esm1 |
None known |
python-virtualenvUbuntu:26.10 |
All versions, plus listed versions 20.36.1+ds-121.4.1+ds-121.5.1+ds-1 |
None known |
python-virtualenvUbuntu:26.04:LTS |
All versions, plus listed versions 20.31.2+ds-120.35.3+ds-120.35.4+ds-120.36.1+ds-1 |
None known |
python-virtualenvopenSUSE:Tumbleweed From an advisory covering 9 CVEs |
before 21.15.1-1.1 | 21.15.1-1.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-115586 is a high vulnerability (CVSS 7.1) in Pypa Virtualenv, published October 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.27%, the 19th percentile. Fixed in virtualenv 21.14.13.
References
github.com/pypa/virtualenv/security/advisories/GHSA-cx6f-vjhg-g3cc
github.com/pypa/virtualenv/pull/3466
github.com/pypa/virtualenv/commit/3598e201f6f044e389136b2347a8a919ac03c5f4
github.com/pypa/virtualenv/releases/tag/21.14.13
github.com/advisories/GHSA-cx6f-vjhg-g3cc
nvd.nist.gov/vuln/detail/CVE-2026-115586
Show 13 moreShow less
github.com/CVEProject/cvelistV5/tree/main/cves/2026/115xxx/CVE-2026-115586.json
github.com/pypa/virtualenv
ubuntu.com/security/CVE-2026-115586
cve.org/CVERecord?id=CVE-2026-115586
suse.com/security/cve/CVE-2026-113714
suse.com/security/cve/CVE-2026-113716
suse.com/security/cve/CVE-2026-113718
suse.com/security/cve/CVE-2026-113720
suse.com/security/cve/CVE-2026-113722
suse.com/security/cve/CVE-2026-113724
suse.com/security/cve/CVE-2026-113726
suse.com/security/cve/CVE-2026-113732
suse.com/security/cve/CVE-2026-115586
Left out here: the rest of 2 source descriptions. The JSON record has them.