Skip to content

On Windows, scan -g / get -g / vex -g find no global npm packages because npm root -g is spawned as bare npm, which never resolves to npm.cmd #434

Description

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

Summary

On Windows, global mode never finds a globally installed npm package. This holds for the default prefix and for a custom npm_config_prefix, on npm 10 and 12, from Git Bash and from PowerShell. Only an explicit --global-prefix <dir> works.

  • scan -g -e npm prints "No global packages found." and exits 0 success with scannedPackages: 0, even though npm root -g lists the package.
  • get <uuid> -g returns partial_failure (exit 1) and patches nothing.
  • vex -g omits the patch as package_not_found.
  • SOCKET_GLOBAL=1 behaves the same way.

It's the same mechanism as #421 (RubyGems / gem.cmd), but on the npm path.

Root cause

get_npm_global_prefix_with (crates/socket-patch-core/src/crawlers/npm_crawler.rs:487) runs runner.run("npm", &["root", "-g"]) through SystemCommandRunner. That calls std::process::Command::new("npm") on the bare name (crates/socket-patch-core/src/utils/process.rs:138). On Windows, std resolves a bare program name to .exe only, so the npm.cmd shim is never found. The spawn fails, get_global_node_modules_paths (npm_crawler.rs:1145) adds nothing, and there's no Windows fallback (only macOS has hard-coded fallbacks). The pnpm, yarn and bun probes next to it use the same bare spawn.

The repo already has the right helper: resolve_tool / command_for in utils/process.rs, which honours PATHEXT and spawns .cmd shims safely.

Impact

This is a silent miss on the most common Windows setup. The maintainer checklist for global mode requires that scan -g "must find every globally installed npm package that has a hosted patch: none missing". Instead, a Windows user (or Windows CI) running scan -g is told there's nothing to patch, with exit 0. get -g fails, and a VEX for global tools can't be produced. Linux and macOS pass the identical probe.

Repro (probe runs on GitHub Actions)

The patch API is a local mock serving a free patch for pkg:npm/left-pad@1.3.0. SPA="--api-url http://127.0.0.1:8765 --org o --api-token fake --patch-server-url http://127.0.0.1:8765".

npm install -g left-pad@1.3.0
npm root -g                                  # C:\npm\prefix\node_modules  (contains left-pad)
socket-patch scan -g -e npm $SPA --json      # status success, scannedPackages 0, packages []   <- bug
socket-patch scan --global-prefix "$(npm root -g)" -e npm $SPA --json   # packages: [pkg:npm/left-pad@1.3.0]   <- works
socket-patch get 1732b55e-2d2d-57b9-95b7-ce027791a596 -g $SPA --download-mode file   # partial_failure, exit 1, nothing patched
# custom prefix: npm install -g --prefix D:\…\gp left-pad@1.3.0; npm_config_prefix=D:\…\gp  -> npm root -g = D:\…\gp\node_modules; scan -g still finds nothing

Same result from pwsh with socket-patch.exe scan -g -e npm … --json: scannedPackages: 0.

Expected vs actual

  • Expected: CLI_CONTRACT.md documents --global / -g as "Operate on globally-installed packages", with --global-prefix defaulting to "(auto)", i.e. discovered via npm root -g. Auto-detection should find C:\npm\prefix\node_modules (or %APPDATA%\npm\node_modules), just as it does on Linux and macOS. If it can't determine the prefix, it should say so loudly instead of reporting a clean, empty scan.
  • Actual: auto-detection silently finds nothing on Windows.

Matrix (main 2463257, Node 24.15.0)

Runner npm scan -g (default prefix) scan -g (custom npm_config_prefix) get -g vex -g --global-prefix scan / get / rollback
windows-latest 10.9.7 FAIL (0 packages, exit 0) FAIL FAIL (exit 1) FAIL pass
windows-latest 12.1.0 FAIL FAIL FAIL FAIL pass
windows-2022 10.9.7 FAIL FAIL FAIL n/a pass
windows-2022 12.1.0 FAIL FAIL FAIL n/a pass
ubuntu-latest 10.9.7 / 12.1.0 pass pass pass pass pass
macos-latest 10.9.7 / 12.1.0 n/a pass pass pass n/a
Linux sandbox 10.9.7 / 12.1.0 pass pass pass pass pass

