Hacktoberfest 2026: die Issues, die Maintainer für den Oktober markiert haben – offen und einsteigerfreundlich. Hacktoberfest-Issues durchsuchen

Global agent mode on pnpm 12 (and 11 without the global virtual store) patches only one of the per-install copies of a package, reports success, and VEX attests not_affected

Offen
#435 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Maintainer antworten meist innerhalb von 1 Tag

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Anfängerfreundlichkeit
68/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
node.js, rust
Bereich
cli, security

Rechercherichtung

Start with crates/socket-patch-core/src/crawlers/npm_crawler.rs, especially resolve_pending_targets at line 922, get_global_node_modules_paths at 1135/1148, and find_store_peer_variant_copies at 1913. Reproduce the two pnpm global installs on /dev/shm using the issue's mock API setup, then compare the crawler with the project-mode layout and the e2e patterns in crates/socket-patch-cli/tests/e2e_redirect_pnpm_build.rs. Done means every physical copy is patched and get, apply, and vex no longer report success or not_affected while a copy remains unpatched.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

agent:triaged bug bughunt pm:pnpm priority:p1

[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).

Summary

pnpm 11+ isolates global installs. Each pnpm add -g … command gets its own install directory, $PNPM_HOME/global/v11/<hash>/, with its own node_modules and, on pnpm 12 (or pnpm 11 with enableGlobalVirtualStore: false), its own node_modules/.pnpm virtual store. pnpm root -g returns the parent global/v11, and socket-patch uses that one directory as the global root.

When two global install groups both contain [email protected] (a direct pnpm add -g [email protected], plus a global tool that depends on left-pad), get -g / apply -g / scan -g --mode agent patch only one of the two physical copies. The tool keeps loading the unpatched copy. The run exits 0 with status: success, applied: 1, a re-run of apply -g says already_patched, and vex -g attests not_affected.

Which copy is missed depends on directory-listing order. When the group holding the direct left-pad link is listed first, the other group's .pnpm/[email protected] store entry is never probed. On ext4 that's about half of fresh setups; on tmpfs it's deterministic (see the repro).

Impact

A globally installed tool (any CLI installed with pnpm add -g) keeps running a vulnerable dependency while socket-patch reports it patched and emits a VEX statement saying the vulnerability isn't exploitable. Nothing in the JSON or on stderr shows that a copy was skipped.

Repro (Linux, pnpm 12.8.2, Node 22)

Uses /dev/shm so the listing order is deterministic. A local mock of the patch API serves a patch for pkg:npm/[email protected] that prepends /*SOCKET_PATCHED*/ to index.js (batch / by-package / view routes with inline blob content, as in crates/socket-patch-cli/tests/e2e_redirect_pnpm_build.rs).

# a global "tool" that depends on left-pad 1.3.0
mkdir wrap && cd wrap
echo '{"name":"lp-wrapper","version":"1.0.0","bin":{"lpw":"cli.js"},"dependencies":{"left-pad":"1.3.0"}}' > package.json
printf '#!/usr/bin/env node\nconsole.log(require("fs").readFileSync(require.resolve("left-pad"),"utf8").slice(0,18))\n' > cli.js
npm pack && cd ..

base=/dev/shm/g; mkdir -p $base/home/bin
export PNPM_HOME=$base/home HOME=$base PATH=$base/home/bin:$PATH
pnpm add -g ./wrap/lp-wrapper-1.0.0.tgz     # group 1: left-pad is transitive (only in its .pnpm)
pnpm add -g [email protected]                 # group 2: left-pad is a direct global package
pnpm root -g                               # $base/home/global/v11
find $base/home -path '*left-pad/index.js' # two copies, one per global/v11/<hash>/node_modules/.pnpm

mkdir w && cd w
socket-patch get pkg:npm/[email protected] -g --yes --json --api-url $MOCK --org test-org --api-token fake
#   exit 0, "status": "success", "applied": 1
lpw                                        # "/* This program is"   <- the tool still loads the original bytes
socket-patch apply -g --json --offline     # success, left-pad: skipped / already_patched
socket-patch vex -g --offline --product pkg:npm/[email protected] --output v.json
#   exit 0, one statement: not_affected, subcomponent pkg:npm/[email protected]

If you install the two groups in the opposite order, both copies are patched.

Expected vs actual

  • Expected: agent mode patches every physical copy of a name@version. find_by_purls documents this ("Returns every physical copy … patching only one leaves a live, vulnerable copy while reporting success (a silent partial)"), as do docs/ecosystems.md (npm row: "any install layout … every store copy") and the agent-mode notes ("apply and rollback patch every store copy of a name@version"). VEX should not attest a patch that a loaded copy doesn't carry.
  • Actual: one copy is patched, the other stays original, and every command reports success.

OS × version (Linux, Node 22, main 2463257)

pnpm global layout copies of [email protected] result
10.34.5 single global/5, shared .pnpm 1 pass
11.0.0 / 11.28.3 (default) per-install dirs, global virtual store (store/v11/links) 1 shared copy pass here (the shared-store write is #361's problem)
11.28.3, enableGlobalVirtualStore: false per-install .pnpm 2 fail (2 of 3 ext4 runs)
12.4.2 per-install .pnpm 2 fail
12.8.1 per-install .pnpm 2 fail (1 of 3 ext4 runs; order-dependent)
12.8.2 per-install .pnpm 2 fail: tmpfs 4/4 in the order above; on a fixed ext4 layout, 3/3 re-runs fail
12.8.2, groups installed in the opposite order per-install .pnpm 2 pass

macOS and Windows weren't probed (no probe branch this run; see the ledger). The walk order there is filesystem-dependent too, so the miss should be possible there as well.

First bad commit

This is a regression. On the same ext4 layout, release 4.0.0 patched both copies in 3/3 clean runs, and main missed one in 3/3. Bisected on the deterministic tmpfs layout:

  • 0b4e645 (b32711f^): pass
  • b32711f "Support vlt in hosted, vendored and agent modes (#269)": first bad (reproduced twice)
  • f6b7fb9, 2463257 (main): fail. Draft PR #365's head 80f4a71 also fails.

Suspect code

  • crates/socket-patch-core/src/crawlers/npm_crawler.rs:922: in resolve_pending_targets, unmatched_names is computed walk-wide. Once any importer tree has matched left-pad (group 2's direct link), every later pnpm store's entries for that name are filtered out, including a different install's own .pnpm that holds a separate physical copy.
  • npm_crawler.rs:972: since b32711f, inside a store entry only a real directory matches (is_real_package_dir_sync). Before that, group 1's .pnpm/lp-wrapper…/node_modules/left-pad link probably matched, which is how 4.0.0 reached the second copy.
  • find_store_peer_variant_copies (npm_crawler.rs:1913) only fans out within the found copy's own virtual store, so it can't recover the other install's copy.
  • get_global_node_modules_paths (npm_crawler.rs:1135, :1148) passes pnpm 11+'s global/v11 as one root. Treating each global/v11/<hash>/node_modules as its own root (as for separate projects) would also avoid the cross-install filter. The analogous project-mode layout (a sharedWorkspaceLockfile: false workspace, where each package has its own .pnpm) patches both copies correctly on pnpm 10.34.5 and 12.8.2, because those are separate roots.
Vorherrschende Sprache
Rust
Sterne
8
Forks
0
Ø Merge
18 Std. 4 Min.
Gemergte PRs (30 T.)
70

Entwicklungsumgebung

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus SocketDev/socket-patch

Alle Issues in SocketDev/socket-patch

Ähnliche Issues

Weitere Issues zu Rust

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.