Every CVE Search/ Get API key

CVE-2026-113714

virtualenv: Downloaded wheel selection and caching precede verification

2.3 LowFix available

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
Passive

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
Low

Exploit likelihood

0.25%Unchanged since Oct 21, 2026
90%50%10%1%

What each source reports

CVE List Score used 2.3

virtualenv: Downloaded wheel selection and caching precede verification

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
CVE-2026-113714
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
Weakness
CWE-354
Updated
2026-11-02
EUVDEUVD-2026-105260 2.3

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
EUVD-2026-105260
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-494p-63j8-28p3 2.3

virtualenv: Downloaded wheel selection and caching precede verification

Summary

Wheel downloads go directly into the reusable wheelhouse, while the verification target is selected from pip's textual output. A verification failure does not remove the downloaded file from that cache.

Anthropic's OSS Scanner reported this as ANT-2026-ST00DRO3 in virtualenv-20261015-005, received on 15 October 2026.

Affected area

Download selection and promotion into the reusable wheel cache.

Attacker-controlled input

A download must fail verification, or the archive selected from pip output must differ from the file later reused. Default-index digest verification must be enabled. Existing custom-index exceptions are outside the promised PyPI comparison.

Affected code

The relevant 21.14.8 source is in wheel download and digest check.

Mechanism

download_wheel() tells pip to write directly into the reusable wheel/house cache. It then selects a path to hash from the first Saved line in pip's stdout, rather than from the file pip actually placed there. A package index can inject such a line through a discarded link's data-requires-python attribute. A separate failure exists without the forged line: a digest mismatch raises after the downloaded wheel has entered the cache, and the rejected bytes remain there. Later wheel selection does not hash the cached file again.

Reproduction and evidence

The scanner used a local index configured in pip.conf, with PyPI digest comparison still enabled. In the control case it served a modified pip wheel alone. virtualenv --upgrade-embed-wheels refused the mismatch, and a subsequent environment used the bundled pip. In the attack case the index added a link whose data-requires-python contained a forged Saved <path> line pointing at a genuine bundled wheel. The upgrade then exited without a digest error, recorded the modified wheel in its update log, and a later environment's pip --version ran a line added by the index. The reproduction used a known absolute path to the genuine bundled wheel; that prerequisite is part of this variant.

The report also shows that a refused wheel can replace a previously accepted file of the same name in wheel/house, leaving rejected bytes available for later use. It does not claim to bypass TLS or affect the default PyPI setup. The gap follows the digest check added in #3251, first released in 21.7.12. PR #3430 downloads to a private temporary directory, verifies the actual archive, and promotes it only after acceptance.

Attack sequence

sequenceDiagram
    participant I as Configured index
    participant P as pip download
    participant C as wheel/house cache
    participant V as virtualenv digest check
    participant N as New environment
    I->>P: modified wheel and forged Saved line
    P->>C: write modified wheel directly
    P-->>V: stdout names genuine bundled wheel
    V->>V: hash genuine wheel and accept
    V->>C: record modified wheel for reuse
    C->>N: seed modified pip without rehash
    Note over P,C: Even a rejected wheel can remain in the cache

Reproducer files

The following files are from the scanner report for ANT-2026-ST00DRO3. They were tested against commit a857a7823c73 in the scanner image. The repro entrypoint expects companion files under /tmp/repro.d; cases that change user IDs need a disposable root-capable container. The last output line is BUG when the reported behavior occurs. These files are included as evidence and were not rerun for this advisory update.

repro

#!/usr/bin/env python3
import atexit
import hashlib
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path

companions = Path("/tmp/repro.d")
work = Path("/tmp/forged-saved-line")
home = work / "home"
app_data = home / ".cache" / "virtualenv"
this_python =…
Record
GHSA-494p-63j8-28p3
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
Weakness
CWE-354
Updated
2026-11-02
NVD 2.3

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
CVE-2026-113714
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-354
Updated
2026-11-02
OSV 2.3

virtualenv: Downloaded wheel selection and caching precede verification

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
CVE-2026-113714
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
Weakness
CWE-354
Updated
2026-11-02
OSVDEBIAN-CVE-2026-113714 2.3

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
DEBIAN-CVE-2026-113714
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Updated
2026-11-02
Show 6 moreShow less
OSVGHSA-494p-63j8-28p3 2.3

virtualenv: Downloaded wheel selection and caching precede verification

Summary

Wheel downloads go directly into the reusable wheelhouse, while the verification target is selected from pip's textual output. A verification failure does not remove the downloaded file from that cache.

Anthropic's OSS Scanner reported this as ANT-2026-ST00DRO3 in virtualenv-20261015-005, received on 15 October 2026.

Affected area

Download selection and promotion into the reusable wheel cache.

Attacker-controlled input

A download must fail verification, or the archive selected from pip output must differ from the file later reused. Default-index digest verification must be enabled. Existing custom-index exceptions are outside the promised PyPI comparison.

