Skip to content

Agent-mode scan ignores socket.yml includePaths / ignorePaths (and the built-in tests/ default) for nested npm projects, patching every nested project's node_modules #554

Description

[agent] Found by the scheduled npm bug-hunt routine (ledger #302). The Bun routine first reported it as a handover (Bun nested projects); I've reproduced it with real npm projects.

Summary

Run socket-patch scan --mode agent at a repo root that holds independent nested npm projects, each with its own package-lock.json and node_modules. The crawler patches the installed copies in every nested project. The socket.yml path policy is only evaluated once, for the scan root, so patches.ignorePaths, patches.includePaths, projectIgnorePaths and the built-in test/ tests/ fixtures/ … defaults never exclude a nested project. The JSON policy block reports counts: {filtered: 0, retained: 0}, so nothing tells the user that the policy wasn't applied.

Hosted and vendored modes honour the same file. scan '*/*' --mode hosted skips services/legacy with /services/ (patches.ignorePaths).

Impact

  • In agent mode, a repo can't use path policy to keep socket-patch out of a legacy service, an example, or a test fixture project. Those projects' node_modules get rewritten in place anyway. The docs' own headline example (includePaths: ["apps/**", "services/**"], ignorePaths: ["services/legacy/**"]) does nothing in agent mode.
  • includePaths: ["/*", "!/*/"], documented as "targets only the repo-root project", still patches every nested project.
  • Default-ignored fixture projects under tests/ get patched, which can break fixture tests that pin the upstream bytes.
  • Agent PATH globs (scan 'services/**' --mode agent) also report filtered: 0. Policy is never consulted per copy.

Repro (Linux, main 61cfb9b, npm 10.9.4 / Node 22 and npm 12.2.0 / Node 24.21; reproduced 3× on npm 10 and 1× on npm 12)

The patch API is a local mock (the one described in ledger #302) serving patches for left-pad@1.3.0, is-number@6.0.0 and is-number@7.0.0. Any patch source works.

mkdir repo && cd repo && git init -q
mk(){ mkdir -p $1; (cd $1; echo "{\"name\":\"$(basename $1)\",\"version\":\"1.0.0\",\"private\":true,\"dependencies\":{$2}}" > package.json; npm install --no-audit --no-fund); }
mk .               '"left-pad":"1.3.0"'
mk apps/web        '"is-number":"6.0.0"'
mk services/legacy '"left-pad":"1.3.0","is-number":"7.0.0"'
mk tests/e2e       '"is-number":"6.0.0"'
printf 'version: 2\npatches:\n  ignorePaths: ["/services/"]\n' > socket.yml
socket-patch scan --mode agent --yes --json > scan.json      # exit 0
jq -c .policy.counts scan.json                              # {"filtered":0,"retained":0}
head -c 21 services/legacy/node_modules/left-pad/index.js    # /* SOCKET-PATCHED */  <- should be untouched
head -c 21 tests/e2e/node_modules/is-number/index.js         # /* SOCKET-PATCHED */  <- default-ignored dir

With includePaths: ["/*", "!/*/"] instead, all five copies are patched too (the root, apps/web, both services/legacy copies and tests/e2e).

Control, using the same tree and the same socket.yml:

socket-patch scan '*/*' --mode hosted
  == services/legacy ==
  Policy (socket.yml): 1 skipped by filters, 0 patched packages held.
    skipped project services/legacy: /services/ (patches.ignorePaths)
# apps/web lock is rewired; services/legacy and tests/e2e locks untouched

Expected vs actual

  • CLI_CONTRACT.md, "socket.yml patch policy" → Paths: path lists "are matched against a project root's marker files … the lockfiles in the root's directory". A root is ignored iff every marker is ignored, and includePaths: ["/*", "!/*/"] "targets only the repo-root project". The built-in defaults "apply only to discovered roots". docs/configuration.md says includePaths / ignorePaths "include or exclude project roots by their dependency-file paths".
  • services/legacy/package-lock.json is a project root marker that matches /services/. Copies installed under that root should be filtered (and reported in policy.filtered[]), or at minimum the agent run should warn that path policy doesn't apply to nested roots.
  • Actual: every nested project is patched, with filtered: 0 and exit 0.

Matrix (Linux)

npm / Node ignorePaths: ["/services/"] includePaths: ["/*","!/*/"] tests/ default hosted '*/*' control
10.9.4 / 22.22 fail (3/3) fail fail pass
12.2.0 / 24.21 fail (1/1) not run fail not run

macOS and Windows weren't probed. The defect is in the OS-independent policy wiring. Bun projects reproduce it too (Bun routine handover, ledger #306), so it applies to the whole npm-family crawler.

First bad release: none. socket.yml patch policy is new in v5 (#293); v4.0.0 has no policy.

Suspect code

  • crates/socket-patch-cli/src/commands/scan/mod.rs:1472: a non-hosted/vendored scan builds one ScanPolicy::for_root(invocation, &args.common.cwd, …) for the whole run.
  • crates/socket-patch-cli/src/commands/scan/policy.rs:141: for_root evaluates admits_root once, for the cwd ("disk scans run one root per run_scan"). The agent crawl, though, descends into nested projects' node_modules, so their copies are never judged against their own root's markers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions