Version in base suite: 2.5.5-1+deb12u4 Base version: composer_2.5.5-1+deb12u4 Target version: composer_2.5.5-1+deb12u5 Base file: /srv/ftp-master.debian.org/ftp/pool/main/c/composer/composer_2.5.5-1+deb12u4.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/c/composer/composer_2.5.5-1+deb12u5.dsc changelog | 7 patches/0022-ProcessExecutor-mask-GitHub-fine-grained-access-toke.patch | 68 ++++ patches/0023-Fix-regexp-to-support-new-GitHub-installation-tokens.patch | 148 ++++++++++ patches/series | 2 4 files changed, 225 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp2pe9erjm/composer_2.5.5-1+deb12u4.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp2pe9erjm/composer_2.5.5-1+deb12u5.dsc: no acceptable signature found diff -Nru composer-2.5.5/debian/changelog composer-2.5.5/debian/changelog --- composer-2.5.5/debian/changelog 2026-04-15 10:33:06.000000000 +0000 +++ composer-2.5.5/debian/changelog 2026-05-14 09:28:34.000000000 +0000 @@ -1,3 +1,10 @@ +composer (2.5.5-1+deb12u5) bookworm; urgency=medium + + * Fix regexp to support new GitHub installation tokens format + (GHSA-f9f8-rm49-7jv2) [CVE-2026-45793] + + -- David Prévot Thu, 14 May 2026 11:28:34 +0200 + composer (2.5.5-1+deb12u4) bookworm; urgency=medium * Fix command injection via malicious Perforce source reference/url diff -Nru composer-2.5.5/debian/patches/0022-ProcessExecutor-mask-GitHub-fine-grained-access-toke.patch composer-2.5.5/debian/patches/0022-ProcessExecutor-mask-GitHub-fine-grained-access-toke.patch --- composer-2.5.5/debian/patches/0022-ProcessExecutor-mask-GitHub-fine-grained-access-toke.patch 1970-01-01 00:00:00.000000000 +0000 +++ composer-2.5.5/debian/patches/0022-ProcessExecutor-mask-GitHub-fine-grained-access-toke.patch 2026-05-14 09:28:34.000000000 +0000 @@ -0,0 +1,68 @@ +From: Stephan +Date: Thu, 21 Aug 2025 10:07:24 +0100 +Subject: ProcessExecutor: mask GitHub fine grained access tokens similar to + URL (#12487) + +Origin: backport, https://github.com/composer/composer/commit/af81eac1816effad4fa959eb79b73ba214254540 +--- + src/Composer/Util/GitHub.php | 2 ++ + src/Composer/Util/ProcessExecutor.php | 4 ++-- + src/Composer/Util/Url.php | 4 ++-- + tests/Composer/Test/Util/ProcessExecutorTest.php | 1 + + 4 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php +index 43a6354..950be24 100644 +--- a/src/Composer/Util/GitHub.php ++++ b/src/Composer/Util/GitHub.php +@@ -23,6 +23,8 @@ use Composer\Pcre\Preg; + */ + class GitHub + { ++ public const GITHUB_TOKEN_REGEX = '{^([a-f0-9]{12,}|gh[a-z]_[a-zA-Z0-9_]+|github_pat_[a-zA-Z0-9_]+)$}'; ++ + /** @var IOInterface */ + protected $io; + /** @var Config */ +diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php +index a8f69ec..b99b52d 100644 +--- a/src/Composer/Util/ProcessExecutor.php ++++ b/src/Composer/Util/ProcessExecutor.php +@@ -417,8 +417,8 @@ class ProcessExecutor + $safeCommand = Preg::replaceCallback('{://(?P[^:/\s]+):(?P[^@\s/]+)@}i', static function ($m): string { + assert(is_string($m['user'])); + +- // if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx) we obfuscate that +- if (Preg::isMatch('{^([a-f0-9]{12,}|gh[a-z]_[a-zA-Z0-9_]+)$}', $m['user'])) { ++ // if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx, github_pat_xxx) we obfuscate that ++ if (Preg::isMatch(GitHub::GITHUB_TOKEN_REGEX, $m['user'])) { + return '://***:***@'; + } + if (Preg::isMatch('{^[a-f0-9]{12,}$}', $m['user'])) { +diff --git a/src/Composer/Util/Url.php b/src/Composer/Util/Url.php +index 5d703a2..88a8a4e 100644 +--- a/src/Composer/Util/Url.php ++++ b/src/Composer/Util/Url.php +@@ -111,8 +111,8 @@ class Url + + $url = Preg::replaceCallback('{^(?P[a-z0-9]+://)?(?P[^:/\s@]+):(?P[^@\s/]+)@}i', static function ($m): string { + assert(is_string($m['user'])); +- // if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx) we obfuscate that +- if (Preg::isMatch('{^([a-f0-9]{12,}|gh[a-z]_[a-zA-Z0-9_]+|github_pat_[a-zA-Z0-9_]+)$}', $m['user'])) { ++ // if the username looks like a long (12char+) hex string, or a modern github token (e.g. ghp_xxx, github_pat_xxx) we obfuscate that ++ if (Preg::isMatch(GitHub::GITHUB_TOKEN_REGEX, $m['user'])) { + return $m['prefix'].'***:***@'; + } + +diff --git a/tests/Composer/Test/Util/ProcessExecutorTest.php b/tests/Composer/Test/Util/ProcessExecutorTest.php +index c42f7de..e4bc098 100644 +--- a/tests/Composer/Test/Util/ProcessExecutorTest.php ++++ b/tests/Composer/Test/Util/ProcessExecutorTest.php +@@ -83,6 +83,7 @@ class ProcessExecutorTest extends TestCase + ['echo https://foo:bar@example.org/', 'echo https://foo:***@example.org/'], + ['echo http://foo@example.org', 'echo http://foo@example.org'], + ['echo http://abcdef1234567890234578:x-oauth-token@github.com/', 'echo http://***:***@github.com/'], ++ ['echo http://github_pat_1234567890abcdefghijkl_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW:x-oauth-token@github.com/', 'echo http://***:***@github.com/'], + ["svn ls --verbose --non-interactive --username 'foo' --password 'bar' 'https://foo.example.org/svn/'", "svn ls --verbose --non-interactive --username 'foo' --password '***' 'https://foo.example.org/svn/'"], + ["svn ls --verbose --non-interactive --username 'foo' --password 'bar \'bar' 'https://foo.example.org/svn/'", "svn ls --verbose --non-interactive --username 'foo' --password '***' 'https://foo.example.org/svn/'"], + ]; diff -Nru composer-2.5.5/debian/patches/0023-Fix-regexp-to-support-new-GitHub-installation-tokens.patch composer-2.5.5/debian/patches/0023-Fix-regexp-to-support-new-GitHub-installation-tokens.patch --- composer-2.5.5/debian/patches/0023-Fix-regexp-to-support-new-GitHub-installation-tokens.patch 1970-01-01 00:00:00.000000000 +0000 +++ composer-2.5.5/debian/patches/0023-Fix-regexp-to-support-new-GitHub-installation-tokens.patch 2026-05-14 09:28:34.000000000 +0000 @@ -0,0 +1,148 @@ +From: Philipp Scheit +Date: Wed, 13 May 2026 09:00:52 +0200 +Subject: Fix regexp to support new GitHub installation tokens format (#12853) + +Origin: upstream, https://github.com/composer/composer/commit/37872360dc9c0f8befc12f741e98db2aa9b1827c +Bug: https://github.com/composer/composer/security/advisories/GHSA-f9f8-rm49-7jv2 +--- + src/Composer/IO/BaseIO.php | 5 +- + src/Composer/Util/GitHub.php | 2 +- + tests/Composer/Test/IO/BaseIOTest.php | 99 +++++++++++++++++++++++++++++++++++ + 3 files changed, 103 insertions(+), 3 deletions(-) + create mode 100644 tests/Composer/Test/IO/BaseIOTest.php + +diff --git a/src/Composer/IO/BaseIO.php b/src/Composer/IO/BaseIO.php +index 710f5a1..5fe4d16 100644 +--- a/src/Composer/IO/BaseIO.php ++++ b/src/Composer/IO/BaseIO.php +@@ -135,9 +135,10 @@ abstract class BaseIO implements IOInterface + + // allowed chars for GH tokens are from https://github.blog/changelog/2021-03-04-authentication-token-format-updates/ + // plus dots which were at some point used for GH app integration tokens +- if (!Preg::isMatch('{^[.A-Za-z0-9_]+$}', $token)) { +- throw new \UnexpectedValueException('Your github oauth token for '.$domain.' contains invalid characters: "'.$token.'"'); ++ if (!Preg::isMatch('{^[.A-Za-z0-9_-]+$}', $token)) { ++ throw new \UnexpectedValueException('Your github oauth token for '.$domain.' contains invalid characters.'); + } ++ + $this->checkAndSetAuthentication($domain, $token, 'x-oauth-basic'); + } + +diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php +index 950be24..2964e7e 100644 +--- a/src/Composer/Util/GitHub.php ++++ b/src/Composer/Util/GitHub.php +@@ -23,7 +23,7 @@ use Composer\Pcre\Preg; + */ + class GitHub + { +- public const GITHUB_TOKEN_REGEX = '{^([a-f0-9]{12,}|gh[a-z]_[a-zA-Z0-9_]+|github_pat_[a-zA-Z0-9_]+)$}'; ++ public const GITHUB_TOKEN_REGEX = '{^([a-f0-9]{12,}|gh[a-z]_[a-zA-Z0-9_.-]+|github_pat_[a-zA-Z0-9_]+)$}'; + + /** @var IOInterface */ + protected $io; +diff --git a/tests/Composer/Test/IO/BaseIOTest.php b/tests/Composer/Test/IO/BaseIOTest.php +new file mode 100644 +index 0000000..b899d86 +--- /dev/null ++++ b/tests/Composer/Test/IO/BaseIOTest.php +@@ -0,0 +1,99 @@ ++ ++ * Jordi Boggiano ++ * ++ * For the full copyright and license information, please view the LICENSE ++ * file that was distributed with this source code. ++ */ ++ ++namespace Composer\Test\IO; ++ ++use Composer\Config; ++use Composer\IO\BufferIO; ++use Composer\Test\TestCase; ++ ++class BaseIOTest extends TestCase ++{ ++ /** ++ * @dataProvider provideValidGithubTokens ++ */ ++ public function testLoadConfigurationAcceptsValidGithubToken(string $token): void ++ { ++ $io = new BufferIO(); ++ $config = new Config(false); ++ $config->merge(['config' => ['github-oauth' => ['github.com' => $token]]]); ++ ++ $io->loadConfiguration($config); ++ ++ $auth = $io->getAuthentication('github.com'); ++ self::assertSame($token, $auth['username']); ++ self::assertSame('x-oauth-basic', $auth['password']); ++ } ++ ++ /** @return array */ ++ public static function provideValidGithubTokens(): array ++ { ++ return [ ++ 'legacy 40-hex PAT' => ['8a7f2c1bdc4e9f06a3b7c2e9d4f1a8b6c5d7e0f2'], ++ 'ghp_ flat token' => ['ghp_n3K9wQ2eL5bV8mY1pX4cZ7aR0fT6sH3uJ8oI'], ++ 'gho_ flat token' => ['gho_M2pQ7vR4xL9eK6bN1cT8aZ0sJ3wY5fH7uG2d'], ++ 'ghu_ flat token' => ['ghu_R5tY8wA1xC4eK7bN0pV3mL6sH9uJ2gD5fQ8z'], ++ 'ghs_ flat token' => ['ghs_K7bN3pV5mL8eR2tY9wA1xC4sH6uJ0gD3fQ8z'], ++ 'ghr_ flat token' => ['ghr_X9aZ2sJ5wY8fH1uG4dR7bN0pV3mL6eK2tQ5c'], ++ // shivammathur/setup-php style: ghs__.. ++ // base64url alphabet includes '-' which the old regex rejected ++ 'ghs_ structured installation token (jwt body)' => [ ++ 'ghs_1234567890_eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJjb21wb3NlciJ9.aB-cDef_GHIjkl-mnoPQR0123456', ++ ], ++ 'github_pat_ fine-grained PAT' => [ ++ 'github_pat_11ABCDEFG0aB1cD2eF3gH4_n3K9wQ2eL5bV8mY1pX4cZ7aR0fT6sH3uJ8oI2pQ7vR4xL9eK6bN', ++ ], ++ ]; ++ } ++ ++ /** ++ * @dataProvider provideUrlBreakingGithubTokens ++ */ ++ public function testLoadConfigurationRejectsTokenWithUrlBreakingCharacters(string $token, string $offending): void ++ { ++ $io = new BufferIO(); ++ $config = new Config(false); ++ $config->merge(['config' => ['github-oauth' => ['github.com' => $token]]]); ++ ++ try { ++ $io->loadConfiguration($config); ++ self::fail('Expected loadConfiguration to reject token containing '.$offending); ++ } catch (\UnexpectedValueException $e) { ++ // Defect #1: the rejected token must not be echoed back into the ++ // exception message — Symfony Console renders it to stderr and CI ++ // log shippers / GitHub Actions secret masking do not reliably ++ // strip it from the framed error block. ++ self::assertStringNotContainsString( ++ $token, ++ $e->getMessage(), ++ 'Exception message must not leak the rejected token value.' ++ ); ++ } ++ } ++ ++ /** @return array */ ++ public static function provideUrlBreakingGithubTokens(): array ++ { ++ return [ ++ 'contains @ (userinfo separator)' => ['ghp_AAAA@evil.example.com', '@'], ++ 'contains : (basic-auth user:pass split)' => ['ghp_AAAA:extra', ':'], ++ 'contains / (path separator)' => ['ghp_AAA/BBB', '/'], ++ 'contains backslash' => ['ghp_AAA\\BBB', '\\'], ++ 'contains ? (query separator)' => ['ghp_AAA?x=1', '?'], ++ 'contains # (fragment)' => ['ghp_AAA#frag', '#'], ++ 'contains space' => ['ghp_AAA BBB', 'space'], ++ 'contains tab' => ["ghp_AAA\tBBB", 'tab'], ++ 'contains CR' => ["ghp_AAA\rBBB", 'CR'], ++ 'contains LF (header injection)' => ["ghp_AAA\nX-Evil: 1", 'LF'], ++ ]; ++ } ++} diff -Nru composer-2.5.5/debian/patches/series composer-2.5.5/debian/patches/series --- composer-2.5.5/debian/patches/series 2026-04-15 10:33:06.000000000 +0000 +++ composer-2.5.5/debian/patches/series 2026-05-14 09:28:34.000000000 +0000 @@ -19,3 +19,5 @@ 0019-Merge-commit-from-fork.patch 0020-Merge-commit-from-fork.patch 0021-Merge-pull-request-from-GHSA-jm6m-4632-36hf.patch +0022-ProcessExecutor-mask-GitHub-fine-grained-access-toke.patch +0023-Fix-regexp-to-support-new-GitHub-installation-tokens.patch