Repository navigation
Update dependency virtualenv to v21 [SECURITY] - #256
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 28, 2026 11:35
6e9cdd0 to
02454c7
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 28, 2026 17:52
02454c7 to
b58a87b
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 31, 2026 21:16
b58a87b to
50e7f6b
Compare
This branch has not been deployed
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.
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
==20.15.1→==21.14.9virtualenv allows command injection through activation scripts for a virtual environment
CVE-2024-53899 / GHSA-rqc4-2hc7-8c8v
More information
Details
virtualenv before 20.26.6 allows command injection through the activation scripts for a virtual environment. Magic template strings are not quoted correctly when replacing. NOTE: this is not the same as CVE-2024-9287.
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv Has TOCTOU Vulnerabilities in Directory Creation
CVE-2026-22702 / GHSA-597g-3phw-6986
More information
Details
Impact
TOCTOU (Time-of-Check-Time-of-Use) vulnerabilities in
virtualenvallow local attackers to perform symlink-based attacks on directory creation operations. An attacker with local access can exploit a race condition between directory existence checks and creation to redirect virtualenv's app_data and lock file operations to attacker-controlled locations.Affected versions: All versions up to and including 20.36.1
Affected users: Any user running
virtualenvon multi-user systems where untrusted local users have filesystem access to shared temporary directories or whereVIRTUALENV_OVERRIDE_APP_DATApoints to a user-writable location.Attack scenarios:
Patches
The vulnerability has been patched by replacing check-then-act patterns with atomic
os.makedirs(..., exist_ok=True)operations.Fixed in: PR #3013
Versions with the fix: 20.36.2 and later
Users should upgrade to version 20.36.2 or later.
Workarounds
If you cannot upgrade immediately:
VIRTUALENV_OVERRIDE_APP_DATApoints to a directory owned by the current user with restricted permissions (mode 0700)virtualenvin shared temporary directories where other users have write accessReferences
Severity
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:LReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv activate.fish runs commands from a path or prompt with a backslash before a quote
CVE-2026-113732 / GHSA-c947-3pg5-gm8q
More information
Details
Impact
FishActivatordefines noquote(), so it inheritsViaTemplateActivator.quote, which returnsshlex.quote(string). That function follows POSIXshrules, where a backslash inside single quotes stays a literal character. fish reads\'inside single quotes as an escaped quote and\\as an escaped backslash. A value with a backslash before a quote therefore ends the literal somewhere other than whereshlex.quoteput the end, and fish expands the command substitution that follows.The environment path reaches
set -gx VIRTUAL_ENV, and--promptorVIRTUALENV_PROMPTreachesset -gx VIRTUAL_ENV_PROMPT. For--prompt "x\'\$(touch PWNED)'\'", the generatedactivate.fishcontains:source bin/activate.fishrunstouch PWNED, and fish prints no error. The same payload as a directory name runs throughVIRTUAL_ENV.--prompt .takes the current directory's name, so running virtualenv inside a directory someone else named is enough. Without the backslash the payload runs nothing, and under bash it runs nothing either.virtualenv generates
activate.fishby default. An attacker needs to influence the environment path or prompt: 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.The bug affects every release up to 21.14.1 that ships
activate.fish. The GHSA-p58f-9548-mpm2 fix in 21.7.13 moved the placeholders outside their quotes but keptshlex.quote.Patches
Fixed in 21.14.2.
activate.fishnow escapes backslashes and single quotes the way fish reads them, so the example above runs nothing and shows the prompt as given. Fix: pypa/virtualenv@3bddeafWorkarounds
Do not create an environment whose path or prompt contains a backslash, and inspect
activate.fishbefore you source one you did not create.Credits
Kevin Stubbings (@Kwstubbs) of GitHub Security Lab reported this issue.
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv activate runs commands from the environment name in zsh with PROMPT_SUBST
CVE-2026-113726 / GHSA-5vjq-rrrf-7h2q
More information
Details
Impact
zsh users activate an environment with the bash
activatescript, which puts the environment name intoPS1. Since21.13.0 the script escapes backslashes, dollar signs and backticks in that name, but only when
BASH_VERSIONis set, sozsh gets the raw value. With the
PROMPT_SUBSToption set, zsh runs parameter expansion, command substitution andarithmetic expansion on
PS1each time it draws the prompt. A name that contains$(...), backticks or${...}thenruns as commands in the user's shell, once per prompt.
The name comes from the environment directory, or from
--promptorVIRTUALENV_PROMPT. After you create anenvironment with:
$ virtualenv 'x$(touch PWNED)y'source 'x$(touch PWNED)y/bin/activate'in a zsh session withPROMPT_SUBSTset createsPWNED, and the prompt shows(xy). Passing that value as--promptto an environment with a plain name has the same effect. WithoutPROMPT_SUBST, zsh shows the name as text and runs nothing. bash runs nothing either.A plain zsh starts with
PROMPT_SUBSToff. oh-my-zsh users have it on: oh-my-zsh loadslib/theme-and-appearance.zshat startup, and that file sets it. An attacker needs to influence the environment path or prompt: 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.zsh also reads
%inPS1as a prompt escape, so a name with%nshowed the user name in place of those characters,though without running commands.
The bug is in releases up to and including 21.14.3. The payload runs on 20.26.6 and 21.14.3, and the reporter confirmed
21.9.1.
Patches
Fixed in 21.14.4. Under zsh,
activatenow escapes the name the way it does under bash and doubles%, so the example above runs nothing and the prompt shows the name as given. zsh gets the escaping even whenPROMPT_SUBSTis off, because a user can set the option after activation; a name with a backslash, dollar sign orbacktick then shows the added backslashes.
Fix: pypa/virtualenv@5b91536
Workarounds
Set
VIRTUAL_ENV_DISABLE_PROMPT=1before you sourceactivate, which leavesPS1alone, or rununsetopt prompt_substif your prompt does not need it. Check the name of an environment you did not create before yousource its
activate.Credits
Kaya Emre Arikan (@kemrec) reported this issue.
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv bash activate runs commands from an environment name with a backslash before $
CVE-2026-113725 / GHSA-vxj4-q4g9-crj7
More information
Details
Summary
The bash
activatescript escapes the environment name for only one of the two passes bash makes overPS1, so abackslash in the name undoes the escaping and a name containing
\$(...)runs as commands in the user's shell eachtime bash draws the prompt.
Affected area
src/virtualenv/activation/bash/activate.sh, the block that prepends the environment name toPS1.Attacker-controlled input
The environment name: the environment directory's name, or the
--promptorVIRTUALENV_PROMPTvalue. An attackerneeds 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
PS1in 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 arithmeticexpansion 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 textthere but runs nothing. zsh does no decode pass and is not affected.
Proof of concept
Impact
Arbitrary command execution in the shell of a user who sources the
activatescript of an environment whose name anattacker 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=1before you sourceactivate, which leavesPS1alone, or runshopt -u promptvarsif your prompt does not need expansion. Check the name of an environment you did not create before you source its
activate.Latest release
21.14.6
Patches
Fixed in 21.14.7. Under bash,
activatenow escapes backslashes, dollar signs and backticks for both passes, so theexample above runs nothing and the prompt shows the name as given. zsh keeps the single escaping it needs.
Fix: pypa/virtualenv@c3104e0
Credits
Found while running the virtualenv test suite as root inside the Anthropic OSS Scanner build image.
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv: Seed replacement does not contain metadata deletion paths
CVE-2026-113719 / GHSA-hhxq-47xx-j5xx
More information
Details
Summary
The app-data seeder joins paths from existing distribution metadata to site-packages and deletes the resulting paths
without checking containment within the environment.
Anthropic's OSS Scanner reported this as
ANT-2026-SXOEHWP7invirtualenv-20261015-005, received on 15 October 2026.Affected area
Removal of a previously installed seed package.
Attacker-controlled input
An environment being reused must contain distribution metadata influenced by another account. The invoking user must
have deletion rights over the paths named by that metadata. Installing a new environment without existing package
metadata does not enter this cleanup path.
Affected code
The relevant 21.14.8 source is in
seed uninstall.
Mechanism
Before reinstalling a seed wheel,
PipInstall._uninstall_previous_version()accepts an existingpip-*.dist-infodirectory in the destination.
_uninstall_dist()reads itstop_level.txtandRECORD, joins each entry tosite-packages, and deletes the result. It does not check whether.., an absolute entry, or a linked parent takes thepath outside the environment. The default app-data seeder reaches this path when it reuses a tree containing
seed-package metadata;
--clearremoves that tree first.Reproduction and evidence
The scanner prepared a project with
.venv/lib/python3.14/site-packages/pip-1.0.dist-info/. Itstop_level.txtcontained
../../../../../Documents; itsRECORDnamed../../../../../notes.txt,,. The user's home initially heldDocuments/thesis/chapter1.txtandnotes.txt. Runningvirtualenv .venvexited successfully but removed both paths.A control tree with
top_level.txtset topipand a normalpip/__init__.pyRECORD entry left the home files inplace. The reproducer prints
BUGonly when the control files survive and both attack targets disappear.The deletion uses the invoking user's permissions and runs during creation, before any use of the environment. It
requires an existing destination that another account could populate; a fresh empty environment does not supply hostile
metadata. The report traces this cleanup to #2186, first released in 20.8.1.
PR #3428 checks each deletion path against the resolved environment root
and refuses to delete the root itself.
Attack sequence
sequenceDiagram participant A as Prepared .venv metadata participant V as App-data seeder participant P as site-packages participant H as User home A->>P: top_level.txt names ../../../../../Documents A->>P: RECORD names ../../../../../notes.txt V->>P: read pip-1.0.dist-info before reinstall P->>H: join entries without containment check V->>H: remove Documents and notes.txt V-->>A: virtualenv exits successfullyReproducer files
The following files are from the scanner report for
ANT-2026-SXOEHWP7. They were tested against commita857a7823c73in the scanner image. The
reproentrypoint expects companion files under/tmp/repro.d; cases that change user IDsneed a disposable root-capable container. The last output line is
BUGwhen the reported behavior occurs. These filesare included as evidence and were not rerun for this advisory update.
reprorepro.d/make_project_tree.pyImpact
Seed cleanup can delete files outside the environment or the environment root itself. The invoking user must have
deletion rights over those paths.
Severity: medium. CVSS 4.5,
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L, assigned during maintainer review.Versions tested
The confirmed release is 21.14.8. The affected range records that release only; it does not establish the first affected
version or exclude earlier releases. Fixed in 21.14.9.
The fix skips cleanup paths outside the resolved environment root, and the root itself, with a warning. Final
links are removed without following their targets; parent links participate in containment checks.
Fix: PR #3428.
Known mitigation
Recreate environments from a trusted empty destination rather than reseeding a tree with untrusted installed metadata.
Affected release
Credits
Reported by Anthropic's OSS Scanner as ANT-2026-SXOEHWP7.
Severity
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:LReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv: Venv configuration readback can reinterpret path boundaries
CVE-2026-113715 / GHSA-qmx5-4p3x-wvqf
More information
Details
Summary
The venv creator reads the standard library's generated pyvenv.cfg with splitlines() and merges all resulting keys over
virtualenv's configuration. Line-boundary characters from destination data can therefore become configuration structure
before output sanitization.
Anthropic's OSS Scanner reported this as
ANT-2026-6ZQHJ6VIinvirtualenv-20261015-005, received on 15 October 2026.Affected area
Configuration readback by the stdlib venv creator.
Attacker-controlled input
An untrusted caller must influence the destination pathname passed to the venv creator. That pathname must contain a
character that Python treats as a line boundary. The builtin creator follows a different configuration-writing path.
Affected code
The relevant 21.14.8 source is in
venv readback,
configuration parser.
Mechanism
With
--creator venv, virtualenv asks the standard library to create an environment, reads itspyvenv.cfgwithstr.splitlines(), and merges the parsed keys over virtualenv's chosen settings. CPython writes the destination into acommand =value without escaping every character thatsplitlines()recognizes as a line boundary. A destinationcontaining U+2028, for example, can therefore supply new
homeandinclude-system-site-packageskeys during readback.Virtualenv writes those keys as real configuration lines. The builtin creator does not use this readback path.
Reproduction and evidence
The scanner used one destination value with U+2028 between
home = /attacker/chosen,include-system-site-packages = true, and an absorbingx =key. Apython -m venv --without-pipcontrol kept thevalue inside its
commandline and reported the base interpreter'ssys._home.virtualenv --creator venv --no-seedon the same value wrote separate injected configuration lines and its interpreter reported
sys._home = /attacker/chosen. The path did not require a literal newline byte. The control establishes that the extrakeys came from virtualenv's readback, not from the standard library's initial file alone.
The altered
homecan change which standard-library files the interpreter loads if an attacker also controls thatlocation;
include-system-site-packages = trueweakens environment isolation. Choosing the venv creator is required,either explicitly or through a rare fallback. The report traces the readback to the 20.x rewrite, first released in
20.0.0b1. PR #3426 rejects line-boundary characters before creation and
rebuilds the final configuration from selected settings.
Attack sequence
sequenceDiagram participant A as Destination supplier participant S as stdlib venv participant V as virtualenv readback participant C as Final pyvenv.cfg participant P as Environment Python A->>S: path contains U+2028 and home = /attacker/chosen S->>C: write path inside command value V->>C: read with splitlines() V->>V: parse suffix as separate keys V->>C: rewrite home and include-system-site-packages P->>C: read injected keys on startup C-->>P: sys._home changes and isolation is disabledReproducer files
The following files are from the scanner report for
ANT-2026-6ZQHJ6VI. They were tested against commita857a7823c73in the scanner image. The
reproentrypoint expects companion files under/tmp/repro.d; cases that change user IDsneed a disposable root-capable container. The last output line is
BUGwhen the reported behavior occurs. These filesare included as evidence and were not rerun for this advisory update.
reproImpact
Destination path data can change configuration boundaries and therefore environment settings. The effect depends on
which settings change and how the environment is subsequently used.
Severity: low.
Versions tested
The confirmed release is 21.14.8. The affected range records that release only; it does not establish the first affected
version or exclude earlier releases. Fixed in 21.14.9.
The fix rejects line boundaries in the supplied destination before filesystem access and checks the resolved
path again before invoking stdlib venv. The final configuration is rebuilt from the selected interpreter and virtualenv
options, without merging the intermediate stdlib file or its vendor-specific keys. Ordinary destination paths remain
supported.
Fix: PR #3426.
Known mitigation
Reject line-boundary characters in externally supplied destination paths, or use the builtin creator where supported.
Affected release
Credits
Reported by Anthropic's OSS Scanner as ANT-2026-6ZQHJ6VI.
Severity
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv: Bash prompt values can change meaning under older compatibility modes
CVE-2026-113723 / GHSA-6w3r-xr62-4xcp
More information
Details
Summary
Caller-supplied prompt text can become executable syntax in the bash activation prompt. An attacker must influence the
value passed to virtualenv, and a user must subsequently activate the environment and draw the prompt.
Affected area
Bash runs with compatibility level 4.2 and draws the environment prompt. Its replacement-string behavior differs from
newer compatibility modes, leaving prompt text subject to a second interpretation. The report concerns POSIX activation.
It does not establish exposure through unrelated activators.
Attacker-controlled input
The attacker controls prompt or destination data passed as a virtualenv argument. The user later sources the generated
activation script.
Affected code
The relevant 21.14.8 source is in
bash prompt assembly.
Mechanism
bin/activatecopies the prompt intoPS1. A shell expandsPS1again when it draws a prompt, so the value mustsurvive both the assignment and that later expansion. The bash and zsh branches escape for their usual prompt rules.
Three other paths still interpret prompt data as commands: dash sourcing
activatedirectly, a child dash inheriting anexported
PS1from bash, and bash running at compatibility level 4.2 or lower. A value such asx$(touch MARKER)ythenruns
touchon prompt display. Creating the environment does not run the payload.Reproduction and evidence
The scanner's child-shell reproducer creates an environment with
--prompt 'x$(touch MARKER)y', sourcesbin/activatein interactive bash, and starts interactive
sh(dash). Its control leavesPS1unexported: neither shell createsMARKER. WithPS1exported, bash displays the prompt without executing the value, but the child dash createsMARKER; the reproducer ends inBUG. The report gives separate console reproductions for direct dash activation andbash compatibility level 4.2. The child-shell path can arise after another environment's activation has exported
PS1.The scanner linked this finding to #3326 and its follow-up #3390. Those
changes cover the usual bash path but leave the three paths above. The proposed change in
#3418 keeps a variable reference in
PS1, so prompt text is expanded asdata when drawn. The private report's reproducer was run by the scanner; the later command-execution result was supplied
by the maintainer review. This advisory does not claim that every POSIX shell or every bash mode is affected.
Introduced
The report traces direct dash sourcing to the first 20.x activator (20.0.0b1), and inherited
PS1to #3390 (firstreleased in 21.14.7). It demonstrates the compatibility-level case on bash 5.2. Those historical boundaries come from
the report; the release confirmed by the maintainer is 21.14.8.
Attack sequence
sequenceDiagram participant A as Prompt supplier participant V as virtualenv participant B as Interactive bash participant D as Child dash A->>V: --prompt x$(touch MARKER)y V->>B: generated bin/activate B->>B: assign escaped prompt to PS1 alt PS1 is not exported B->>D: start sh with its own PS1 D-->>B: marker absent else PS1 is exported B->>D: start sh with inherited PS1 D->>D: expand $(touch MARKER) on prompt draw D-->>B: marker created endReproducer files
The following files are from the scanner report for
ANT-2026-1MOI2X3T. They were tested against commita857a7823c73in the scanner image. The
reproentrypoint expects companion files under/tmp/repro.d; cases that change user IDsneed a disposable root-capable container. The last output line is
BUGwhen the reported behavior occurs. These filesare included as evidence and were not rerun for this advisory update.
reprorepro.d/user_session.shImpact
An affected activation can execute commands with the user's privileges. Severity is high, following the generated-script
precedent in GHSA-p58f-9548-mpm2. The
attack requires control of argument data and subsequent user activation; it does not require replacing an executable or
controlling app-data contents.
Versions tested
The supplied follow-up review marks command execution as independently confirmed by its author. The implementation
review used source inspection and patched literal-value checks, without rerunning that exploit. The relevant activation
source is identical between release 21.14.8 and upstream/main 233359cb. The maintainer previously confirmed validation
of the reported findings on 21.14.8. The first affected version has not been established. Fixed in 21.14.9.
Known mitigation
Use trusted prompt and destination values. On older releases, disable the environment prompt with
VIRTUAL_ENV_DISABLE_PROMPT=1. Recreate environments with virtualenv 21.14.9 or later.
Affected release
Credits
The original report was supplied by Anthropic's OSS Scanner as ANT-2026-1MOI2X3T. The subsequent maintainer-provided
review identified the remaining activation path.
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).
virtualenv: Seeded files retain permissions from an older cache
CVE-2026-113720 / GHSA-fc2g-4w5p-6whm
More information
Details
Summary
The app-data copy seeder uses shutil.copy, which preserves cached file modes independently of the current umask. A cache
populated under permissive permissions can therefore produce files with wider access in later environments.
Anthropic's OSS Scanner reported this as
ANT-2026-487MH6OWinvirtualenv-20261015-005, received on 15 October 2026.Affected area
Permissions of files copied from app-data into a new environment.
Attacker-controlled input
The cache must contain files created with broader permissions than the current environment creator intends. Another
account must also be able to traverse the new environment and reach those files. A private parent directory prevents
that access. No control of a private live cache is assumed.
Affected code
The relevant 21.14.8 source is in
copy helpers.
Mechanism
The default app-data seeder extracts a wheel into a reusable cache and copies its files into later environments with
shutil.copy(). That function applies the cached source mode to each destination file after creating it, overriding thecurrent run's umask. If the cache image was built under umask
000, a later environment created under umask022canreceive mode-
0666pip files. The cache may be private while the new environment is reachable by other local accounts.Reproduction and evidence
The scanner created two histories with separate private app-data caches. In the control, the owner's first run used
umask
022; a later environment had no files writable by another account, and that account could not append topip/__init__.py. In the attack history, the first run used umask000and the later run still used022. The reportcounted 478 of 493 environment files writable by others. A second unprivileged uid appended a line to pip, and
bin/pip --versionran it as the environment owner. The cache itself remained under a mode-0700home in bothhistories.
An attacker cannot force the owner's earlier umask through this finding; the attack also needs a traversable
destination. A mode-
002cache can expose group write access under suitable group permissions. The scanner datesapp-data copy seeding to #1578, first released in 20.0.2. PR #3425
creates copied files under a fixed non-writable-by-others policy further restricted by the current umask.
Attack sequence
sequenceDiagram participant U as Environment owner participant C as Private app-data cache participant E as Later environment participant A as Other local account U->>C: first seed extraction under umask 000 C-->>U: cached pip files have mode 0666 U->>E: create environment under umask 022 C->>E: shutil.copy reapplies mode 0666 A->>E: append code to pip/__init__.py U->>E: run bin/pip --version E-->>U: appended code runs as ownerReproducer files
The following files are from the scanner report for
ANT-2026-487MH6OW. They were tested against commita857a7823c73in the scanner image. The
reproentrypoint expects companion files under/tmp/repro.d; cases that change user IDsneed a disposable root-capable container. The last output line is
BUGwhen the reported behavior occurs. These filesare included as evidence and were not rerun for this advisory update.
reprorepro.d/other_user_appends_to_pip.pyImpact
Copied seed files can remain readable or writable by other accounts despite the current umask. Exposure requires those
accounts to be able to traverse the destination's parent directories. An attacker need not control the private source
cache.
Severity: low.
Versions tested
The confirmed release is 21.14.8. The affected range records that release only; it does not establish the first affected
version or exclude earlier releases. Fixed in 21.14.9.
The fix creates destination files with mode
0644, or0755for executable sources, filtered further by thecurrent umask. It does not grant group or world write access, even with a permissive umask. Source modes are not
preserved, including modes more restrictive than this creation policy.
Fix: PR #3425.
Known mitigation
Create environments beneath a directory accessible only to their owner. Recreate environments whose permissions were
inherited from a permissive cache.
Affected release
Credits
Reported by Anthropic's OSS Scanner as ANT-2026-487MH6OW.
Severity
CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
virtualenv: Xonsh activation literals depend on the script decoding codec
CVE-2026-113718 / GHSA-394h-qwmc-48pf
More information
Details
Summary
Caller-supplied prompt text can become executable syntax in the xonsh activation script. An attacker must influence the
value passed to virtualenv, and a user must subsequently activate the environment.
Affected area
Xonsh reads a UTF-8 activation script using a legacy multibyte encoding such as GBK. Printable non-ASCII bytes can
change how the reader recognizes an adjacent quote. The report concerns POSIX activation. It does not establish exposure
through unrelated activators.
Attacker-controlled input
The attacker controls prompt or destination data passed as a virtualenv argument. The user later sources the generated
activation script.
Affected code
The relevant 21.14.8 source is in
xonsh quote(),
UTF-8 template writer.
Mechanism
The generator writes
activate.xshas UTF-8. Itsquote()usedrepr(), which leaves printable non-ASCII charactersin the file and backslash-escapes an embedded quote. Xonsh's
sourcereads the file using$XONSH_ENCODING. With GBKor Big5, the UTF-8 bytes of
中end in0xad, which pairs with the following0x5cbackslash as one decoded character.The backslash no longer escapes the quote. A prompt containing both quote kinds can then close its Python string literal
and supply statements that run during activation. With UTF-8 source decoding, the same value stays inside the literal.
Reproduction and evidence
The original scanner finding,
ANT-2026-PAUVP7YL, demonstrated this byte-level failure inactivate_this.py: a hostileprompt loaded through
exec(open(file).read(), ...)under GBK created a marker, while UTF-8 andrunpy.run_pathcontrols did not. The xonsh path in this advisory was identified in the subsequent maintainer-provided review.
PR #3432 shows the xonsh source-decoding chain and changes
quote()toascii(), which writes non-ASCII characters as escapes. Its regression test encodes the generated literal as ASCII,decodes it as GBK, and checks that
ast.literal_evalreturns the original prompt.The attack needs control of the prompt or destination value, an affected xonsh source encoding, and later sourcing of
the generated file. The original
activate_this.pyreproducer does not itself prove xonsh execution; that result isattributed to the separate review. Existing
activate.xshfiles must be regenerated after the fix.Attack sequence
sequenceDiagram participant A as Prompt supplier participant V as virtualenv quote() participant F as activate.xsh bytes participant X as xonsh source A->>V: 中 plus escaped quote and statements V->>F: repr writes UTF-8 中 followed by backslash alt XONSH_ENCODING is UTF-8 F->>X: backslash remains an escape X-->>A: prompt remains data else XONSH_ENCODING is GBK or Big5 F->>X: decoder consumes backslash as trail byte X->>X: quote closes and statements run end Note over V,X: ascii() emits only ASCII bytes and preserves the literalReproducer files
The follow