Repository navigation
Description
[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).
Summary
#1308 (the fix for #477) added redirect_pypi_reinstall_required / vendor_pypi_reinstall_required for PDM, uv and Pipenv, because their plain sync keeps an installed same-version build once the lock goes back to the registry release. Plain pip behaves the same way: pip install -r requirements.txt reports Requirement already satisfied: six==1.16.0 and keeps the patched wheel. requirements.txt entries were deliberately left on the generic note (#1308: "Poetry, requirements.txt, Hatch and PEP 751 entries keep the generic note"). As a result:
- Hosted
rollback/ vendoredrollbackprintNote: 1 unwired package keeps its patched bytes in installed trees until the next package-manager install.For pip that's false: the nextpip install -rkeeps them. vendor --revertprints onlyReverted 1 vendored package., with no advisory. It also deletes.socket/vendor/pypi/<uuid>/six-…whl, while the installed six'sdirect_url.jsonstill points at that file.
Impact
- The venv silently keeps the Socket build after the user un-patched. That's harmless for security, but it's the exact After a hosted or vendored PDM rollback, pdm sync / pdm install keep the patched build installed, though rollback says the next install restores it #477 mismatch between what the CLI says and what pip does.
pip freezekeeps emitting the Socket source:six @ http://<patch-server>/patch/pypi/six/…whl#sha256=…after a hosted rollback, so apip freeze > requirements.txtre-introduces the hosted pin the user just rolled back. After a vendored revert it emitssix @ file:///…/.socket/vendor/pypi/<uuid>/six-1.16.0-…whl. That file no longer exists, sopip install -r frozen.txtin a new venv fails withERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory: '…/.socket/vendor/pypi/…/six-1.16.0-py2.py3-none-any.whl'.
Repro
A local mock of the patch API grants pkg:pypi/six@1.16.0 with a patched wheel (SAFE = True appended to six.py), the same fixture shape as tests/mode_migration_pypi.rs. Real pip, real PyPI.
git init -q
printf 'certifi==2024.2.2\nsix==1.16.0\n' > requirements.txt
git add -A && git commit -qm init
socket-patch scan --mode hosted --yes # or: --mode vendored
python -m venv .venv && .venv/bin/pip install -q -r requirements.txt
.venv/bin/python -c 'import six; print(getattr(six,"SAFE",0))' # True (patched)
socket-patch rollback # or, vendored: socket-patch vendor --revert
# Note: 1 unwired package keeps its patched bytes in installed trees until the next package-manager install.
git status --short # requirements.txt restored byte-identical
.venv/bin/pip install -r requirements.txt # Requirement already satisfied: six==1.16.0 ...
.venv/bin/python -c 'import six; print(getattr(six,"SAFE",0))' # still True
.venv/bin/pip freeze | grep six # six @ http://…/patch/pypi/six/… (vendored: six @ file:///…/.socket/vendor/…whl, now deleted)Expected vs actual
- Expected: CLI_CONTRACT.md's
reinstall_requiredrow says installed trees keep patched bytes "until the next package-manager install". Where that isn't true, the contract's own After a hosted or vendored PDM rollback, pdm sync / pdm install keep the patched build installed, though rollback says the next install restores it #477 rule (thevendor_pypi_reinstall_required/redirect_pypi_reinstall_requiredrows) is to name the reinstall that restores upstream. For pip that ispip install --force-reinstall --no-deps six==1.16.0(orpip uninstall -y six && pip install -r requirements.txt). The forward direction already handles pip's same-version behaviour:redirect_pypi_stale_installwarns that a warm venv keeps the upstream build. - Actual: a hosted rollback prints the generic note, which is wrong for pip.
vendor --revertprints nothing. The patched build stays, andpip freezepoints at the patch server or at the deleted vendored wheel.
Matrix (Linux, main cbbbdb9, socket-patch 5.0.1 build)
| pip / CPython | hosted rollback |
vendored vendor --revert |
vendored rollback |
|---|---|---|---|
| pip 26.2.2 / 3.12 | repro (patched kept, generic note) | repro (patched kept, no advisory, freeze → deleted file) | repro (generic note) |
| pip 20.3.4 / 3.8 | repro | repro | not run |
Each cell ran twice. The pip behaviour is pip's resolver, not OS-specific (Requirement already satisfied for a same-version installed dist, whatever its direct_url.json says). macOS and Windows weren't probed, because this routine's probe branches are blocked.
First bad: present since the unwind advisories landed (#1308). Before that, every PyPI tool got only the generic note.
Suspect code
crates/socket-patch-cli/src/commands/pypi_reinstall.rs:43(Tool::of_flavor) and:54(Tool::of_file):requirements/requirements.txtmap toNone, so no advisory is pushed and the qualifier atcrates/socket-patch-cli/src/commands/rollback.rs:451isn't added.crates/socket-patch-cli/src/commands/pypi_reinstall.rs:75(remedy): no pip arm.
Related: #477. Its uv-routine comment showed the same keep for uv pip install -r requirements.txt and suggested uv pip install --reinstall <pkg> for the requirements / pylock lane. A pip remedy is the plain-pip counterpart. PEP 751 pylock.toml installed by pip install -r pylock.toml likely behaves the same; that wasn't tested here.
No probe runs (probe branches are blocked for this routine).
Activity
mikolalysenko commentedon Nov 1, 2026
[agent] Triage:
priority:p1(pip / requirements.txt). Not a duplicate: #977 is about reverting after the user edits the socket comment, and #477 / #1308 deliberately left requirements.txt on the generic note. No open PR covers it.Generated by Claude Code
mikolalysenko commentedon Nov 1, 2026
[agent] Claiming this issue (shared root cause:
pypi_reinstall::Toolhas no pip arm, so requirements.txt unwinds get the generic note or no advisory). Branch: agent/fix-pip-reinstall-advisory. Claim-ID: 2026-11-01T20:21:19Z-2bf094Generated by Claude Code
mikolalysenko commentedon Nov 1, 2026
[agent] Draft PR: #1954
Generated by Claude Code
Name pip's reinstall after a requirements unwind
Keep pip's remedy to the requirements file
Name the requirements files pip reinstalls from