Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessServe matching candidate from non-PyPI index
-
DeliveryUser runs virtualenv --seeder pip --download
-
ExploitDigest comparison path bypassed
-
Executionpip installs unverified wheel
-
ImpactLow-integrity code seeded into environment
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A08:2025 - Software or Data Integrity Failures
ASVS 5.0
V15 - Secure Coding and Architecture Code integrity verification
|
| Exploitation Conditions | Requires virtualenv prior to 21.14.9 invoked with --seeder pip and --download, while pip/virtualenv index environment variables remain unset so the default-index PyPI SHA-256 comparison policy stays active, AND a non-PyPI index is in use that offers a candidate matching the bundled package name and version. The user must actively run the virtualenv seeding operation (UI:P). Explicit custom-index configurations already opt out of the comparison and are outside this issue. Strong limiting factors: the bypass does not defeat TLS and does not give the attacker control of the index response, exploitation is high-complexity with specific attack requirements, and resulting impact is only low integrity - so realistic risk is far below what the network vector alone suggests. |
| Impact | This is a genuinely low-priority issue despite a network attack vector. The vendor CVSS 4.0 base score is only 2.3, and the composite signals explain why: AV:N but AC:H (high complexity) and AT:P (specific attack requirements must be present), with UI:P meaning a user must actively run virtualenv seeding, and only Low vulnerable-system impact (VC:L/VI:L/VA:L). No EPSS score, KEV listing, or public POC is present in the provided data, so exploitation likelihood signals are all absent rather than elevated. The flaw is a narrow integrity-verification gap (CWE-494): the pip seeder's --download path bypassed virtualenv's download_wheel()/verify_wheel_digest() SHA-256 comparison against PyPI metadata. Critically, the description itself states this condition does NOT defeat TLS or grant control of the index response, so an attacker cannot trivially inject a malicious wheel - they would need an index that already legitimately serves a candidate matching the bundled name and version. PR:N (unauthenticated per the 4.0 vector) is technically accurate but misleading in isolation: real-world abuse depends on an attacker already being positioned as a non-PyPI index source. Treat this as a defense-in-depth hardening fix, not an actively-weaponizable vulnerability. |
| Exploit ScenarioDerived | An organization bootstraps environments with virtualenv (< 21.14.9) using --seeder pip --download while index environment variables are left unset, so the default-index PyPI digest comparison policy is active but a non-PyPI index is in use. pip is allowed to consult that index and select a candidate matching the bundled package name and version without virtualenv comparing its SHA-256 against PyPI metadata, so an unverified wheel can be seeded into the new environment. No public exploit identified at time of analysis, and the condition does not by itself defeat TLS or let the attacker forge the index response. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Oct 20, 2026
Integrity-verification bypass in PyPA virtualenv before 21.14.9 allows a non-PyPI package index response to be consumed for a bundled seeder wheel without the default PyPI SHA-256 digest comparison ever being performed. The condition is narrow: it only arises when virtualenv is invoked with --seeder pip and --download, index environment variables are left unset so the default-index comparison policy is still active, and a non-PyPI index is configured that offers a candidate matching the bundled distribution name and version. Per our assessment (CVSS:3.1 AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L) this is unauthenticated but high-complexity and user-initiated, does not defeat TLS, does not give an attacker control of the index response, and produces only a low, integrity-focused outcome; no public exploit code has been identified at time of analysis and there is no KEV listing. The vendor fixed the flaw in 21.14.9 by staging downloads in a private temporary directory and installing pinned wheels with --no-index.
RemediationAI
Upgrade to virtualenv 21.14.9 or later, which forces --no-index for seeder installs and routes every downloaded wheel through the private-staging-directory and PyPI digest verification path (see https://github.com/pypa/virtualenv/security/advisories/GHSA-cx28-75q4-x26q and https://github.com/pypa/virtualenv/releases/tag/21.14.9); also pin the virtualenv version in build images and CI so downstream reinstalls cannot silently revert. If upgrading must be deferred, avoid the vulnerable combination entirely: do not run virtualenv with --seeder pip together with --download - use the default app-data or bundled seeder, which goes through download_wheel()/verify_wheel_digest(), or pre-populate the wheel cache with vetted archives and pass --no-download (optionally with --extra-search-dir pointing at a locally curated directory) so no index lookup occurs at all; the trade-off is that seeding then depends on locally mirrored wheels that must be refreshed manually. Because the bypass only matters when resolution can reach an index you do not control, restrict egress from build hosts to a pinned internal mirror via proxy or firewall rules, though this is an infrastructure control rather than a fix and adds maintenance overhead. Setting an explicit custom-index configuration removes the PyPI comparison policy but should not be treated as a remediation - it explicitly opts out of the verification this CVE is about.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in pypi
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
virtualenv's pip seeder historically allowed two acquisition paths: its own download_wheel() routine, which invokes pip download and then calls verify_wheel_digest() to compare the fetched archive against PyPI's published SHA-256 metadata when the default index policy is in force, and a passthrough path used by PipInvoke.get_pip_install_cmd(), which called get_wheel(download=False) and let pip itself resolve the requirement against the configured index. Because that second path never touched download_wheel()/verify_wheel_digest(), an index candidate matching the bundled package and version could be installed with no comparison to PyPI digests at all. This is CWE-494 (Download of Code Without Integrity Check): the software fetches an executable artifact and installs it without validating its authenticity or integrity. Note that explicitly configured custom indexes already opt out of the PyPI comparison by design, so the practical attack surface is limited to configurations where the comparison policy is nominally active but the resolution still reaches a non-PyPI index. The upstream fix in PR #3430 restructures the flow so pip always runs with --no-index, each selected wheel is copied into a TemporaryDirectory alongside --find-links, and download_wheel() stages and validates the artifact before anything is moved into the reusable wheel cache, raising UnverifiedWheelError when PyPI cannot supply a digest.
Know when Virtualenv gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
AC:H and UI:R because an attacker must position a matching non-PyPI index candidate under a specific unset-env config and the victim must run seeding; impact is only low integrity, no TLS/index control.
Primary rating from Vendor (GitHub_M).
CVSS VectorVendor: GitHub_M
Lifecycle Timeline
2DescriptionCVE.org
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, PipInvoke.get_pip_install_cmd() handles --seeder pip --download by calling get_wheel(download=False) and then allowing pip to consult the configured index, bypassing virtualenv's download_wheel() and verify_wheel_digest() path. When virtualenv's default-index PyPI digest comparison remains enabled, an index candidate matching the bundled package and version can therefore be selected without comparison to PyPI SHA-256 metadata. Explicit custom-index settings already opt out of that comparison and remain outside this verification-bypass claim; the demonstrated configuration used a non-PyPI index while leaving the index environment variables unset so the comparison policy remained active. This condition does not independently defeat TLS or establish control of the index response. This issue is fixed in version 21.14.9.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
More in Virtualenv
View allvirtualenv before 20.26.6 allows command injection through the activation scripts for a virtual environment. Rated high
virtualenv versions prior to 21.7.12 cache and seed pip/setuptools wheels without verifying their bytes, so an attacker
Configuration injection in PyPA virtualenv before 21.7.11 lets a line boundary embedded in a prompt value inject extra k
Command injection in PyPA virtualenv before 21.14.9 lets an attacker who controls the destination path of a virtual envi
Command injection in virtualenv's bash activation script prior to 21.14.7 allows a crafted environment name or --prompt
Command injection in the bash, zsh, and fish activation scripts generated by virtualenv releases prior to 21.7.13 lets a
Command injection in PyPA virtualenv before 21.7.12 allows an attacker who controls the environment's prompt value - via
Command injection in virtualenv's bash activator lets an attacker-supplied --prompt value be stored unescaped in PS1 and
Caller-influenced prompt text in virtualenv's bash activation script prior to 21.14.9 can be re-expanded by bash as exec
A code injection flaw in PyPA virtualenv before 21.14.9 lets attacker-influenced non-ASCII characters in the xonsh promp
Symlink following in virtualenv's safe_delete() deletion-error handler (all versions prior to 21.14.9) lets a local atta
Symlink-following file writes in virtualenv before 21.14.9 let a second, unprivileged local account redirect generated e
EUVD-2026-105298
GHSA-cx28-75q4-x26q
NVD
CVE.org
EPSS
Vulnrichment
Exploit-DB