Repository navigation
chore(deps): update dependency virtualenv to v21 [security] - #188
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
Contributor
Author
Branch automerge failureThis PR was configured for branch automerge. However, this is not possible, so it has been raised as a PR instead.
|
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
January 13, 2026 20:43
37b920b to
4e30633
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
2 times, most recently
from
March 30, 2026 21:18
4e30633 to
9807e7c
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
2 times, most recently
from
April 27, 2026 21:10
9807e7c to
0072927
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 7, 2026 00:30
0072927 to
d70104c
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 8, 2026 21:54
d70104c to
75e4e3a
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 25, 2026 00:43
75e4e3a to
ea0b307
Compare
renovate
Bot
force-pushed
the
renovate/pypi-virtualenv-vulnerability
branch
from
October 25, 2026 17:24
ea0b307 to
d7a7b6a
Compare
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.23.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: Downloaded seed wheels (pip/setuptools) are not integrity-checked before use
CVE-2026-102930 / GHSA-94p9-xgh2-xp45
More information
Details
download_wheel()runspip downloadand hands the result straight to the seeder, with nothing checking the bytes it gets back. The embedded pip and setuptools wheels carry aBUNDLE_SHA256that virtualenv checks on every load, but a wheel fetched over the network for the periodic-update feature or the--downloadflag had no equivalent verification.A compromised index, a stale mirror, or a MITM'd download (when TLS is intercepted, e.g. via a misconfigured or malicious CA) could substitute a different wheel under the same distribution/version/filename, and virtualenv would cache and seed it into every environment created afterward with no warning.
Fixed by computing the downloaded wheel's sha256 and comparing it against the digest PyPI's public JSON API reports for that exact release, independent of which index actually served the file. The check is skipped, not treated as failure, when a custom index (
PIP_INDEX_URL,PIP_EXTRA_INDEX_URL,PIP_INDEX) is configured, since a private index can legitimately serve a different, intentionally rebuilt wheel. Fix: https://github.com/pypa/virtualenv/pull/3251Severity
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/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 writes prompt values into pyvenv.cfg without sanitizing line boundaries, allowing configuration injection
CVE-2026-102938 / GHSA-9h9j-4vrj-gf7g
More information
Details
Summary
pyvenv.cfgis a line-based format with no escape syntax.PyEnvCfg.write()wrote values verbatim, whilePyEnvCfg._read_values()parses the file withstr.splitlines(). A value containing a line boundary therefore became additional configuration lines, and because reading is last-wins, the injected keys replaced any key written earlier in the file.Impact
The
promptvalue is the reachable input: it is set by--prompt, by theVIRTUALENV_PROMPTenvironment variable, or from the config file, andwrite()emitspromptbeforehome. A crafted prompt can therefore sethomein the generatedpyvenv.cfg:homeis what tooling reads to locate the base interpreter, so a consumer that trusts it can be pointed elsewhere.implementation,version_info,version,executable,commandandvirtualenvare also written beforepromptand can be replaced the same way.This requires the prompt to come from somewhere other than the person running the command, for example a CI job templating a branch name into it, tooling deriving an environment name from user-supplied data, or an inherited
VIRTUALENV_PROMPT. Where the operator supplies the prompt directly they already control the command line, and the effect is corruption rather than privilege gain: the value is truncated at the boundary and read back with a dangling quote.Details
The boundary set is the one
str.splitlines()recognizes, which is wider than\n:\r,\v,\f, the file, group and record separators,U+0085,U+2028andU+2029were all written through unchanged and all split the line when read back.Patches
PyEnvCfg.write()now collapses those boundaries to spaces as it serializes each line, so it cannot emit a structurally invalid file regardless of what a caller places incontent.Workarounds
Do not pass externally influenced data as the virtualenv prompt. Strip line boundaries from any value before using it as
--promptorVIRTUALENV_PROMPT.Severity
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/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: Command injection via --prompt in activate.bat (batch activator)
CVE-2026-102937 / GHSA-x78j-v8h9-3j2q
More information
Details
BatchActivator.quote()returned its input unchanged, the only activator with no escaping at all.--prompt, theVIRTUALENV_PROMPTenvironment variable, and the config file all set the prompt, andactivate.batwrites it straight into@set "VAR=value". A prompt containing a double quote closes that string early, and whatever follows runs as live cmd.exe syntax:runs
calcthe moment someone activates the environment. Confirmed on a real Windows runner. Anyone who templates a prompt from untrusted input (a CI job using a branch name, a wrapper script deriving an environment name from user input) hands an attacker code execution in the shell of anyone who activates the resulting venv on Windows.Fixed in 21.7.12 by escaping the characters cmd.exe treats as live syntax inside this construct. Fix: https://github.com/pypa/virtualenv/pull/3250
Severity
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:P/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 bash and fish activation scripts execute commands embedded in paths
CVE-2026-102925 / GHSA-p58f-9548-mpm2
More information
Details
Impact
The generated
activate(bash/zsh) andactivate.fishscripts interpolate ashlex.quote-ed value into a position that quotes it a second time. The extra quotes terminate the quoted run early and leave part of the value parsed as shell code, so a path containing shell metacharacters runs commands when a user sources the activation script.activateis affected through the virtual environment's own path, on the branch that reports a relocated environment:For a destination named
x'$(id)'y,shlex.quoteemits'x'"'"'$(id)'"'"'y'. The inner"closes the enclosing double quote and$(id)is left unquoted. Backtick and;payloads reach the same result. This branch runs whenever the recorded path is absent, which is the normal case for a virtual environment copied or distributed to another machine.activate.fishis affected through the interpreter's Tcl/Tk library paths:shlex.quotealready returns'/tcl/(cmd)/lib', so the rendered line is''/tcl/(cmd)/lib''. fish concatenates the adjacent quoted runs and expands the(cmd)left between them. The same doubling also splits a path containing a space into two list elements, corruptingTCL_LIBRARYandTK_LIBRARY.This is the same defect class as GHSA-x78j-v8h9-3j2q, which covered
activate.bat.Patches
Fixed in 21.7.13 by moving the placeholders outside the surrounding quotes, so each value keeps only the quoting
shlex.quoteapplied. Fix: https://github.com/pypa/virtualenv/pull/3252Workarounds
Avoid creating or distributing virtual environments whose path contains
',`,$,;,(or), and inspect the generated activation script before sourcing one you did not create.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.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: Generated POSIX console launchers interpret destination paths as shell syntax
CVE-2026-113722 / GHSA-c978-59m8-c77v
More information
Details
Summary
A destination path supplied to virtualenv can be interpreted as shell syntax by a generated POSIX console launcher. The
app-data seeder and pip delegate launcher generation to distlib but supply the interpreter path in a form that does not
preserve it as literal data in the shell wrapper.
Affected area
POSIX console scripts generated by the app-data seeder or pip when the interpreter path requires a shell wrapper.
Windows executable launchers are not affected by this path.
Attacker-controlled input
An attacker must influence the environment destination passed to virtualenv, and the user must subsequently run a
generated launcher. The attacker does not need to modify an installed executable or control the user's app-data cache.
Affected code
The relevant 21.14.8 source is in
ScriptMakerCustom.
Mechanism
The app-data seeder passes the environment's interpreter path through
enquote_executable()to distlib'sScriptMaker.It adds double quotes for a path with a space but does not escape shell expansions inside those quotes. When the path
has a space or the shebang exceeds 127 bytes, distlib emits a
#!/bin/shwrapper with a line shaped like'''exec' "<path>" "$0" "$@". The shell expands$(...)and backticks in that path when the launcher is run. The samewrapper must also parse as Python, which exposes additional quote and backslash cases. Environment creation and
activation do not trigger the shell payload.
Reproduction and evidence
The scanner created three environments: a control under
my -touch PWNED- project, one undermy $(touch PWNED) project, and one under a path of more than 127 bytes ending in$(touch${IFS}PWNED). Each wascreated without producing
PWNED. Running the generatedbin/pip --versioncreatedPWNEDin both attack cases; thecontrol created none. The long-path case reached the shell wrapper without a space. This reproduces execution by the
generated launcher, with the user's privileges, rather than during seeding.
The report dates the app-data seeder's distlib launcher integration to #1579, first released in 20.0.2.
PR #3417 quotes the raw interpreter path for the POSIX wrapper and
repairs launchers written by the pip seeder. Pip's own distlib can write a vulnerable wrapper again during a later pip
install or upgrade; the proposed virtualenv fix does not change pip's vendored code. The maintainer-provided follow-up
review reports that later path separately.
Attack sequence
sequenceDiagram participant A as Destination supplier participant V as virtualenv seeder participant D as distlib ScriptMaker participant S as Generated bin/pip participant U as User A->>V: path contains $(touch PWNED) V->>D: pass interpreter path with incomplete shell quoting D->>S: write /bin/sh wrapper Note over V,S: no payload runs during creation or activation U->>S: pip --version S->>S: shell expands path and runs touch S-->>U: PWNED appears with user's privilegesReproducer files
The following files are from the scanner report for
ANT-2026-5DZVLGL1. 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.
reproFix scope and remaining exposure
PR #3417 changes launcher generation by the app-data seeder and repairs POSIX shell wrappers after pip seeding. The
pip-seeder repair uses the same quoting rule and updates the installed RECORD hashes and sizes. It does not modify pip's
vendored distlib.
A later independent pip installation or upgrade can replace a corrected launcher with an affected one. Existing
launchers are not repaired by upgrading virtualenv alone. Recreate environments with the corrected virtualenv to
regenerate them, and use trusted destination paths. Recreation does not protect launchers subsequently rewritten by pip.
The follow-up review reports reproduction through the previous pip seeder and after a pip upgrade; those results are
attributed to the reviewer.
Impact
Running an affected launcher can execute commands with the calling user's privileges. Classification: high, CVSS 7.8,
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, following the project'sgenerated-script precedent. The vector
assumes attacker-controlled path data and a user invoking the resulting launcher. No prior victim privileges are
required; successful command execution can affect the user's data and processes.
Versions tested
The maintainer confirmed validation of the reported findings on release 21.14.8. The subsequent review
records reproduction on unfixed upstream/main; the affected launcher integration and dependency declarations are
unchanged from 21.14.8. The first affected release has not been established. Fixed in 21.14.9.
Known mitigation
Use an environment destination chosen by a trusted user. For older releases, avoid generating
environments at destinations derived from untrusted input.
Affected release
Credits
Reported by Anthropic's OSS Scanner as ANT-2026-5DZVLGL1.
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: Deletion recovery can change a symlink target's mode
CVE-2026-113724 / GHSA-43mp-35q3-x3fq
More information
Details
Summary
The deletion error handler obtains permission bits with lstat() but applies them through chmod(), which follows
symlinks. On an eligible unlink failure this can change permissions outside the destination.
Anthropic's OSS Scanner reported this as
ANT-2026-UK90QC27invirtualenv-20261015-005, received on 15 October 2026.Affected area
Permission recovery after a filesystem deletion error.
Attacker-controlled input
A deletion must fail on a symbolic-link entry and enter the permission-recovery branch. The invoking user must have
permission to change the target mode. This is conditional error handling, not the normal successful unlink path.
Affected code
The relevant 21.14.8 source is in
safe_delete().
Mechanism
safe_delete()asksshutil.rmtree()to retry a failed removal after adding owner-write permission. Its error handlercalls
os.lstat(path)for mode bits butos.chmod(path, ...)to apply them. For a symlink,lstatreads the link'smode while
chmodfollows the link. A link inside a read-only directory can therefore cause the target's mode to become0777before the retry fails. The target must be owned by the invoking user and initially not writable by that user;root does not reach this failure path.
Reproduction and evidence
The scanner packed a project containing a mode-
0555venv/bin/python/directory and a symlink inside it pointing tothe user's mode-
0400keys/deploy.pem. An unprivileged user unpacked it and ran both plainvirtualenv venvandvirtualenv --clear venv. Both commands exited withPermissionErrornaming the directory insidevenv, while thekey's mode changed from
0400to0777. A control archive with a writable directory completed normally and left thekey at
0400. The read-only mode survivestarorcp -a; a normal git clone does not preserve this setup.This changes permissions; it does not read the target or complete the virtualenv operation. Another account can use the
widened mode only if it can traverse the target's parent directories. The scanner dates the active error handler to
#3222, first released in 21.7.5. PR #3427 avoids POSIX mode changes on
deletion failure and handles Windows read-only entries without following reparse points.
Attack sequence
sequenceDiagram participant A as Prepared read-only venv tree participant V as safe_delete error handler participant L as Symlink in venv participant K as User's 0400 key A->>V: virtualenv venv or --clear venv V->>L: unlink fails in 0555 directory V->>L: lstat returns link mode 0777 V->>L: chmod follows symlink L->>K: target mode becomes 0777 V-->>A: retry fails with PermissionError Note over K: Other accounts can use new mode if parent paths are traversableReproducer files
The following files are from the scanner report for
ANT-2026-UK90QC27. 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_archive.pyImpact
Permission recovery after a failed deletion can change an unrelated target's access bits. This requires the deletion
failure to occur on a link and the invoking user to have permission to change the target mode.
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 leaves POSIX modes unchanged when deletion fails. Windows read-only recovery uses a native handle that
opens the entry itself without following reparse points and denies replacement until the attribute update finishes. It
rejects reparse points and clears only the read-only attribute. Concurrent replacement of parent directories remains
outside this protection.
Fix: PR #3427.
Known mitigation
Avoid recursively clearing destinations populated by another account. If cleanup fails, inspect the failing entry before
retrying with broader privileges.
Affected release
Credits
Reported by Anthropic's OSS Scanner as ANT-2026-UK90QC27.
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: Builtin creation follows linked environment directories
CVE-2026-113716 / GHSA-cw65-gx24-rc82
More information
Details
Summary
The builtin creator accepts existing directory symlinks through ensure_dir() and writes environment files through those
directories. This permits writes outside the requested destination.
Anthropic's OSS Scanner reported this as
ANT-2026-MUFJEG11invirtualenv-20261015-005, received on 15 October 2026.Affected area
Directory preparation before environment files are created.
Attacker-controlled input
Another account must be able to prepare linked entries below the chosen destination before creation. The invoking user
must have access to the linked destination. This does not require write access to the target itself under the other
account.
Affected code
The relevant 21.14.8 source is in
builtin creator,
ensure_dir().
Mechanism
The builtin creator calls
ensure_dir()forbin,lib, and package directories.ensure_dir()accepts an existingsymlink to a directory because
exists()follows it. Interpreter creation, seed installation, and activation-scriptgeneration then write through that parent link. A final-file check cannot prevent a write through a linked parent.
--clearremoves the planted tree first; ordinary creation over a prepared destination reaches it.Reproduction and evidence
The scanner's project tree contains
venv/bin -> ../../.local/binand a.pthfile in the environment's package tree.With the user's initially empty
~/.local/binfirst onPATH,virtualenv venvexited successfully and wrotepython*,pip*, andactivate*into~/.local/bin. The marker was absent immediately after creation; a laterpip --versionon the user'sPATHloaded the seeded environment and the planted.pthcode wrote the marker. Asecond case began with the
binlink resolving inside the environment; replacement of a planted package link duringseeding changed where it resolved, and writes again reached
~/.local/bin.The attacker must prepare entries in an existing destination. The
.pthcontent is already attacker-controlled andwould run if the user used that environment directly; the added effect is creation-time writing or replacement outside
the requested tree and an executable on the user's
PATHpointing at it. The scanner traces this default builtin pathto 20.0.0b1. PR #3429 replaces linked directory components before
creation. Concurrent parent replacement remains a separate case.
Attack sequence
sequenceDiagram participant A as Prepared destination participant V as Builtin creator participant L as venv/bin symlink participant H as ~/.local/bin participant U as User A->>L: point to ../../.local/bin V->>L: ensure_dir accepts existing directory link V->>H: write python, pip and activate files through link V-->>U: creation reports success U->>H: run pip found on PATH H->>A: interpreter loads planted .pth A-->>U: marker createdReproducer files
The following files are from the scanner report for
ANT-2026-MUFJEG11. 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/checkout_with_bin_link.pyrepro.d/checkout_with_bin_link_staying_inside.pyImpact
Creation can place or replace environment files outside the selected tree. The written bytes are environment-creation
output; arbitrary attacker-selected file contents are not established.
Severity: medium. CVSS 4.5, `CVSS:3.1/AV:L/A