Skip to content

fix(vendor): carry berry lock original across patch updates - #190

Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/berry-vendor-wiring-key
Open

fix(vendor): carry berry lock original across patch updates#190
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/berry-vendor-wiring-key

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

Three audit findings (2026-08-13 vendored-mode audit) against the yarn berry vendor backend:

  1. High — berry wiring key embeds the patch uuid, breaking the pre-vendor-original carry-forward. Re-vendoring after a patch update (new uuid) rewrites our own stale file: lock entry and records original: None; the ledger carry-forward in persist_vendor_entry matched wiring records by byte-equal (file, kind, key), and berry's lock key is the file: locator key embedding .socket/vendor/npm/<uuid>/… — so the uuid change that caused the re-vendor also broke the match, and the registry original recorded at first vendoring was dropped. A later vendor --revert then deleted the artifact dir but left the dangling file: entry in yarn.lock (vendor_lock_entry_drifted), permanently breaking yarn install --immutable until a plain install re-resolved it.

  2. Low — post-pack wiring failures orphan .socket/vendor artifacts. Failures after stage_patch_pack wrote the tarball (berry: tarball re-read, cache-checksum refusal such as a non-ASCII filename, package.json serialize, commit; classic: lock block vanished, lock write) returned done_failure without removing the freshly created uuid dir. No ledger entry exists for a failed wiring, so --revert could never clean it up — contradicting the module contract that a failure leaves the project byte-untouched.

  3. Medium — berry vendoring silently skipped npm-alias descriptors of the patched package. An alias@npm:<name>@… lock entry resolving the patched version is semantically out of reach for a name-keyed resolutions entry, but the skip was silent: with both a plain and an aliased entry, only the plain one vendored and the aliased copy kept installing the unpatched bytes with zero warnings; with only the alias entry, the generic vendor_lock_entry_not_found ("make sure the package is installed") sent users to a yarn install that changes nothing.

Fix

  1. Wiring identity is now uuid-agnostic: new vendor::path::wiring_key_matches() treats keys as equal when they are byte-equal, or when they are equal after normalizing every embedded canonical .socket/vendor/<eco>/<uuid> level (normalize_vendor_uuids, gated on known ecosystem dirs and canonical uuids — non-vendored keys still only match byte-equal). persist_vendor_entry uses it for the carry-forward, which is already scoped to the same candidate purl, so no cross-package matching is possible. No ledger schema change; old ledgers parse and match unchanged. Revert after a patch update now restores yarn.lock and package.json byte-for-byte.

  2. New done_failure_unstage() in npm_common unwinds the uuid dir at every post-pack wiring-failure site in the berry and classic backends — but only when this run created the dir (a preexisting same-uuid dir may still be referenced by live wiring), with non-recursive empty-parent pruning so sibling artifacts are untouched. Deliberate scope: failures inside stage_patch_pack are unchanged — the audited finding covered post-pack wiring sites.

  3. scan_berry_target now collects alias-descriptor entries resolving the patched version (via classic's pattern_real_name). When a plain entry also vendors, each aliased entry gets a loud vendor_alias_entry_skipped warning naming the unreachable copy; when the alias is the only consumer, the vendor_lock_entry_not_found refusal explains that berry resolutions are name-keyed and cannot reach aliased descriptors. Aliases of other versions stay quiet (out of the patch's scope).

No findings were skipped.

Testing

  • New regression test revendor_new_uuid_carries_original_forward_yarn_berry (in_process_vendor): vendor under uuid A → bump manifest to uuid B → re-vendor → assert the lock wiring record regained the registry original → --revert → assert yarn.lock and package.json byte-exact. Independently re-verified RED on the pre-fix carry-forward (fails at exactly the original-carry assertion with original: null).
  • New unit tests: wiring_key_matching_is_uuid_agnostic_for_vendored_paths (path.rs), alias_descriptor_entry_warns_and_stays_untouched + alias_only_lock_refuses_with_alias_detail (berry), post_pack_wiring_failure_unwinds_the_staged_artifact (berry, non-ASCII-filename vector), lock_write_failure_unwinds_the_staged_artifact (classic, unix read-only-root vector).
  • Suites green: cargo test -p socket-patch-core --lib (2080 passed), cargo test -p socket-patch-cli --test in_process_vendor (28 passed), cargo test -p socket-patch-cli --test e2e_vendor_yarn_berry_build (6 passed, real corepack yarn capstone incl. immutable fresh-checkout install + revert), plus e2e_vendor_yarn_classic_build, repair_vendor_flavors_e2e, repair_vendor_e2e, scan_vendor_e2e during implementation.
  • Touched files are rustfmt-clean.

🤖 Generated with Claude Code


Note

Medium Risk
Changes yarn berry and classic vendor lock/wiring and artifact cleanup paths that affect revert and failure semantics; scoped to vendor backends with substantial regression tests, but incorrect uuid key matching could still mis-revert lockfiles.

Overview
Fixes three yarn berry vendor audit issues around re-vendor after a patch uuid change, failed wiring cleanup, and npm alias lock entries.

Re-vendor / revert: Berry lock wiring keys embed .socket/vendor/.../<uuid>/, so a patch update broke byte-equal carry-forward of the pre-vendor original in persist_vendor_entry. Matching now uses wiring_key_matches (uuid-normalized keys for embedded vendor paths), so --revert can restore yarn.lock and package.json byte-for-byte after re-vendor.

Post-pack failures: Yarn berry and classic backends call done_failure_unstage after tarball staging when later wiring steps fail, removing a newly created uuid artifact dir (not pre-existing dirs) so failures do not leave orphan .socket/vendor trees without ledger entries.

Berry aliases: scan_berry_target collects alias@npm:<name>@… entries for the patched version—warnings when a plain entry still vendors, and a clearer refusal when aliases are the only consumer (name-keyed resolutions cannot reach them).

Reviewed by Cursor Bugbot for commit c9e29bb. Configure here.

Re-vendoring a yarn berry project under a new patch uuid dropped the
pre-vendor registry lock entry from the ledger: the berry lock wiring
key is the file: locator key, which embeds the uuid, so the
carry-forward in persist_vendor_entry never matched the record being
replaced. A later `vendor --revert` then deleted the artifact dir but
left the dangling file: entry in yarn.lock, breaking
`yarn install --immutable` until a plain install re-resolved it.
Wiring identity is now uuid-agnostic (wiring_key_matches normalizes
the embedded .socket/vendor/<eco>/<uuid> level), so revert restores
the pristine registry entry byte-for-byte; old ledgers parse and
match unchanged.

Also: post-pack wiring failures in the yarn berry and classic
backends now unwind the freshly created .socket/vendor uuid dir
(previously orphaned with no ledger entry, so revert could never
remove it), and berry vendoring surfaces npm-alias descriptors of
the patched package - a loud vendor_alias_entry_skipped warning when
a plain entry also vendors, and an alias-aware refusal detail when
the alias is the only consumer - instead of silently leaving that
copy on the unpatched bytes.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants