Repository navigation
Fix hosted pnpm pin of a lock pnpm ignores (#1074) - #1431
Conversation
Assisted-by: Claude Code:claude-opus-5-5
With pnpm's `lockfile` setting turned off (`lockfile: false` in pnpm-workspace.yaml for pnpm 11+, `lockfile=false` in .npmrc for pnpm 9/10), pnpm never reads pnpm-lock.yaml and installs the upstream package from the registry. Hosted scans still pinned the committed lock, added `trustLockfile: true` and reported success for a patch no install applies. Hosted mode now leaves every pnpm lock untouched for such a project and warns `redirect_pnpm_lockfile_disabled`, pointing at `--mode vendored` (pnpm applies its overrides without a lock). The setting is read the way the gitBranchLockfile refusal already reads its own (project files, governing workspace, environment), through a shared lookup. Refs #1074 Assisted-by: Claude Code:claude-opus-5-5
With pnpm's `lockfile` setting off, pnpm never reads pnpm-lock.yaml, yet lock-only `vex` (no node_modules: the usual CI/SBOM run) attested a hosted pin there as not_affected while pnpm installed the upstream package. The hosted references of the root and member pnpm locks now keep no lockfile basis in that case, so only an installed tree whose copies verify can attest them, and one patched_ref_unattributable per lock names them. Vendored references are unchanged: pnpm applies the overrides that wire them without a lock. Rush locks are not affected. Adds a real-pnpm 11 e2e leg covering both the hosted refusal and the lock-only VEX, and documents the setting in the CLI contract. Fixes #1074 Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups for the #1074 refusal: - Read `lockfile` from the file the project's pnpm major reads (YAML on pnpm 11+, .npmrc on pnpm 10 and older), from the install record, the packageManager pin or the lock grammar; with no major known, either file turning it off counts. A pnpm 9 project with a stray YAML `lockfile: true` and `.npmrc` `lockfile=false` is now refused. - Read only the project's own files through the view, so the re-scan read cache is no longer disabled by an ancestor walk on every run, and keep the environment out of `vex`. - Refuse only in a pnpm project, so an npm project's `.npmrc` keeps npm's hints, and name `--mode vendored` only when a pnpm lock exists for it to wire. - Share the root-lock withholding with the gitBranchLockfile refusal. Refs #1074 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ 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 e7e7bb2. Configure here.
|
Ready for review (burn-down agent)
Generated by Claude Code |
Every pnpm (7 through 11) reads `lockfile` only when it is a boolean and otherwise falls back to npm's `package-lock` to decide whether it uses its lock. A pnpm 9/10 project with the common npm-era `package-lock=false` in .npmrc therefore never reads pnpm-lock.yaml, yet hosted mode still pinned the lock and lock-only vex attested it. The hosted refusal and the VEX withholding now take `package-lock` (`packageLock` in pnpm-workspace.yaml) into account when `lockfile` is unset; an explicit `lockfile=true` still wins. Refs #1074 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[agent] Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1074
Root cause
socket-patch never read pnpm's
lockfilesetting (lockfile: falseinpnpm-workspace.yamlfor pnpm 11+,lockfile=falsein.npmrcfor pnpm 9/10). With it off, pnpm neither reads nor writespnpm-lock.yamland installs every dependency from the registry. So:trustLockfile: trueand reportedappliedfor a patch no install applies;vex/discover/npm.rsextract_pnpm_lock) gave those pins the lockfile basis and attestednot_affectedwhilepnpm installfetched the upstream package.Fix
utils/pnpm_workspace.rslockfile_disabled): reads the setting from the file the project's pnpm major reads. pnpm 11+ readspnpm-workspace.yaml; pnpm 10 and older read.npmrc(the issue's matrix shows pnpm 10 ignores the YAML key). Within each source,lockfilecounts when it is a boolean. Otherwise npm'spackage-lock/packageLockcounts, because every pnpm from 7 to 11 computesuseLockfilethat way. The major comes fromnode_modules/.modules.yaml, else thepackageManagerpin, else the lock grammar, through the existingpnpm_settings_majorhelpers. With no major known, either file turning it off counts, since withholding is the safe side. Only the project's own files are read, through the view's fingerprinted reader. Hosted mode also reads thenpm_config_*/pnpm_config_*environment;vexdoes not.hosted/engine.rs): only in a pnpm project (a root pnpm lock or apnpm-workspace.yaml), every pnpm lock (root and member) is left out and the run warnsredirect_pnpm_lockfile_disabled. Nothing is written and notrustLockfilekey is added. The remedy names--mode vendoredonly when a pnpm lock exists for it to wire; otherwise it says to remove the setting and runpnpm install. This is the same contract asredirect_pnpm_git_branch_lockfile: exit 0 with the warning,applied: 0. Both refusals share onewithhold_root_pnpm_lockshelper. Disk and in-memory engines behave the same.vex/discover/npm.rs): hosted refs in the root and member locks keep no lock pin, and onepatched_ref_unattributableper lock names them, so only an installed tree whose copies verify can attest. Vendored refs are untouched. Rush locks are excluded. This mirrors the contract for a vlt lock that vlt discards.Known limit: a workspace member scanned from its own directory, with no settings file of its own, does not inherit its root's
lockfile: false. Walking the ancestors would invalidate the re-scan read cache on every run (see review finding 1 below). Wrappers (npm/,pypi/,gem/) only dispatch the binary, so they need no change.Tests (red → green)
lockfile: false(disk + in-memory)hosted::engine::tests::a_disabled_lockfile_is_never_pinned.npmrclockfile=falsesharedWorkspaceLockfile: false)a_disabled_lockfile_pins_no_member_lock.npmrc)a_disabled_lockfile_is_read_where_the_pnpm_major_reads_itpackage-lock=false/packageLock: falsefallback;lockfile=truewinspackage_lock_false_disables_the_lockfile.npmrc→ no pnpm refusala_disabled_lockfile_without_a_pnpm_lockan_enabled_lockfile_is_pinnedlockfileboth spellings,package-lock=false, member locks)vex::discover::npm::tests::pnpm_disabled_lockfile_withholds_the_lockfile_basise2e_redirect_pnpm_build::pnpm11_disabled_lockfile_is_neither_pinned_nor_attestedsuccess,applied: 1, onlyredirect_pnpm_trust_lockfileThe e2e leg runs in the existing
e2e_redirect_pnpm_buildmatrix on all three OSes. Locally it ran against real corepack pnpm@11 withSOCKET_PATCH_PNPM_E2E_REQUIRED=1. After the fix the re-scan returnsapplied: 0withredirect_pnpm_lockfile_disabled, the lock and workspace file are byte-identical, and lock-onlyvexexits non-zero without the purl. The control, with the setting removed, attests. On 0363b42,e2e_redirect_pnpm_build(20) andin_process_redirect_pnpm(32) both pass.Local gate
cargo fmt --all -- --check✅cargo clippy --workspace --all-features -- -D warnings✅cargo test --workspace --all-features --no-fail-fast(on e7e7bb2): 246 test binaries pass. 13 tests fail, and every one also fails onorigin/mainin this sandbox. They are write-failure and permission tests (chmod 0o555 dirs, unremovable locks), plus one bun test, and the sandbox runs as root, so root's writes succeed. I re-ran each of the 13 onmainand got the same failures. None touch pnpm. On 0363b42,socket-patch-core --libgives 6340 passed, and only the same 4 root-only tests fail.Review
Reviewer thread (
package-lockfallback): confirmed in the pnpm 7.33.5 / 8.15.9 / 9.15.9 / 10.34.7 / 11.28.5 bundles and fixed in 0363b42. The thread is resolved./code-review high, fixed in e7e7bb2:
--mode vendoredremedy was a dead end with no lock. It is now named only when a pnpm lock exists.vex.vexno longer reads them.pnpm_refusalfield doc was stale. Updated.Not fixed:
rollback/removemust still find these pins to restore the registry resolution. The vlt "discarded lock" case works the same way (ref kept, basis withheld). The scan surfaces the refusal warning.overrides): socket-patch's pnpm vendor backends always writeoverrides, which pnpm applies without a lock (verified in the issue on 12.10.1). A hand-edited lock with no override is out of scope here.lockfile_disabledreturns before the major lookup unless a file sets the key. The remaining cost is two cached reads per run.🤖 Generated with Claude Code
https://claude.ai/code/session_01AY4fDHDqR3oTHhUXpqBGcU
Note
Medium Risk
Changes hosted redirect and lock-only VEX behavior for pnpm; it is fail-closed (no silent pins) but can block attestations that previously succeeded from committed locks alone.
Overview
Fixes false confidence when pnpm ignores the lockfile (#1074). If
lockfileis off (YAML on pnpm 11+,.npmrcon older majors, or npmpackage-lock=false), pnpm installs from the registry and never applies hosted pins inpnpm-lock.yaml.The change adds
lockfile_disableddetection (pnpm major from install record,packageManager, or lock grammar) and, in pnpm projects only, skips all pnpm lock rewrites before branch-lock handling—emittingredirect_pnpm_lockfile_disabled, leaving locks and workspace YAML untouched, and not addingtrustLockfile. Lock-only VEX still sees hosted URLs but drops the lockfile basis and reportspatched_ref_unattributableper lock (Rush locks unchanged; vendored refs unchanged).Docs (CLI_CONTRACT, ecosystems table) and unit/e2e tests cover YAML vs
.npmrc, member locks,package-lock=false, and real pnpm 11 behavior.Reviewed by Cursor Bugbot for commit 0363b42. Configure here.
Generated by Claude Code