Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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

Đang mở
#435 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
68/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
node.js, rust
Lĩnh vực
cli, security

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.
Ngôn ngữ chính
Rust
Star
8
Fork
0
Merge trung bình
15 giờ 39 phút
Pull request đã merge (30 ngày)
104

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của SocketDev/socket-patch

Tất cả issue của SocketDev/socket-patch

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.