Repository navigation
Fix yarn classic VEX ignoring bundled copies (#758) - #1712
Conversation
Assisted-by: Claude Code:claude-opus-5-5
yarn 1 unpacks a package's bundleDependencies from its tarball into node_modules/<parent>/node_modules and yarn.lock records nothing about them. A hosted or vendored scan wired the lock block, said nothing, and the VEX document claimed not_affected while the bundled copy shipped unpatched. When yarn.lock holds a Socket wiring, read the installed node_modules for bundled copies (each named bundle entry, its dependencies beside it, and everything nested under them). Lockfile discovery records each as an unpatched copy beside the parent's lock entry, so `vex` and the in-run VEX withhold the attestation and hosted scans name the copy with redirect_copy_unpatched. Vendored scans warn with vendor_bundled_instance_skipped, as they do for vlt. Fixes #758 Assisted-by: Claude Code:claude-opus-5-5
Follow-ups from review of the bundled-copy fix: - Walk each workspace member's own node_modules too, reached through its link in the root node_modules, so a bundle yarn couldn't hoist is still found. Links leaving the project are never followed. - Count a bundled package's peers as part of the bundle, as npm 7+ packs them. - Name every bundled copy a parent ships, not just the first one: the hosted warning was de-duplicated per lock entry. - Tell the user to vendor or update the bundling package, not to "depend on the registry release", which they already do. - When the only installed copy is bundled, vendoring now refuses with vendor_lock_entry_not_rewritable naming it instead of suggesting `yarn install`. Refs #758 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
1 similar comment
|
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 44f1a19. Configure here.
|
Labeled Ready for review by the burn-down agent.
Generated by Claude Code |
Final review briefWhat it does. yarn classic unpacks a package's Risk: low–medium. The new code runs only for yarn classic projects whose Look here
Verified
Changes I made: none. Open questions (non-blocking): Auto-merge is armed: approving this PR sends it straight to the merge queue. Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #758
Root cause
A yarn classic project can install the same
name@versiontwice: oncefrom its
yarn.lockblock, and once unpacked from another package'starball because that package lists it in
bundledDependencies.yarn.lockrecords nothing about the bundled copy. Hosted, vendored andlockfile VEX discovery all read only
yarn.lockfor yarn classic, so noneof them can see the bundled copy: the scan wires the lock block, says
nothing, and the OpenVEX document says
not_affectedwhilenode_modules/<parent>/node_modules/<pkg>ships unpatched.vlt had the same gap (#471) and npm/pnpm/bun read it from their locks.
Fix
vendor::yarn_classic_bundled::bundled_copiesreads bundled copiesfrom the installed
node_modules: every package whosepackage.jsondeclares
bundleDependencies/bundledDependencies(array,trueor object form), each bundled package under its own
node_modules,the dependencies of those found beside them, and everything nested
under any of them. Packages yarn placed in a parent's
node_modulesfrom the lock (the parent bundles nothing) are not bundled copies.
Symlinks are never followed, and the walk is capped like vlt's.
unpatched copy beside the parent's lock entry (only when
yarn.lockholds a Socket wiring, so unpatched projects pay nothing). That reuses
the existing same-lock contest: the ref is withheld from attestation
but still managed (rollback/remove still work), and the hosted engine's
existing
redirect_copy_unpatchedwarning names the copy and keeps theuuid out of the in-run VEX's
assume_applied.vendor_bundled_instance_skipped, as vltdoes. When the only installed copy is a bundled one, vendoring refuses
with
vendor_lock_entry_not_rewritablenaming it (not "runyarn install").node_modules(reached through the member's link in the root
node_modules; linksleaving the project are never followed), and counts a bundled
package's peers as bundled (npm 7+ packs them).
redirect_copy_unpatchednames every bundled copy a parent ships(it was de-duplicated per lock entry), and for a bundled copy tells
the user to vendor or update the bundling package instead of the
generic "depend on the registry release".
its limit: a lock-only
vexwith nonode_modulescan't see the copy(yarn.lock has no trace of it).
The npm/PyPI/gem wrappers only dispatch to the binary, so they need no
change.
Tests (red → green)
true, transitive and nested bundle shapes; other version and non-bundled controls)vex::discover::yarn::tests::classic_installed_bundled_copy_contests_the_refvendor::yarn_classic_lock::tests::issue_758_bundled_copy_is_namednode_modules, link outside the projectvendor::yarn_classic_bundled::tests::*vendor_lock_entry_not_rewritablevendor::yarn_classic_lock::tests::issue_758_only_a_bundled_copy_is_refused_as_not_rewritablevexdon't atteste2e_redirect_yarn_classic_build::classic_bundled_copy_is_named_and_not_attestedCommands run locally:
cargo fmt --all -- --check: cleancargo clippy --workspace --all-features -- -D warnings: cleancargo test -p socket-patch-core --all-features --lib -- vex::discover vendor::yarn vendor::vlt_bundled hosted::: 738 passedSOCKET_PATCH_YARN_CLASSIC_E2E_VERSION=1.22.22 SOCKET_PATCH_YARN_E2E_REQUIRED=1 cargo test -p socket-patch-cli --all-features --test e2e_redirect_yarn_classic_build classic_bundled_copy: 1 passedcargo test --workspace --all-features --no-fail-fast(debuginfo offto fit the sandbox disk): every suite passes except 11 tests that
fail here for environment reasons, none touching yarn: 8
permission-denial tests (the sandbox runs as root, so read-only dirs
and files stay writable:
copy_tree,vlt_heal,pypi_poetry,pypi_requirements,in_process_redirectwrite-failure and vlt-healcases, two
repaircases), onee2e_bun_lockbtest that needs a liveblob download, and one that pins a bun lock literal different from the
local bun 1.4.4. CI runs these as a normal user.
CI and Bugbot
CI is green on
44f1a19(38 ran, includingyarn-classic 1.22.22, which runsthe new e2e; 50 skipped by path filters). Bugbot reviewed
44f1a19andfound no issues.
Review
/code-review highran on the diff. Fixed: workspace membernode_modules, peer deps in the bundle, per-copy warning de-dup, thebundled-copy remedy text, and the only-bundled vendored refusal code.
Left as known limits (see below): the walk cap and the lack of a cache
across discovery passes. Also noted: a bundled copy also counts as
evidence against another lock (package-lock / pnpm) wiring the same
version, but only when yarn.lock has some Socket wiring, since the walk
is gated on that.
The e2e returns early on yarn 1.0–1.6, which install nothing from a
file:tarball (the same limit the suite already handles elsewhere).Known limits, not fixed here:
node_modulesentries (as vlt's is); atree past that may miss a copy.
when
yarn.lockholds a Socket wiring.Note on cost: when
yarn.lockhas a Socket wiring, discovery now readseach installed package's
package.jsononce per discovery pass. That'sthe only way to find a bundle, since yarn.lock doesn't record one.
Generated by Claude Code