fix(hosted): refuse pnpm v5/v6 lock keys fail-closed - #183
Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Open
fix(hosted): refuse pnpm v5/v6 lock keys fail-closed#183Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Conversation
The pnpm redirect grammar only matches lockfileVersion 9 packages keys (name@version, optionally quoted or /-prefixed). pnpm v6 embeds resolved peers in the key itself (/name@1.0.0(peer@2.0.0):) and v5.x uses path-style keys (/name/1.0.0:, peers suffixed _peer@ver), so those entries silently escaped the rewrite. Worst case was fail-open: a v6 lock holding BOTH /pkg@1.0.0: and /pkg@1.0.0(peer@2.0.0): got the plain entry rewritten, the dep was counted redirected, recorded, and attested via --vex, while every dependent resolving through the peered entry still installed the unpatched upstream tarball with no warning at all. Pure-peered v6 and v5.x deps degraded to a bare entry-not-found warning that never named the offending key. Detect v5/v6-grammar keys for the target name@version before rewriting and refuse the dep outright across the whole lock set — nothing rewritten, nothing confirmed — with a redirect_pnpm_unsupported_lock_key warning naming each unmatched key and the lock it lives in, plus the remedy (regenerate with pnpm >=9). v9 locks are unaffected: their peer-suffixed snapshots: keys never start with / and carry no resolution. The docs matrix now states the pnpm v9 constraint for hosted mode. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Aug 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
The hosted-mode pnpm rewriter's grammar only matches lockfileVersion 9
packages:keys (name@version, optionally quoted or/-prefixed). pnpm v6 embeds resolved peers in the key itself (/name@1.0.0(peer@2.0.0):) and v5.x uses path-style keys (/name/1.0.0:, peers suffixed_peer@ver), so those entries silently escaped the rewrite.Audit findings addressed (2026-08-13 audit, both in
crates/socket-patch-core/src/patch/redirect/mod.rs):/pkg@1.0.0:and/pkg@1.0.0(peer@2.0.0):had only the plain entry rewritten;matched_anybecame true so no warning fired, the dep was counted redirected, recorded, and attested via--vex— while every dependent resolving through the peered entry still installed the unpatched upstream tarball.redirect_pnpm_entry_not_foundthat never named the offending key, anddocs/ecosystems.mdclaimed blanketpnpm-lock.yamlhosted support with no version caveat.Fix
Fail closed at the rewriter boundary rather than extending the grammar: before rewriting each dep, a legacy-grammar detector scans every pnpm lock in the set for v5/v6 keys targeting that
name@version. Any hit refuses the dep outright across ALL locks — nothing rewritten anywhere, so the dep is never confirmed, recorded, or attested — and emits aredirect_pnpm_unsupported_lock_keywarning naming each unmatched key and the lock it lives in, with the remedy (regenerate with pnpm >= 9 and re-run).snapshots:keys never start with/and carry noresolution:, so the detector cannot trip on them (verified by existing goldens and a guard test).docs/ecosystems.mdhosted matrix now states the pnpm v9 constraint, and a new pnpm bullet in the npm hosted-mode notes documents the refusal semantics.Refusal was chosen over grammar extension because extending would require real-pnpm-generated v5/v6 fixtures plus a matching TS-backend rewriter change to preserve the shared-golden byte-consistency contract (cross-repo scope). Refusal is strictly stronger than the pre-fix behavior.
Known residual (rewriter-boundary limit): a lock already fail-open-rewritten by a pre-fix run still confirms on re-run, because the artifact URL is baked into the unchanged file and confirmation scans final texts; the new warning still fires naming the surviving legacy key.
Testing
patch::redirect::tests(all red-state verified — with the detection block removed, all four refusal tests fail on pre-fix code, reproducing the silent fail-open / bare entry-not-found behavior):pnpm_v6_mixed_plain_and_peered_is_refused— the medium finding: no edits, no files, warning names the peered key, noentry_not_foundpnpm_v6_pure_peered_key_is_refused_by_name— scoped pure-peered v6 key named in the warningpnpm_v5_path_style_keys_are_refused_by_name— both/name/versionand_peer-suffixed v5.4 keys namedpnpm_legacy_lock_in_set_refuses_the_dep_everywhere— v9 root + legacy Rush nested lock: nothing rewritten anywhere; warning names key AND lock pathpnpm_v6_plain_key_without_peered_sibling_still_rewrites— guard against overreachcargo test -p socket-patch-core --lib— 2080 passedcargo test -p socket-patch-core --test redirect_golden— 1 passed (all shared goldens incl. npm/pnpm basic + nested-rush-lock byte-identical)cargo test -p socket-patch-cli --test in_process_redirect_pnpm— 3 passedNo skipped findings.
🤖 Generated with Claude Code
Note
Medium Risk
Changes hosted npm redirect and attestation semantics for legacy pnpm locks—safer than partial rewrite, but operators on v5/v6 must upgrade locks to get redirects.
Overview
Hosted pnpm redirect used to only rewrite lockfileVersion 9
packages:keys. pnpm v6 can list the same dep as both/pkg@1.0.0:and/pkg@1.0.0(peer@2.0.0):; rewriting only the plain entry could mark the dep redirected/attested while peered resolution still pulled the upstream tarball.Before rewriting each dep, the rewriter scans all
pnpm-lock.yamlfiles for v5/v6 keys that target thatname@version(v6 peer-in-key and v5 path-style/name/version/_peersuffixes). Any match refuses the whole dep—no lock in the set is edited—and emitsredirect_pnpm_unsupported_lock_keynaming each key and lock file, instead ofredirect_pnpm_entry_not_found. Plain v6 keys with no legacy sibling for that dep still rewrite.docs/ecosystems.mdnow states hosted pnpm is v9-only and documents the refusal behavior.Reviewed by Cursor Bugbot for commit 93f86d5. Configure here.