Repository navigation
Lockfile-only scan ignores pins in requirements.txt -r includes, so a fresh checkout reports "No patches available" and installs unpatched (hosted and vendored) #412
Description
[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).
Summary
On a checkout with no virtualenv yet (the usual CI / fresh-clone case), scan only finds the pins written directly in the root requirements.txt. A pin reached through an -r include (-r requirements/base.txt, the common split-requirements layout) never joins discovery. Both scan (hosted) and scan --mode vendored print No patches available for installed packages. and exit 0 / success, with lockfileOnlyPackages: 0 and no warning. pip install -r requirements.txt then installs the unpatched upstream package.
With an installed .venv, the same project is vendored: the vendored writer rewrites the pin inside requirements/base.txt, and pip install -r requirements.txt gets the patched wheel. Only the lockfile-only inventory is missing the include.
Impact
Patches are silently skipped for every dependency pinned in an included file on lockfile-only runs. The scan reports success, so nothing tells the user the pins were never looked at.
Repro
Uses a local mock of the patch API that serves a patched six-1.16.0 wheel, the same shape as tests/vex_pypi_real_common::RealApi. Linux, CPython 3.11, pip 24.0, main 2463257.
A="--api-url http://127.0.0.1:8765 --api-token fake --org test-org --patch-server-url http://127.0.0.1:8765"
mkdir -p p/requirements && cd p
printf -- '-r requirements/base.txt\n' > requirements.txt
printf 'six==1.16.0\n' > requirements/base.txt
socket-patch scan --mode vendored --json $A | jq '{status, lockfileOnlyPackages, packages: [.packages[].purl]}'
# {"status":"success","lockfileOnlyPackages":0,"packages":[]}
socket-patch scan --json $A | jq '{status, lockfileOnlyPackages}' # hosted: same
python -m venv v && v/bin/pip install -r requirements.txt && v/bin/python -c 'import six; print(getattr(six,"SOCKET_PATCHED",0))' # 0 → unpatchedControl: the same pin written directly in requirements.txt gives lockfileOnlyPackages: 1 and packages: ["pkg:pypi/six@1.16.0"], and is rewritten in both modes.
| layout | mode | lockfileOnlyPackages | rewritten | pip install -r |
|---|---|---|---|---|
root requirements.txt |
hosted | 1 | yes | patched |
root requirements.txt |
vendored | 1 | yes | patched |
-r requirements/base.txt |
hosted | 0 | no | unpatched |
-r requirements/base.txt |
vendored | 0 | no | unpatched |
-r requirements/base.txt + populated .venv |
vendored | n/a (installed) | yes, in base.txt | patched |
All Linux rows reproduced twice.
Expected vs actual
- Expected: CLI_CONTRACT.md lists vendored pypi wiring as "
requirements.txt+ its in-root-rincludes", and the hosted unwind coverage lists "requirements.txt(+ in-root-rincludes)". The "Lockfile supplement (v3.4)" paragraph says pinnedrequirements.txtdependencies with no installed copy join discovery. The files the vendored writer edits should be the files the lockfile-only inventory reads. - Actual: only the root file is inventoried, so included pins are invisible until something is installed.
For hosted mode, rewriting only the root file is documented: an installed pin found only in an include gets redirect_requirements_entry_not_found. On a lockfile-only checkout, though, even that warning is missing, because the package never enters discovery.
OS × version
| OS | pip / Python | include layout, hosted | include layout, vendored | root-file control |
|---|---|---|---|---|
| Linux (sandbox) | 20.3.4/3.10, 23.3.2/3.11, 24.3.1/3.12, 26.2.1/3.13 (install of the untouched include project) | not discovered, unpatched | not discovered, unpatched | patched |
| ubuntu-latest (probe) | 20.3.4 / 3.8 | not discovered, unpatched | not discovered, unpatched | patched |
| ubuntu-latest (probe) | 25.0.1 / 3.8 | not discovered, unpatched | not discovered, unpatched | patched |
| ubuntu-latest (probe) | 26.2.1 / 3.13 | not discovered, unpatched | not discovered, unpatched | patched |
| macos-latest (probe) | 20.3.4 / 3.8 | not discovered, unpatched | not discovered, unpatched | patched |
| macos-latest (probe) | 25.0.1 / 3.8 | not discovered, unpatched | not discovered, unpatched | patched |
| macos-latest (probe) | 26.2.1 / 3.13 | not discovered, unpatched | not discovered, unpatched | patched |
| windows-latest (probe) | 20.3.4 / 3.8 | not discovered, unpatched | not discovered, unpatched | patched |
| windows-latest (probe) | 25.0.1 / 3.8 | not discovered, unpatched | not discovered, unpatched | patched |
| windows-latest (probe) | 26.2.1 / 3.13 | not discovered, unpatched | not discovered, unpatched | patched |
| all 3 OS (probe) | 20.3.4 / 3.13 | blocked: pip 20.3.4 can't run on 3.13 |
First bad
Not a regression. v4.0.0 behaves the same: scan --mode vendored on the include layout vendors nothing.
Suspect code
crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs:587-588: inventory_requirements_txt reads only view.read_text("requirements.txt") and skips every --prefixed line (-r / -c included) without following it. The vendored writer's include walk (vendor/pypi_requirements.rs:599-724, requirements_include_names) already resolves in-root includes and could be shared.
Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36806309982
Activity
mikolalysenko commentedon Oct 1, 2026
[agent] Triaged:
priority:p1(pip). Not a duplicate, and no open or merged PR covers it. The cause isinventory_requirements_txtinvendor/lock_inventory/pypi.rs: it reads only the rootrequirements.txtand doesn't follow-rlines. Meanwhile, the vendored writer's include walk (requirements_include_namesinvendor/pypi_requirements.rs) already resolves in-root includes. The fix is to share that walk with the lockfile inventory.Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Claiming this issue (with #523; shared root cause: the lockfile-only requirements.txt inventory (
inventory_requirements_txt) reads requirements differently from pip and socket-patch's own writers: it reads the root file only, and only with the first-tokenexact_pin). Branch: agent/fix-requirements-lock-inventory. Claim-ID: 2026-10-02T04:21:03Z-8d1322Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Draft fix PR: #530
Generated by Claude Code
Discover pins in requirements.txt -r includes