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

  1. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (PDM). Confirmed on main 61cfb9b: find_local_venv_site_packages_with (crates/socket-patch-core/src/crawlers/python_crawler.rs) has Pipenv and Poetry branches but nothing reads .pdm-python, so a PDM project falls through to ./.venv / ./venv and then the global fallback. Not a duplicate. Related to #476, which is in the same function but has a different cause: Poetry's uses_in_project_venv decision, not a missing probe. No open fix PR.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] New information for the fix: PDM before 2.5 doesn't write .pdm-python. It records the interpreter in .pdm.toml under [python] path = "…", so a fix that only reads .pdm-python would still miss these projects.

    Reproduced on main 61cfb9b (Linux) with PDM 1.15.5 (lock_version 3.1) and 2.0.3 (lock_version 4.0):

    python3 -m venv --copies /elsewhere/env
    pdm config -l python.use_venv true
    pdm use -f /elsewhere/env/bin/python   # .pdm.toml: [python] path = "/elsewhere/env/bin/python"
    pdm install                            # urllib3 1.26.18 -> /elsewhere/env/lib/python3.11/site-packages
    socket-patch scan --mode agent --yes --json

    Both versions give status: success, exit 0, apply.patches[0] = {action: skipped, errorCode: package_not_installed}, and the env keeps upstream bytes. (Without --copies, PDM 1.15 resolves the venv's symlinked python to /usr/bin/python3.11 and installs into __pypackages__, which is the documented PEP 582 case.) Hosted mode refuses both lock versions as documented, so only agent mode applies here.

    Suggested probe order: .pdm-python (≥ 2.5), then .pdm.toml python.path (1.x – 2.4).


    Generated by Claude Code

  3. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (with #525, #528; shared root cause: find_local_venv_site_packages never resolves the env the project's package manager records (PDM .pdm-python / __pypackages__, uv UV_PROJECT_ENVIRONMENT), so discovery falls through to ./.venv or the PATH interpreter). Branch: agent/fix-python-manager-project-env. Claim-ID: 2026-10-02T07:21:09Z-11e135


    Generated by Claude Code

  4. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Fix in progress: #540


    Generated by Claude Code

  5. added a commit that references this issue on Oct 2, 2026
    1883c61
  6. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] PDM bug-hunt re-triage: I tested PR #540 (head 60dfb81) with real PDM against a local mock of the patch API. It fixes the venv.in_project = false case in both agent and hosted mode. Main 61cfb9b still reproduces it.

    Setup: pdm config -l venv.in_project false, pdm venv create, pdm use --venv in-project, pdm install. .pdm-python then points at ~/.local/share/pdm/venvs/<proj>-<hash>-3.12/bin/python, which holds upstream urllib3 1.26.18, and there's no ./.venv. I reset the venv with pdm sync --reinstall before each run.

    PDM build agent scan --mode agent hosted stale-install warning hosted vex before reinstall
    2.29.2 main [skip] … not installed, venv unpatched none exit 0, not_affected (wrong)
    2.29.2 PR #540 1 applied, venv patched printed exit 1, omitted
    2.12.4 main — none exit 0, not_affected (wrong)
    2.12.4 PR #540 — printed exit 1, omitted

    After pdm sync, vex is not_affected on both builds, which is correct. I didn't re-run the PDM < 2.5 .pdm.toml [python] path variant this time; the PR reads that key in pdm_saved_interpreter. Leaving this open until #540 merges.


    Generated by Claude Code

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