Repository navigation
Fix hosted yarn classic rescan of v4 pins (#1462) - #1463
Mikola Lysenko (mikolalysenko) merged 6 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A hosted rescan of a yarn classic lock never fetched the served tarball of an artifact the lock already pinned with a sha1, so a pin v4.0.0 wrote for a patch whose package.json adds a dependency the lock doesn't lock was confirmed as applied with no warning. yarn then installed the patched package without that dependency, and vex attested it. The rescan now reads the served tarball for every pinned artifact too, so the stale pin gets the redirect_yarn_classic_dep_manifest_unlocked warning (now saying the earlier pin is not confirmed) and is listed as not hosted, with either grant shape. The lock's blocks are still read at most once, and never on a fully pinned rescan. vex no longer attests a hosted npm copy whose patch changes its package.json when a declared, non-optional dependency does not resolve from the copy; it is omitted as dependency_not_installed until the dependency is installed. Fixes #1462 Assisted-by: Claude Code:claude-opus-5-5
Covers the hoisted, bundled and pnpm-isolated layouts, optional dependencies, copies outside node_modules and unsafe dependency names. Refs #1462 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
pnpm, bun isolated and npm linked installs reach a package through a root-level symlink into a store, where its dependencies sit beside it. The new vex dependency check walked up from the symlink, so it missed those siblings and omitted a correctly installed patch. It now starts from the real path, runs its file probes off the async runtime, reuses the shared npm name check, and words the omission so it fits every package manager. Refs #1462 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
The vex dependency check opened the installed package.json with a plain read, so a FIFO planted at that path would hang vex. It now reads through the shared regular-file opener, like every other workspace read. Refs #1462 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Ready for review (burn-down agent)
Generated by Claude Code |
Final review briefWhat it does: A hosted yarn classic rescan used to skip every artifact Risk: low to medium. Every change fails closed: rows are left unconfirmed or omitted, never wrongly attested. The medium part is operational. Each Look here:
Verified:
Changes I made: none. Open questions (non-blocking):
Update for 5fffa69: the only commit since this brief is a one-line docs change to Auto-merge (squash) is armed, so approving sends it straight to the merge queue. Generated by Claude Code |
The vex check omits any hosted npm-family copy whose patched package.json declares a non-optional dependency that does not resolve, whatever the reason. A yarn classic pin from before the served-manifest check is one example, not the only cause, and the lookup starts from the copy's real path. Refs #1462 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPgtA8XT3RDR96zCibCmo
|
[agent] Both open questions in the review brief are right.
Generated by Claude Code |
|
bugbot run 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 5fffa69. Configure here.
|
Still Ready for review at Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1462
Summary
Hosted yarn classic users upgrading from v4.0.0 (the current npm
latest) can have ayarn.lockpin that v4 wrote for a patch whosepackage.jsonadds a dependency the lock never locked. yarn installs the patched package without that dependency. Onmainsuch a pin passes silently:appliedand no warning. With a sha512-only grant it warns, but says the package "is not pinned", which is wrong because the earlier pin is still in the lock.vexandscan --vexattestnot_affectedafter install.Root cause
select_classic_artifacts(hosted/engine.rs) skipped every artifact the lock already pins as"<url>#…"when the grant carries a sha1. The servedpackage.jsonwas never read, so the Yarn classic: a patch that adds a dependency to the package's own package.json leaves vendored yarn.lock with a dangling dependency (offline frozen install fails, lock churns), and hosted silently installs without it #591 dependency check inrewrite_yarn_classic_with(patch/redirect/mod.rs) never ran: it only runs when a manifest exists.Fix
scan/hosted.rsis sequential; fetching concurrently would be a follow-up). That is the only way to catch a pin an earlier release wrote, because nothing in the lock records whether the check ran.redirect_yarn_classic_dep_manifest_unlockedwarning, and the detail now says "an earlier run's pin of it is not confirmed … installs the patched package without them" when the lock already pins this artifact. Being refused means it is never confirmed (therefused_yarn_classic_uuidspath), so it is listed as aredirect_unconfirmedskip and in-runscan --vexdoesn't attest it. Following the contract for unconfirmed rows (Decide: one shape for the--jsontop-levelerror(scan and get emit both a string and a {code, message} object) #704), the exit code and status stay unchanged, and the lock is left as found.package.json. This applies to hosted npm-family copies only. A non-optionaldependenciesentry that node can't resolve from the installed copy is omitted with a newdependency_not_installedtag. Resolution follows node's lookup:node_modules/<name>in the copy or in any non-node_modulesancestor, which covers bundled, hoisted, and pnpm/bun-sibling layouts. A copy outside anode_modulestree, an unreadable manifest, or a name that isn't a plain package name never counts as evidence. The check runs only when the patch record touchespackage.json, so other patches see no change.CLI_CONTRACT.mddocuments the new omission tag and the extended warning. The npm/pypi/gem wrappers only dispatch to the binary, so they need no change.Tests (red on main → green)
scanhosted_yarn_classic_sha1::issue_1462_v4_pin_with_unlocked_dependency_is_not_confirmed_sha1_grant…_sha512_only_grantvexattests after installe2e_vex_lockfileyarn::hosted_patch_adding_an_uninstalled_dependency_is_omitted(classic and berry 2/3/4)vex::verify::tests::unresolved_npm_dependency_follows_node_lookupclassic_artifact_targets_read_the_lock_blocks_at_most_once(now expects pinned artifacts selected, still 0 block reads)Commands:
cargo test -p socket-patch-cli --all-features --test scan hosted_yarn_classic,cargo test -p socket-patch-cli --all-features --test e2e_vex_lockfile yarn -- --include-ignored, pluscargo fmt --all -- --check(clean),cargo clippy --workspace --all-features -- -D warnings(clean) andcargo test -p socket-patch-core --all-features --lib(6408 pass). Four unrelated core tests fail locally only because this sandbox runs as root, which ignores the read-only permissions they rely on (copy_treesymlinked root,vlt_healunremovable lock,pypi_poetry/pypi_requirementswrite failure). The full CLI matrix runs in CI; it didn't fit on the sandbox disk./code-review highfindingsFixed in
Resolve npm deps from the copy's real path:run_blocking.npm_common::is_safe_npm_name.Not changed, with reasons:
file:block selects a fetch. This needs a hand-edited lock. The cost is one fetch, and the rewriter still skips non-registry blocks.package.json.Not covered
Lock-only
vex(nothing installed) still attests a stale v4 pin from its wiring. With no installed copy and no network fetch there is no local evidence of the served manifest. Whether hosted VEX should need installed evidence by default is the open #1099 decision.🤖 Generated with Claude Code
https://claude.ai/code/session_01VDPgtA8XT3RDR96zCibCmo
Note
Medium Risk
Changes hosted scan confirmation and VEX attestation for npm/yarn classic; rescans may fetch more tarballs and withhold attestations until dependencies are installed, but locks are left unchanged and behavior fails closed on manifest mismatch.
Overview
Fixes #1462: hosted yarn classic pins from older releases could stay “confirmed” even when the served patch adds a
package.jsondependency thatyarn.locknever locked—yarn still installs the patched files, but not that dependency.Hosted scan/rescan now fetches and inspects the served tarball for already-pinned classic artifacts (not only fresh pins), so the existing unlocked-dependency check runs on every run. Stale pins get
redirect_yarn_classic_dep_manifest_unlockedwith wording that the earlier pin is not confirmed, land inrefused_yarn_classic_uuids, and are treated like other unconfirmed hosted rows (lock unchanged, exit policy unchanged per #704).VEX adds a hosted npm-family guard after file verification: if the patch touches
package.json, any non-optional declared dependency that Node cannot resolve from the installed copy (hoisted, bundled, or store-sibling layouts via real path) omits attestation with new tagdependency_not_installed. Optional deps and copies outsidenode_modulesare excluded.CLI_CONTRACT.md, CLI omission messaging, and e2e/scan/core tests document and lock in the behavior.Reviewed by Cursor Bugbot for commit 5fffa69. Configure here.
Generated by Claude Code