Skip to content

Global mode misses every Bun global package when BUN_INSTALL_BIN or BUN_INSTALL_GLOBAL_DIR is set: scan -g reports success with nothing found, get -g / vex -g patch and attest nothing #443

Description

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

Summary

socket-patch doesn't ask Bun where its global packages are. It runs bun pm bin -g, which returns Bun's global bin directory, and then assumes the packages sit at <bin>/../install/global/node_modules. That only holds for Bun's default layout. Bun lets you move each directory on its own, through the documented BUN_INSTALL_BIN and BUN_INSTALL_GLOBAL_DIR environment variables (or globalBinDir / globalDir in bunfig):

  • With BUN_INSTALL_BIN=~/.local/bin (a common way to put Bun's global bins on an existing PATH dir), bun pm bin -g prints ~/.local/bin. socket-patch then looks in ~/.local/install/global/node_modules, which doesn't exist. The real packages are still in $BUN_INSTALL/install/global/node_modules.
  • With BUN_INSTALL_GLOBAL_DIR=<dir>, the packages live in <dir>/node_modules, but bun pm bin -g still prints $BUN_INSTALL/bin, so socket-patch looks in the default location and finds nothing.

get_global_node_modules_paths drops a non-existent directory without a word (p.is_dir()), so in both cases Bun's globals simply vanish from global mode.

Impact

It's a silent miss. scan -g exits 0 with status: success and no Bun packages, and scan -g --mode agent exits 0 with "No patches available for installed packages." So a user, or a CI job that patches globally installed CLIs, is told there's nothing to patch while a vulnerable global tool stays unpatched. get -g does fail (exit 1, "matched no installed package"), and vex -g can't attest the patch. The only workaround is to pass --global-prefix explicitly.

This isn't a regression: release 4.0.0 behaves the same way.

Repro (Linux; same on macOS and Windows)

The patch API is a local mock (SOCKET_PROXY_URL=http://127.0.0.1:8787) serving a free patch for pkg:npm/semver@7.6.0 (bin/semver.js).

export HOME=$(mktemp -d) BUN_INSTALL=$HOME/.bun PATH=$HOME/.bun/bin:$PATH   # bun 1.4.2 copied into $BUN_INSTALL/bin
export BUN_INSTALL_BIN=$HOME/.local/bin                                      # or: BUN_INSTALL_GLOBAL_DIR=$HOME/gdir
bun add -g semver@7.6.0
bun pm bin -g                       # $HOME/.local/bin
ls $BUN_INSTALL/install/global/node_modules   # semver  (BUN_INSTALL_GLOBAL_DIR case: $HOME/gdir/node_modules/semver)

socket-patch scan -g --json         # status success, packages []                 <- bug
socket-patch scan -g --mode agent   # exit 0, "No patches available for installed packages."  <- bug
socket-patch get -g 11111111-1111-4111-8111-111111111111   # exit 1, "targeted manifest patch matched no installed package"
socket-patch scan --global-prefix $BUN_INSTALL/install/global/node_modules --mode agent   # patched, works

Without either variable, the same steps find the package, patch it, and vex -g / rollback -g (byte-exact) pass.

Expected vs actual

  • Expected: CLI_CONTRACT.md documents --global / -g as "Operate on globally-installed packages", with --global-prefix defaulting to (auto). Auto-detection should find the directory Bun actually installs global packages into, the same one bun add -g writes and bun pm ls -g reports (<globalDir> node_modules (N installed) on 1.4.2). If it can't determine that directory, it should say so instead of returning a clean, empty scan.
  • Actual: the global node_modules path is guessed from the bin dir, so any non-default BUN_INSTALL_BIN or BUN_INSTALL_GLOBAL_DIR makes every Bun global package disappear, with exit 0.

Matrix (main 2463257)

bun add -g semver@7.6.0 is-number@7.0.0, then scan -g report / scan -g --mode agent / get -g / vex -g:

OS Bun default layout BUN_INSTALL_BIN set BUN_INSTALL_GLOBAL_DIR set
Linux (sandbox + ubuntu-latest) 1.1.45, 1.2.23, 1.3.14, 1.4.2 pass FAIL FAIL
macos-latest 1.1.45, 1.2.23, 1.3.14, 1.4.2 pass FAIL FAIL
windows-latest 1.3.14, 1.4.2 pass FAIL FAIL
windows-latest 1.1.45, 1.2.23 blocked (bun add -g itself failed in the probe's space + unicode temp dir) blocked blocked

Release 4.0.0 (Linux, Bun 1.4.2, BUN_INSTALL_BIN): same failure. It's not a regression.

The paths contained a space and ü on every runner. scan -g --mode hosted refused correctly (exit 2) in every cell.

Suspect code

  • crates/socket-patch-core/src/crawlers/npm_crawler.rs:583 (parse_bun_bin_output) derives <bin>/../install/global/node_modules from the bun pm bin -g output.
  • crates/socket-patch-core/src/crawlers/npm_crawler.rs:1154 adds that path only if it exists, so a wrong guess is dropped silently.

Possible fixes: honour BUN_INSTALL_GLOBAL_DIR, then $BUN_INSTALL/install/global, then ~/.bun/install/global, the way Bun resolves it. Or parse the directory from the first line of bun pm ls -g. Also warn when a detected package manager's global dir can't be found.

Open PR #442 changes how this probe is spawned (GlobalProbeRunner), but it leaves this path derivation unchanged.

Probe run

https://github.com/SocketDev/socket-patch/actions/runs/36830650075 (3 OS × Bun 1.1.45 / 1.2.23 / 1.3.14 / 1.4.2; layouts: default, BUN_INSTALL_BIN, BUN_INSTALL_GLOBAL_DIR, npm-installed bun)

Activity

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