GeezerVison is showing github.com — npm crawler never looks in Rush's common/temp store: hosted `vex` attests a transitive dep not_affected while its installed copy is unpatched, and agent apply reports it package_not_installed · Issue #518 · SocketDev/socket-patch
npm crawler never looks in Rush's common/temp store: hosted vex attests a transitive dep not_affected while its installed copy is unpatched, and agent apply reports it package_not_installed #518Description[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
In a Rush monorepo, pnpm installs every package into common/temp/node_modules/.pnpm/…. Each project's node_modules holds only symlinks to its direct dependencies, and there's no .modules.yaml beside them. The npm crawler's workspace walk skips any directory named temp (SKIP_DIRS in crates/socket-patch-core/src/crawlers/npm_crawler.rs:24). So common/temp/node_modules is never crawled, and a transitive dependency that is installed there counts as "not installed". scan from the repo root lists it as notInstalled: true (lockfile-only, found through the Rush lock fallback).
This has two effects:
- Hosted
vex attests without installed evidence. After scan --mode hosted pins common/config/rush/pnpm-lock.yaml, vex finds no installed copy. So it falls back to the not-installed lockfile basis and writes not_affected / inline_mitigations_already_exist, even though common/temp/node_modules/.pnpm/[email protected]/… still holds the upstream bytes. On a plain pnpm project in the same state (lock pinned, tree not yet reinstalled), vex correctly omits the patch (not_applied, "No applied patches … to attest").
- Agent mode can't patch Rush transitive deps.
apply with a manifest entry for the installed transitive package reports skipped / package_not_installed (partialFailure), and the store copy stays unpatched. This failure is loud, not silent.
Direct dependencies aren't affected: apps/app/node_modules/is-number is crawled. With is-number as a direct dep in the same stale state, vex correctly reports not_applied.
Impact
- The VEX document claims
not_affected for a vulnerable package whose installed bytes are unpatched. That's the case CLI_CONTRACT's hosted VEX row rules out: "Installed evidence wins: hash_mismatch / not_applied are omitted. With nothing installed, a discovered reference whose lock pins the artifact … attests from that pin." Here something is installed, but the crawler never looked. It's the same window the redirect_pnpm_trust_lockfile warning tells users to check with vex ("Run socket-patch vex after installation to verify the patched files"), so on Rush that check passes before the install has happened, and it would also pass over a tampered or reverted store copy.
- Agent mode doesn't work for any transitive dependency in a Rush repo.
- Note:
rush update / rush install (including --purge) do pick up the hosted pin and install patched bytes, so the hosted rewrite itself works. The problem is the evidence check.
Repro (Linux, pnpm via Rush 5.180.0)
npm i -g @microsoft/rush@5
mkdir rr && cd rr && git init -q && rush init
# rush.json: "pnpmVersion": "9.15.9" (also reproduced with "8.15.9");
# "projects": [{"packageName":"app","projectFolder":"apps/app"}]
mkdir -p apps/app
echo '{"name":"app","version":"1.0.0","dependencies":{"to-regex-range":"5.0.1"}}' > apps/app/package.json # [email protected] transitively
rush update
head -c 14 common/temp/node_modules/.pnpm/[email protected]/node_modules/is-number/index.js # upstream bytes
socket-patch scan --json # [email protected] -> "notInstalled": true, lockfileOnlyPackages: 1
socket-patch scan --mode hosted --json --yes # success, redirected 1, rewrittenFiles [common/config/rush/pnpm-lock.yaml]
socket-patch vex --output vex.json --product pkg:npm/[email protected]
# -> "Wrote OpenVEX document with 1 statement": not_affected / inline_mitigations_already_exist
head -c 14 common/temp/node_modules/.pnpm/[email protected]/node_modules/is-number/index.js # still upstream
Agent-mode twin: stage a .socket/manifest.json (plus blobs) for pkg:npm/[email protected] whose beforeHash matches the installed index.js. Then socket-patch apply --json → partialFailure, event skipped / package_not_installed ("No installed package matches this PURL"), and the file is unchanged.
The patch data came from a local mock of the patch API (batch / package grant / view / tarball routes, SOCKET_PATCH_SERVER_URL + SOCKET_NPM_REGISTRY pointed at it), the same shape as tests/e2e_redirect_pnpm_build.rs.
Expected vs actual
|
Expected (CLI_CONTRACT: hosted VEX, "installed evidence wins") |
Actual |
scan from the Rush root |
[email protected] found installed under common/temp |
notInstalled: true |
hosted vex with the lock pinned and the store unpatched |
omitted, not_applied (like plain pnpm) |
not_affected |
agent apply for an installed transitive dep |
applied to the common/temp/.pnpm copy |
package_not_installed |
Matrix
| OS |
Rush |
pnpm (lock) |
hosted vex false attestation |
agent apply transitive |
direct dep |
| Linux |
5.180.0 |
9.15.9 (9.0) |
reproduces (2/2) |
reproduces |
pass (not_applied) |
| Linux |
5.180.0 |
8.15.9 (6.0) |
reproduces |
untested |
untested |
| macOS / Windows |
|
|
untested (no probe branch this run) |
|
|
Tested on main 61cfb9b (CLI 4.0.0 workspace version). Not bisected: the not-installed lockfile basis arrived with the v5 manifest-less VEX (#251), and "temp" has been in SKIP_DIRS for a long time.
Suspect code
crates/socket-patch-core/src/crawlers/npm_crawler.rs:24: SKIP_DIRS contains "temp", and workspace_children (~line 1500) applies it, so common/temp/node_modules is never reached. A Rush-aware root (when rush.json is present, also crawl common/temp/node_modules, including its .pnpm store) would fix both symptoms. The hosted engine already special-cases Rush locks (hosted/engine.rs rush_repo).
crates/socket-patch-cli/src/commands/vex_consumed.rs: an empty copy list is read as "not installed" and the lockfile basis excuses it. That's only safe when the crawl could have seen the store.
Activitymikolalysenko commented on Oct 2, 2026 CollaboratorAuthor More actions [agent] Triaged: priority:p1 (pnpm, npm family). Not a duplicate, and no open fix PR.
Shares root cause with #493: the npm crawler's local root discovery (NpmCrawler::find_local_node_modules_dirs in crates/socket-patch-core/src/crawlers/npm_crawler.rs) only collects directories literally named node_modules that the workspace walk reaches. It never asks the package manager where the install root actually is, so Rush's common/temp/node_modules (cut off by "temp" in SKIP_DIRS) and yarn classic's --modules-folder are both missed. That causes both the agent package_not_installed and the hosted vex lockfile-basis false attestation. Will be fixed together.
Related but separate: #519 (PnP has no installed tree for the crawler to find at all; that fix belongs in the vex.rs lockfile-basis excuse).
Generated by Claude Code mikolalysenko commented on Oct 2, 2026 CollaboratorAuthor More actions [agent] Claiming this issue (with #493; shared root cause: npm crawl-root discovery ignores package-manager-configured install roots). Branch: agent/fix-npm-crawler-configured-roots. Claim-ID: 2026-10-02T01:21:13Z-dc19eb
Generated by Claude Code mikolalysenko commented on Oct 2, 2026 CollaboratorAuthor More actions
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|