Affected code

The relevant 21.14.8 source is in wheel download and digest check.

Mechanism

download_wheel() tells pip to write directly into the reusable wheel/house cache. It then selects a path to hash from the first Saved line in pip's stdout, rather than from the file pip actually placed there. A package index can inject such a line through a discarded link's data-requires-python attribute. A separate failure exists without the forged line: a digest mismatch raises after the downloaded wheel has entered the cache, and the rejected bytes remain there. Later wheel selection does not hash the cached file again.

Reproduction and evidence

The scanner used a local index configured in pip.conf, with PyPI digest comparison still enabled. In the control case it served a modified pip wheel alone. virtualenv --upgrade-embed-wheels refused the mismatch, and a subsequent environment used the bundled pip. In the attack case the index added a link whose data-requires-python contained a forged Saved <path> line pointing at a genuine bundled wheel. The upgrade then exited without a digest error, recorded the modified wheel in its update log, and a later environment's pip --version ran a line added by the index. The reproduction used a known absolute path to the genuine bundled wheel; that prerequisite is part of this variant.

The report also shows that a refused wheel can replace a previously accepted file of the same name in wheel/house, leaving rejected bytes available for later use. It does not claim to bypass TLS or affect the default PyPI setup. The gap follows the digest check added in #3251, first released in 21.7.12. PR #3430 downloads to a private temporary directory, verifies the actual archive, and promotes it only after acceptance.

Attack sequence

sequenceDiagram
    participant I as Configured index
    participant P as pip download
    participant C as wheel/house cache
    participant V as virtualenv digest check
    participant N as New environment
    I->>P: modified wheel and forged Saved line
    P->>C: write modified wheel directly
    P-->>V: stdout names genuine bundled wheel
    V->>V: hash genuine wheel and accept
    V->>C: record modified wheel for reuse
    C->>N: seed modified pip without rehash
    Note over P,C: Even a rejected wheel can remain in the cache

Reproducer files

The following files are from the scanner report for ANT-2026-ST00DRO3. They were tested against commit a857a7823c73 in the scanner image. The repro entrypoint expects companion files under /tmp/repro.d; cases that change user IDs need a disposable root-capable container. The last output line is BUG when the reported behavior occurs. These files are included as evidence and were not rerun for this advisory update.

repro

#!/usr/bin/env python3
import atexit
import hashlib
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path

companions = Path("/tmp/repro.d")
work = Path("/tmp/forged-saved-line")
home = work / "home"
app_data = home / ".cache" / "virtualenv"
this_python =…
Record
GHSA-494p-63j8-28p3
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
Weakness
CWE-354
Updated
2026-11-02
OSVUBUNTU-CVE-2026-113714 2.3

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
UBUNTU-CVE-2026-113714
CVSS 4.0
2.3 LowCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
Updated
2026-11-02
OSVAZL-112583 No score

CVE-2026-113714 affecting package python-virtualenv 20.36.1-10

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
AZL-112583
Updated
2026-11-05
OSVBIT-virtualenv-2026-113714 No score

virtualenv: Downloaded wheel selection and caching precede verification

virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, download_wheel() writes package-index downloads directly into the reusable wheel/house cache before selecting a verification target from pip's textual Saved output. With default-index digest comparison enabled, a controlled package index can inject a misleading Saved line through a discarded link's data-requires-python attribute; this forged-line variant also requires knowledge of an absolute path to the genuine bundled wheel. The wrong path can then be hashed, while a digest mismatch leaves the rejected archive in the cache for later selection without verification. A separate rejected-cache-file variant requires control over downloaded data or archive selection but not the forged-line prerequisites. Explicit custom-index exceptions are outside the promised PyPI comparison, and the issue does not independently bypass TLS or establish control of default PyPI. This issue is fixed in version 21.14.9.

Record
BIT-virtualenv-2026-113714
Updated
2026-11-02
OSVCLSA-2026-1793107254 No score

TuxCare security update for virtualenv (8 CVEs)

TuxCare rebuilt virtualenv to address 8 CVEs. Fixed in 20.39.1.post5+tuxcare.

Record
CLSA-2026-1793107254
Updated
2026-11-02
OSVopenSUSE-SU-2026:12372-1 No score

python313-virtualenv-21.15.1-1.1 on GA media

These are all security issues fixed in the python313-virtualenv-21.15.1-1.1 package on the GA media of openSUSE Tumbleweed.

