GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — npm agent-mode apply never patches an npm-aliased install (lp@npm:left-pad), yet VEX attests the package not_affected · Issue #356 · SocketDev/socket-patch Closed Labels agent:triagedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:npmnpmnpmpriority:p1 Description[agent] Found by the scheduled npm bug-hunt routine (ledger #302). Summarynpm aliases (
Vendored mode handles the same alias correctly: Impact
Repro (Linux, main
|
| npm 6.14.18 (lock v1) | npm 8.19.4 (v2) | npm 10.9.7 (v3) | npm 12.1.0 (v3) | |
|---|---|---|---|---|
Linux, main f6b7fb9 |
reproduces | reproduces | reproduces (workspace root + alias, VEX not_affected) |
reproduces ×2 (alias-only, mixed + VEX) |
| Linux, release 4.0.0 | reproduces (alias-only) | |||
| Linux, release 3.3.0 | reproduces (alias-only) | |||
| macOS / Windows | not probed (resolver logic is OS-independent) |
Not a regression: 3.3.0 and 4.0.0 behave the same.
crates/socket-patch-core/src/crawlers/npm_crawler.rs:797 (find_by_purls): the resolver target's dir_key is built from the purl name, so only node_modules/<purl-name> is ever probed.crates/socket-patch-core/src/crawlers/npm_crawler.rs:978-985 (visit_resolver_dir): it also requires found_name == target.dir_key. That correctly rejects a different package living under the purl's directory name, but it means a copy whose directory name differs from its package.json name (an alias) can never match. The vendored lock rewriter already resolves alias edges by the entry's name (vendor/npm_lock.rs:882), so the two modes disagree.[agent] Triaged: priority:p1 (npm). This isn't a duplicate, and I found no existing fix PR. The cause is in agent-mode find_by_purls (crawlers/npm_crawler.rs), which resolves a purl by its node_modules/<name> path, so it never reaches an alias directory. That's separate from the in-flight npm lock work in #337 and #345.
Generated by Claude Code
[agent] Cross-OS probe (main f6b7fb9 built on each runner, Node 22): reproduces on every OS that finished.
| OS | npm 10.9.4 | npm 12.1.0 |
|---|---|---|
| ubuntu-latest | left-pad patched, lp unpatched, VEX not_affected |
same |
| macos-latest | same | same |
| windows-latest | (still running at time of writing) | same |
Run: https://github.com/SocketDev/socket-patch/actions/runs/36758644937
Generated by Claude Code
[agent] This also reproduces under Bun (the Bun bug-hunt routine, ledger #306). Tested on main f6b7fb9, bun 1.4.2, Linux.
{"left-pad":"1.3.0","lp":"npm:[email protected]"} (bun writes "lp": ["[email protected]", "", {}, "sha512-…"]): scan --mode agent returns success/exit 0 and patches node_modules/left-pad only. node_modules/lp/index.js stays unpatched, and vex (with setup.manual: ["npm"]) attests pkg:npm/[email protected] not_affected.apply gives partialFailure / exit 1, package_not_installed, which matches the npm alias-only cell.Same code path (find_by_purls), so no separate Bun issue.
Generated by Claude Code
[agent] Re-triage on main 2463257 (the v5 consolidation, #277): still reproduces, Linux, npm 10.9.7.
"lp": "npm:[email protected]", with a free patch for pkg:npm/[email protected] served by a local mock of the patch API:
scan --mode agent --download-mode file exits with partial_failure. It discovers pkg:npm/[email protected] (rollout new: 1), but node_modules/lp/index.js stays unpatched.apply --json returns partialFailure, exit 1, with {"action":"skipped","purl":"pkg:npm/[email protected]","errorCode":"package_not_installed"}.Hosted mode (the v5 default) and vendored mode handle the same alias correctly: the redirect is written, a fresh npm ci installs the patched bytes, vex returns not_affected, and rollback restores the lock byte for byte. So the defect is now limited to agent mode.
Generated by Claude Code
[agent] Re-triage on main 61cfb9b, which includes the crawler change for relocated stores (#365): still reproduces. Linux, npm 10.9.4.
{"lp":"npm:[email protected]"}, with a free patch for pkg:npm/[email protected] served by a local mock of the patch API:
scan --mode agent --json --yes: partial_failure, exit 1, found: 1, downloaded: 1, applied: 0. node_modules/lp/index.js stays the original.apply --json: partialFailure, [('skipped', 'package_not_installed')].Generated by Claude Code
[agent] This also reproduces under pnpm with node-linker=hoisted (the pnpm bug-hunt routine, ledger #303). Tested on main 61cfb9b, Linux, Node 22. Reproduced twice per version.
{"isnum":"npm:[email protected]","to-regex-range":"5.0.1"} with node-linker=hoisted (in .npmrc, plus nodeLinker: hoisted in pnpm-workspace.yaml on 11+). pnpm writes two physical copies: node_modules/is-number (hoisted for to-regex-range) and node_modules/isnum (the alias). The patch is a hand-staged agent manifest for pkg:npm/[email protected] (marker prepended to index.js), with a GHSA attached.
| pnpm | apply --json |
node_modules/is-number |
node_modules/isnum (what require('isnum') loads) |
vex |
|---|---|---|---|---|
| 9.15.9 | success, applied 1 |
patched | unpatched | — |
| 10.34.5 | success, applied 1 |
patched | unpatched | — |
| 12.8.1 | success, applied 1 |
patched | unpatched | not_affected for pkg:npm/[email protected] |
pnpm's default isolated linker is not affected: the alias symlinks into the shared .pnpm/[email protected] directory, so the one copy gets patched (pass on 7.33.7 / 9.15.9 / 10.34.5 / 11.28.3 / 12.8.1). Hosted mode handles the alias correctly on pnpm 7–12 (ledger #303, run 7). It's the same find_by_purls path as above, so I haven't opened a separate pnpm issue.
mkdir ha && cd ha echo '{"name":"ha","version":"1.0.0","private":true,"dependencies":{"isnum":"npm:[email protected]","to-regex-range":"5.0.1"}}' > package.json echo node-linker=hoisted > .npmrc pnpm install # stage .socket/manifest.json + blobs for pkg:npm/[email protected] (index.js), then: socket-patch apply --json # success, applied 1 head -c 20 node_modules/is-number/index.js # patched head -c 20 node_modules/isnum/index.js # original
Generated by Claude Code
[agent] Re-triage on main 203e092, which now includes #517 (agent vex checks every installed copy) and #555 (the #403 fix): still reproduces, and the scan exit code got less helpful. Linux, npm 10.9.4, local mock patch API for pkg:npm/[email protected], 2/2 runs.
{"left-pad":"1.3.0","lp":"npm:[email protected]"}: scan --mode agent --yes --json gives success, exit 0. node_modules/left-pad is patched, node_modules/lp stays the original, and vex still exits 0 with not_affected for pkg:npm/[email protected]. Fix agent vex checking only one installed copy (#516) #517 doesn't cover it, because the alias directory is never one of the "installed copies". apply --json afterwards gives partialFailure.{"lp":"npm:[email protected]"}: scan --mode agent now returns success, exit 0. On 61cfb9b it returned partial_failure, exit 1. The manifest records the patch, but nothing is applied. Standalone apply --json still gives partialFailure, exit 1. vex refuses (package_not_found), so at least that case no longer attests.The exit-0 change in agent scan lines up with #555 treating skipped patched deps as non-failures.
Generated by Claude Code
[agent] Claiming this issue (shared root cause: agent-mode find_by_purls only probes node_modules/<purl-name> and requires dir name == package.json name, so npm-alias install dirs are never treated as copies of the purl). Branch: agent/fix-npm-agent-alias-copies. Claim-ID: 2026-10-04T01:21:12Z-75c940
Generated by Claude Code