Oct 24, 2026·5 min read·3 visits
virtualenv prior to 21.14.9 fails to sanitize Unicode line break characters in destination directory paths, allowing configuration parameter injection into pyvenv.cfg when using the stdlib venv creator.
In virtualenv versions prior to 21.14.9, creating isolated Python virtual environments using the standard library creator (`--creator venv`) allows configuration key injection into `pyvenv.cfg` if an attacker can control or influence the destination directory path containing Unicode line break characters.
The virtualenv package is a widely used Python ecosystem tool for creating isolated Python runtime environments. When invoking virtualenv, users can specify various virtual environment creators, including CPython's standard library venv module (--creator venv). During execution, this creator invokes standard library components to bootstrap the basic environment structure and write an initial runtime configuration file named pyvenv.cfg.
In virtualenv versions prior to 21.14.9, creating an environment using the standard library venv creator with a destination directory containing special Unicode line break characters allows arbitrary key-value injection into pyvenv.cfg. The standard library venv module writes user-supplied directory paths directly into configuration keys such as command without escaping line boundaries.
During subsequent initialization and readback operations, virtualenv parsed pyvenv.cfg using Python's native str.splitlines() method. Because str.splitlines() breaks strings on non-ASCII Unicode line separators (e.g., U+2028 Line Separator or U+2029 Paragraph Separator), embedded characters split single parameters across multiple lines. Substrings following the line boundary are interpreted as top-level configuration key-value pairs, corrupting the configuration state.
The vulnerability stems from two combined design behaviors: unescaped key formatting by standard library venv.EnvBuilder and broad line break recognition during parsing in virtualenv.
When virtualenv delegates environment creation to venv.EnvBuilder, CPython writes the execution command into pyvenv.cfg without escaping non-printable or Unicode line separator characters. If the target directory path contains a Unicode line break, the key value written to disk spans multiple physical lines in logical text terms.
Python's str.splitlines() implementation recognizes a wide range of code points beyond standard ASCII carriage return (\r) and line feed (\n) characters. These include Vertical Tab (\x0b), Form Feed (\x0c), File Separator (\x1c), Group Separator (\x1d), Record Separator (\x1e), Next Line (\x85), Line Separator (\u2028), and Paragraph Separator (\u2029).
Prior to version 21.14.9, virtualenv's set_pyenv_cfg() method in src/virtualenv/create/via_global_ref/venv.py read back the intermediate pyvenv.cfg file generated by standard library venv using str.splitlines() and merged all parsed pairs into virtualenv's active configuration (self.pyenv_cfg.update(venv_content)). An attacker capable of placing a line break sequence into the path string can inject keys such as home or include-system-site-packages into the returned dictionary.
In affected versions, virtualenv did not validate destination paths for line break characters before invoking venv.EnvBuilder, nor did it filter parsed parameters upon reading back pyvenv.cfg.
The flaw was mitigated in commit b0008a58885ded9227faf8f910c6c402712c750a (PR #3426) by introducing path validation prior to execution and removing the intermediate configuration readback mechanism.
@classmethod
def validate_dest(cls, raw_value: str) -> str:
# Validates destination path and enforces line boundary checks
return cls._check_line_boundaries(super().validate_dest(cls._check_line_boundaries(raw_value)))
@staticmethod
def _check_line_boundaries(value: str) -> str:
# Standard library venv writes destination into pyvenv.cfg without escaping line boundaries.
if collapse_line_boundaries(value) != value:
msg: Final = (
f"destination {value!r} must not contain a line boundary as venv cannot record it in pyvenv.cfg"
)
raise ArgumentTypeError(msg)
return value
def set_pyenv_cfg(self) -> None:
super().set_pyenv_cfg()
if self.interpreter.implementation == "GraalPy":
# Set required launcher options directly without parsing intermediate venv output
self.pyenv_cfg["venvlauncher_command"] = self.interpreter.system_exe
self.pyenv_cfg["base-executable"] = self.interpreter.system_exeThe fix performs explicit validation using collapse_line_boundaries(). If any Unicode line boundary characters are detected inside the user-provided destination string, virtualenv immediately raises an ArgumentTypeError before venv writes any config files to disk.
To exploit this vulnerability, an attacker must influence the destination path argument passed to virtualenv when --creator venv is used. This typically occurs in automated build pipelines, web applications hosting Python environment provisioning services, or multi-tenant developer systems where users supply directory names.
An attacker constructs a directory path string containing a embedded Unicode Line Separator character (\u2028), followed by target configuration directives:
/tmp/env\u2028include-system-site-packages = true
When venv.EnvBuilder writes pyvenv.cfg, the command entry contains the unescaped path string. Upon readback via str.splitlines(), virtualenv parses include-system-site-packages = true as a distinct key-value entry rather than part of the path value.
Alternative key injections include altering the home directive (\u2028home = /attacker/path), which controls where standard library CPython binaries and modules are loaded from during environment runtime startup.
The impact of this key injection depends on the specific keys injected into pyvenv.cfg during environment creation.
Injecting include-system-site-packages = true breaks environment isolation, causing the created virtual environment to load global site packages from the base Python installation. In multi-tenant environments, this can allow unauthorized exposure to packages or data present in global directories.
Injecting home = /attacker/controlled_path alters CPython's library resolution search order during interpreter startup. If an attacker controls or can write to the specified path, CPython may load malicious modules during startup sequence execution, leading to potential arbitrary code execution within the context of the user running the environment.
The primary remediation for CVE-2026-113715 is upgrading virtualenv to version 21.14.9 or higher.
Upgrade the package using pip:
pip install --upgrade "virtualenv>=21.14.9"Verify that the installed version meets or exceeds the patched version:
virtualenv --versionIf immediate upgrading is not possible, applications calling virtualenv programmatically should sanitize input target path arguments by stripping or rejecting string values containing Unicode line boundary characters (\u2028, \u2029, \r, \n, \v, \f, \x85).
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
virtualenv PyPA | < 21.14.9 | 21.14.9 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-113715 / GHSA-qmx5-4p3x-wvqf |
| CWE ID | CWE-93 (Improper Neutralization of CRLF Sequences) |
| CVSS v4.0 Score | 2.0 (Low) |
| CVSS Vector | CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N |
| Affected Component | virtualenv (--creator venv) |
| Exploit Maturity | None (No public exploit) |
| Patched Version | 21.14.9 |
The software receives input containing CRLF or Unicode line boundary sequences and fails to sanitize them before writing into a line-structured configuration file, enabling attribute injection.
A command injection vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to execute arbitrary commands when a user sources a generated shell activation script containing a crafted prompt value.
virtualenv prior to version 21.14.9 contains a local permission inheritance vulnerability in its app-data copy seeder. Due to the reliance on Python's standard shutil.copy() function, permissions from cached packages are replicated directly onto newly created virtual environment files, ignoring the active process umask and allowing unprivileged local attackers to inject code.
A code injection vulnerability in pypa/virtualenv prior to version 21.14.9 allows arbitrary command execution in xonsh shells using legacy multibyte encodings when sourcing activate.xsh.
Prior to version 21.14.9, virtualenv downloads wheel files directly into its reusable persistent cache directory prior to verifying PyPI digests or compatibility. Combined with unsanitized stdout parsing from pip, a controlled package index can cause virtualenv to calculate digests against pre-existing local files while leaving unverified archives in the shared persistent cache.
A verification bypass vulnerability in virtualenv allows unverified Python wheel installation when using pip seeding with the download flag. Affected versions fail to perform SHA-256 digest validation against PyPI metadata before invoking pip.
Insecure temporary file usage (CWE-377) in virtualenv app-data seeder creates dangling symlinks pointing to deleted temporary directories, allowing local attackers on shared systems to execute arbitrary code.