Record
openSUSE-SU-2026:12372-1
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
virtualenv
PyPI
before 21.14.9
21.14.9
python-virtualenv
Azure Linux:3
through 20.36.1-10 None known
virtualenv
Bitnami
before 21.14.9
21.14.9
virtualenv
TuxCare:PyPI
From an advisory covering 8 CVEs
20.39.1 up to 20.39.1.post5+tuxcare, plus listed versions
20.39.1.post1+tuxcare20.39.1.post2+tuxcare20.39.1.post3+tuxcare20.39.1.post4+tuxcare
20.39.1.post5+tuxcare
python-virtualenv
Debian:12
All versions, plus listed versions
20.17.1+ds-120.19.0+ds-120.21.0+ds-120.23.0+ds-120.23.0+ds-2
Show 28 moreShow less
20.24.1+ds-120.24.6+ds-120.24.6+ds-220.25.0+ds-120.25.0+ds-220.25.1+ds-120.26.1+ds-120.26.2+ds-120.26.6+ds-120.27.0+ds-120.28.0+ds-120.29.1+ds-120.29.3+ds-120.30.0+ds-120.30.0+ds-220.30.0+ds-320.31.2+ds-120.33.1+ds-120.34.0+ds-120.35.3+ds-120.35.4+ds-120.36.1+ds-120.38.0+ds-121.2.0+ds-121.2.4+ds-121.3.1+ds-121.4.1+ds-121.5.1+ds-1
None known
python-virtualenv
Debian:13
All versions, plus listed versions
20.31.2+ds-120.31.2+ds-1+deb13u120.33.1+ds-120.34.0+ds-120.35.3+ds-1
Show 8 moreShow less
20.35.4+ds-120.36.1+ds-120.38.0+ds-121.2.0+ds-121.2.4+ds-121.3.1+ds-121.4.1+ds-121.5.1+ds-1
None known
python-virtualenv
Debian:14
All versions, plus listed versions
20.31.2+ds-120.33.1+ds-120.34.0+ds-120.35.3+ds-120.35.4+ds-1
Show 7 moreShow less
20.36.1+ds-120.38.0+ds-121.2.0+ds-121.2.4+ds-121.3.1+ds-121.4.1+ds-121.5.1+ds-1
None known
virtualenv
PyPI
before 21.14.9, plus listed versions
0.80.8.10.8.20.8.30.8.4
Show 45 moreShow less
0.90.9.10.9.21.01.11.101.10.11.111.11.11.11.21.11.31.11.41.11.51.11.61.21.31.3.11.3.21.3.31.3.41.41.4.11.4.21.4.31.4.41.4.51.4.61.4.71.4.81.4.91.4rc11.51.5.11.5.21.61.6.11.6.21.6.31.6.41.71.7.11.7.1.11.7.1.21.7.21.8
21.14.9
python-virtualenv
Ubuntu:14.04:LTS
All versions, plus listed versions
1.10.1-11.11-11.11-21.11.4-11.11.4-1ubuntu1
None known
python-virtualenv
Ubuntu:16.04:LTS
All versions, plus listed versions
1.11.6+ds-114.0.5+ds-114.0.5+ds-214.0.5+ds-415.0.0+ds-1
Show 6 moreShow less
15.0.0+ds-215.0.1+ds-115.0.1+ds-215.0.1+ds-315.0.1+ds-3ubuntu115.0.1+ds-3ubuntu1.1
None known
Show 7 moreShow less
Affected packages
PackageAffectedFixed
python-virtualenv
Ubuntu:18.04:LTS
All versions, plus listed versions
15.1.0+ds-115.1.0+ds-1.1
None known
python-virtualenv
Ubuntu:Pro:20.04:LTS
All versions, plus listed versions
15.1.0+ds-215.1.0+ds-2build115.1.0+ds-2ubuntu120.0.13-120.0.17-120.0.17-1ubuntu0.320.0.17-1ubuntu0.420.0.17-1ubuntu0.4+esm1
None known
python-virtualenv
Ubuntu:Pro:22.04:LTS
All versions, plus listed versions
20.4.6+ds-220.10.0+ds-120.12.1+ds-120.13.0+ds-120.13.0+ds-220.13.0+ds-2ubuntu0.1~esm1
None known
python-virtualenv
Ubuntu:Pro:24.04:LTS
All versions, plus listed versions
20.24.1+ds-120.24.6+ds-120.24.6+ds-220.25.0+ds-120.25.0+ds-220.25.0+ds-2ubuntu0.1~esm1
None known
python-virtualenv
Ubuntu:26.10
All versions, plus listed versions
20.36.1+ds-121.4.1+ds-121.5.1+ds-1
None known
python-virtualenv
Ubuntu:26.04:LTS
All versions, plus listed versions
20.31.2+ds-120.35.3+ds-120.35.4+ds-120.36.1+ds-1
None known
python-virtualenv
openSUSE:Tumbleweed
From an advisory covering 9 CVEs
before 21.15.1-1.1
21.15.1-1.1

History

History
RecordedChangedSources
affected
affected, factsOSV
affected, aliasesOSV
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-113714 is a low vulnerability (CVSS 2.3) in Pypa Virtualenv, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.25%, the 16th percentile. Fixed in virtualenv 21.14.9.

References

Show 14 moreShow less
This page doesn't list everything

Left out here: 254 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open