{"schema":"threatline.record/2","record":{"id":"CVE-2026-102938","aliases":["BIT-virtualenv-2026-102938","GHSA-9h9j-4vrj-gf7g","PYSEC-2026-4012"],"summary":"virtualenv writes prompt values into pyvenv.cfg without sanitizing line boundaries, allowing configuration injection","details":"### Summary\n\n`pyvenv.cfg` is a line-based format with no escape syntax. `PyEnvCfg.write()` wrote values verbatim, while `PyEnvCfg._read_values()` parses the file with `str.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.\n\n### Impact\n\nThe `prompt` value is the reachable input: it is set by `--prompt`, by the `VIRTUALENV_PROMPT` environment variable, or from the config file, and `write()` emits `prompt` before `home`. A crafted prompt can therefore set `home` in the generated `pyvenv.cfg`:\n\n```console\n$ virtualenv --prompt $'x\"\\nhome = /attacker/path\\nprompt = \"z' venv\n$ grep '^home' venv/pyvenv.cfg\nhome = /attacker/path\n```\n\n`home` is what tooling reads to locate the base interpreter, so a consumer that trusts it can be pointed elsewhere. `implementation`, `version_info`, `version`, `executable`, `command` and `virtualenv` are also written before `prompt` and can be replaced the same way.\n\nThis 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.\n\n### Details\n\nThe 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+2028` and `U+2029` were all written through unchanged and all split the line when read back.\n\n### Patches\n\n`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 in `content`.\n\n### Workarounds\n\nDo not pass externally influenced data as the virtualenv prompt. Strip line boundaries from any value before using it as `--prompt` or `VIRTUALENV_PROMPT`.","categories":["Libraries & packages"],"product":"virtualenv","vendor":"pypa","ecosystem":"pip","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-9h9j-4vrj-gf7g","source_modified_at":"2026-09-30T23:53:59Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"pip","name":"virtualenv"},"range":"<= 21.7.10"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-102938","source_modified_at":"2026-09-30T19:53:20.817Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"virtualenv","vendor":"pypa","versions":[{"version":"< 21.7.11","status":"affected"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-9h9j-4vrj-gf7g","source_modified_at":"2026-10-06T15:25:56.172772842Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"PyPI","name":"virtualenv","purl":"pkg:pypi/virtualenv"},"ranges":[{"events":[{"introduced":"0"},{"fixed":"21.7.11"}],"type":"ECOSYSTEM"}],"versions":["0.8","0.8.1","0.8.2","0.8.3","0.8.4","0.9","0.9.1","0.9.2","1.0","1.1","1.10","1.10.1","1.11","1.11.1","1.11.2","1.11.3","1.11.4","1.11.5","1.11.6","1.2","1.3","1.3.1","1.3.2","1.3.3","1.3.4","1.4","1.4.1","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.4.9","1.4rc1","1.5","1.5.1","1.5.2","1.6","1.6.1","1.6.2","1.6.3","1.6.4","1.7","1.7.1","1.7.1.1","1.7.1.2","1.7.2","1.8"]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-9h9j-4vrj-gf7g","source_modified_at":"2026-09-30T23:53:59Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"pip","name":"virtualenv"},"version":"21.7.11"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-9h9j-4vrj-gf7g","source_modified_at":"2026-10-06T15:25:56.172772842Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"PyPI","name":"virtualenv","purl":"pkg:pypi/virtualenv"},"version":"21.7.11"}],"cvss":5.8,"severity":"Medium","kev":{"listed":false},"epss":{"probability":0.0014,"percentile":0.03058,"score_date":"2026-10-23"},"published_at":"2026-09-29T20:58:20.696Z","modified_at":"2026-10-06T15:25:56.172772842Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-9h9j-4vrj-gf7g","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-09-30T23:53:59Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-102938","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-09-30T19:53:20.817Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-9h9j-4vrj-gf7g","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-06T15:25:56.172772842Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-102938","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/pypa/virtualenv/security/advisories/GHSA-9h9j-4vrj-gf7g","tags":["x_refsource_CONFIRM"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-102938","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/pypa/virtualenv/pull/3247","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/pypa/virtualenv/commit/a30f995461043acb6cacbf3a890951563ccf7140","tags":["x_refsource_MISC"]},{"url":"https://github.com/pypa/advisory-database/tree/main/vulns/virtualenv/PYSEC-2026-4012.yaml","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/pypa/virtualenv/releases/tag/21.7.11","tags":["x_refsource_MISC"]},{"url":"https://pypi.org/project/virtualenv","source":"GitHub Advisory Database"},{"url":"https://github.com/advisories/GHSA-9h9j-4vrj-gf7g","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"none","automatable":"no","technical_impact":"partial","assessed_at":"2026-09-30T19:53:10.688613Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"product","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-06T15:25:56.172772842Z","content_hash":"67390e12a81184664ce4fa58c28cce056d385ea53cd7a43c268f357a50f85a26"}}
