fix(vendor): accurate refusals for bundled/alias/CRLF lock shapes - #185
Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Open
fix(vendor): accurate refusals for bundled/alias/CRLF lock shapes#185Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Conversation
Three diagnostics fixes so vendored-mode refusals name the real cause
instead of sending users down the wrong remediation path:
- npm: when every lock instance of the target is bundled inside a
parent's tarball or a link, vendoring refused with the generic
vendor_lock_entry_not_found ("run `npm install`" -- which cannot
help) and silently dropped the stays-UNPATCHED advisories, since a
Refused outcome has no warnings channel. It now refuses with
vendor_lock_entry_not_rewritable and carries the per-instance
UNPATCHED advisories in the refusal detail.
- npm v2 legacy `dependencies` mirror: `bundled: true` nodes are no
longer rewritten (parity with the packages scan's inBundle skip --
npm never installs a bundled copy from `resolved`, and rewriting it
desynced the two lock halves), and alias consumers
(`"alias": {"version": "npm:real@ver"}`) now emit a loud
vendor_legacy_alias_skipped warning: npm 6 reading the mirror still
installs the unpatched registry bytes through the alias.
- pnpm: a CRLF pnpm-lock.yaml (Windows autocrlf checkout) passed the
version sniff but failed every structural probe, surfacing as the
misleading vendor_lock_entry_not_found. It now fails closed up
front with vendor_lockfile_crlf_unsupported, naming the line
endings and the LF-normalization remedy.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Aug 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.
Problem
Three vendored-mode diagnostics defects from the 2026-08-13 audit sent users down the wrong remediation path or silently hid security-relevant facts:
npm: all-bundled/all-link target refuses with a misleading "not found" and drops the UNPATCHED advisories (medium). When every lock instance of the target is bundled inside a parent's tarball (
inBundle) or a link,scan_lock_matchesreturns zero rewritable matches andvendor_npmrefused with the genericvendor_lock_entry_not_found("make sure the package is installed and locked (npm install)"). The entry IS in the lock,npm installcannot help, and becauseVendorOutcome::Refusedhas no warnings channel, the per-instance "that copy stays UNPATCHED" advisories were silently discarded. Concrete scenario: a lock whose onlyleft-pad@1.3.0entries are aninBundle: truenested copy and a workspace link — the user is told the package isn't installed instead of being told it cannot be patched by vendoring and remains vulnerable.npm v2 legacy
dependenciesmirror: bundled nodes wrongly rewritten, alias consumers silently unpatched (low).rewrite_legacy_treerewrotebundled: truenodes (thepackagesscan skips theirinBundletwins, so the two lock halves desynced — npm never installs a bundled copy fromresolved), and alias consumers ("alias": {"version": "npm:left-pad@1.3.0"}) matched neither key nor version, so npm 6 clients reading the mirror keep installing the unpatched registry bytes through the alias with zero warning.pnpm: CRLF lockfile refuses with a misleading "not found" (low). A CRLF
pnpm-lock.yaml(Windows autocrlf checkout) passescheck_lock_version(str::linesstrips\r) but breaks every structural probe (split_lineskeeps the trailing\r, sopackages:never matches"packages:\r"), surfacing asvendor_lock_entry_not_found— wrong diagnosis for a lock pnpm itself accepts.Fix
vendor_npm: whenmatchesis empty but the scan pushedvendor_bundled_instance_skipped/vendor_link_entry_skippedwarnings, refuse with the newvendor_lock_entry_not_rewritablecode whose detail carries the per-instance stays-UNPATCHED advisories verbatim and explicitly saysnpm installwill not help. The genuinely-absent case keeps the existingvendor_lock_entry_not_found. Both codes remain non-benign (refusal_is_benignunchanged), so exit behavior is unchanged.rewrite_legacy_tree: skipsbundled: truenodes (parity with thepackagesscan'sinBundleskip) and emits a loudvendor_legacy_alias_skippedwarning for alias consumers, naming the JSON-pointer node and that npm 6 still installs UNPATCHED bytes through it. Aliases are warned rather than rewritten because the legacy alias spelling has no proven equivalent rewrite — fail loud, not fail open.vendor_pnpm: fails closed immediately after the version sniff with the newvendor_lockfile_crlf_unsupportedrefusal, naming the CRLF line endings and the LF-normalization remedy, before any structural probe can produce the wrong diagnosis.Skipped (out of lane scope, third gap of the low-severity legacy-mirror finding): recomputing the legacy node's
requiresmap from the patched manifest. A correct recompute needs the staged patched package.json plumbed intorewrite_legacy_treeplus a proven mapping to npm 6'srequiresgrammar; doing it without a spike risks corrupting v2 locks for a purely npm-6-era reader.Testing
npm_lock.rs::tests::all_bundled_or_link_instances_refuse_with_accurate_reasonnpm_lock.rs::tests::v2_legacy_bundled_node_is_not_rewrittennpm_lock.rs::tests::v2_legacy_alias_node_warns_and_keeps_registry_resolutionpnpm_lock.rs::tests::crlf_lock_refuses_naming_line_endingscargo test -p socket-patch-core --lib— 2079 passedcargo test -p socket-patch-cli --test in_process_vendor— 27 passedcargo test -p socket-patch-cli --test e2e_vendor_npm_build— 7 passedcargo test -p socket-patch-cli --test e2e_vendor_pnpm_build— passed🤖 Generated with Claude Code
Note
Medium Risk
Changes vendor lock rewrite and refusal paths that affect which lock bytes are edited and what users see when patches cannot apply; incorrect behavior could leave unpatched installs or mislead remediation, but scope is diagnostic/parity fixes with targeted tests.
Overview
Improves npm and pnpm vendoring diagnostics when lock entries exist but cannot be rewritten, so users are not told to run
npm install/pnpm installincorrectly and security-relevant UNPATCHED facts are not dropped.For npm, when every matching lock instance was skipped (bundled
inBundleor link), refusal now usesvendor_lock_entry_not_rewritablewith bundled/link skip details in the message instead ofvendor_lock_entry_not_found. The v2 legacydependenciesmirror now skipsbundled: truenodes (aligned withpackagesinBundle) and emitsvendor_legacy_alias_skippedfornpm:name@veralias nodes left at registry resolution for npm 6 readers.For pnpm, CRLF
pnpm-lock.yamlis rejected up front withvendor_lockfile_crlf_unsupportedand LF normalization guidance before structural parsing can misdiagnose a missing package entry.Regression tests cover all-bundled/link refusal, legacy bundled skip, legacy alias warning, and CRLF refusal.
Reviewed by Cursor Bugbot for commit 03fe6cd. Configure here.