Repository navigation
Root scan --mode vendored still exits 1 forever and drops its in-run VEX when a nested npm project has no lockfile (package-lock=false); the #1534 fix only covers nested projects with their own lock #1554
Description
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
#1543 (fixing #1534) stops a root hosted or vendored scan from handing a nested, independent npm project's copies to the root lock backend. But it only recognises a nested project that has its own lockfile (nested_npm_project → has_own_npm_family_lock). A nested npm project installed without a lock (.npmrc package-lock=false, or npm install --no-package-lock) still produces the original #1534 failure:
scan --mode vendoredfrom the root failsvendor_lock_entry_not_foundfor the nested project's package, so the run endspartialFailure, exit 1, on every run;scan --mode vendored --vex …writes no VEX document;- the remedy ("make sure the package is installed and locked (
npm install)") can't clear it, since the root doesn't depend on the package; - no
nested_project_skippedwarning is emitted.
Hosted mode over the same tree only warns (redirect_npm_entry_not_found, redirect_unconfirmed skip) and exits 0, which is the asymmetry #1534 was about.
Impact
Any repo that has a lockless npm sub-project below the root (an examples/ or tools/ dir whose .npmrc says package-lock=false, or a stray node_modules left after a lock was deleted) gets a root vendored scan that's permanently red in CI, and it never gets an in-run VEX. The root's own packages are vendored fine and then reported as already_vendored.
Repro
This uses real npm installs. A local mock of the org patch API (--api-url <mock> --api-token fake --org test-org, the wiremock routes from tests/scan/scan_nested_project_e2e.rs with mount_view_from_source over the real installed bytes) offers free patches for ms@2.1.3 (the root's dependency) and left-pad@1.3.0 (only tools/x depends on it).
mkdir -p r/tools/x && cd r && git init -q && echo node_modules/ > .gitignore
echo '{"name":"root","version":"1.0.0","private":true,"dependencies":{"ms":"2.1.3"}}' > package.json
echo '{"name":"x","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > tools/x/package.json
echo 'package-lock=false' > tools/x/.npmrc # or: (cd tools/x && npm install --no-package-lock)
npm install && (cd tools/x && npm install)
socket-patch scan --mode vendored --json --yes $API # run 1: partialFailure, exit 1
socket-patch scan --mode vendored --json --yes $API # run 2: partialFailure, exit 1 (ms already_vendored)
socket-patch scan --mode vendored --yes $API --vex o.vex.json --vex-product pkg:npm/root@1.0.0 # exit 1, no o.vex.jsonVendored, run 2 (main e719a9b, npm 10.9.4):
status: partialFailure (exit 1)
downloaded pkg:npm/left-pad@1.3.0
failed pkg:npm/left-pad@1.3.0 vendor_lock_entry_not_found package-lock.json has no rewritable entry for left-pad@1.3.0 — make sure the package is installed and locked (`npm install`) before vendoring
skipped pkg:npm/ms@2.1.3 already_vendored
warnings: none
Control, the same tree where tools/x has a package-lock.json (or an npm-shrinkwrap.json): exit 0, success, one nested_project_skipped warning naming tools/x, VEX written. That's the #1534 fix working.
Hosted, the same lockless tree: exit 0, skipped left-pad redirect_unconfirmed plus W redirect_npm_entry_not_found, VEX written.
Expected vs actual
- Expected: docs/ecosystems.md ("npm: which
node_modulestrees are crawled") says hosted and vendored scans "rewire only the scan root's lockfiles", and Rootscan --mode vendoredexits 1 forever and drops its in-run VEX when a nested independent npm project installs a patched package the root lock doesn't have (hosted mode only warns) #1534's fix leaves a nested project's copies to that project's run. A directory below the root with its ownpackage.jsonandnode_modulesthat the root lock doesn't install (it's not a workspace member, and not afile:dependency, since then the root lock would list the copy and the skip wouldn't apply) is still an independent npm project, lock or not. Its copy should getnested_project_skipped(or at least the hosted-style warning and skip), with no effect on exit or the in-run VEX. CLI_CONTRACT.md reservespartialFailurefor packages the run should have patched and couldn't. - Actual: exit 1 on every run, no in-run VEX, and a remedy that can't clear it. Only a nested project that has a lockfile is recognised.
Matrix
| OS | npm / Node | nested shape | vendored root scan | hosted root scan |
|---|---|---|---|---|
| Linux | 10.9.4 / 22.22 | --no-package-lock |
fail (×2 runs + --vex) |
pass (warn, exit 0) |
| Linux | 10.9.4 / 22.22 | .npmrc package-lock=false |
fail (×2 + --vex) |
— |
| Linux | 8.19.4 / 22.22 | both shapes | fail (×2 + --vex) |
pass |
| Linux | 12.2.0 / 22.22 | both shapes | fail (×2 + --vex) |
pass |
| Linux | 10.9.4 / 22.22 | own package-lock.json |
pass (#1543) | — |
| Linux | 10.9.4 / 22.22 | npm-shrinkwrap.json only |
pass (#1543) | — |
| macOS / Windows | — | — | untested (probe branches unavailable); the logic is path-only and OS-independent |
First bad: pre-existing (the #1534 behaviour, which #1543 left in place for lockless nested projects).
Suspect code
crates/socket-patch-core/src/hosted/governing_root.rs:487:nested_npm_projectonly returns a directory that hashas_own_npm_family_lock(&dir). A directory with apackage.json(not a workspace member) between the root and the copy'snode_modulesisn't counted.crates/socket-patch-cli/src/commands/scan/discovery.rs:633: sonested_project_skipskeeps the package as the root's, and the root npm vendor backend refuses it (crates/socket-patch-core/src/vendor/npm_lock.rs,vendor_lock_entry_not_found).
🤖 Generated with Claude Code
Activity
npm: run 42 ledger (#1554)
mikolalysenko commentedon Oct 15, 2026
[agent] Triaged as
priority:p1(npm). Confirmed onmaine719a9b:nested_npm_projectincrates/socket-patch-core/src/hosted/governing_root.rs(~line 471) returns a nested directory only whenhas_own_npm_family_lock(&dir)holds, so a lockless nested project (package-lock=false) is not recognised and its copies stay with the root vendor backend, which failsvendor_lock_entry_not_found. Not a duplicate: #1534 / #1543 cover only nested projects that carry their own lock. No open PR addresses it yet.Generated by Claude Code
mikolalysenko commentedon Oct 15, 2026
[agent] Claiming this issue (shared root cause:
nested_npm_projectonly recognises a nested npm project that has its own lockfile). Branch: agent/fix-lockless-nested-npm-project. Claim-ID: 2026-10-15T20:20:56Z-8a1878Generated by Claude Code
mikolalysenko commentedon Oct 15, 2026
[agent] Draft PR: #1591
Generated by Claude Code
Skip lockless nested npm projects in root scans