GeezerVison is showing github.com — Fix requirements.txt writers ignoring pip hash mode (#376) by mikolalysenko · Pull Request #383 · SocketDev/socket-patch
Conversation
LLM Description written by Claude Code:claude-opus-5-5
Fixes #376
Summary
Hosted and vendored scans no longer break pip install -r on a requirements.txt that has no hashes.
Scope: this PR originally also fixed #378 (setup writing an unhashed hook line into a hash-pinned requirements.txt). #277 removed the setup command, so that part was dropped when main was merged in at a2dedc5, and #378 was closed as no longer applicable.
Root cause
pip's hash-checking mode is all or nothing. It turns on for the whole install as soon as any requirement carries --hash, and then every requirement, transitive dependencies included, has to be ==-pinned and hashed. The hosted redirect (patch/redirect/requirements.rs) and the vendored writer (vendor/pypi_requirements.rs::vendor_line) always added --hash, without checking which mode the requirements set was in. In an unhashed file that switches the mode on, and pip then refuses every other line.
Fix
- Shared check:
utils::requirements::requires_hashes. It is true when any line has a --hash option (any algorithm) or the file sets --require-hashes. Comments and URL #sha256= fragments don't count.
- Hosted: a hashed file keeps
--hash on the patched line, as before. An unhashed file gets name @ <url>#sha256=<hex> instead. Checked by hand:
- pip 24 and uv 0.8 both verify the fragment (a wrong hash fails the install);
- the fragment does not turn on pip's hash mode for other lines;
- it also satisfies hash mode when that mode is on;
- VEX already reads it (
integrity_of).
- Vendored: the mode is taken from the whole requirements tree (the root file plus its
-r includes). A hashed tree keeps the vendor line unchanged. An unhashed tree gets the committed wheel path with no --hash, because pip can't read a fragment on a bare path. The inventory, VEX and the in-sync ledger fallback already accept a hashless vendor line.
- Rebuild guard: when there is no ledger entry, the in-sync rebuild guard falls back to the pin on the wired line. A hashless line now still pins its wheel path (
wired_pin_in returns an empty sha256 and pin_matches checks the path only). A rebuild under another filename is refused instead of leaving the line pointing at a missing file. Bugbot found this; it is fixed in fcc4fcc.
- Re-scans are no-ops: every line counts toward the mode, the writer's own lines included, so a re-scan never flips a file between the two shapes. A file that an older version already wrote with
--hash into an unhashed set keeps that shape; running rollback followed by a re-scan rewrites it in the new form.
Checklist
- Hosted:
redirect::requirements::tests::unhashed_file_pins_the_artifact_by_url_fragment_not_hash_option and hashed_file_keeps_the_hash_option. Both were red before the fix and are green after.
- Vendored:
vendor::pypi_requirements::tests::unhashed_requirements_get_an_unhashed_vendor_line (red → green) and hashes_in_an_include_keep_the_vendor_line_hashed.
- Vendored rebuild guard:
vendor::pypi::tests::in_sync_ledgerless_rebuild_of_unhashed_line_keeps_the_wired_path (red → green).
- Real pip: a new
unhashed cell (six==1.16.0 + idna==3.7) in e2e_vex_build::pip. Locally on pip 24.3.1 every hosted and vendored step passes: wire, install-patched, manifest-less VEX, re-scan.
- Shared check:
utils::requirements::tests::requires_hashes_reads_pip_hash_checking_mode.
Test updates (intended behavior change)
Several existing tests expected --hash in unhashed files, which is the #376 bug itself. They now expect the unhashed forms:
- the unit tests in the hosted and vendored writers;
- the redirect fixture and the VEX discovery golden (
redirect-pypi.json);
in_process_get_hosted_ecosystems, e2e_hosted_production, e2e_vendored_production and e2e_vendor_pypi_build.
Two tests needed more than a new expected string:
vendor_ledger_schema_e2e: the base-binary parity test now names this one intended difference. The legacy fixtures are unchanged, so the legacy-revert coverage stays.
- The pin-guard tests and the marker e2e rely on a hashed pin, so their inputs are now hash-pinned.
An earlier commit on this branch accidentally reformatted about 55 unrelated files, and e0ed1aa reverts them. main isn't rustfmt-clean and CI doesn't check formatting, so this PR leaves formatting alone.
Evidence
cargo clippy --workspace --all-features -- -D warnings: clean on fcc4fcc.
cargo test --workspace --all-features (local, sandbox runs as root): every failure is a test that needs a write, removal or permission check to fail, which root bypasses. None exercises code this PR changes.
SOCKET_PATCH_PIP_E2E_VERSIONS=24 SOCKET_PATCH_PIP_E2E_REQUIRED=1 cargo test -p socket-patch-cli --all-features --test e2e_vex_build -- pip:: --ignored: passes for all 4 cells × 2 modes (pre-merge).
- CI is green on
fcc4fcc, and Bugbot found no new issues on it.
- Not touched: the npm, pypi and gem wrappers (they only dispatch to the binary).
🤖 Generated with Claude Code
https://claude.ai/code/session_017aQf44e9818AbFKDYnuAHZ
Note Medium Risk
Changes how hosted and vendored requirements.txt pins are written, which affects install behavior for mixed hashed/unhashed projects; behavior is heavily tested but wrong detection could weaken or break installs.
Overview
Fixes pip breaking when requirements.txt is patched in an unhashed tree (#376). Hosted and vendored rewrites no longer unconditionally add --hash, which turns on pip’s global hash-checking mode and rejects every other requirement.
A shared requires_hashes helper detects whether the file (or included tree) is already in hash-checking mode. Hosted redirects pin integrity with a URL #sha256= fragment when the file is unhashed, and keep --hash when it is already hashed. Vendored wheel lines omit --hash in unhashed trees (path-only pin); the in-sync rebuild guard treats an empty sha256 as path-only via pin_matches.
CLI contract, redirect/VEX fixtures, and e2e tests (including a new pip VEX unhashed cell) are updated to match the new shapes.
Reviewed by Cursor Bugbot for commit fcc4fcc. Configure here.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Assisted-by: Claude Code:claude-opus-5-5
This was referenced Sep 30, 2026
A hosted or vendored scan added --hash to the patched line of a
requirements file that had no hashes. pip then turns on hash-checking
mode for the whole install, so every other requirement and every
transitive dependency failed to install (#376).
Both writers now check whether the requirements set is already in
hash-checking mode. If it is, they keep writing --hash as before. If
not, hosted pins the patched wheel with the url's #sha256= fragment,
which pip still verifies, and vendored writes the committed wheel path
without a hash.
Assisted-by: Claude Code:claude-opus-5-5
socket-patch setup appended an unpinned, unhashed socket-patch[hook]
line to requirements.txt even when the file (or an -r include) was in
pip's hash-checking mode. pip then refused to install anything from it,
while setup reported success (#378).
setup now reports an error and leaves such a file untouched. setup
--remove also drops a hook line together with its backslash
continuation lines, so no stray --hash line is left behind.
Assisted-by: Claude Code:claude-opus-5-5
Adds an 'unhashed' cell (six plus idna, no hashes) to the real-pip
capstone. It asserts the wiring adds no --hash and that pip installs
the patched six. Also updates the redirect fixture to the #sha256=
url form written for unhashed files.
Assisted-by: Claude Code:claude-opus-5-5
The production and vendored e2e legs write a one-line unhashed
requirements.txt and then asserted a --hash pin, which is the #376
behavior itself. They now assert that no --hash is added. The hosted
leg also asserts the #sha256= url pin, which pip and uv both verify.
Assisted-by: Claude Code:claude-opus-5-5
- The VEX discovery golden now shows the #sha256= url on the hosted
requirements ref.
- The hosted get test expects the url-fragment pin.
- The vendor ledger parity test allows the one intended change from
the base binary: no --hash in an unhashed requirements.txt.
- The marker e2e installs with --require-hashes, so its input is now
hash-pinned the way pip-compile writes it.
Assisted-by: Claude Code:claude-opus-5-5
cursor[bot] reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
cursor
Bot
left a comment
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stale Bugbot comment from a previous run.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
An earlier cargo fmt --all run reformatted about 55 files that this
fix doesn't otherwise touch; main isn't rustfmt-clean and CI doesn't
check formatting. This restores those files to main so the PR diff
only holds the hash-mode change and its tests.
Assisted-by: Claude Code:claude-opus-5-5
cursor[bot] reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
cursor
Bot
left a comment
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stale Bugbot comment from a previous run.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Bring the pip hash-mode fix onto the v5 workflow from #277. Main removed
the setup command, so the #378 hook-dependency guard in
setup/pypi/edit.rs has no home and is dropped with the file. The #376
half still applies: hosted and vendored requirements.txt writers only
emit --hash when the tree is already in pip's hash-checking mode. The
hosted-get test docs keep main's "no ledger" wording with the fragment
pin, and CLI_CONTRACT.md's requirements rows now describe the
conditional --hash.
Co-Authored-By: Claude <[email protected]>
Claude-Session: https://claude.ai/code/session_01GQoii5oP1pwcJh5mzzo1HU
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
cursor
Bot
left a comment
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stale Bugbot comment from a previous run.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
A requirements vendor line written into an unhashed requirements set has
no --hash, so wired_pin_in returned no pin and a ledgerless in-sync
rebuild skipped the guard entirely, including the path check. A rebuilt
wheel at another filename would then leave the wired line pointing at a
file that does not exist.
wired_pin_in now pins the path of a hashless line with an empty sha256,
and the guard treats an empty pinned sha256 as path-only. A hash that is
present but malformed still pins nothing, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017aQf44e9818AbFKDYnuAHZ
cursor[bot] reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit fcc4fcc. Configure here.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
[burn-down agent] Ready for review on fcc4fcc: mergeable, 0 commits behind main.
Generated by Claude Code
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Oct 2, 2026
* Start fix for #475
Assisted-by: Claude Code:claude-opus-5-5
* Match requirements pins like pip does (PEP 440)
A hand-written pin such as `six==1.16` installs six 1.16.0, but the
hosted requirements.txt rewrite compared versions as raw strings and
skipped it, so `scan` exited 0 and the project stayed unpatched. The
vendored requirements writer and the Hatch rewriter refused the same
pins as "not pinned".
Add a small PEP 440 equality helper (zero-padded release segments,
leading zeros, case and pre/post/dev spellings) and use it for `==`
pins in all three writers. `===` keeps plain string equality, as PEP
440 defines it.
Fixes #475
Assisted-by: Claude Code:claude-opus-5-5
* Test hosted get over a PEP 440-equivalent pin
Mirrors the #475 repro end to end: `get <uuid> --mode hosted` over
`requests==2.31`, `==2.31.0.0` and `Requests==02.31.0` must redirect
the pin to the hosted wheel. Fails on main, passes with the fix.
Assisted-by: Claude Code:claude-opus-5-5
* Expect #sha256= pin in the PEP 440 hosted-get test
Since #383, a hosted scan of a requirements.txt that is not in
hash-checking mode pins the patched wheel with the url's #sha256=
fragment rather than --hash, so the PEP 440 regression test now
expects that form, matching the existing hosted pypi test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CVbFzTeYSTvB5iKjY6FRg7
---------
Co-authored-by: Claude <[email protected]>
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Labels
Ready for review
Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review
Milestone
No milestone
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|