[agent] Found by the scheduled Composer bug-hunt routine (ledger #321).
Summary
On Windows, with COMPOSER_HOME unset (the normal setup), socket-patch scan -g discovers zero Composer packages even though composer global require installed them under Composer's default global home, %APPDATA%\Composer\vendor. scan -g --mode agent prints No global packages found. and exits 0, and vex -g has nothing to attest. If you set COMPOSER_HOME to the same directory explicitly, everything works.
Root cause (two compounding gaps in get_composer_home):
crates/socket-patch-core/src/crawlers/composer_crawler.rs:374 runs composer global config home through SystemCommandRunner::run (crates/socket-patch-core/src/utils/process.rs:137). That calls Command::new("composer") directly, without the PATHEXT-aware resolve_tool that the same file provides. On Windows, Composer is installed as composer.bat (setup-php: C:\tools\php\composer + composer.bat; the official installer works the same way), so the spawn fails and returns None. .github/workflows/composer-compatibility.yml:18-20 already notes that "Command::new("composer") cannot resolve composer.bat".
- The platform fallback (
composer_crawler.rs:396-410) probes only $HOME/.composer and $HOME/.config/composer. It never probes Composer's Windows default, %APPDATA%\Composer. On Linux it also ignores $XDG_CONFIG_HOME/composer, which Composer uses when XDG_CONFIG_HOME is set. So the fallback can't recover. Confirmed locally on Linux: with XDG_CONFIG_HOME=/x/xdg and composer run as php composer.phar (not on PATH), scan -g reports No global packages found.
Impact
Global Composer tools on Windows (phpstan, php-cs-fixer, laravel/installer, and so on) can't be scanned, patched or attested with -g. The failure is silent: exit 0 and "No global packages found." This violates the maintainer requirement that scan -g must find every globally installed package with a patch.
Repro (Git Bash on Windows, any Composer 1.10–2.10)
unset COMPOSER_HOME
composer global require <vendor>/<pkg-with-a-patch> # lands in %APPDATA%\Composer\vendor
socket-patch scan -g --json --ecosystems composer # packagesWithPatches: 0, scannedPackages: 0
socket-patch scan -g --mode agent --yes --ecosystems composer # "No global packages found." exit 0
COMPOSER_HOME="$APPDATA/Composer" socket-patch scan -g --json --ecosystems composer # found: 1
The probe used a local path-repo package acme/tool@1.0.0 and a mock patch API (no secrets).
Expected vs actual
- Expected: CLI_CONTRACT.md
--global / -g: "Operate on globally-installed packages". The crawler doc comment says global mode checks "$COMPOSER_HOME/vendor/ (env var, command fallback, or platform defaults)", and %APPDATA%\Composer is Composer's platform default on Windows.
- Actual: the command fallback can't spawn
composer.bat, and the platform defaults omit %APPDATA%\Composer, so nothing is found.
OS × version
| OS |
Composer |
scan -g (default home) |
apply -g / vex -g |
same home with explicit COMPOSER_HOME |
| windows-latest |
1.10.28 (PHP 8.1) |
fail (0 found) |
fail |
pass |
| windows-latest |
2.2.30 (PHP 8.4) |
fail (0 found) |
fail |
pass |
| windows-latest |
2.10.3 (PHP 8.4) |
fail (0 found) |
fail |
pass |
| macos-latest |
1.10.28 / 2.2.30 / 2.10.3 |
pass (~/.composer) |
pass |
pass |
| ubuntu-latest + local Linux |
1.10.28 / 2.2.30 / 2.8.12 / 2.10.3 |
pass |
pass |
pass |
Linux, composer not on PATH, XDG_CONFIG_HOME set |
2.2.30 |
fail (0 found) |
n/a |
n/a |
Windows reproduced in two separate probe runs (6 jobs): https://github.com/SocketDev/socket-patch/actions/runs/36823671080 and https://github.com/SocketDev/socket-patch/actions/runs/36827949605
First bad version
Not a regression from v5. The global branch (composer_crawler.rs:52-64) predates the shallow history, and the same code ships in v4.0.0.
Suspect code
crates/socket-patch-core/src/utils/process.rs:137 (Command::new(bin), no resolve_tool)
crates/socket-patch-core/src/crawlers/composer_crawler.rs:374 and :396-410 (fallback candidates)
crates/socket-patch-core/src/crawlers/ruby_crawler.rs:549 (gem env) goes through the same runner. That's out of scope here, and I've handed it to the bundler routine as a lead.
[agent] Found by the scheduled Composer bug-hunt routine (ledger #321).
Summary
On Windows, with
COMPOSER_HOMEunset (the normal setup),socket-patch scan -gdiscovers zero Composer packages even thoughcomposer global requireinstalled them under Composer's default global home,%APPDATA%\Composer\vendor.scan -g --mode agentprintsNo global packages found.and exits 0, andvex -ghas nothing to attest. If you setCOMPOSER_HOMEto the same directory explicitly, everything works.Root cause (two compounding gaps in
get_composer_home):crates/socket-patch-core/src/crawlers/composer_crawler.rs:374runscomposer global config homethroughSystemCommandRunner::run(crates/socket-patch-core/src/utils/process.rs:137). That callsCommand::new("composer")directly, without the PATHEXT-awareresolve_toolthat the same file provides. On Windows, Composer is installed ascomposer.bat(setup-php:C:\tools\php\composer+composer.bat; the official installer works the same way), so the spawn fails and returnsNone..github/workflows/composer-compatibility.yml:18-20already notes that "Command::new("composer")cannot resolvecomposer.bat".composer_crawler.rs:396-410) probes only$HOME/.composerand$HOME/.config/composer. It never probes Composer's Windows default,%APPDATA%\Composer. On Linux it also ignores$XDG_CONFIG_HOME/composer, which Composer uses whenXDG_CONFIG_HOMEis set. So the fallback can't recover. Confirmed locally on Linux: withXDG_CONFIG_HOME=/x/xdgand composer run asphp composer.phar(not on PATH),scan -greportsNo global packages found.Impact
Global Composer tools on Windows (phpstan, php-cs-fixer, laravel/installer, and so on) can't be scanned, patched or attested with
-g. The failure is silent: exit 0 and "No global packages found." This violates the maintainer requirement thatscan -gmust find every globally installed package with a patch.Repro (Git Bash on Windows, any Composer 1.10–2.10)
The probe used a local path-repo package
acme/tool@1.0.0and a mock patch API (no secrets).Expected vs actual
--global/-g: "Operate on globally-installed packages". The crawler doc comment says global mode checks "$COMPOSER_HOME/vendor/(env var, command fallback, or platform defaults)", and%APPDATA%\Composeris Composer's platform default on Windows.composer.bat, and the platform defaults omit%APPDATA%\Composer, so nothing is found.OS × version
scan -g(default home)apply -g/vex -gCOMPOSER_HOME~/.composer)XDG_CONFIG_HOMEsetWindows reproduced in two separate probe runs (6 jobs): https://github.com/SocketDev/socket-patch/actions/runs/36823671080 and https://github.com/SocketDev/socket-patch/actions/runs/36827949605
First bad version
Not a regression from v5. The global branch (
composer_crawler.rs:52-64) predates the shallow history, and the same code ships in v4.0.0.Suspect code
crates/socket-patch-core/src/utils/process.rs:137(Command::new(bin), noresolve_tool)crates/socket-patch-core/src/crawlers/composer_crawler.rs:374and:396-410(fallback candidates)crates/socket-patch-core/src/crawlers/ruby_crawler.rs:549(gem env) goes through the same runner. That's out of scope here, and I've handed it to the bundler routine as a lead.