Repository navigation
Fix scans missing renamed yarn copies (#939, #1236) - #1489
Merged
Mikola Lysenko (mikolalysenko) merged 8 commits intoOct 13, 2026
Merged
Conversation
Assisted-by: Claude Code:claude-opus-5-5
This was referenced Oct 13, 2026
Yarn keys a file: tarball or directory, a url tarball and a berry portal:/link: dependency by the name the depender gave it, so "lp2": "file:./left-pad-1.3.0.tgz" locks as lp2@file:... while node_modules/lp2 is left-pad@1.3.0. Only VEX discovery could tell which package such a copy really holds, and it skipped berry portal:/link: copies entirely, so a lock-only vex still attested not_affected while that copy installed unpatched (#939). Move the copy-identity reading into formats::yarn::copy (CopyRef), so every reader takes the package from the copy the same way: the tarball's or directory's package.json, a registry url's path, or, for portal:/link:, the target's package.json name and version (the lock only records 0.0.0-use.local). VEX now records portal:/link: copies too. Assisted-by: Claude Code:claude-opus-5-5
A hosted scan pinned the registry entry and reported a clean success while the same yarn.lock also installed the patched name@version under another dependency name (a renamed file: tarball in yarn classic, any renamed file:/url/portal:/link: copy in yarn berry). That copy stays unpatched, and nothing in the scan output said so (#939, #1236). The run already checks its own result with lockfile discovery and names each pnpm copy the pin cannot reach (redirect_copy_unpatched). Do the same for yarn.lock, skipping a copy a yarn classic skip warning already names, so every renamed copy discovery sees is reported and the in-run VEX never assumes the patch applied. Assisted-by: Claude Code:claude-opus-5-5
Vendored scans keyed every check on the lock entry's own name, so a yarn berry copy of the patched package under another dependency name got no warning at all, and a yarn classic renamed file: tarball was missed too (#939, #1236). The resolutions entry or re-lock vendoring writes never reaches such a copy, so it installs unpatched. Add vendor::yarn_copies, which lists those copies for either lock grammar through the shared copy reader. The classic backend now names renamed tarballs as well, and the berry backend names every renamed copy with vendor_copy_unpatched while still wiring the registry entry. Assisted-by: Claude Code:claude-opus-5-5
Describe how hosted and vendored scans and vex treat a yarn copy of the patched package declared under another dependency name, and list the widened redirect_copy_unpatched and new vendor_copy_unpatched codes. Assisted-by: Claude Code:claude-opus-5-5
5 tasks
- hosted: skip a copy only when a yarn rewriter warning names the same root yarn.lock entry, so a pnpm or unrelated warning never hides one - vendored berry: a package installed only under other dependency names is refused as vendor_lock_entry_not_rewritable naming those copies (in the loop and the download plan), not "not found, run yarn install", which can't help - vendored: never read a copy of another version (no needless tarball unpacking) or socket-patch's own .socket/ wiring as a user's copy - keep the formats layer free of async code: the tarball read off the runtime moves to vendor::yarn_copies::copy_identity Assisted-by: Claude Code:claude-opus-5-5
A berry package installed only under other dependency names is now refused as vendor_lock_entry_not_rewritable; say so beside the other renamed-copy behavior. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 13, 2026 18:10
Collaborator
Author
|
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 36b49c6. Configure here.
Tanmay Singla (Tanmay182003)
approved these changes
Oct 13, 2026
Mikola Lysenko (mikolalysenko)
deleted the
agent/fix-yarn-renamed-copy-identity
branch
October 13, 2026 19:02
This was referenced Oct 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.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #939
Refs #1236
Summary
Yarn keys a
file:tarball,file:directory, URL tarball and berryportal:/link:dependency by the name the depender gave it."lp2": "file:./left-pad-1.3.0.tgz"locks aslp2@file:…, butnode_modules/lp2is really left-pad@1.3.0. No pin or vendored wiring ofleft-padreaches that copy, so it installs unpatched. Before this PR:file:/URL copy of the patched package locked under another dependency name, so lockfile VEX (and vendored VEX after install) attests not_affected while that copy installs unpatched #939);file:tarball (directories and URLs were already named) (Yarn classic hosted and vendored scans miss afile:directory copy of the patched package declared under another dependency name, soscan --vexand lock-onlyvexattest not_affected while that copy installs unpatched #1236 follow-up);vexignored berryportal:/link:copies, so it attestednot_affectedwhilenode_modules/lp2was unpatched left-pad (Yarn berry hosted and vendored scans miss afile:/URL copy of the patched package locked under another dependency name, so lockfile VEX (and vendored VEX after install) attests not_affected while that copy installs unpatched #939, bug-hunt run 25).Root cause
Only VEX discovery could work out which package a renamed copy holds (
record_copiesinvex/discover/yarn.rs), and it skippedportal:/link:. The hosted rewriters and vendored backends key their checks on the lock entry's own name, so they never see the copy, and each used a weaker reader or none.Fix
formats::yarn::copy::CopyRef): from a classic block or a berry locator it names the copy (Tarball,Directory,Linkedforportal:/link:,Url) and reads its package: thepackage.jsoninside a tarball, a directory'spackage.json, the registry package a URL's path names, or forportal:/link:the target'spackage.jsonname and version (the lock records0.0.0-use.local). VEX discovery now uses it and also recordsportal:/link:copies.hosted/engine.rs): the run already checks its own result with lockfile discovery and names each pnpm copy a pin can't reach (redirect_copy_unpatched, Warn during pnpm scans when non-registry copies cannot be patched #935). That pass now coversyarn.locktoo, so every renamed copy discovery sees is named and the in-run VEX never assumes the patch applied. A copy a yarn classic skip warning already names (same lock key) is not named twice.vendor/yarn_copies.rs): lists the renamed copies ofname@versionin either lock grammar through the same reader. The classic backend now names renamed tarballs too (vendor_yarn_classic_non_registry_entry_skipped). The berry backend names every renamed copy with the newvendor_copy_unpatchedand still wires the registry entry.docs/ecosystems.mdandCLI_CONTRACT.mddescribe both codes.Fixing this at the hosted engine's discovery pass covers every yarn copy shape in one place (classic and berry), instead of adding the same name check to each rewriter.
Policy note:
portal:/link:The bug-hunt asked whether a
portal:/link:target holding the patchedname@versionshould contest the ref. This PR says yes. Yarn linksnode_modules/lp2to that directory and noresolutionspin reaches it, so attestingnot_affectedbeside it is a false attestation. Holding back the attestation is the safe choice. A target holding another package or version contests nothing (tested).Out of scope (why #1236 stays
Refs)#1236's renamed git copy: yarn 1's lock records no package name for it, so a lock-only reader can't tell it is the patched package without a policy decision. Post-install
vexalready catches it. #1236 stays open for that.Also: the in-memory hosted engine (service) only receives the candidate files, not a user's
file:tarball or directory, so there the copy can't be read and is not named. Disk scans (the CLI) read it.Test evidence
New regression tests, each failing on
main(verified in a cleanorigin/mainworktree with only the tests applied: 5/5 red) and passing here:portal:/link:lockfile VEXvex::discover::yarn::tests::issue_939_berry_portal_and_link_copies_contest_the_reffile:tgz / dir / URL /portal:/link:hosted_memory_engine::issue_939_berry_renamed_copy_is_namedvendor::yarn_berry_lock::tests::issue_939_other_name_copies_are_namedfile:tarballhosted_memory_engine::issue_1236_classic_renamed_file_tarball_copy_is_namedfile:tarballvendor::yarn_classic_lock::tests::issue_1236_other_name_file_tarball_copy_is_namedformats::yarn::copy::tests::*Each test also has a control: a copy holding another package (or, for
portal:, another version) is not named and does not contest.The berry vendored backend also refuses a package installed only under other dependency names as
vendor_lock_entry_not_rewritable, naming the copies. It does this in the loop and in the download plan, the same as classic. It used to say "not found, runyarn install", which can't help:vendor::yarn_berry_lock::tests::issue_939_only_other_name_copies_are_refused_as_not_rewritable.Commands run locally (all on the final head)
cargo fmt --all -- --check✅cargo clippy --workspace --all-features -- -D warnings✅cargo test -p socket-patch-core --all-features --lib: 6457 passed, 4 failed. The 4 are root-only:copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_…andpypi_requirements::wire_failure_…depend on chmod denying a write, and this sandbox runs as uid 0. None touch yarn code.hosted_memory_engine(42),hosted_memory_parity(42),in_process_redirect_pnpm(37),in_process_vendor(133),in_process_rollback_hosted(41),in_process_get_modes,in_process_alternate_installers,covgap_commands_{rollback,scan_hosted,scan_mod,vex},mode_migration_npm: all pass. The only failures are the same root-only*_write_failure*tests (3 incovgap_commands_vendor, 3 inin_process_redirect). A fullcargo test --workspacecould not link every test binary within this sandbox's disk allowance, so CI runs the rest.e2e_redirect_yarn_classic_build(37 ✅),e2e_vendor_yarn_classic_build(38 ✅) on yarn 1.22.22;e2e_redirect_yarn_berry_build(35 ✅),e2e_vendor_yarn_berry_build(42 ✅) on yarn 4.18.0, all withSOCKET_PATCH_YARN_E2E_REQUIRED=1./code-review highfindingsFixed: the hosted dedup is scoped to yarn rewriter warnings on the root
yarn.lock; the berry only-other-name refusal; the vendored reader skips other-version copies before any read, and skips.socket/wiring.Not fixed, and why:
classic_copy_real_name). Moving it ontoCopyRef, or dropping its warning, would rename the documentedredirect_yarn_classic_directory_skipped/_non_registry_entry_skippedcodes for renamed directories and URLs that Fix yarn classic copies locked under another name (#1236) #1242 shipped and the classic e2e asserts. This PR keeps those codes and lets the engine pass name only what the rewriter can't see (tarballs). Unifying them is a follow-up.link:copy under another name isn't detected. Yarn 1 VEX and both scans treat every classiclink:entry as first-party source, same-name included. Changing that is a policy change beyond these issues, so I left it.ClassicProjectkeeps the lock text beside its blocks / the tarball is unpacked whole. The tarball read reuses the capped archive decoder VEX already used. With the version pre-filter, vendoring only unpacks copies at the patched version. The extra text is oneArc<str>per run.🤖 Generated with Claude Code
https://claude.ai/code/session_019yaJ6RS3d3c9ZvVGhYYy9X
Generated by Claude Code