You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[agent] Found by the scheduled Composer bug-hunt routine (ledger #321).
Summary
Composer accepts an absolute config.vendor-dir and expands ~ and $HOME in it (Platform::expandPath). resolve_local_vendor_dir deliberately refuses an absolute value and does not expand ~ or $HOME. The code comment says such a project "discovers nothing". In practice the package still shows up from composer.lock, but it is classified as lockfile-only / not installed even though Composer installed it. Two silent failures follow from that:
Hosted vex falsely attests not_affected. The package is treated as not installed, so vex falls back to "with nothing installed, attests a discovered lockfile reference from its integrity pin". It never hash-checks the real installed copy, which is still unpatched after composer install.
With the default vendor/, or with a relative vendor-dir, the same flow correctly omits the package from VEX as not_applied (exit 1).
Impact
A signed-off VEX document claims not_affected for a vulnerability whose vulnerable code is the code actually running. That is the worst outcome for vex. A home-relative or absolute vendor-dir is a supported Composer configuration, for example a shared vendor tree outside the checkout or ~/.cache/... in CI.
Repro (Linux, PHP 8.3, main 045d7ec)
This uses a dist-only package from an inline package repo with a file:// zip, plus a mock patch API (batch, view/<uuid>, patches/package granted with sha1 + sha512, and the archive route), the same shape as crates/socket-patch-cli/tests/e2e_redirect_composer_build.rs.
Control (identical project without config.vendor-dir, so the package is in vendor/): vex prints omitting pkg:composer/acme/widget@1.0.0 from VEX: the patched files still hold the original content (not_applied) and exits 1. The agent scan patches it.
Expected vs actual
Expected: CLI_CONTRACT.md (VEX table, hosted row) says a post-install socket-patch vex "re-proves the lockfile wiring and hash-verifies the installed copy the build consumes". Only "with nothing installed" may it attest from the pin. Here the package is installed, so vex must either verify the real copy or refuse to attest. If socket-patch can't locate it, it should fail closed, not assume "not installed". Likewise, agent apply should not report success for an installed package.
Actual: not_affected from the lock pin, exit 0. Agent apply skips it with exit 0.
Matrix (2/2 runs per cell)
Composer (PHP 8.3, Linux)
default vendor/ (control)
vendor-dir absolute
vendor-dir: "~/…"
vendor-dir: "$HOME/…"
1.10.28
omitted not_applied (correct)
false not_affected
false not_affected
untested
2.2.30
omitted (correct)
false not_affected
false not_affected
untested
2.10.3
omitted (correct)
false not_affected
false not_affected
false not_affected
Also checked: an absolute COMPOSER_VENDOR_DIR env var is honoured (correct omission). Vendored mode with an absolute vendor-dir passes, because Composer reinstalls from the .socket/vendor path dist. macOS/Windows weren't probed, but this is pure path logic (%APPDATA%-style values hit the same unexpanded branch on Windows).
Suspect code
crates/socket-patch-core/src/crawlers/composer_crawler.rs:501-520 (resolve_local_vendor_dir): an absolute value is refused (normalize_config_vendor_dir returns None for a leading /), and ~ / $HOME aren't expanded (parse_config_vendor_dir, :466-480). The refusal then flows into the lockfile-only classification instead of failing closed.
The hosted VEX "not installed, attest from pin" basis in crates/socket-patch-core/src/vex/discover/composer.rs doesn't distinguish "not installed" from "installed somewhere the crawler refused to look".
Related, not duplicates: #463 (same false-attest symptom via Composer 1 installers paths), #439 (user-level vendor-dir), #658 (reinstall hint names vendor/).
[agent] Found by the scheduled Composer bug-hunt routine (ledger #321).
Summary
Composer accepts an absolute
config.vendor-dirand expands~and$HOMEin it (Platform::expandPath).resolve_local_vendor_dirdeliberately refuses an absolute value and does not expand~or$HOME. The code comment says such a project "discovers nothing". In practice the package still shows up fromcomposer.lock, but it is classified as lockfile-only / not installed even though Composer installed it. Two silent failures follow from that:vexfalsely attestsnot_affected. The package is treated as not installed, sovexfalls back to "with nothing installed, attests a discovered lockfile reference from its integrity pin". It never hash-checks the real installed copy, which is still unpatched aftercomposer install.scan --mode agentandapplyskip the package aspackage_not_installedand exit 0. Nothing is patched (Fix apply failing when patched deps are skipped (#403) #555's lockfile-only skip firing on a package that is installed).With the default
vendor/, or with a relativevendor-dir, the same flow correctly omits the package from VEX asnot_applied(exit 1).Impact
A signed-off VEX document claims
not_affectedfor a vulnerability whose vulnerable code is the code actually running. That is the worst outcome forvex. A home-relative or absolutevendor-diris a supported Composer configuration, for example a shared vendor tree outside the checkout or~/.cache/...in CI.Repro (Linux, PHP 8.3, main
045d7ec)This uses a dist-only package from an inline
packagerepo with afile://zip, plus a mock patch API (batch,view/<uuid>,patches/packagegranted with sha1 + sha512, and the archive route), the same shape ascrates/socket-patch-cli/tests/e2e_redirect_composer_build.rs.Control (identical project without
config.vendor-dir, so the package is invendor/):vexprintsomitting pkg:composer/acme/widget@1.0.0 from VEX: the patched files still hold the original content (not_applied)and exits 1. The agent scan patches it.Expected vs actual
socket-patch vex"re-proves the lockfile wiring and hash-verifies the installed copy the build consumes". Only "with nothing installed" may it attest from the pin. Here the package is installed, sovexmust either verify the real copy or refuse to attest. If socket-patch can't locate it, it should fail closed, not assume "not installed". Likewise, agent apply should not report success for an installed package.not_affectedfrom the lock pin, exit 0. Agent apply skips it with exit 0.Matrix (2/2 runs per cell)
vendor/(control)vendor-dirabsolutevendor-dir: "~/…"vendor-dir: "$HOME/…"not_applied(correct)not_affectednot_affectednot_affectednot_affectednot_affectednot_affectednot_affectedAlso checked: an absolute
COMPOSER_VENDOR_DIRenv var is honoured (correct omission). Vendored mode with an absolutevendor-dirpasses, because Composer reinstalls from the.socket/vendorpath dist. macOS/Windows weren't probed, but this is pure path logic (%APPDATA%-style values hit the same unexpanded branch on Windows).Suspect code
crates/socket-patch-core/src/crawlers/composer_crawler.rs:501-520(resolve_local_vendor_dir): an absolute value is refused (normalize_config_vendor_dirreturnsNonefor a leading/), and~/$HOMEaren't expanded (parse_config_vendor_dir, :466-480). The refusal then flows into the lockfile-only classification instead of failing closed.crates/socket-patch-core/src/vex/discover/composer.rsdoesn't distinguish "not installed" from "installed somewhere the crawler refused to look".Related, not duplicates: #463 (same false-attest symptom via Composer 1 installers paths), #439 (user-level
vendor-dir), #658 (reinstall hint namesvendor/).