Repository navigation
Fix vendored scans failing on nested npm projects (#1534) - #1543
Merged
Mikola Lysenko (mikolalysenko) merged 4 commits intoOct 14, 2026
Merged
Mikola Lysenko (mikolalysenko) merged 4 commits into
Mikola Lysenko (mikolalysenko) merged 4 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
force-pushed
the
agent/fix-vendored-nested-project-copies
branch
from
October 14, 2026 18:23
f24ccbd to
8b0102e
Compare
A root scan --mode vendored over a repository with an independent nested npm project (tools/x with its own lock) fails every run with vendor_lock_entry_not_found and drops its in-run VEX (#1534). These tests capture that, plus the scan . tools/x form and two controls: a package the root also locks, and a workspace member's stray lock. Assisted-by: Claude Code:claude-opus-5-5
Hosted and vendored scans rewire only the scan root's lockfiles, but the crawl also reads node_modules of nested, independent npm projects. A vendored root scan handed those copies to the root lock backend, which refused them, so the run failed with exit 1 every time and wrote no in-run VEX, with a remedy (npm install) that could not clear it. Now a patched npm package is left out of a hosted or vendored run when the root's lockfiles do not list it, its copy sits in a nested project with its own lock (not a workspace member whose lock is ignored), and the root's own install has no copy. One nested_project_skipped warning per project names it and the PATH to pass. Exit and status unchanged. Fixes #1534 Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups for the nested_project_skipped warning: - the PATH it suggests is relative to the directory the command runs in (or absolute), not to a PATH-list leg or --cwd, and is quoted when a shell would split it; - an unreadable node_modules keeps every package the root's instead of dropping one the root may install; - npm purls are classified through Ecosystem::from_purl, the remedy spells each mode out so the remedy-command check parses it, and the e2e reads the envelope through common::envelope. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 14, 2026 19:18
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 695464d. Configure here.
Tanmay Singla (Tanmay182003)
approved these changes
Oct 14, 2026
Mikola Lysenko (mikolalysenko)
deleted the
agent/fix-vendored-nested-project-copies
branch
October 14, 2026 20:00
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 #1534
Summary
Some repositories hold an independent npm project below the root, such as
tools/x/withits own
package-lock.jsonandnode_modules. A rootscan --mode vendoredin such arepository used to fail on every run (exit 1,
partialFailure) and skip the in-run VEX.It now leaves that project's packages to that project's own run and prints a
nested_project_skippedwarning naming the PATH to pass. The exit code and status areunchanged.
Root cause
The scan crawls every
node_modulesbelow the root, including a nested project's.Agent and report-only scans assign each crawled copy to the project that owns it
(
judge_nested_roots, #554). Hosted and vendored scans skip that step, so a vendored scanpassed
tools/x's copy ofleft-padto the root'spackage-lock.jsonbackend. The rootlock has no entry for it, and the backend treats a missing entry as the hard
vendor_lock_entry_not_foundrefusal. The suggested fix (npm install) can't clear that,so the run fails every time. Hosted mode only skipped the package (
redirect_unconfirmed).Fix
core
hosted::governing_root::nested_npm_project(scan_root, copy)finds the nearestdirectory below the scan root, above the copy's first
node_modules, that is its ownnpm-family lock root. A workspace member whose lock its package manager ignores
(
member_stray_lock, Hosted and vendored scans run from an npm workspace member that has a stray package-lock.json of its own rewrite that lock, which npm ignores, and report success while npm installs the unpatched package and VEX attests not_affected #1094/Hosted and vendored scans from a Bun workspace member with a stray bun.lock / bun.lockb pin that ignored lock, exit 0, and lock-only VEX attests not_affected while Bun installs the unpatched package #1101/Hosted and vendored scans from a vlt workspace member with a stray vlt-lock.json write to that lock, which vlt ignores, and exit 0 (vendored VEX then attests not_affected whilevlt cifails) #1134) does not count, because the root's lock installsthat member.
cli
scan::discovery::nested_project_skipsruns after the patch query in hosted andvendored root scans. It drops a patched npm purl only when all three hold:
node_modulesin the root's own install has another copy. The crawl keeps one copyper purl, so this check stops a package the root also installs from being dropped. If a
tree can't be read, every package stays with the root.
Each nested project gets one
nested_project_skippedrun warning that names the purls,the directory, and the command to run (
socket-patch scan --mode vendored tools/x). Thepath is relative to the shell's directory, so it's correct under
--cwdand on PATH-listlegs, and it's quoted when needed.
Docs: a
CLI_CONTRACT.mdcode-table row, and a paragraph indocs/ecosystems.md("npm: which
node_modulestrees are crawled").Test evidence
New
crates/socket-patch-cli/tests/scan/scan_nested_project_e2e.rs, run withcargo test -p socket-patch-cli --all-features --test scan scan_nested_project_e2e.On
mainwith the tests committed, 3 fail (red); with the fix, all pass (green).vendor_lock_entry_not_foundroot_vendored_scan_warns_about_a_nested_projects_copies(2 runs)--vexwrites no in-run documentroot_vendored_scan_with_vex_writes_the_documentscan --mode vendored . tools/xstays redvendored_scan_of_root_and_nested_path_succeedspackage_the_root_also_locks_is_still_vendored_at_the_rootworkspace_member_with_stray_lock_is_not_a_nested_project--cwdwarning_names_the_path_from_the_shells_directoryUnit tests:
governing_root::tests::nested_npm_project_finds_the_independent_lock_rootandscan::discovery::tests::shell_word_quotes_only_what_the_shell_would_split.Local checks:
cargo fmt --all -- --check: ok.cargo clippy --workspace --all-features -- -D warnings: ok.cargo test -p socket-patch-cli --all-features --test e2e_redirect_npm_build -- --ignored(real npm): 6 passed.
cargo test --workspace --all-features: every failure from this change is fixed. 10 testsstill fail locally, all for environment reasons:
covers the
*_write_failure_*tests,repair_*_unremovable,copy_tree's read-onlyparent, and
vlt heal_invalidation_failure;e2e_bun_lockbfetches a blob from the live patch API, which isn't reachable here.None of them touch scan's npm discovery. CI runs them as a non-root user.
Review (
/code-review high)Fixed:
Not fixed:
rule uses lock markers too, and without a lock there is no project boundary to read. A root
vendored scan still refuses such a copy (
vendor_lock_entry_not_found), as before. Thisis a narrower case than the one reported, which has a lock.
.leg ofscan . tools/xstill warns abouttools/xeven though the next legpatches it. The warning is accurate for that leg, and silencing it would mean passing sibling
PATHs into each leg.
nested_npm_projectandScanPolicy::nested_ownershare a walk shape but usedifferent rules. Agent mode doesn't exclude stray-lock members. Unifying them would change
agent-mode ownership, which is out of scope here.
node_moduleswalk happens only when some patched package is a candidate,which is rare. Reusing
NpmCrawlSnapshotwould mean threading it throughrun_scan'sagent path.
nested_npm_project. It runs once per patchedcandidate, not once per crawled package.
scannedPackagesstill counts the nested copy. It counts what the crawl found; thecopy just isn't selected. The in-run VEX test confirms the copy isn't attested.
🤖 Generated with Claude Code
https://claude.ai/code/session_0165v5yieeSffKsNoz1RbLKx
Generated by Claude Code
Note
Medium Risk
Changes npm discovery and lock-backend inputs for hosted/vendored scans; behavior is gated and covered by e2e tests, but wrong nested/root attribution could skip patching or still mis-wire locks.
Overview
Fixes #1534: root
scan --mode hosted/vendoredno longer treats patched npm packages that live only under a nested, independent npm project (own lockfile, not a workspace member) as root lock targets.Behavior: After patch discovery, hosted/vendored root scans call
nested_project_skips, which usesnested_npm_projectto attribute crawled copies to nested lock roots, drops those purls when the root lock does not list them and the root install has no other copy, and emits onenested_project_skippedwarning per nested project with a copy-pasteablesocket-patch scan --mode <mode> <dir>path (shell-relative, POSIX-quoted when needed). Exit/status stay success instead ofvendor_lock_entry_not_found/ partial failure; in-run VEX no longer omits attestations for the root-only work.Docs: New
nested_project_skippedrow inCLI_CONTRACT.mdand npm crawl note indocs/ecosystems.md.Tests:
scan_nested_project_e2e.rsplus unit tests fornested_npm_projectandshell_word.Reviewed by Cursor Bugbot for commit 695464d. Configure here.