Repository navigation
Fix hosted yarn classic rescan of v4 pins (#1462) - #1463
Mikola Lysenko (mikolalysenko) wants to merge 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 |
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 fdde961. Configure here.
|
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):
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 |
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 hosted wiring; behavior is intentionally stricter (more warnings/omissions, extra tarball fetches on Yarn Classic rescan) but locks are not rewritten and exit codes stay the same for unconfirmed pins.
Overview
Fixes #1462: hosted Yarn Classic pins from older releases could attest or confirm patches whose served
package.jsonadds dependenciesyarn.locknever locked, while Yarn still installs the patched package without those deps.Hosted rescan now fetches the served tarball for every Yarn Classic artifact the lock already pins (not only unpinned/registry-copy deps), so the existing manifest-vs-lock check runs on every run. Stale v4-style pins get
redirect_yarn_classic_dep_manifest_unlockedwith wording that the earlier pin is not confirmed; they stay unconfirmed (redirect_unconfirmed), the lock is untouched, and exit behavior is unchanged per #704.VEX adds omission tag
dependency_not_installedfor hosted npm-family copies when the patch touchespackage.jsonand a non-optional declared dependency cannot be resolved via Node’snode_moduleslookup (hoisted, bundled, pnpm/bun siblings via real path). CLI contract and user-facing omission text are updated; e2e and scan tests cover rescans (sha1 and sha512 grants) and VEX attest/omit behavior.Reviewed by Cursor Bugbot for commit fdde961. Configure here.
Generated by Claude Code