Repository navigation
chore(deps): update dependency virtualenv to v21.14.9 [security] - #146
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
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:
21.12.1→21.14.9virtualenv activation scripts run code from a path that contains a placeholder name
CVE-2026-113776 / GHSA-8rjx-v5ww-45pp
More information
Details
Impact
ViaTemplateActivator.instantiate_templatereplaces the placeholders in an activation template one key at a time, each across the whole text. When a value that it already substituted and quoted contains the name of a later placeholder, the later pass rewrites that name inside the quoted literal. The inserted value brings its own quotes, which close the outer literal, and the rest of the path runs as code when a user activates the environment.__VIRTUAL_NAME__holds the environment's directory name, so a destination such asA__VIRTUAL_NAME__B/x'$(touch PWNED)'yis enough. In the defaultactivatefor bash, theVIRTUAL_ENVline renders as:Sourcing that script runs
touch PWNED. The same payload in a path without the placeholder name runs nothing.activate.fishruns the command the same way, and the reporter also showed Python code execution throughactivate.xshandbin/activate_this.py. Inactivate.csh, a directory named after a prompt display placeholder, such as__VIRTUAL_PROMPT_DISPLAY_TCSH__, breaks the script withUnmatched '''.No change to
quote()can stop this, because the rewrite happens afterquote()ran; it defeatsshlex.quoteandrepr()alike.An attacker needs to influence the destination path or
--prompt: a CI job or wrapper tool that builds the path from a branch or project name, or a directory in a cloned repository or unpacked archive.The bug affects versions 20.26.6 through 21.14.1. Version 20.26.6 first quoted template values (#2768) and introduced the per-key loop.
Patches
Fixed in 21.14.2. Activation scripts now keep a placeholder name in the environment's path or prompt as plain text, and activating the example above runs nothing. Fix: pypa/virtualenv@602b266
Workarounds
Do not create an environment whose path or
--promptcontains a placeholder name such as__VIRTUAL_NAME__,__VIRTUAL_ENV__or__VIRTUAL_PROMPT__, and inspect the generated activation script before 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.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/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 removes pre-existing symbolic links and Windows junctions along executable and library directory paths.
Each replacement is created immediately, and an existing entry must be a real directory. A warning names each replaced
link or junction. Directories can still be replaced after preparation; this does not protect against concurrent tree
replacement.
Fix: PR #3429.
Known mitigation
Create environments in fresh directories beneath a parent controlled only by their owner. Do not reuse trees prepared by
another account.
Affected release
Credits
Reported by Anthropic's OSS Scanner as ANT-2026-MUFJEG11.
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: Generated files can follow existing destination links
CVE-2026-113721 / GHSA-558c-9hv2-4prj
More information
Details
Summary
Generated activation files and pyvenv.cfg can follow existing links and write outside the requested environment. The
template writer tests exists() before unlinking, which misses dangling symlinks; the configuration writer opens the
existing path directly.
Anthropic's OSS Scanner reported this as
ANT-2026-XBEOCBS1invirtualenv-20261015-005, received on 15 October 2026.Affected area
Final file entries written during environment creation and seeding.
Attacker-controlled input
Another account must be able to prepare file links in the destination before creation. The invoking user must have write
permission at the linked target. The destination parent must remain controlled during creation; concurrent
parent-directory replacement is a separate concern.
Affected code
The relevant 21.14.8 source is in
template writer,
configuration writer.
Mechanism
ViaTemplateActivator._generate()removes an existing output only whenPath.exists()is true. A dangling symlinktherefore survives, and
write_bytes()creates its target outside the environment. Other generated files have relatedfinal-entry behavior.
PyEnvCfg.write()openspyvenv.cfgwithout first replacing an existing link; a symlink or hardlink there can overwrite an existing target. These paths require a prepared destination tree and the invoking user's
permission to write the linked target.
Reproduction and evidence
The scanner's project tree contains
venv/bin/activate -> ../../../.bash_aliasesand an executablevenv/bin/basename.Before creation, a new interactive bash does not create the marker. A plain
virtualenv venvwrites the activationscript through the dangling link, creating
~/.bash_aliasesas a regular file. Debian's stock.bashrcsources thatfile; the next interactive bash adds
venv/bintoPATHand the plantedbasenamewrites the marker. The reproducerchecks that the link remains,
~/.bash_aliasesappears, and the second shell executes the planted file. A separatemanual check links
pyvenv.cfgto~/.bashrcand observes the latter replaced with generated configuration text.The link lets creation write outside the requested environment. The attacker chooses the target path but the generated
bytes come from virtualenv. The executable in the example was already in the prepared tree; this finding is not
arbitrary code injection into an otherwise trusted destination. The scanner traces the template behavior to #1454, first
released in 20.0.0b1. PR #3431 replaces pre-existing output links and
uses exclusive creation for generated text. It does not make a destination safe if another account can replace parent
directories during creation.
Attack sequence
sequenceDiagram participant A as Prepared project tree participant V as virtualenv writer participant H as User home participant B as Next interactive bash A->>V: venv/bin/activate links to missing ~/.bash_aliases V->>V: Path.exists() follows link and returns false V->>H: write activation bytes through link H-->>B: ~/.bash_aliases now exists B->>H: .bashrc sources ~/.bash_aliases B->>A: PATH reaches planted venv/bin/basename A-->>B: marker created Note over V,H: pyvenv.cfg can also overwrite an existing linked targetReproducer files
The following files are from the scanner report for
ANT-2026-XBEOCBS1. 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.
repro