Probe runs: https://github.com/SocketDev/socket-patch/actions/runs/36825128447 (3 OS × npm 10/12, the hosted cycle plus global mode) and https://github.com/SocketDev/socket-patch/actions/runs/36826141655 (Windows-focused: default prefix, custom prefix, explicit prefix, PowerShell).

Activity

  1. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] The Yarn classic (1.x) bug-hunt routine (ledger #304) confirms the same miss on the yarn probe (get_yarn_global_prefix_with → bare yarn, so yarn.cmd is never found).

    Probe https://github.com/SocketDev/socket-patch/actions/runs/36827174726 ran on main 2463257, with yarn installed via npm i -g yarn@<v> and a mock patch API serving pkg:npm/left-pad@1.3.0:

    yarn global add left-pad@1.3.0 is-number@7.0.0
    yarn global dir      # C:\Users\runneradmin\AppData\Local\Yarn\Data\global   (packages present)
    socket-patch scan -g …              # left-pad / is-number not reported         -> scan_report FAIL
    socket-patch scan -g --mode agent … # exit 0, global copy left unpatched        -> agent_apply FAIL
    SOCKET_GLOBAL=1 socket-patch scan --mode agent …                                -> FAIL
    socket-patch vex -g …               # exit 2, nothing attested                  -> FAIL
    windows-latest scan -g agent apply vex -g SOCKET_GLOBAL=1
    yarn 1.10.1 FAIL FAIL FAIL FAIL
    yarn 1.22.22 FAIL FAIL FAIL FAIL

    The identical script passes on ubuntu-latest and macos-latest for yarn 1.10.1 and 1.22.22. A fix through resolve_tool should cover the yarn (and pnpm / bun) probes in npm_crawler.rs as well as npm. Yarn 1.0.x has a separate, non-Windows problem (no yarn global dir subcommand), filed as #437.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Shares root cause with #421, #438, #440: the global package-manager probes go through SystemCommandRunner::run, which spawns the bare tool name with Command::new(bin) (no PATHEXT, so gem.cmd / npm.cmd / yarn.cmd / composer.bat are never found on Windows) and inherits the project cwd (so a Berry project's global script runs). Will be fixed together. Triage: priority:p1 (npm-family).

    [agent] Claiming this issue (with #421, #438, #440; shared root cause: global PM probes spawn the bare tool name from the project cwd via SystemCommandRunner). Branch: agent/fix-global-probe-tool-spawn. Claim-ID: 2026-10-01T07:21:08Z-824845


    Generated by Claude Code

  3. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Fix in progress: #442


    Generated by Claude Code

  4. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] New data from the Bun bug-hunt routine (ledger #306): the Bun probe hits the same failure, now confirmed with a real install.

    On windows-latest I ran npm install -g --prefix <dir> bun@<ver>, so PATH holds only the npm shims (bun, bun.cmd, bun.ps1) and no bun.exe. Then bun add -g semver@7.6.0 is-number@7.0.0 into $BUN_INSTALL:

    • scan -g --json gives success with packages: [], and scan -g --mode agent exits 0 with "No patches available". get -g exits 1, and vex -g has nothing to attest.
    • That holds on Bun 1.3.14 and 1.4.2. The same layout passes on ubuntu-latest and macos-latest.
    • A standalone Bun install (bun.exe on PATH) passes on Windows. So it's only the npm-shim case, which fits the bare Command::new("bun") in get_bun_global_prefix_with (npm_crawler.rs:567).

    Run: https://github.com/SocketDev/socket-patch/actions/runs/36830650075 (cell npm_installed_bun).

    There's a separate Bun bug that #442 doesn't cover: the bun pm bin -g → ../install/global/node_modules derivation is wrong whenever BUN_INSTALL_BIN / BUN_INSTALL_GLOBAL_DIR is set. That's filed as #443.


    Generated by Claude Code

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