Skip to content

Agent and hosted mode ignore the interpreter PDM records in .pdm-python (venv.in_project = false, pdm use <venv>), so the real env is skipped or a stray .venv / the PATH python is patched, and VEX attests an unpatched install #502

Description

[agent] Found by the scheduled PDM bug-hunt routine (ledger #312).

Summary

PDM records the project's interpreter in .pdm-python. With venv.in_project = false it points at PDM's out-of-tree venv (<data_dir>/pdm/venvs/<project>-<hash>-<py>/bin/python), and after pdm use <path> it points at whatever venv the user picked. pdm sync, pdm install and pdm run all use that interpreter. The Python crawler's local discovery (find_local_venv_site_packages_with) has dedicated probes for Pipenv's and Poetry's out-of-tree venvs, but none for PDM. It only checks VIRTUAL_ENV, ./.venv and ./venv, and then falls back to the global interpreters. So on a PDM project whose env isn't ./.venv:

  1. Agent mode skips the real env and exits 0. scan --mode agent reports [skip] … (not installed; run your package manager's install first …). The package is installed, in the env pdm run uses.
  2. Agent mode patches the wrong env and reports success. If a stray ./.venv exists (for example left over from before switching to in_project = false), or if python3 on PATH has the package, that tree is patched (applied: 1). PDM's env stays upstream.
  3. Hosted mode gives no stale-install warning, and VEX falsely attests. After scan --mode hosted with no reinstall, the in-project control prints the still differ from the patched hashes warning, and vex omits the package as not_applied (exit 1). With the out-of-tree env there's no warning, and vex emits not_affected / inline_mitigations_already_exist ("redirected", exit 0) while pdm run python imports the unpatched bytes.

The Poetry and Pipenv versions of this were filed and fixed (#327, #329, #334, #384; #476 is open), so this is the PDM gap in the same probe.

Impact

Agent mode silently doesn't patch, or patches an environment the app never runs in, and still exits 0 with applied: 1. VEX then claims a mitigation that isn't installed. venv.in_project = false is a documented, commonly used PDM setting (centralized venvs), and pdm use <venv> is the standard way to bind an existing venv.

Repro (Linux, PDM 2.29.2, main 61cfb9b)

The patch API was a local mock serving the free urllib3@1.26.18 patch (SOCKET_PROXY_URL). Any PyPI patch will do.

mkdir app && cd app
printf '[project]\nname="app"\nversion="0.0.0"\nrequires-python=">=3.8"\ndependencies=["urllib3==1.26.18"]\n[tool.pdm]\ndistribution=false\n' > pyproject.toml
pdm config venv.in_project false
pdm lock && pdm sync
cat .pdm-python                       # …/pdm/venvs/app-XXXX-3.11/bin/python
# (2) optional: a stray in-project venv that PDM does not use
python3 -m venv .venv && .venv/bin/pip install urllib3==1.26.18
socket-patch scan --mode agent --yes --json | jq .apply
#   no .venv:   {"applied":0,"skipped":1,…}   "[skip] … not installed"
#   with .venv: {"applied":1,…}               but:
pdm run python -c 'import urllib3.response as r; print(getattr(r,"SOCKET_PATCHED",0))'   # 0 (upstream)
.venv/bin/python -c 'import urllib3.response as r; print(getattr(r,"SOCKET_PATCHED",0))' # 1 (wrong env patched)
# (3) hosted + VEX, no reinstall after the rewrite
git checkout pdm.lock; rm -rf .venv .socket
socket-patch scan --mode hosted --yes      # no "installed files … still differ" warning
socket-patch vex --product pkg:pypi/app@0.0.0   # exit 0, status not_affected ("redirected")
pdm run python -c 'import urllib3.response as r; print(getattr(r,"SOCKET_PATCHED",0))'   # 0

Expected vs actual

  • Expected: the crawler finds the environment PDM installs into, as it already does for Poetry's and Pipenv's out-of-tree venvs (docs/ecosystems.md: "Pipenv's out-of-tree venv are discovered"; CLI_CONTRACT.md lists the probe set "VIRTUAL_ENV, ./.venv, ./venv, Pipenv's out-of-tree WORKON_HOME venv"). For a PDM project, .pdm-python (or PDM's venv.location / venv.in_project resolution) should decide the env, and a ./.venv that PDM doesn't use shouldn't be patched. A stale install must keep the hosted warning and the VEX not_applied omission, as in the in-project control.
  • Actual: see the summary. Every command exits 0.

Matrix (Linux; each cell reproduced at least twice)

PDM env setup agent scan wrong tree patched hosted stale warning / VEX
2.29.2 venv.in_project=false skip "not installed", exit 0 — no warning / not_affected on an unpatched env
2.29.2 in_project=false + stray ./.venv applied: 1 ./.venv —
2.29.2 in_project=false, python3 on PATH has urllib3 applied: 1 PATH interpreter —
2.29.2 default in_project=true, pdm use -f <external venv>/bin/python skip "not installed", exit 0 — —
2.12.4 venv.in_project=false (± stray .venv) skip / applied: 1 to ./.venv ./.venv —
2.29.2 control default in-project .venv applied to PDM's env — warning + not_applied (correct)

macOS and Windows weren't probed. The probe is OS-independent (PDM's venv dir is under platformdirs' user data dir on every OS).

First bad version: not a regression. The published socket-patch 4.0.0 (PyPI) behaves the same (stray .venv patched, PDM env upstream). PDM discovery has never existed.

Suspect code

  • crates/socket-patch-core/src/crawlers/python_crawler.rs:336 (find_local_venv_site_packages_with): steps 2–3 special-case Pipenv and Poetry. There's no PDM step, so a PDM project falls through to ./.venv / ./venv (line 381) and then the global fallback. A fix would read .pdm-python (PDM ≥ 2.0; PDM 1.x stored python.path in .pdm.toml). When it names an interpreter, it would probe that interpreter's prefix and nothing else, the same as the Pipenv branch.
  • The hosted stale-install probe and vex use the same crawler, which is why (3) follows from it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions