Skip to content

On Windows, scan -g finds no Composer global packages in the default %APPDATA%\Composer home, so apply -g and vex -g silently do nothing #438

Description

[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):

  1. 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".
  2. 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/[email protected] 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.

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