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
scan --mode hosted over a project whose Composer package is currently vendored doesn't revert the vendored wiring first, unlike cargo, npm and golang (and PyPI in open PR #503). The hosted rewriter edits the vendored lock entry in place. It changes dist.type from path to zip, points dist.url at the hosted archive and sets shasum. It keeps two fields that socket-patch's vendored mode wrote:
"dist.reference": "<patch uuid>" (vendored mode replaces the upstream commit with the patch uuid)
"transport-options": {"symlink": false}
The run only warns redirect_supersedes_vendored and tells the user to run socket-patch remove <purl>. Following that advice doesn't touch the lock ("composer.lock entry … no longer points into .socket/vendor/composer/; left alone").
rollback refuses, so the hosted state can't be undone except through git: Cannot restore pkg:composer/psr/log@3.0.2 to its upstream registry entry: … the lock pins dist.reference "9f6b2c4e-…" but packagist now serves "f16e1d58…"; restore it from version control instead.
The committed .socket/vendor/composer/<uuid>/ artifact and its ledger entry are orphaned (warned about, but not reconciled).
The other direction works correctly. Hosted → vendored restores the upstream packagist entry first (vendor_takeover_reverted_redirect). Running vendor --revert and then scan --mode hosted produces a lock byte-identical to a direct hosted scan, and that lock installs patched on Composer 1.10.28.
Repro (Linux, main 61cfb9b)
The fixture is a real packagist-origin lock: composer.json{"require":{"psr/log":"3.0.2"}}, created with composer update --no-install. A local mock of the patch API grants uuid 9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f for pkg:composer/psr/log@3.0.2 (patches/package with sha1 and sha512 integrity, plus a single-top-dir zip of the patched package). secure-http: false is set in $COMPOSER_HOME/config.json for the loopback mock.
Control on the same fixture: vendor --revert → scan --mode hosted gives reference: f16e1d58… with no transport-options, and Composer 1.10.28 installs the patched bytes.
Expected vs actual
Expected: CLI_CONTRACT.md, "Takeover reconciliation", says "Hosted → vendored and vendored → hosted (redirect_takeover_reverted_vendored) both work in place on the locks the target mode accepts". A vendored Composer purl should be reverted to its recorded original (upstream) entry before the hosted rewrite, as cargo, npm and golang are. The result should match a direct hosted scan: an installable lock on every supported Composer version (docs/testing/composer-compatibility.md: 1.10 → 2.10) that rollback can restore.
Actual: the vendored entry is rewritten in place. The patch-uuid reference and transport-options survive, Composer 1 can't install, and rollback refuses.
Matrix (reproduced twice from scratch, plus the original run)
OS
Composer (PHP)
Install after vendored → hosted
rollback
Linux
1.10.28 (8.3)
fails, ValueError, exit 255
refuses (reference mismatch)
Linux
2.2.30 (8.3)
installs patched
refuses
Linux
2.8.12 (8.3)
installs patched
refuses
Linux
2.10.3 (8.3)
installs patched
refuses
macOS and Windows weren't probed. The trigger is pure lock text, and #399's probe (https://github.com/SocketDev/socket-patch/actions/runs/36903408294) already shows that Composer 1.10.28 crashes on any non-stream transport-options key on ubuntu, macOS and Windows, on PHP 7.2–8.4.
Not bisected: with these flags, v4.0.0's vendored scan exits 1, so it gives no baseline.
Suspect code
crates/socket-patch-cli/src/commands/scan/hosted.rs:1512: takeover_capable only admits pkg:cargo/, pkg:npm/ and pkg:golang/ (PR Fix PyPI vendored to hosted takeover being refused (#328) #503 adds pkg:pypi/), so a vendored pkg:composer/ purl skips dispatch_revert_one and goes straight to the rewriter.
[agent] Found by the scheduled Composer bug-hunt routine (ledger #321).
Summary
scan --mode hostedover a project whose Composer package is currently vendored doesn't revert the vendored wiring first, unlike cargo, npm and golang (and PyPI in open PR #503). The hosted rewriter edits the vendored lock entry in place. It changesdist.typefrompathtozip, pointsdist.urlat the hosted archive and setsshasum. It keeps two fields that socket-patch's vendored mode wrote:"dist.reference": "<patch uuid>"(vendored mode replaces the upstream commit with the patch uuid)"transport-options": {"symlink": false}The run only warns
redirect_supersedes_vendoredand tells the user to runsocket-patch remove <purl>. Following that advice doesn't touch the lock ("composer.lock entry … no longer points into .socket/vendor/composer/; left alone").Impact
transport-optionsintostream_context_create(), which fails withUncaught ValueError: Options should have the form ["wrappername"]["optionname"] = $value(StreamContextFactory.php:153, exit 255). This is the same Composer 1 failure mode Hosted Composer rewrite keeps the entry's transport-options, so Composer sends a private repository's auth headers to the hosted patch URL #399 reported for path repositories, but here socket-patch wrote the offending key itself.rollbackrefuses, so the hosted state can't be undone except through git:Cannot restore pkg:composer/psr/log@3.0.2 to its upstream registry entry: … the lock pins dist.reference "9f6b2c4e-…" but packagist now serves "f16e1d58…"; restore it from version control instead..socket/vendor/composer/<uuid>/artifact and its ledger entry are orphaned (warned about, but not reconciled).The other direction works correctly. Hosted → vendored restores the upstream packagist entry first (
vendor_takeover_reverted_redirect). Runningvendor --revertand thenscan --mode hostedproduces a lock byte-identical to a direct hosted scan, and that lock installs patched on Composer 1.10.28.Repro (Linux, main
61cfb9b)The fixture is a real packagist-origin lock:
composer.json{"require":{"psr/log":"3.0.2"}}, created withcomposer update --no-install. A local mock of the patch API grants uuid9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5fforpkg:composer/psr/log@3.0.2(patches/packagewith sha1 and sha512 integrity, plus a single-top-dir zip of the patched package).secure-http: falseis set in$COMPOSER_HOME/config.jsonfor the loopback mock.Control on the same fixture:
vendor --revert→scan --mode hostedgivesreference: f16e1d58…with notransport-options, and Composer 1.10.28 installs the patched bytes.Expected vs actual
redirect_takeover_reverted_vendored) both work in place on the locks the target mode accepts". A vendored Composer purl should be reverted to its recorded original (upstream) entry before the hosted rewrite, as cargo, npm and golang are. The result should match a direct hosted scan: an installable lock on every supported Composer version (docs/testing/composer-compatibility.md: 1.10 → 2.10) thatrollbackcan restore.referenceandtransport-optionssurvive, Composer 1 can't install, androllbackrefuses.Matrix (reproduced twice from scratch, plus the original run)
rollbackmacOS and Windows weren't probed. The trigger is pure lock text, and #399's probe (https://github.com/SocketDev/socket-patch/actions/runs/36903408294) already shows that Composer 1.10.28 crashes on any non-stream
transport-optionskey on ubuntu, macOS and Windows, on PHP 7.2–8.4.Not bisected: with these flags, v4.0.0's vendored
scanexits 1, so it gives no baseline.Suspect code
crates/socket-patch-cli/src/commands/scan/hosted.rs:1512:takeover_capableonly admitspkg:cargo/,pkg:npm/andpkg:golang/(PR Fix PyPI vendored to hosted takeover being refused (#328) #503 addspkg:pypi/), so a vendoredpkg:composer/purl skipsdispatch_revert_oneand goes straight to the rewriter.crates/socket-patch-core/src/formats/composer/hosted.rs:154(rewrite_composer_lock) keeps the entry'stransport-optionsanddist.reference. Thetransport-optionspart is the same root cause as Hosted Composer rewrite keeps the entry's transport-options, so Composer sends a private repository's auth headers to the hosted patch URL #399; this issue is about the missing takeover revert.