Repository navigation
Description
[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).
Summary
Poetry keeps one out-of-tree virtualenv per Python minor you've used with a project (<name>-<hash>-py3.11, <name>-<hash>-py3.12, …). It runs and installs into the activated one, which is recorded in <virtualenvs.path>/envs.toml by poetry env use ([<name>-<hash>] minor = "3.12").
Since #330 (5678b76), poetry_virtualenv_site_packages returns every matching -py* env, sorted by directory name (crates/socket-patch-core/src/crawlers/python_crawler.rs:881, venvs.sort()). It never reads envs.toml. Agent mode then patches only one copy: the first in lexicographic order. When that isn't the activated env, the results are:
scan --mode agentreportsadded(exit 0) and patches the inactive env. The env thatpoetry run/poetry shelluse stays unpatched.- A follow-up
applyreportsalready_patchedand does nothing. vexattestsnot_affected/inline_mitigations_already_existfor the project.
Because the sort is lexicographic, the pick isn't even "lowest version": py3.10 sorts before py3.13, and py3.10 would sort before py3.9.
Impact
This is a common setup: anyone who has moved a project to a newer Python with poetry env use 3.12 keeps the old env around. Patches land in a dead env, and the VEX document says the live one is fixed. The run looks fully successful, with nothing printed to stderr.
Repro (real Poetry, Linux, main 61cfb9b)
Any agent patch for six@1.16.0 works. I used a local mock of the patch API (batch + view, the same shapes as crates/socket-patch-cli/tests/in_process_pypi_multi_release.rs).
SP=/path/to/target/release/socket-patch
A="--api-url $MOCK --api-token sktsec_..._api --org o"
mkdir envmulti && cd envmulti
cat > pyproject.toml <<'T'
[tool.poetry]
name = "envmulti"
version = "0.1.0"
description = ""
authors = ["x <x@x>"]
package-mode = false
[tool.poetry.dependencies]
python = ">=3.10"
six = "1.16.0"
T
poetry lock
poetry env use python3.11 && poetry install --no-root
poetry env use python3.12 && poetry install --no-root # py3.12 is now the activated env
poetry env list --full-path
# …/envmulti-HDRmhnzz-py3.11
# …/envmulti-HDRmhnzz-py3.12 (Activated)
$SP scan --mode agent $A --ecosystems pypi --yes --json # status success, action "added"
grep -c SOCKET-PATCHED ~/.cache/pypoetry/virtualenvs/envmulti-*/lib/*/site-packages/six.py
# …-py3.11/lib/python3.11/site-packages/six.py:1 <- patched (inactive env)
# …-py3.12/lib/python3.12/site-packages/six.py:0 <- NOT patched (activated env)
poetry run python -c "import six; print(six.__file__)" # …-py3.12/…/six.py, unpatched
$SP apply $A --ecosystems pypi --json # skipped, already_patched
$SP vex $A --output vex.json # exit 0, statement: not_affectedExpected vs actual
- Expected: the patch goes to the env Poetry uses for the project, or to every env that holds the package. Fix Poetry venv discovery to match Poetry (#327, #329) #330's stated goal is venv discovery that matches Poetry. For that, the activated env (
envs.toml'sminor, else the env matching the interpreter Poetry would pick) has to come first.vexshould only attest copies that are actually patched. - Actual: the alphabetically first
<name>-<hash>-py*env is patched. The activated env keeps upstream bytes,applycalls italready_patched, andvexattestsnot_affected.
OS × version matrix (Linux, main 61cfb9b)
There were 9 independent fresh-project runs. The first row ran twice. Every interpreter here is a real separate CPython (Ubuntu 3.10, 3.11, 3.12 and 3.13), with Poetry pip-installed into its own venv.
| Poetry | envs (first created → activated) | virtualenvs.path |
Patched env | Activated env patched? | VEX |
|---|---|---|---|---|---|
| 2.5.1 | 3.11 → 3.12 | custom (POETRY_VIRTUALENVS_PATH) |
py3.11 | no | not_affected ✗ |
| 2.5.1 | 3.11 → 3.12 | default cache dir, package-mode = false |
py3.11 | no | not_affected ✗ |
| 2.5.1 | 3.11 → 3.12 | default cache dir, package mode | py3.11 | no | not_affected ✗ |
| 2.5.1 | 3.10 → 3.13 | custom | py3.10 | no | not_affected ✗ |
| 1.8.5 | 3.11 → 3.12 | custom | py3.11 | no | not_affected ✗ |
| 2.5.1 | 3.12 → 3.11 | custom | py3.11 | yes (by luck) | ok |
| 2.5.1 | 3.13 → 3.10 | custom | py3.10 | yes (by luck) | ok |
| 1.8.5 | 3.12 → 3.11 | custom | py3.11 | yes (by luck) | ok |
macOS and Windows aren't tested (this routine's probe branches are blocked), but the selection logic is platform-independent.
First bad version
This isn't a regression in a release. v4.0.0 and v3.3.0 don't discover these out-of-tree envs at all in this layout: package_not_installed, exit 0 (the #327 family). The wrong-env pick arrived with #330 (5678b76), which is unreleased.
Suspect code
crates/socket-patch-core/src/crawlers/python_crawler.rs:881:venvs.sort()orders the per-minor envs by name. Poetry'sEnvManager.get()readsenvs.toml(minor) for the active env, and falls back to the current interpreter's minor.crates/socket-patch-cli/src/commands/vex.rs:556:collapse_to_first. Agent-mode npmvexhashes only the first installed copy of a package, so it attests not_affected while another nested copy of the same name@version is unpatched #516 / PR Fix agent vex checking only one installed copy (#516) #517 cover the "VEX checks only the first copy" half for npm. That alone wouldn't fix this, becauseapplyhere also leaves the activated copy alone and reportsalready_patched.
Activity
mikolalysenko commentedon Oct 2, 2026
[agent] There's a second trigger with the same root cause (socket-patch doesn't read
envs.toml). Reproduced 2/2 on main61cfb9bwith Poetry 2.5.1.Poetry's
EnvManager.get()(2.5.1,poetry/utils/env/env_manager.py) runsif not in_venv or env is not None:and only falls through toVIRTUAL_ENV/CONDA_PREFIXwhenenvs.tomlhas no entry for the project. So afterpoetry env use 3.12, an unrelated activated venv in the shell is ignored by Poetry. socket-patch'sfind_local_venv_site_packages_withstep 1 (python_crawler.rs:348) returnsVIRTUAL_ENVfirst, unconditionally, for a Poetry project:The fix for #526 (honour
envs.toml'sminor) should also gate theVIRTUAL_ENVshortcut for Poetry projects the way Poetry does, the same way it's already gated for Pipenv (pipenv_uses_virtual_env). That includesCONDA_PREFIX/CONDA_DEFAULT_ENV != "base", which socket-patch doesn't read at all. I haven't tested conda (it isn't available in the sandbox).Generated by Claude Code
poetry: run 7 (#526 filed)
mikolalysenko commentedon Oct 2, 2026
[agent] Shares root cause with #476: socket-patch's Poetry venv lookup doesn't follow Poetry's
EnvManager.get(). It ignoresenvs.toml(the env picked bypoetry env use, which also overridesVIRTUAL_ENV), and it treatsin-project = trueas "only./.venv" even when./.venvdoesn't exist. Will be fixed together.Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Claiming this issue (with #476; shared root cause: Poetry venv lookup doesn't follow Poetry's EnvManager.get() env selection). Branch: agent/fix-poetry-env-selection. Claim-ID: 2026-10-02T03:21:02Z-899515
Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Draft fix PR: #527
Generated by Claude Code
Patch the Poetry env that Poetry actually uses
Fix Poetry venv selection ignoring envs.toml (#476, #526) (#527)