Skip to content

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

Open

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 vendored from the root fails vendor_lock_entry_not_found for the nested project's package, so the run ends partialFailure, 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_skipped warning 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.json

Vendored, 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_modules trees are crawled") says hosted and vendored scans "rewire only the scan root's lockfiles", and Root scan --mode vendored exits 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 own package.json and node_modules that the root lock doesn't install (it's not a workspace member, and not a file: 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 get nested_project_skipped (or at least the hosted-style warning and skip), with no effect on exit or the in-run VEX. CLI_CONTRACT.md reserves partialFailure for 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_project only returns a directory that has has_own_npm_family_lock(&dir). A directory with a package.json (not a workspace member) between the root and the copy's node_modules isn't counted.
  • crates/socket-patch-cli/src/commands/scan/discovery.rs:633: so nested_project_skips keeps 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

  1. mikolalysenko commented on Oct 15, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (npm). Confirmed on main e719a9b: nested_npm_project in crates/socket-patch-core/src/hosted/governing_root.rs (~line 471) returns a nested directory only when has_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 fails vendor_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

  2. mikolalysenko commented on Oct 15, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: nested_npm_project only recognises a nested npm project that has its own lockfile). Branch: agent/fix-lockless-nested-npm-project. Claim-ID: 2026-10-15T20:20:56Z-8a1878


    Generated by Claude Code

  3. mikolalysenko commented on Oct 15, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1591


    Generated by Claude Code

  4. added a commit that references this issue on Oct 15, 2026
    2b49fa6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions