Skip to content

Vendored PDM < 2.11 gives no stale-install warning, so after pdm sync the unpatched release stays installed while vex attests not_affected #641

Description

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

Summary

PDM releases before 2.11 (lock_version 2 / 4.3 / 4.4) don't replace a package that's already installed at the same version. Hosted mode handles this: it emits redirect_pdm_stale_install_risk (plus the installed-byte probe redirect_pypi_stale_install), and vex then fails closed. Vendored mode handles neither. scan --mode vendored on a warm PDM env exits 0 with no stale warning. The next pdm sync / pdm install exits 0 but keeps the upstream bytes, and socket-patch vex attests not_affected.

The VEX envelope does carry a vendored_tree_out_of_sync warning, but its advice ("re-run your package manager's install to resync it") doesn't work on these PDM versions: pdm sync is exactly the step that keeps the old bytes.

Impact

A team on PDM 1.x / 2.0–2.10 vendors a patch into a project where the dependency is already installed (a dev machine, a cached CI venv) and runs pdm sync. Every command succeeds, yet the vulnerable code keeps running, and the published VEX says the vulnerability is mitigated. Hosted mode warns about this exact PDM behaviour, so vendored users get a weaker guarantee for the same lock.

Repro

Uses a local mock of the patch API that serves a patched urllib3 1.26.18 wheel, with a marker line appended to urllib3/response.py. Any free urllib3 1.26.18 patch shows the same thing.

uv venv tools/2.10.4 --python 3.11 && uv pip install --python tools/2.10.4/bin/python pdm==2.10.4
mkdir proj && cd proj
cat > pyproject.toml <<'EOF'
[project]
name = "x"
version = "0.0.0"
requires-python = ">=3.8"
dependencies = ["urllib3==1.26.18"]
[tool.pdm]
distribution = false
EOF
uv venv .venv --python ../tools/2.10.4/bin/python
export VIRTUAL_ENV=$PWD/.venv
../tools/2.10.4/bin/pdm lock && ../tools/2.10.4/bin/pdm sync      # warm env: upstream urllib3 installed
socket-patch scan --mode vendored --json --yes                     # exit 0; vendor events: only vendor_prebuilt_downloaded
../tools/2.10.4/bin/pdm sync                                       # exit 0, "All packages are synced to date"
grep -c SOCKET-PATCH-MARKER .venv/lib/python3.11/site-packages/urllib3/response.py   # 0 -> still upstream
socket-patch vex --json -O vex.json                                # exit 0; statement: not_affected / inline_mitigations_already_exist

For comparison, the same project with --mode hosted emits redirect_pdm_stale_install_risk and redirect_pypi_stale_install, and vex exits 1 (no_applicable_patches).

Expected vs actual

  • Expected: the same treatment as hosted on these locks. docs/testing/pdm-compatibility.md says PDM < 2.11 keeps an already-installed same-version package, and redirect/pdm.rs warns proactively once per lock for that reason. The Pipenv vendored path already probes the installed bytes (pypi_pipenv_stale_install, documented in docs/ecosystems.md). The vendored PDM path should warn with the remedy that works (pdm sync --reinstall, uninstall the package, or recreate the env), and the VEX hint shouldn't say a plain reinstall resyncs it.
  • Actual: no warning from the vendored scan. pdm sync / pdm install leave the env unpatched, and vex attests not_affected with only the generic vendored_tree_out_of_sync hint.

Matrix (Linux, main 045d7ec, real PDM)

PDM lock_version vendored scan warns? installed after pdm sync vex
1.4.5 2 no (only pypi_pdm_legacy_sync_required) upstream not_affected
1.5.3 2 no (only pypi_pdm_legacy_sync_required) upstream not_affected
2.8.2 4.3 no upstream not_affected
2.10.4 4.4 no upstream (pdm sync and pdm install; reproduced twice) not_affected
2.11.2 4.4.1 — patched (pass) not_affected (correct)
2.29.2 4.5.1 — patched (pass) not_affected (correct)
2.10.4, --mode hosted (control) 4.4 yes (redirect_pdm_stale_install_risk, redirect_pypi_stale_install) upstream fails closed

macOS and Windows weren't probed. The behaviour comes from PDM's installer, not the OS.

Suspect code

  • crates/socket-patch-core/src/vendor/pypi_pdm.rs:95-97 only adds pypi_pdm_legacy_sync_required for lock_version == "2". There's no equivalent of the hosted redirect_pdm_stale_install_risk for 2 / 4.3 / 4.4.
  • crates/socket-patch-core/src/vendor/pypi.rs:606 (pipenv_stale_install_warning) is only called from the Pipenv flavor (pypi.rs:881), so the PDM flavor never probes the installed bytes.
  • crates/socket-patch-core/src/patch/redirect/pdm.rs:42-55 is the hosted counterpart to mirror.
  • crates/socket-patch-cli/src/commands/vex.rs:677-690 is the vendored_tree_out_of_sync advice.

Related but distinct: #477 (the reverse direction, after rollback).

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