Repository navigation
With Poetry virtualenvs.create = false, agent mode patches a stray ./venv (or ./.venv under in-project = false) instead of the system env Poetry installed into, and VEX attests not_affected #671
Description
[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).
Summary
When a Poetry project sets virtualenvs.create = false (in poetry.toml, config.toml or POETRY_VIRTUALENVS_CREATE=false, which is the usual container setup), Poetry installs into the system interpreter (EnvManager.get() → get_system_env()). The only exception is an existing ./.venv when virtualenvs.in-project isn't explicitly false. Poetry never uses ./venv.
socket-patch's crawler has step 3 to stop a stray .venv / venv from shadowing Poetry's env. With create = false, though, poetry_virtualenvs_root returns None, step 3 finds nothing, and step 4 then probes ./.venv and ./venv. If one of them exists (for example, a developer's local venv copied into an image by COPY . .), socket-patch patches that tree and never reaches the global fallback. The site-packages Poetry installed into, and that poetry run imports, stays unpatched. socket-patch vex then attests not_affected / inline_mitigations_already_exist.
Impact
The vulnerable copy stays in use, socket-patch reports success (exit 0), and the VEX document claims the vulnerability is mitigated. That's a false attestation.
Repro (Linux, real Poetry 2.5.1 and 1.8.5)
SYS is a standalone CPython 3.12 (a copy of a uv-managed interpreter) standing in for a container's system Python, with Poetry pip-installed into it and first on PATH. The patch API is a local mock that serves a six.py patch for six@1.16.0, using the same routes as tests/vex_pypi_real_common.
export PATH=$SYS/bin:$PATH
mkdir demo && cd demo
cat > pyproject.toml <<'EOF'
[tool.poetry]
name = "demo"
version = "0.1.0"
description = ""
authors = ["x <x@x.x>"]
package-mode = false
[tool.poetry.dependencies]
python = ">=3.9"
six = "1.16.0"
EOF
printf '[virtualenvs]\ncreate = false\n' > poetry.toml # or: export POETRY_VIRTUALENVS_CREATE=false
python3.11 -m venv venv && venv/bin/pip install six==1.16.0 # stray venv Poetry never uses
poetry install # installs six into $SYS site-packages
socket-patch scan --mode agent --yes --api-url $MOCK --api-token fake --org test-org
poetry run python -c "import six; print(six.__file__, getattr(six, 'SOCKET_PATCHED', 0))"
socket-patch vex -O v.json --api-url $MOCK --api-token fake --org test-orgActual:
Summary: 1 of 1 targeted patch applied, 0 already patched, 0 not found on disk
system copy patched: 0 stray venv copy patched: 1
poetry run imports …/sys/lib/python3.12/site-packages/six.py SOCKET_PATCHED= 0
VEX: not_affected inline_mitigations_already_exist
Expected
The docs, docs/testing/poetry-compatibility.md ("Mode notes"), say: "virtualenvs.create = false (containers) means Poetry installed into the system interpreter, which the project-marker global fallback covers." The crawler's own step-3 comment says that "a stray .venv / venv left by another tool does not shadow the env Poetry installed into". So with create = false, agent mode should patch the env Poetry uses: ./.venv only when in-project isn't false and .venv is a directory, otherwise the system interpreter. It should never patch ./venv. vex must not attest a patch that the installed copy doesn't carry.
Matrix (Linux, main 045d7ec)
| Poetry | Config | Stray tree | Patched | Poetry's copy | VEX |
|---|---|---|---|---|---|
| 2.5.1 | poetry.toml create = false |
./venv |
./venv |
unpatched | not_affected ❌ |
| 2.5.1 | POETRY_VIRTUALENVS_CREATE=false |
./venv |
./venv |
unpatched | not_affected ❌ |
| 2.5.1 | create = false, in-project = false |
./.venv |
./.venv |
unpatched | not_affected ❌ |
| 1.8.5 | create = false |
./venv |
./venv |
unpatched | not_affected ❌ |
| 1.8.5 | create = false, in-project = false |
./.venv |
./.venv |
unpatched | not_affected ❌ |
| 2.5.1 | create = false, in-project unset |
./.venv (Poetry uses it) |
./.venv |
patched | ok ✅ (control) |
| 2.5.1 | create = false |
none | system | patched | ok ✅ (control) |
Each failing cell reproduced at least twice. Poetry 1.8.5 and 2.5.1 have the same EnvManager.get() order: in_project_venv_exists(), then if not create_venv: return self.get_system_env(). macOS and Windows weren't tested (probe branches are blocked for this routine), but the code path isn't platform-specific.
First bad version
Release 4.0.0 already patches the stray ./venv. Its vex was blocked by the old setup gate (ecosystem_not_setup), so the false VEX attestation is new on main now that setup is gone. 3.3.0's agent scan didn't patch either copy in this layout. Open PR #644 (data-dir and placeholder model) doesn't change this: I built its head and both stray layouts still fail the same way.
Suspect code
crates/socket-patch-core/src/crawlers/python_crawler.rs:401: step 3 is skipped (it returns empty) whencreate == Some(false)(poetry_virtualenvs_root, line 982), so control falls through.crates/socket-patch-core/src/crawlers/python_crawler.rs:408: step 4 probes./.venvand./venvfor a Poetry project whose config rules both out. For a Poetry project withcreate = false, it should return the in-project.venvonly whenin_project_venv_exists, otherwise go straight to the global fallback.
Activity
mikolalysenko commentedon Oct 3, 2026
[agent] Triage: priority:p1 (Poetry). This is a correctness bug: VEX attests
not_affectedfor a copy Poetry never imports. Confirmed on main045d7ec. Incrates/socket-patch-core/src/crawlers/python_crawler.rs, step 3 returns nothing whenvirtualenvs.create = false. Control then falls through to step 4, which probes./.venvand./venvwithout checking Poetry's own rule (in_project_venv_exists, else system env).I found no duplicate. It's related to #608/#640 (open PR #644, Poetry data-dir model) but has a separate cause, and the reporter confirmed #644's head doesn't fix it.
Generated by Claude Code
poetry: run 13 ledger (#866 filed; #608/#640 verified; #671/#450 stil…
mikolalysenko commentedon Oct 9, 2026
[agent] Claiming this issue (with #866, #1023; shared root cause: Poetry env discovery does not mirror EnvManager.get as one final answer, so an empty or per-placement Poetry result falls through to stray ./.venv / ./venv probes, the active shell env, or the global site-packages). Branch: agent/fix-poetry-env-final-resolution. Claim-ID: 2026-10-09T10:20:53Z-e18342
Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Draft PR: #1259
Generated by Claude Code
Add failing tests for Poetry env discovery
Resolve a Poetry project's env as one answer
poetry: run 18 ledger (#1447 filed; #1282/#1023/#671 verified)