Skip to content

Fix global PM probes spawning bare names from the project (#421, #434, #438, #440) - #442

Open
Mikola Lysenko (mikolalysenko) wants to merge 9 commits into
mainfrom
agent/fix-global-probe-tool-spawn
Open

Mikola Lysenko (mikolalysenko) wants to merge 9 commits into
mainfrom
agent/fix-global-probe-tool-spawn

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #421
Fixes #434
Fixes #438
Fixes #440

Summary

On Windows, scan -g, get -g and vex -g now find globally installed npm, yarn, pnpm, bun, RubyGems and Composer packages. Before this change they reported an empty, successful scan. The npm-family global lookups also no longer run inside the scanned project, so a Yarn Berry project's "global" script can't run or choose the directory that gets scanned (and patched) as the global install. Composer's global home also falls back to Composer's own platform defaults.

Priority note: the cluster is p1 (npm/yarn/RubyGems) and includes one p2 member (#438, Composer), because they share the same boundary.

Root cause

Global discovery asks each package manager where its global tree lives: npm root -g, yarn global dir, pnpm root -g, bun pm bin -g, gem env gemdir|gempath and composer global config home. Every one of these probes went through SystemCommandRunner::run (crates/socket-patch-core/src/utils/process.rs), which called Command::new(bin) on the bare tool name. That caused two problems:

Fix

  • SystemCommandRunner resolves the program with the existing resolve_tool (PATHEXT on Windows, absolute PATH entries only, so a tool planted in the project via . on PATH is never run) and spawns the resolved path through command_for. On Windows, if the lookup finds nothing, it falls back to std's own .exe search. That keeps App Execution Aliases such as the Store python3.exe working; std never searches the cwd on Windows.
  • New GlobalProbeRunner runs the probe from a neutral directory: the user's home (absolute only), otherwise the drive root, and never the project. The npm, yarn, pnpm and bun global probes and composer global config home use it. gem env keeps the project cwd on purpose, because rbenv and chruby choose the Ruby from the project's .ruby-version, and local mode uses the same lookup.
  • get_composer_home now probes Composer's own defaults: %APPDATA%\Composer first on Windows, and ~/.composer, then $XDG_CONFIG_HOME/composer, then ~/.config/composer elsewhere. Relative or empty variables are ignored.
  • The existing Composer HOME-fallback tests now also unset APPDATA and XDG_CONFIG_HOME, as they already do for HOME and PATH. On a Windows runner the real %APPDATA%\Composer otherwise outranks the HOME candidates they stage.

Tests

The new suite crates/socket-patch-core/tests/global_probe_spawn_e2e.rs puts fake tools on PATH the way they install on each OS: an executable sh script on Unix, and a <name>.cmd shim with no .exe on Windows.

Issue Test Red without the fix
#434 npm_family_global_probes_find_the_installed_shims (npm/pnpm/bun) Windows only (shim not found). Passes on test (windows-latest)
#434 (yarn), #440 yarn_global_probe_runs_outside_the_scanned_project Linux, verified locally: left: "/tmp/…/proj", right: "/tmp/…/home". On Windows it also fails, because the shim isn't found. Passes on windows-latest
#440 (hardening) global_probe_ignores_a_tool_planted_on_a_relative_path_entry Linux, verified locally: left: Ok("/planted/node_modules")
#421 global_gem_paths_come_from_the_installed_gem_shim Windows only (gem.cmd). Passes on windows-latest
#438 (1) composer_home_comes_from_the_installed_composer_shim Windows only (composer.cmd). Passes on windows-latest
#438 (2) composer_home_falls_back_to_xdg_config_home (Unix), composer_home_falls_back_to_appdata_on_windows (Windows) Linux, verified locally: left: []. The APPDATA test passes on windows-latest

There is also a unit test, neutral_probe_dir_prefers_an_absolute_home_and_never_the_cwd.

CI: everything is green on 059b07b, including test (windows-latest). Bugbot found no issues on 059b07b.

Commands run locally (Linux):

  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test -p socket-patch-core --test global_probe_spawn_e2e: 6/6 pass with the fix, 3/6 fail with src/ stashed (the 3 Linux-observable cases above)
  • The Composer, npm and ruby crawler suites and the utils::process unit tests all pass.
  • cargo test --workspace --all-features --no-fail-fast: everything passes except 12 permission-injection tests (*_write_failure_*, *unremovable*, relax_loop_must_not_traverse_symlinked_root). They fail only because this sandbox runs as uid 0, which ignores chmod 0o555. The 4 core-lib ones pass when rerun as nobody. None of them touch the probe code. CI runs as non-root.
  • Formatting: main isn't rustfmt-clean under the pinned 1.93.1 toolchain, and CI doesn't gate on it. To keep this diff to the 7 files the fix needs, I didn't apply a workspace-wide cargo fmt.
  • No wrapper changes are needed. npm/, pypi/ and gem/ only dispatch the binary.

Follow-ups (not in this PR)

🤖 Generated with Claude Code

https://claude.ai/code/session_01KKwBoKTsqpGR3ZcCAcEyCA


Note

Medium Risk
Changes subprocess spawning and global discovery paths used by scan -g; behavior is safer (neutral cwd, no relative PATH tools) but touches cross-platform CLI resolution where regressions would affect global scans only.

Overview
Global mode (-g) now discovers machine-wide npm, yarn, pnpm, bun, RubyGems, and Composer installs reliably instead of often returning an empty scan.

Command spawning for package-manager probes no longer uses bare names: SystemCommandRunner resolves tools via resolve_tool (PATHEXT on Windows for npm.cmd / gem.cmd / composer.bat, absolute PATH entries only so a project-local fake binary is not run). A new GlobalProbeRunner runs npm-family and Composer global probes from a neutral directory (absolute home, else drive root), so Yarn Berry cannot answer yarn global dir via a project "global" script or cwd-sensitive config.

Composer global home discovery uses GlobalProbeRunner for composer global config home and adds platform fallbacks aligned with Composer: %APPDATA%\Composer on Windows and $XDG_CONFIG_HOME/composer / ~/.config/composer elsewhere. Composer e2e tests unset APPDATA / XDG_CONFIG_HOME when staging HOME-based fallbacks.

New global_probe_spawn_e2e tests cover Windows shims, yarn cwd isolation, relative PATH hardening, gem, and Composer fallbacks; neutral_probe_dir has a unit test. CHANGELOG documents the fix.

Reviewed by Cursor Bugbot for commit 465db05. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Global mode asked npm, yarn, pnpm, bun, gem and composer where their
global installs live by spawning the bare tool name. On Windows those
tools are .cmd/.bat shims that a bare spawn never finds, so scan -g
silently reported nothing to patch. The yarn probe also ran inside the
scanned project, where Yarn Berry runs the project's own "global"
script and its output picked the directory scanned as global.

Probes now resolve the tool through PATHEXT (and never from a relative
PATH entry), npm-family global probes run from the home directory, and
Composer's home falls back to %APPDATA%\Composer and
$XDG_CONFIG_HOME/composer like Composer does.

Fixes #421, #434, #438, #440.

Assisted-by: Claude Code:claude-opus-5-5
A Windows App Execution Alias (the Store python3.exe) is a reparse
point the PATH lookup can't stat, though a bare spawn launches it. The
Python probe shares this runner, so fall back to the bare name on
Windows when the lookup finds nothing, rather than lose an interpreter
that used to be found. Also satisfies clippy's redundant closure lint.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] Two red checks on 3033eee, neither in code this PR touches:

  • e2e (ubuntu-latest, e2e_redirect_maven_build, 3.8.9) died in the fixture warm-up against Maven Central, before socket-patch ran: Could not find artifact org.apache.maven.plugins:maven-dependency-plugin:jar:3.6.1 in central. The diff doesn't touch Maven, and Maven discovery doesn't use the probe runner. I'll re-run it once when the CI run completes.
  • Pipenv matrix (macos-latest, …): one leg (2018.11.26 direct hosted, rollbackRestoresLockBytes) failed after PyPI request transport failed retries. The same leg passed on b29d415, and the only change since is behind cfg!(windows) plus a clippy lint, so macOS runs identical code. That's the transport-blip class Fix Pipenv matrix flake on 503/DNS transport blips #419 targets. I've re-run the failed job once.

If either fails again on re-run, I'll treat it as real and root-cause it.


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Global Composer discovery now also tries Composer's own defaults,
%APPDATA%\Composer and $XDG_CONFIG_HOME/composer. On a Windows runner
APPDATA points at a real Composer home, which outranks the ~/.composer
and ~/.config/composer candidates these tests stage. Unset both
variables there, as the tests already do for HOME and PATH, so they
keep exercising the HOME candidates.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

An earlier cargo fmt run over the whole workspace reformatted 125
files this change doesn't touch. Restore them to main so the diff
holds only the global-probe fix.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 1, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[burn-down agent] Ready for review on 059b07b: mergeable, 0 commits behind main.

  • CI: 405/405 check runs green on 059b07b (6 skipped by workflow conditions, none failing).
  • Bugbot: reviewed 059b07b, no new issues (earlier runs on 3033eee/a5ef5a0 are stale); 0 unresolved review threads.
  • Reviewer focus: the PATHEXT-aware tool resolution in crates/socket-patch-core/src/utils/process.rs and how the Composer/npm global probes now spawn resolved tools instead of bare names from the project cwd; new e2e coverage in tests/global_probe_spawn_e2e.rs.

Note: Slack announcement could not be sent this run (no Slack send tool available in the agent session); next run will retry.


Generated by Claude Code

Union the CHANGELOG Fixed entries from both sides.

Co-Authored-By: Claude <[email protected]>
@mikolalysenko Mikola Lysenko (mikolalysenko) removed the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 1, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/tests/crawler_composer_e2e.rs
The no-composer and empty-HOME tests still inherited APPDATA and
XDG_CONFIG_HOME. Global discovery now probes %APPDATA%\Composer and
$XDG_CONFIG_HOME/composer, so a machine with a real Composer home
there made both tests see a vendor dir and fail. Unset both variables
in these tests too, as the sibling HOME-fallback tests already do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KKwBoKTsqpGR3ZcCAcEyCA
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 465db05. Configure here.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

e2e (ubuntu-latest, mode_migration_vlt, 1.0.0-rc.14, …) failed on 465db05 before any test ran, and the cause isn't in this PR. Downloading the e2e-bin-ubuntu-latest artifact (146 MB) took about 1.5 minutes and left target/e2e-bin/ empty, so the next step failed with cp: cannot stat 'target/e2e-bin/socket-patch'. This PR doesn't touch the workflow or the artifact upload. I'll re-run the job once when the rest of CI run 36870777682 has finished; GitHub refuses a re-run while the run is still going.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment