--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:composer
User: [email protected]
Usertags: pu
[ This issue is similar to #1134502 for trixie, with another fix for an
older issue. ]
Hi,
As agreed with the security team, I’d like to get CVE-2026-40261 and
CVE-2026-40261 fixed via an upcoming point release. The changes are
limited, and only about a niche feature.
The proposed update also fixes CVE-2023-43655, which is hardly
exploitable, but this removes an issue from the security-tracker.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
[x] the issue is verified as fixed in unstable
Thanks in advance,
Regards,
taffit
diff -Nru composer-2.5.5/debian/changelog composer-2.5.5/debian/changelog
--- composer-2.5.5/debian/changelog 2025-12-30 17:01:22.000000000 +0100
+++ composer-2.5.5/debian/changelog 2026-04-15 12:33:06.000000000 +0200
@@ -1,3 +1,14 @@
+composer (2.5.5-1+deb12u4) bookworm; urgency=medium
+
+ * Fix command injection via malicious Perforce source reference/url
+ [CVE-2026-40261]
+ * Fix ommand injection via malicious Perforce repository definition
+ [CVE-2026-40176]
+ * Fix remote Code Execution via web-accessible composer.phar
+ [CVE-2023-43655]
+
+ -- David Prévot <[email protected]> Wed, 15 Apr 2026 12:33:06 +0200
+
composer (2.5.5-1+deb12u3) bookworm; urgency=medium
* Backport fix from composer 2.2.26:
diff -Nru composer-2.5.5/debian/patches/0019-Merge-commit-from-fork.patch composer-2.5.5/debian/patches/0019-Merge-commit-from-fork.patch
--- composer-2.5.5/debian/patches/0019-Merge-commit-from-fork.patch 1970-01-01 01:00:00.000000000 +0100
+++ composer-2.5.5/debian/patches/0019-Merge-commit-from-fork.patch 2026-04-15 12:33:06.000000000 +0200
@@ -0,0 +1,47 @@
+From: Jordi Boggiano <[email protected]>
+Date: Tue, 14 Apr 2026 10:34:08 +0200
+Subject: Merge commit from fork
+
+Origin: upstream, https://github.com/composer/composer/commit/91f077050c13e49e22554b991c81378ce8b5ee16
+Bug: https://github.com/composer/composer/security/advisories/GHSA-gqw4-4w2p-838q
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-40261
+---
+ src/Composer/Util/Perforce.php | 2 +-
+ tests/Composer/Test/Util/PerforceTest.php | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php
+index d24209a..4ca0f8c 100644
+--- a/src/Composer/Util/Perforce.php
++++ b/src/Composer/Util/Perforce.php
+@@ -353,7 +353,7 @@ class Perforce
+ chdir($this->path);
+ $p4SyncCommand = $this->generateP4Command('sync -f ');
+ if (null !== $sourceReference) {
+- $p4SyncCommand .= '@' . $sourceReference;
++ $p4SyncCommand .= ProcessExecutor::escape('@' . $sourceReference);
+ }
+ $this->executeCommand($p4SyncCommand);
+ chdir($prevDir);
+diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php
+index 6fd37e5..c81d729 100644
+--- a/tests/Composer/Test/Util/PerforceTest.php
++++ b/tests/Composer/Test/Util/PerforceTest.php
+@@ -539,7 +539,7 @@ class PerforceTest extends TestCase
+ public function testSyncCodeBaseWithoutStream(): void
+ {
+ $this->processExecutor->expects(
+- ['p4 -u user -c composer_perforce_TEST_depot -p port sync -f @label'],
++ ['p4 -u user -c composer_perforce_TEST_depot -p port sync -f \'@label\''],
+ true
+ );
+
+@@ -551,7 +551,7 @@ class PerforceTest extends TestCase
+ $this->setPerforceToStream();
+
+ $this->processExecutor->expects(
+- ['p4 -u user -c composer_perforce_TEST_depot_branch -p port sync -f @label'],
++ ['p4 -u user -c composer_perforce_TEST_depot_branch -p port sync -f \'@label\''],
+ true
+ );
+
diff -Nru composer-2.5.5/debian/patches/0020-Merge-commit-from-fork.patch composer-2.5.5/debian/patches/0020-Merge-commit-from-fork.patch
--- composer-2.5.5/debian/patches/0020-Merge-commit-from-fork.patch 1970-01-01 01:00:00.000000000 +0100
+++ composer-2.5.5/debian/patches/0020-Merge-commit-from-fork.patch 2026-04-15 12:33:06.000000000 +0200
@@ -0,0 +1,236 @@
+From: Jordi Boggiano <[email protected]>
+Date: Tue, 14 Apr 2026 10:41:57 +0200
+Subject: Merge commit from fork
+
+Co-authored-by: Stephan Vock <[email protected]>
+
+Origin: backport, https://github.com/composer/composer/commit/4f02616e6fba3b1baf8d45725f847841b44fc15c
+Bug: https://github.com/composer/composer/security/advisories/GHSA-wg36-wvj6-r67p
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-40176
+---
+ src/Composer/Util/Perforce.php | 6 +--
+ tests/Composer/Test/Util/PerforceTest.php | 82 ++++++++++++++++++++++++-------
+ 2 files changed, 67 insertions(+), 21 deletions(-)
+
+diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php
+index 4ca0f8c..99d1a42 100644
+--- a/src/Composer/Util/Perforce.php
++++ b/src/Composer/Util/Perforce.php
+@@ -310,11 +310,11 @@ class Perforce
+ public function generateP4Command(string $command, bool $useClient = true): string
+ {
+ $p4Command = 'p4 ';
+- $p4Command .= '-u ' . $this->getUser() . ' ';
++ $p4Command .= '-u ' . ProcessExecutor::escape($this->getUser()) . ' ';
+ if ($useClient) {
+- $p4Command .= '-c ' . $this->getClient() . ' ';
++ $p4Command .= '-c ' . ProcessExecutor::escape($this->getClient()) . ' ';
+ }
+- $p4Command .= '-p ' . $this->getPort() . ' ' . $command;
++ $p4Command .= '-p ' . ProcessExecutor::escape($this->getPort()) . ' ' . $command;
+
+ return $p4Command;
+ }
+diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php
+index c81d729..98a6965 100644
+--- a/tests/Composer/Test/Util/PerforceTest.php
++++ b/tests/Composer/Test/Util/PerforceTest.php
+@@ -128,10 +128,56 @@ class PerforceTest extends TestCase
+ {
+ $command = 'do something';
+ $p4Command = $this->perforce->generateP4Command($command);
+- $expected = 'p4 -u user -c composer_perforce_TEST_depot -p port do something';
++ $expected = "p4 -u 'user' -c 'composer_perforce_TEST_depot' -p 'port' do something";
+ $this->assertEquals($expected, $p4Command);
+ }
+
++ public function testGenerateP4CommandEscapesPortInjection(): void
++ {
++ $perforce = new Perforce(
++ ['depot' => 'depot', 'branch' => 'branch', 'p4user' => 'user', 'unique_perforce_client_name' => 'TEST'],
++ 'localhost:1666; touch /tmp/pwned',
++ 'path',
++ $this->processExecutor,
++ false,
++ $this->io
++ );
++ $command = $perforce->generateP4Command('login -s', false);
++ self::assertStringNotContainsString('-p localhost:1666; touch /tmp/pwned', $command);
++ self::assertStringContainsString('-p '.ProcessExecutor::escape('localhost:1666; touch /tmp/pwned'), $command);
++ }
++
++ public function testGenerateP4CommandEscapesUserInjection(): void
++ {
++ $perforce = new Perforce(
++ ['depot' => 'depot', 'branch' => 'branch', 'p4user' => 'user; id', 'unique_perforce_client_name' => 'TEST'],
++ 'port',
++ 'path',
++ $this->processExecutor,
++ false,
++ $this->io
++ );
++ $command = $perforce->generateP4Command('login -s', false);
++ self::assertStringNotContainsString('-u user; id', $command);
++ self::assertStringContainsString('-u '.ProcessExecutor::escape('user; id'), $command);
++ }
++
++ public function testGenerateP4CommandEscapesClientInjection(): void
++ {
++ $perforce = new Perforce(
++ ['depot' => 'foo; id #', 'branch' => 'branch', 'p4user' => 'user', 'unique_perforce_client_name' => 'TEST'],
++ 'port',
++ 'path',
++ $this->processExecutor,
++ false,
++ $this->io
++ );
++ $command = $perforce->generateP4Command('do something');
++ $expectedClient = 'composer_perforce_TEST_foo; id #';
++ self::assertStringNotContainsString('-c '.$expectedClient.' ', $command);
++ self::assertStringContainsString('-c '.ProcessExecutor::escape($expectedClient), $command);
++ }
++
+ public function testQueryP4UserWithUserAlreadySet(): void
+ {
+ $this->perforce->queryP4user();
+@@ -319,7 +365,7 @@ class PerforceTest extends TestCase
+ public function testIsLoggedIn(): void
+ {
+ $this->processExecutor->expects(
+- [['cmd' => 'p4 -u user -p port login -s']],
++ [['cmd' => "p4 -u 'user' -p 'port' login -s"]],
+ true
+ );
+ $this->perforce->isLoggedIn();
+@@ -328,7 +374,7 @@ class PerforceTest extends TestCase
+ public function testConnectClient(): void
+ {
+ $this->processExecutor->expects(
+- ['p4 -u user -c composer_perforce_TEST_depot -p port client -i < path/composer_perforce_TEST_depot.p4.spec'],
++ ["p4 -u 'user' -c 'composer_perforce_TEST_depot' -p 'port' client -i < path/composer_perforce_TEST_depot.p4.spec"],
+ true
+ );
+
+@@ -342,11 +388,11 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -c composer_perforce_TEST_depot_branch -p port streams '.ProcessExecutor::escape('//depot/...'),
++ 'cmd' => "p4 -u 'user' -c 'composer_perforce_TEST_depot_branch' -p 'port' streams ".ProcessExecutor::escape('//depot/...'),
+ 'stdout' => 'Stream //depot/branch mainline none \'branch\'' . PHP_EOL,
+ ],
+ [
+- 'cmd' => 'p4 -u user -p port changes '.ProcessExecutor::escape('//depot/branch/...'),
++ 'cmd' => "p4 -u 'user' -p 'port' changes ".ProcessExecutor::escape('//depot/branch/...'),
+ 'stdout' => 'Change 1234 on 2014/03/19 by [email protected]_test_client \'test changelist\'',
+ ],
+ ],
+@@ -362,7 +408,7 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -p port changes '.ProcessExecutor::escape('//depot/...'),
++ 'cmd' => "p4 -u 'user' -p 'port' changes ".ProcessExecutor::escape('//depot/...'),
+ 'stdout' => 'Change 5678 on 2014/03/19 by [email protected]_test_client \'test changelist\'',
+ ],
+ ],
+@@ -378,7 +424,7 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -c composer_perforce_TEST_depot -p port labels',
++ 'cmd' => "p4 -u 'user' -c 'composer_perforce_TEST_depot' -p 'port' labels",
+ 'stdout' => 'Label 0.0.1 2013/07/31 \'First Label!\'' . PHP_EOL . 'Label 0.0.2 2013/08/01 \'Second Label!\'' . PHP_EOL,
+ ],
+ ],
+@@ -397,7 +443,7 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -c composer_perforce_TEST_depot_branch -p port labels',
++ 'cmd' => "p4 -u 'user' -c 'composer_perforce_TEST_depot_branch' -p 'port' labels",
+ 'stdout' => 'Label 0.0.1 2013/07/31 \'First Label!\'' . PHP_EOL . 'Label 0.0.2 2013/08/01 \'Second Label!\'' . PHP_EOL,
+ ],
+ ],
+@@ -421,7 +467,7 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -p port depots',
++ 'cmd' => "p4 -u 'user' -p 'port' depots",
+ 'stdout' => 'Depot depot 2013/06/25 stream /p4/1/depots/depot/... \'Created by Me\'',
+ ],
+ ],
+@@ -438,7 +484,7 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -c composer_perforce_TEST_depot -p port print '.ProcessExecutor::escape('//depot/composer.json'),
++ 'cmd' => "p4 -u 'user' -c 'composer_perforce_TEST_depot' -p 'port' print ".ProcessExecutor::escape('//depot/composer.json'),
+ 'stdout' => PerforceTest::getComposerJson(),
+ ],
+ ],
+@@ -460,11 +506,11 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -p port files '.ProcessExecutor::escape('//depot/[email protected]'),
++ 'cmd' => "p4 -u 'user' -p 'port' files ".ProcessExecutor::escape('//depot/[email protected]'),
+ 'stdout' => '//depot/composer.json#1 - branch change 10001 (text)',
+ ],
+ [
+- 'cmd' => 'p4 -u user -c composer_perforce_TEST_depot -p port print '.ProcessExecutor::escape('//depot/composer.json@10001'),
++ 'cmd' => "p4 -u 'user' -c 'composer_perforce_TEST_depot' -p 'port' print ".ProcessExecutor::escape('//depot/composer.json@10001'),
+ 'stdout' => PerforceTest::getComposerJson(),
+ ],
+ ],
+@@ -489,7 +535,7 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -c composer_perforce_TEST_depot_branch -p port print '.ProcessExecutor::escape('//depot/branch/composer.json'),
++ 'cmd' => "p4 -u 'user' -c 'composer_perforce_TEST_depot_branch' -p 'port' print ".ProcessExecutor::escape('//depot/branch/composer.json'),
+ 'stdout' => PerforceTest::getComposerJson(),
+ ],
+ ],
+@@ -512,11 +558,11 @@ class PerforceTest extends TestCase
+ $this->processExecutor->expects(
+ [
+ [
+- 'cmd' => 'p4 -u user -p port files '.ProcessExecutor::escape('//depot/branch/[email protected]'),
++ 'cmd' => "p4 -u 'user' -p 'port' files ".ProcessExecutor::escape('//depot/branch/[email protected]'),
+ 'stdout' => '//depot/composer.json#1 - branch change 10001 (text)',
+ ],
+ [
+- 'cmd' => 'p4 -u user -c composer_perforce_TEST_depot_branch -p port print '.ProcessExecutor::escape('//depot/branch/composer.json@10001'),
++ 'cmd' => "p4 -u 'user' -c 'composer_perforce_TEST_depot_branch' -p 'port' print ".ProcessExecutor::escape('//depot/branch/composer.json@10001'),
+ 'stdout' => PerforceTest::getComposerJson(),
+ ],
+ ],
+@@ -539,7 +585,7 @@ class PerforceTest extends TestCase
+ public function testSyncCodeBaseWithoutStream(): void
+ {
+ $this->processExecutor->expects(
+- ['p4 -u user -c composer_perforce_TEST_depot -p port sync -f \'@label\''],
++ ["p4 -u 'user' -c 'composer_perforce_TEST_depot' -p 'port' sync -f '@label'"],
+ true
+ );
+
+@@ -551,7 +597,7 @@ class PerforceTest extends TestCase
+ $this->setPerforceToStream();
+
+ $this->processExecutor->expects(
+- ['p4 -u user -c composer_perforce_TEST_depot_branch -p port sync -f \'@label\''],
++ ["p4 -u 'user' -c 'composer_perforce_TEST_depot_branch' -p 'port' sync -f '@label'"],
+ true
+ );
+
+@@ -647,7 +693,7 @@ class PerforceTest extends TestCase
+
+ $testClient = $this->perforce->getClient();
+ $this->processExecutor->expects(
+- ['p4 -u ' . self::TEST_P4USER . ' -p ' . self::TEST_PORT . ' client -d ' . ProcessExecutor::escape($testClient)],
++ ["p4 -u '" . self::TEST_P4USER . "' -p '" . self::TEST_PORT . "' client -d " . ProcessExecutor::escape($testClient)],
+ true
+ );
+
diff -Nru composer-2.5.5/debian/patches/0021-Merge-pull-request-from-GHSA-jm6m-4632-36hf.patch composer-2.5.5/debian/patches/0021-Merge-pull-request-from-GHSA-jm6m-4632-36hf.patch
--- composer-2.5.5/debian/patches/0021-Merge-pull-request-from-GHSA-jm6m-4632-36hf.patch 1970-01-01 01:00:00.000000000 +0100
+++ composer-2.5.5/debian/patches/0021-Merge-pull-request-from-GHSA-jm6m-4632-36hf.patch 2026-04-15 12:33:06.000000000 +0200
@@ -0,0 +1,27 @@
+From: Jordi Boggiano <[email protected]>
+Date: Fri, 29 Sep 2023 10:15:51 +0200
+Subject: Merge pull request from GHSA-jm6m-4632-36hf
+
+Origin: upstream, https://github.com/composer/composer/commit/955a48e6319c8962e5cd421b07c00ab3c728968c
+Bug: https://github.com/composer/composer/security/advisories/GHSA-jm6m-4632-36hf
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2023-43655
+---
+ bin/composer | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/bin/composer b/bin/composer
+index 238e669..ded411f 100755
+--- a/bin/composer
++++ b/bin/composer
+@@ -2,6 +2,11 @@
+ <?php
+
+ if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
++ if (0 === strpos(__FILE__, 'phar:') && ini_get('register_argc_argv')) {
++ echo 'Composer cannot be run safely on non-CLI SAPIs with register_argc_argv=On. Aborting.'.PHP_EOL;
++ exit(1);
++ }
++
+ echo 'Warning: Composer should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
+ }
+
diff -Nru composer-2.5.5/debian/patches/series composer-2.5.5/debian/patches/series
--- composer-2.5.5/debian/patches/series 2025-12-30 17:00:22.000000000 +0100
+++ composer-2.5.5/debian/patches/series 2026-04-15 12:33:06.000000000 +0200
@@ -16,3 +16,6 @@
0016-Merge-pull-request-from-GHSA-47f6-5gq3-vx9c.patch
0017-Fix-test.patch
0018-Merge-commit-from-fork.patch
+0019-Merge-commit-from-fork.patch
+0020-Merge-commit-from-fork.patch
+0021-Merge-pull-request-from-GHSA-jm6m-4632-36hf.patch
signature.asc
Description: PGP signature
--- End Message ---