Version in base suite: 2.0.9-2+deb11u1 Base version: composer_2.0.9-2+deb11u1 Target version: composer_2.0.9-2+deb11u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/c/composer/composer_2.0.9-2+deb11u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/c/composer/composer_2.0.9-2+deb11u2.dsc autoload.php.tpl | 24 changelog | 12 clean | 1 patches/0001-Use-homade-autoload-for-CLI.patch | 4 patches/0013-Import-Pcre.patch | 1079 ++++++++++ patches/0014-Merge-pull-request-from-GHSA-7c6p-848j-wh5h.patch | 410 +++ patches/series | 2 rules | 21 8 files changed, 1535 insertions(+), 18 deletions(-) diff -Nru composer-2.0.9/debian/autoload.php.tpl composer-2.0.9/debian/autoload.php.tpl --- composer-2.0.9/debian/autoload.php.tpl 2022-05-29 09:00:09.000000000 +0000 +++ composer-2.0.9/debian/autoload.php.tpl 2024-02-18 08:05:37.000000000 +0000 @@ -1,17 +1,17 @@ Sun, 18 Feb 2024 09:05:37 +0100 + composer (2.0.9-2+deb11u1) bullseye; urgency=medium * Fix code injection vulnerability [CVE-2022-24828] (Closes: #1009960) diff -Nru composer-2.0.9/debian/clean composer-2.0.9/debian/clean --- composer-2.0.9/debian/clean 2022-05-29 09:00:09.000000000 +0000 +++ composer-2.0.9/debian/clean 2024-02-18 08:05:37.000000000 +0000 @@ -1,3 +1,4 @@ .phpunit.result.cache +build/ src/Composer/autoload.php vendor/ diff -Nru composer-2.0.9/debian/patches/0001-Use-homade-autoload-for-CLI.patch composer-2.0.9/debian/patches/0001-Use-homade-autoload-for-CLI.patch --- composer-2.0.9/debian/patches/0001-Use-homade-autoload-for-CLI.patch 2022-05-29 09:02:53.000000000 +0000 +++ composer-2.0.9/debian/patches/0001-Use-homade-autoload-for-CLI.patch 2024-02-18 08:05:37.000000000 +0000 @@ -8,7 +8,7 @@ 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/composer b/bin/composer -index cadff6d..5b1c87c 100755 +index cadff6d..5a0519d 100755 --- a/bin/composer +++ b/bin/composer @@ -1,4 +1,4 @@ @@ -22,7 +22,7 @@ setlocale(LC_ALL, 'C'); -require __DIR__.'/../src/bootstrap.php'; -+require 'Composer/autoload.php'; ++require __DIR__ . '/../share/php/Composer/autoload.php'; use Composer\Console\Application; use Composer\XdebugHandler\XdebugHandler; diff -Nru composer-2.0.9/debian/patches/0013-Import-Pcre.patch composer-2.0.9/debian/patches/0013-Import-Pcre.patch --- composer-2.0.9/debian/patches/0013-Import-Pcre.patch 1970-01-01 00:00:00.000000000 +0000 +++ composer-2.0.9/debian/patches/0013-Import-Pcre.patch 2024-02-18 08:05:37.000000000 +0000 @@ -0,0 +1,1079 @@ +From: =?utf-8?q?David_Pr=C3=A9vot?= +Date: Sat, 17 Feb 2024 12:41:04 +0100 +Subject: Import Pcre + +--- + src/Composer/Pcre/MatchAllResult.php | 46 +++ + src/Composer/Pcre/MatchAllStrictGroupsResult.php | 46 +++ + src/Composer/Pcre/MatchAllWithOffsetsResult.php | 48 +++ + src/Composer/Pcre/MatchResult.php | 39 ++ + src/Composer/Pcre/MatchStrictGroupsResult.php | 39 ++ + src/Composer/Pcre/MatchWithOffsetsResult.php | 41 ++ + src/Composer/Pcre/PcreException.php | 60 +++ + src/Composer/Pcre/Preg.php | 428 +++++++++++++++++++++ + src/Composer/Pcre/Regex.php | 174 +++++++++ + src/Composer/Pcre/ReplaceResult.php | 43 +++ + src/Composer/Pcre/UnexpectedNullMatchException.php | 20 + + 11 files changed, 984 insertions(+) + create mode 100644 src/Composer/Pcre/MatchAllResult.php + create mode 100644 src/Composer/Pcre/MatchAllStrictGroupsResult.php + create mode 100644 src/Composer/Pcre/MatchAllWithOffsetsResult.php + create mode 100644 src/Composer/Pcre/MatchResult.php + create mode 100644 src/Composer/Pcre/MatchStrictGroupsResult.php + create mode 100644 src/Composer/Pcre/MatchWithOffsetsResult.php + create mode 100644 src/Composer/Pcre/PcreException.php + create mode 100644 src/Composer/Pcre/Preg.php + create mode 100644 src/Composer/Pcre/Regex.php + create mode 100644 src/Composer/Pcre/ReplaceResult.php + create mode 100644 src/Composer/Pcre/UnexpectedNullMatchException.php + +diff --git a/src/Composer/Pcre/MatchAllResult.php b/src/Composer/Pcre/MatchAllResult.php +new file mode 100644 +index 0000000..4310c53 +--- /dev/null ++++ b/src/Composer/Pcre/MatchAllResult.php +@@ -0,0 +1,46 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++final class MatchAllResult ++{ ++ /** ++ * An array of match group => list of matched strings ++ * ++ * @readonly ++ * @var array> ++ */ ++ public $matches; ++ ++ /** ++ * @readonly ++ * @var 0|positive-int ++ */ ++ public $count; ++ ++ /** ++ * @readonly ++ * @var bool ++ */ ++ public $matched; ++ ++ /** ++ * @param 0|positive-int $count ++ * @param array> $matches ++ */ ++ public function __construct(int $count, array $matches) ++ { ++ $this->matches = $matches; ++ $this->matched = (bool) $count; ++ $this->count = $count; ++ } ++} +diff --git a/src/Composer/Pcre/MatchAllStrictGroupsResult.php b/src/Composer/Pcre/MatchAllStrictGroupsResult.php +new file mode 100644 +index 0000000..69dcd06 +--- /dev/null ++++ b/src/Composer/Pcre/MatchAllStrictGroupsResult.php +@@ -0,0 +1,46 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++final class MatchAllStrictGroupsResult ++{ ++ /** ++ * An array of match group => list of matched strings ++ * ++ * @readonly ++ * @var array> ++ */ ++ public $matches; ++ ++ /** ++ * @readonly ++ * @var 0|positive-int ++ */ ++ public $count; ++ ++ /** ++ * @readonly ++ * @var bool ++ */ ++ public $matched; ++ ++ /** ++ * @param 0|positive-int $count ++ * @param array> $matches ++ */ ++ public function __construct(int $count, array $matches) ++ { ++ $this->matches = $matches; ++ $this->matched = (bool) $count; ++ $this->count = $count; ++ } ++} +diff --git a/src/Composer/Pcre/MatchAllWithOffsetsResult.php b/src/Composer/Pcre/MatchAllWithOffsetsResult.php +new file mode 100644 +index 0000000..032a02c +--- /dev/null ++++ b/src/Composer/Pcre/MatchAllWithOffsetsResult.php +@@ -0,0 +1,48 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++final class MatchAllWithOffsetsResult ++{ ++ /** ++ * An array of match group => list of matches, every match being a pair of string matched + offset in bytes (or -1 if no match) ++ * ++ * @readonly ++ * @var array> ++ * @phpstan-var array}>> ++ */ ++ public $matches; ++ ++ /** ++ * @readonly ++ * @var 0|positive-int ++ */ ++ public $count; ++ ++ /** ++ * @readonly ++ * @var bool ++ */ ++ public $matched; ++ ++ /** ++ * @param 0|positive-int $count ++ * @param array> $matches ++ * @phpstan-param array}>> $matches ++ */ ++ public function __construct(int $count, array $matches) ++ { ++ $this->matches = $matches; ++ $this->matched = (bool) $count; ++ $this->count = $count; ++ } ++} +diff --git a/src/Composer/Pcre/MatchResult.php b/src/Composer/Pcre/MatchResult.php +new file mode 100644 +index 0000000..e951a5e +--- /dev/null ++++ b/src/Composer/Pcre/MatchResult.php +@@ -0,0 +1,39 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++final class MatchResult ++{ ++ /** ++ * An array of match group => string matched ++ * ++ * @readonly ++ * @var array ++ */ ++ public $matches; ++ ++ /** ++ * @readonly ++ * @var bool ++ */ ++ public $matched; ++ ++ /** ++ * @param 0|positive-int $count ++ * @param array $matches ++ */ ++ public function __construct(int $count, array $matches) ++ { ++ $this->matches = $matches; ++ $this->matched = (bool) $count; ++ } ++} +diff --git a/src/Composer/Pcre/MatchStrictGroupsResult.php b/src/Composer/Pcre/MatchStrictGroupsResult.php +new file mode 100644 +index 0000000..126ee62 +--- /dev/null ++++ b/src/Composer/Pcre/MatchStrictGroupsResult.php +@@ -0,0 +1,39 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++final class MatchStrictGroupsResult ++{ ++ /** ++ * An array of match group => string matched ++ * ++ * @readonly ++ * @var array ++ */ ++ public $matches; ++ ++ /** ++ * @readonly ++ * @var bool ++ */ ++ public $matched; ++ ++ /** ++ * @param 0|positive-int $count ++ * @param array $matches ++ */ ++ public function __construct(int $count, array $matches) ++ { ++ $this->matches = $matches; ++ $this->matched = (bool) $count; ++ } ++} +diff --git a/src/Composer/Pcre/MatchWithOffsetsResult.php b/src/Composer/Pcre/MatchWithOffsetsResult.php +new file mode 100644 +index 0000000..ba4d4bc +--- /dev/null ++++ b/src/Composer/Pcre/MatchWithOffsetsResult.php +@@ -0,0 +1,41 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++final class MatchWithOffsetsResult ++{ ++ /** ++ * An array of match group => pair of string matched + offset in bytes (or -1 if no match) ++ * ++ * @readonly ++ * @var array ++ * @phpstan-var array}> ++ */ ++ public $matches; ++ ++ /** ++ * @readonly ++ * @var bool ++ */ ++ public $matched; ++ ++ /** ++ * @param 0|positive-int $count ++ * @param array $matches ++ * @phpstan-param array}> $matches ++ */ ++ public function __construct(int $count, array $matches) ++ { ++ $this->matches = $matches; ++ $this->matched = (bool) $count; ++ } ++} +diff --git a/src/Composer/Pcre/PcreException.php b/src/Composer/Pcre/PcreException.php +new file mode 100644 +index 0000000..218b2f2 +--- /dev/null ++++ b/src/Composer/Pcre/PcreException.php +@@ -0,0 +1,60 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++class PcreException extends \RuntimeException ++{ ++ /** ++ * @param string $function ++ * @param string|string[] $pattern ++ * @return self ++ */ ++ public static function fromFunction($function, $pattern) ++ { ++ $code = preg_last_error(); ++ ++ if (is_array($pattern)) { ++ $pattern = implode(', ', $pattern); ++ } ++ ++ return new PcreException($function.'(): failed executing "'.$pattern.'": '.self::pcreLastErrorMessage($code), $code); ++ } ++ ++ /** ++ * @param int $code ++ * @return string ++ */ ++ private static function pcreLastErrorMessage($code) ++ { ++ if (function_exists('preg_last_error_msg')) { ++ return preg_last_error_msg(); ++ } ++ ++ // older php versions did not set the code properly in all cases ++ if (PHP_VERSION_ID < 70201 && $code === 0) { ++ return 'UNDEFINED_ERROR'; ++ } ++ ++ $constants = get_defined_constants(true); ++ if (!isset($constants['pcre'])) { ++ return 'UNDEFINED_ERROR'; ++ } ++ ++ foreach ($constants['pcre'] as $const => $val) { ++ if ($val === $code && substr($const, -6) === '_ERROR') { ++ return $const; ++ } ++ } ++ ++ return 'UNDEFINED_ERROR'; ++ } ++} +diff --git a/src/Composer/Pcre/Preg.php b/src/Composer/Pcre/Preg.php +new file mode 100644 +index 0000000..6fe0172 +--- /dev/null ++++ b/src/Composer/Pcre/Preg.php +@@ -0,0 +1,428 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++class Preg ++{ ++ /** @internal */ ++ public const ARRAY_MSG = '$subject as an array is not supported. You can use \'foreach\' instead.'; ++ /** @internal */ ++ public const INVALID_TYPE_MSG = '$subject must be a string, %s given.'; ++ ++ /** ++ * @param non-empty-string $pattern ++ * @param array $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * @return 0|1 ++ * ++ * @param-out array $matches ++ */ ++ public static function match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int ++ { ++ self::checkOffsetCapture($flags, 'matchWithOffsets'); ++ ++ $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); ++ if ($result === false) { ++ throw PcreException::fromFunction('preg_match', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * Variant of `match()` which outputs non-null matches (or throws) ++ * ++ * @param non-empty-string $pattern ++ * @param array $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * @return 0|1 ++ * @throws UnexpectedNullMatchException ++ * ++ * @param-out array $matches ++ */ ++ public static function matchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int ++ { ++ $result = self::match($pattern, $subject, $matchesInternal, $flags, $offset); ++ $matches = self::enforceNonNullMatches($pattern, $matchesInternal, 'match'); ++ ++ return $result; ++ } ++ ++ /** ++ * Runs preg_match with PREG_OFFSET_CAPTURE ++ * ++ * @param non-empty-string $pattern ++ * @param array $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_OFFSET_CAPTURE are always set, no other flags are supported ++ * @return 0|1 ++ * ++ * @param-out array}> $matches ++ */ ++ public static function matchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int ++ { ++ $result = preg_match($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); ++ if ($result === false) { ++ throw PcreException::fromFunction('preg_match', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * @param non-empty-string $pattern ++ * @param array> $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * @return 0|positive-int ++ * ++ * @param-out array> $matches ++ */ ++ public static function matchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int ++ { ++ self::checkOffsetCapture($flags, 'matchAllWithOffsets'); ++ self::checkSetOrder($flags); ++ ++ $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL, $offset); ++ if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false ++ throw PcreException::fromFunction('preg_match_all', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * Variant of `match()` which outputs non-null matches (or throws) ++ * ++ * @param non-empty-string $pattern ++ * @param array> $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * @return 0|positive-int ++ * @throws UnexpectedNullMatchException ++ * ++ * @param-out array> $matches ++ */ ++ public static function matchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int ++ { ++ $result = self::matchAll($pattern, $subject, $matchesInternal, $flags, $offset); ++ $matches = self::enforceNonNullMatchAll($pattern, $matchesInternal, 'matchAll'); ++ ++ return $result; ++ } ++ ++ /** ++ * Runs preg_match_all with PREG_OFFSET_CAPTURE ++ * ++ * @param non-empty-string $pattern ++ * @param array> $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported ++ * @return 0|positive-int ++ * ++ * @param-out array}>> $matches ++ */ ++ public static function matchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): int ++ { ++ self::checkSetOrder($flags); ++ ++ $result = preg_match_all($pattern, $subject, $matches, $flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, $offset); ++ if (!is_int($result)) { // PHP < 8 may return null, 8+ returns int|false ++ throw PcreException::fromFunction('preg_match_all', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * @param string|string[] $pattern ++ * @param string|string[] $replacement ++ * @param string $subject ++ * @param int $count Set by method ++ * ++ * @param-out int<0, max> $count ++ */ ++ public static function replace($pattern, $replacement, $subject, int $limit = -1, int &$count = null): string ++ { ++ if (!is_scalar($subject)) { ++ if (is_array($subject)) { ++ throw new \InvalidArgumentException(static::ARRAY_MSG); ++ } ++ ++ throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); ++ } ++ ++ $result = preg_replace($pattern, $replacement, $subject, $limit, $count); ++ if ($result === null) { ++ throw PcreException::fromFunction('preg_replace', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * @param string|string[] $pattern ++ * @param callable(array): string $replacement ++ * @param string $subject ++ * @param int $count Set by method ++ * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set ++ * ++ * @param-out int<0, max> $count ++ */ ++ public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int &$count = null, int $flags = 0): string ++ { ++ if (!is_scalar($subject)) { ++ if (is_array($subject)) { ++ throw new \InvalidArgumentException(static::ARRAY_MSG); ++ } ++ ++ throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); ++ } ++ ++ $result = preg_replace_callback($pattern, $replacement, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); ++ if ($result === null) { ++ throw PcreException::fromFunction('preg_replace_callback', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * Variant of `replaceCallback()` which outputs non-null matches (or throws) ++ * ++ * @param string $pattern ++ * @param callable(array): string $replacement ++ * @param string $subject ++ * @param int $count Set by method ++ * @param int-mask $flags PREG_OFFSET_CAPTURE or PREG_UNMATCHED_AS_NULL, only available on PHP 7.4+ ++ * ++ * @param-out int<0, max> $count ++ */ ++ public static function replaceCallbackStrictGroups(string $pattern, callable $replacement, $subject, int $limit = -1, int &$count = null, int $flags = 0): string ++ { ++ return self::replaceCallback($pattern, function (array $matches) use ($pattern, $replacement) { ++ return $replacement(self::enforceNonNullMatches($pattern, $matches, 'replaceCallback')); ++ }, $subject, $limit, $count, $flags); ++ } ++ ++ /** ++ * @param array): string> $pattern ++ * @param string $subject ++ * @param int $count Set by method ++ * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set ++ * ++ * @param-out int<0, max> $count ++ */ ++ public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int &$count = null, int $flags = 0): string ++ { ++ if (!is_scalar($subject)) { ++ if (is_array($subject)) { ++ throw new \InvalidArgumentException(static::ARRAY_MSG); ++ } ++ ++ throw new \TypeError(sprintf(static::INVALID_TYPE_MSG, gettype($subject))); ++ } ++ ++ $result = preg_replace_callback_array($pattern, $subject, $limit, $count, $flags | PREG_UNMATCHED_AS_NULL); ++ if ($result === null) { ++ $pattern = array_keys($pattern); ++ throw PcreException::fromFunction('preg_replace_callback_array', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * @param int-mask $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE ++ * @return list ++ */ ++ public static function split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array ++ { ++ if (($flags & PREG_SPLIT_OFFSET_CAPTURE) !== 0) { ++ throw new \InvalidArgumentException('PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead'); ++ } ++ ++ $result = preg_split($pattern, $subject, $limit, $flags); ++ if ($result === false) { ++ throw PcreException::fromFunction('preg_split', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * @param int-mask $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_OFFSET_CAPTURE is always set ++ * @return list ++ * @phpstan-return list}> ++ */ ++ public static function splitWithOffsets(string $pattern, string $subject, int $limit = -1, int $flags = 0): array ++ { ++ $result = preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE); ++ if ($result === false) { ++ throw PcreException::fromFunction('preg_split', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * @template T of string|\Stringable ++ * @param string $pattern ++ * @param array $array ++ * @param int-mask $flags PREG_GREP_INVERT ++ * @return array ++ */ ++ public static function grep(string $pattern, array $array, int $flags = 0): array ++ { ++ $result = preg_grep($pattern, $array, $flags); ++ if ($result === false) { ++ throw PcreException::fromFunction('preg_grep', $pattern); ++ } ++ ++ return $result; ++ } ++ ++ /** ++ * Variant of match() which returns a bool instead of int ++ * ++ * @param non-empty-string $pattern ++ * @param array $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * ++ * @param-out array $matches ++ */ ++ public static function isMatch(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool ++ { ++ return (bool) static::match($pattern, $subject, $matches, $flags, $offset); ++ } ++ ++ /** ++ * Variant of `isMatch()` which outputs non-null matches (or throws) ++ * ++ * @param non-empty-string $pattern ++ * @param array $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * @throws UnexpectedNullMatchException ++ * ++ * @param-out array $matches ++ */ ++ public static function isMatchStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool ++ { ++ return (bool) self::matchStrictGroups($pattern, $subject, $matches, $flags, $offset); ++ } ++ ++ /** ++ * Variant of matchAll() which returns a bool instead of int ++ * ++ * @param non-empty-string $pattern ++ * @param array> $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * ++ * @param-out array> $matches ++ */ ++ public static function isMatchAll(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool ++ { ++ return (bool) static::matchAll($pattern, $subject, $matches, $flags, $offset); ++ } ++ ++ /** ++ * Variant of `isMatchAll()` which outputs non-null matches (or throws) ++ * ++ * @param non-empty-string $pattern ++ * @param array> $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * ++ * @param-out array> $matches ++ */ ++ public static function isMatchAllStrictGroups(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): bool ++ { ++ return (bool) self::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset); ++ } ++ ++ /** ++ * Variant of matchWithOffsets() which returns a bool instead of int ++ * ++ * Runs preg_match with PREG_OFFSET_CAPTURE ++ * ++ * @param non-empty-string $pattern ++ * @param array $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * ++ * @param-out array}> $matches ++ */ ++ public static function isMatchWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool ++ { ++ return (bool) static::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); ++ } ++ ++ /** ++ * Variant of matchAllWithOffsets() which returns a bool instead of int ++ * ++ * Runs preg_match_all with PREG_OFFSET_CAPTURE ++ * ++ * @param non-empty-string $pattern ++ * @param array> $matches Set by method ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * ++ * @param-out array}>> $matches ++ */ ++ public static function isMatchAllWithOffsets(string $pattern, string $subject, ?array &$matches, int $flags = 0, int $offset = 0): bool ++ { ++ return (bool) static::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); ++ } ++ ++ private static function checkOffsetCapture(int $flags, string $useFunctionName): void ++ { ++ if (($flags & PREG_OFFSET_CAPTURE) !== 0) { ++ throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use ' . $useFunctionName . '() instead'); ++ } ++ } ++ ++ private static function checkSetOrder(int $flags): void ++ { ++ if (($flags & PREG_SET_ORDER) !== 0) { ++ throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the type of $matches'); ++ } ++ } ++ ++ /** ++ * @param array $matches ++ * @return array ++ * @throws UnexpectedNullMatchException ++ */ ++ private static function enforceNonNullMatches(string $pattern, array $matches, string $variantMethod) ++ { ++ foreach ($matches as $group => $match) { ++ if (null === $match) { ++ throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.'); ++ } ++ } ++ ++ /** @var array */ ++ return $matches; ++ } ++ ++ /** ++ * @param array> $matches ++ * @return array> ++ * @throws UnexpectedNullMatchException ++ */ ++ private static function enforceNonNullMatchAll(string $pattern, array $matches, string $variantMethod) ++ { ++ foreach ($matches as $group => $groupMatches) { ++ foreach ($groupMatches as $match) { ++ if (null === $match) { ++ throw new UnexpectedNullMatchException('Pattern "'.$pattern.'" had an unexpected unmatched group "'.$group.'", make sure the pattern always matches or use '.$variantMethod.'() instead.'); ++ } ++ } ++ } ++ ++ /** @var array> */ ++ return $matches; ++ } ++} +diff --git a/src/Composer/Pcre/Regex.php b/src/Composer/Pcre/Regex.php +new file mode 100644 +index 0000000..112fa32 +--- /dev/null ++++ b/src/Composer/Pcre/Regex.php +@@ -0,0 +1,174 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++class Regex ++{ ++ /** ++ * @param non-empty-string $pattern ++ */ ++ public static function isMatch(string $pattern, string $subject, int $offset = 0): bool ++ { ++ return (bool) Preg::match($pattern, $subject, $matches, 0, $offset); ++ } ++ ++ /** ++ * @param non-empty-string $pattern ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ */ ++ public static function match(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchResult ++ { ++ self::checkOffsetCapture($flags, 'matchWithOffsets'); ++ ++ $count = Preg::match($pattern, $subject, $matches, $flags, $offset); ++ ++ return new MatchResult($count, $matches); ++ } ++ ++ /** ++ * Variant of `match()` which returns non-null matches (or throws) ++ * ++ * @param non-empty-string $pattern ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * @throws UnexpectedNullMatchException ++ */ ++ public static function matchStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchStrictGroupsResult ++ { ++ $count = Preg::matchStrictGroups($pattern, $subject, $matches, $flags, $offset); ++ ++ return new MatchStrictGroupsResult($count, $matches); ++ } ++ ++ /** ++ * Runs preg_match with PREG_OFFSET_CAPTURE ++ * ++ * @param non-empty-string $pattern ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported ++ */ ++ public static function matchWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchWithOffsetsResult ++ { ++ $count = Preg::matchWithOffsets($pattern, $subject, $matches, $flags, $offset); ++ ++ return new MatchWithOffsetsResult($count, $matches); ++ } ++ ++ /** ++ * @param non-empty-string $pattern ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ */ ++ public static function matchAll(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllResult ++ { ++ self::checkOffsetCapture($flags, 'matchAllWithOffsets'); ++ self::checkSetOrder($flags); ++ ++ $count = Preg::matchAll($pattern, $subject, $matches, $flags, $offset); ++ ++ return new MatchAllResult($count, $matches); ++ } ++ ++ /** ++ * Variant of `matchAll()` which returns non-null matches (or throws) ++ * ++ * @param non-empty-string $pattern ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported ++ * @throws UnexpectedNullMatchException ++ */ ++ public static function matchAllStrictGroups(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllStrictGroupsResult ++ { ++ self::checkOffsetCapture($flags, 'matchAllWithOffsets'); ++ self::checkSetOrder($flags); ++ ++ $count = Preg::matchAllStrictGroups($pattern, $subject, $matches, $flags, $offset); ++ ++ return new MatchAllStrictGroupsResult($count, $matches); ++ } ++ ++ /** ++ * Runs preg_match_all with PREG_OFFSET_CAPTURE ++ * ++ * @param non-empty-string $pattern ++ * @param int-mask $flags PREG_UNMATCHED_AS_NULL and PREG_MATCH_OFFSET are always set, no other flags are supported ++ */ ++ public static function matchAllWithOffsets(string $pattern, string $subject, int $flags = 0, int $offset = 0): MatchAllWithOffsetsResult ++ { ++ self::checkSetOrder($flags); ++ ++ $count = Preg::matchAllWithOffsets($pattern, $subject, $matches, $flags, $offset); ++ ++ return new MatchAllWithOffsetsResult($count, $matches); ++ } ++ /** ++ * @param string|string[] $pattern ++ * @param string|string[] $replacement ++ * @param string $subject ++ */ ++ public static function replace($pattern, $replacement, $subject, int $limit = -1): ReplaceResult ++ { ++ $result = Preg::replace($pattern, $replacement, $subject, $limit, $count); ++ ++ return new ReplaceResult($count, $result); ++ } ++ ++ /** ++ * @param string|string[] $pattern ++ * @param callable(array): string $replacement ++ * @param string $subject ++ * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set ++ */ ++ public static function replaceCallback($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult ++ { ++ $result = Preg::replaceCallback($pattern, $replacement, $subject, $limit, $count, $flags); ++ ++ return new ReplaceResult($count, $result); ++ } ++ ++ /** ++ * Variant of `replaceCallback()` which outputs non-null matches (or throws) ++ * ++ * @param string $pattern ++ * @param callable(array): string $replacement ++ * @param string $subject ++ * @param int-mask $flags PREG_OFFSET_CAPTURE or PREG_UNMATCHED_AS_NULL, only available on PHP 7.4+ ++ */ ++ public static function replaceCallbackStrictGroups($pattern, callable $replacement, $subject, int $limit = -1, int $flags = 0): ReplaceResult ++ { ++ $result = Preg::replaceCallbackStrictGroups($pattern, $replacement, $subject, $limit, $count, $flags); ++ ++ return new ReplaceResult($count, $result); ++ } ++ ++ /** ++ * @param array): string> $pattern ++ * @param string $subject ++ * @param int-mask $flags PREG_OFFSET_CAPTURE is supported, PREG_UNMATCHED_AS_NULL is always set ++ */ ++ public static function replaceCallbackArray(array $pattern, $subject, int $limit = -1, int $flags = 0): ReplaceResult ++ { ++ $result = Preg::replaceCallbackArray($pattern, $subject, $limit, $count, $flags); ++ ++ return new ReplaceResult($count, $result); ++ } ++ ++ private static function checkOffsetCapture(int $flags, string $useFunctionName): void ++ { ++ if (($flags & PREG_OFFSET_CAPTURE) !== 0) { ++ throw new \InvalidArgumentException('PREG_OFFSET_CAPTURE is not supported as it changes the return type, use '.$useFunctionName.'() instead'); ++ } ++ } ++ ++ private static function checkSetOrder(int $flags): void ++ { ++ if (($flags & PREG_SET_ORDER) !== 0) { ++ throw new \InvalidArgumentException('PREG_SET_ORDER is not supported as it changes the return type'); ++ } ++ } ++} +diff --git a/src/Composer/Pcre/ReplaceResult.php b/src/Composer/Pcre/ReplaceResult.php +new file mode 100644 +index 0000000..3384771 +--- /dev/null ++++ b/src/Composer/Pcre/ReplaceResult.php +@@ -0,0 +1,43 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++final class ReplaceResult ++{ ++ /** ++ * @readonly ++ * @var string ++ */ ++ public $result; ++ ++ /** ++ * @readonly ++ * @var 0|positive-int ++ */ ++ public $count; ++ ++ /** ++ * @readonly ++ * @var bool ++ */ ++ public $matched; ++ ++ /** ++ * @param 0|positive-int $count ++ */ ++ public function __construct(int $count, string $result) ++ { ++ $this->count = $count; ++ $this->matched = (bool) $count; ++ $this->result = $result; ++ } ++} +diff --git a/src/Composer/Pcre/UnexpectedNullMatchException.php b/src/Composer/Pcre/UnexpectedNullMatchException.php +new file mode 100644 +index 0000000..f123828 +--- /dev/null ++++ b/src/Composer/Pcre/UnexpectedNullMatchException.php +@@ -0,0 +1,20 @@ ++ ++ * ++ * For the full copyright and license information, please view ++ * the LICENSE file that was distributed with this source code. ++ */ ++ ++namespace Composer\Pcre; ++ ++class UnexpectedNullMatchException extends PcreException ++{ ++ public static function fromFunction($function, $pattern) ++ { ++ throw new \LogicException('fromFunction should not be called on '.self::class.', use '.PcreException::class); ++ } ++} diff -Nru composer-2.0.9/debian/patches/0014-Merge-pull-request-from-GHSA-7c6p-848j-wh5h.patch composer-2.0.9/debian/patches/0014-Merge-pull-request-from-GHSA-7c6p-848j-wh5h.patch --- composer-2.0.9/debian/patches/0014-Merge-pull-request-from-GHSA-7c6p-848j-wh5h.patch 1970-01-01 00:00:00.000000000 +0000 +++ composer-2.0.9/debian/patches/0014-Merge-pull-request-from-GHSA-7c6p-848j-wh5h.patch 2024-02-18 08:05:37.000000000 +0000 @@ -0,0 +1,410 @@ +From: =?utf-8?q?Bastien_Roucari=C3=A8s?= +Date: Sun, 18 Feb 2024 15:03:21 +0000 +Subject: Merge pull request from GHSA-7c6p-848j-wh5h + +* Fix automatic disabling of plugins when running non-interactive as root + +* Fix usage of possibly compromised installed.php/InstalledVersions.php at runtime, refs GHSA-7c6p-848j-wh5h + +* Fix InstalledVersionsTest regression + +Origin: upstream, https://github.com/composer/composer/commit/77e3982918bc1d886843dc3d5e575e7e871b27b7 +Bug: https://github.com/composer/composer/security/advisories/GHSA-7c6p-848j-wh5h +Bug-Debian: https://bugs.debian.org/1063603 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-24821 +--- + src/Composer/Command/BaseCommand.php | 15 +++ + src/Composer/Console/Application.php | 17 +++ + src/Composer/Factory.php | 14 ++- + src/Composer/Repository/FilesystemRepository.php | 76 +++++++++++++- + tests/Composer/Test/InstalledVersionsTest.php | 12 ++- + .../Test/Repository/Fixtures/installed.php | 4 +- + .../Repository/Fixtures/installed_relative.php | 116 +++++++++++++++++++++ + 7 files changed, 245 insertions(+), 9 deletions(-) + create mode 100644 tests/Composer/Test/Repository/Fixtures/installed_relative.php + +diff --git a/src/Composer/Command/BaseCommand.php b/src/Composer/Command/BaseCommand.php +index d4c90a1..322278c 100644 +--- a/src/Composer/Command/BaseCommand.php ++++ b/src/Composer/Command/BaseCommand.php +@@ -132,6 +132,21 @@ abstract class BaseCommand extends Command + { + // initialize a plugin-enabled Composer instance, either local or global + $disablePlugins = $input->hasParameterOption('--no-plugins'); ++ $disableScripts = $input->hasParameterOption('--no-scripts'); ++ ++ $application = parent::getApplication(); ++ if ($application instanceof Application && $application->getDisablePluginsByDefault()) { ++ $disablePlugins = true; ++ } ++ if ($application instanceof Application && $application->getDisableScriptsByDefault()) { ++ $disableScripts = true; ++ } ++ ++ if ($this instanceof SelfUpdateCommand) { ++ $disablePlugins = true; ++ $disableScripts = true; ++ } ++ + $composer = $this->getComposer(false, $disablePlugins); + if (null === $composer) { + $composer = Factory::createGlobal($this->getIO(), $disablePlugins); +diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php +index 9d3557e..43c9009 100644 +--- a/src/Composer/Console/Application.php ++++ b/src/Composer/Console/Application.php +@@ -63,6 +63,7 @@ class Application extends BaseApplication + + private $hasPluginCommands = false; + private $disablePluginsByDefault = false; ++ private $disableScriptsByDefault = false; + + /** + * @var string Store the initial working directory at startup time +@@ -548,6 +549,22 @@ class Application extends BaseApplication + return $commands; + } + ++ /** ++ * @return bool ++ */ ++ public function getDisablePluginsByDefault() ++ { ++ return $this->disablePluginsByDefault; ++ } ++ ++ /** ++ * @return bool ++ */ ++ public function getDisableScriptsByDefault() ++ { ++ return $this->disableScriptsByDefault; ++ } ++ + /** + * Get the working directory at startup time + * +diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php +index cd6662c..5946b85 100644 +--- a/src/Composer/Factory.php ++++ b/src/Composer/Factory.php +@@ -18,6 +18,7 @@ use Composer\IO\IOInterface; + use Composer\Package\Archiver; + use Composer\Package\Version\VersionGuesser; + use Composer\Package\RootPackageInterface; ++use Composer\Repository\FilesystemRepository; + use Composer\Repository\RepositoryManager; + use Composer\Repository\RepositoryFactory; + use Composer\Repository\WritableRepositoryInterface; +@@ -275,7 +276,7 @@ class Factory + * @throws \UnexpectedValueException + * @return Composer + */ +- public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $cwd = null, $fullLoad = true) ++ public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $cwd = null, $fullLoad = true, $disableScripts = false) + { + $cwd = $cwd ?: getcwd(); + +@@ -336,9 +337,14 @@ class Factory + // load auth configs into the IO instance + $io->loadConfiguration($config); + +- // load existing Composer\InstalledVersions instance if available +- if (!class_exists('Composer\InstalledVersions', false) && file_exists($installedVersionsPath = $config->get('vendor-dir').'/composer/InstalledVersions.php')) { +- include $installedVersionsPath; ++ // load existing Composer\InstalledVersions instance if available and scripts/plugins are allowed, as they might need it ++ // we only load if the InstalledVersions class wasn't defined yet so that this is only loaded once ++ if (false === $disablePlugins && false === $disableScripts && !class_exists('Composer\InstalledVersions', false) && file_exists($installedVersionsPath = $config->get('vendor-dir').'/composer/installed.php')) { ++ // force loading the class at this point so it is loaded from the composer phar and not from the vendor dir ++ // as we cannot guarantee integrity of that file ++ if (class_exists('Composer\InstalledVersions')) { ++ FilesystemRepository::safelyLoadInstalledVersions($installedVersionsPath); ++ } + } + } + +diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php +index 2823e6e..628b222 100644 +--- a/src/Composer/Repository/FilesystemRepository.php ++++ b/src/Composer/Repository/FilesystemRepository.php +@@ -18,6 +18,7 @@ use Composer\Package\RootPackageInterface; + use Composer\Package\AliasPackage; + use Composer\Package\Dumper\ArrayDumper; + use Composer\Installer\InstallationManager; ++use Composer\Pcre\Preg; + use Composer\Util\Filesystem; + + /** +@@ -202,7 +203,7 @@ class FilesystemRepository extends WritableArrayRepository + ksort($versions['versions']); + ksort($versions); + +- $fs->filePutContentsIfModified($repoDir.'/installed.php', 'filePutContentsIfModified($repoDir.'/installed.php', 'dumpToPhpCode($versions).';'."\n"); + $installedVersionsClass = file_get_contents(__DIR__.'/../InstalledVersions.php'); + // while not strictly needed since https://github.com/composer/composer/pull/9635 - we keep this for BC + // and overall broader compatibility with people that may not use Composer's ClassLoader. They can +@@ -219,4 +220,77 @@ class FilesystemRepository extends WritableArrayRepository + } + } + } ++ ++ /** ++ * As we load the file from vendor dir during bootstrap, we need to make sure it contains only expected code before executing it ++ * ++ * @internal ++ * @param string $path ++ * @return bool ++ */ ++ public static function safelyLoadInstalledVersions($path) ++ { ++ $installedVersionsData = @file_get_contents($path); ++ $pattern = <<<'REGEX' ++{(?(DEFINE) ++ (? -? \s*+ \d++ (?:\.\d++)? ) ++ (? true | false | null ) ++ (? (?&string) (?: \s*+ \. \s*+ (?&string))*+ ) ++ (? (?: " (?:[^"\\$]*+ | \\ ["\\0] )* " | ' (?:[^'\\]*+ | \\ ['\\] )* ' ) ) ++ (? array\( \s*+ (?: (?:(?&number)|(?&strings)) \s*+ => \s*+ (?: (?:__DIR__ \s*+ \. \s*+)? (?&strings) | (?&value) ) \s*+, \s*+ )*+ \s*+ \) ) ++ (? (?: (?&number) | (?&boolean) | (?&strings) | (?&array) ) ) ++) ++^<\?php\s++return\s++(?&array)\s*+;$}ix ++REGEX; ++ if (is_string($installedVersionsData) && Preg::isMatch($pattern, trim($installedVersionsData))) { ++ \Composer\InstalledVersions::reload(eval('?>'.Preg::replace('{=>\s*+__DIR__\s*+\.\s*+([\'"])}', '=> '.var_export(dirname($path), true).' . $1', $installedVersionsData))); ++ ++ return true; ++ } ++ ++ return false; ++ } ++ ++ /** ++ * @param array $array ++ * @param int $level ++ * ++ * @return string ++ */ ++ private function dumpToPhpCode(array $array = array(), $level = 0) ++ { ++ $lines = "array(\n"; ++ $level++; ++ ++ foreach ($array as $key => $value) { ++ $lines .= str_repeat(' ', $level); ++ $lines .= is_int($key) ? $key . ' => ' : var_export($key, true) . ' => '; ++ ++ if (is_array($value)) { ++ if (!empty($value)) { ++ $lines .= $this->dumpToPhpCode($value, $level); ++ } else { ++ $lines .= "array(),\n"; ++ } ++ } elseif ($key === 'install_path' && is_string($value)) { ++ if ($this->filesystem->isAbsolutePath($value)) { ++ $lines .= var_export($value, true) . ",\n"; ++ } else { ++ $lines .= "__DIR__ . " . var_export('/' . $value, true) . ",\n"; ++ } ++ } elseif (is_string($value)) { ++ $lines .= var_export($value, true) . ",\n"; ++ } elseif (is_bool($value)) { ++ $lines .= ($value ? 'true' : 'false') . ",\n"; ++ } elseif (is_null($value)) { ++ $lines .= "null,\n"; ++ } else { ++ throw new \UnexpectedValueException('Unexpected type '.gettype($value)); ++ } ++ } ++ ++ $lines .= str_repeat(' ', $level - 1) . ')' . ($level - 1 == 0 ? '' : ",\n"); ++ ++ return $lines; ++ } + } +diff --git a/tests/Composer/Test/InstalledVersionsTest.php b/tests/Composer/Test/InstalledVersionsTest.php +index 58cf1d3..9506226 100644 +--- a/tests/Composer/Test/InstalledVersionsTest.php ++++ b/tests/Composer/Test/InstalledVersionsTest.php +@@ -19,7 +19,10 @@ class InstalledVersionsTest extends TestCase + { + public function setUp(): void + { +- InstalledVersions::reload(require __DIR__.'/Repository/Fixtures/installed.php'); ++ $this->root = $this->getUniqueTmpDirectory(); ++ ++ $dir = $this->root; ++ InstalledVersions::reload(require __DIR__.'/Repository/Fixtures/installed_relative.php'); + } + + public function testGetInstalledPackages() +@@ -33,6 +36,7 @@ class InstalledVersionsTest extends TestCase + 'foo/impl', + 'foo/impl2', + 'foo/replaced', ++ 'meta/package', + ); + $this->assertSame($names, InstalledVersions::getInstalledPackages()); + } +@@ -173,17 +177,21 @@ class InstalledVersionsTest extends TestCase + $this->assertSame(array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', ++ 'type' => 'library', ++ 'install_path' => $this->root . '/./', + 'aliases' => array( + '1.10.x-dev', + ), + 'reference' => 'sourceref-by-default', + 'name' => '__root__', ++ 'dev' => true, + ), InstalledVersions::getRootPackage()); + } + + public function testGetRawData() + { +- $this->assertSame(require __DIR__.'/Repository/Fixtures/installed.php', InstalledVersions::getRawData()); ++ $dir = $this->root; ++ $this->assertSame(require __DIR__.'/Repository/Fixtures/installed_relative.php', InstalledVersions::getRawData()); + } + + /** +diff --git a/tests/Composer/Test/Repository/Fixtures/installed.php b/tests/Composer/Test/Repository/Fixtures/installed.php +index 38a4f71..f825454 100644 +--- a/tests/Composer/Test/Repository/Fixtures/installed.php ++++ b/tests/Composer/Test/Repository/Fixtures/installed.php +@@ -65,10 +65,10 @@ return array( + ), + 'foo/impl2' => array( + 'provided' => array( +- '2.0', ++ 0 => '2.0', + ), + 'replaced' => array( +- '2.2', ++ 0 => '2.2', + ), + ), + 'foo/replaced' => array( +diff --git a/tests/Composer/Test/Repository/Fixtures/installed_relative.php b/tests/Composer/Test/Repository/Fixtures/installed_relative.php +new file mode 100644 +index 0000000..2fc8ef2 +--- /dev/null ++++ b/tests/Composer/Test/Repository/Fixtures/installed_relative.php +@@ -0,0 +1,116 @@ ++ ++ * Jordi Boggiano ++ * ++ * For the full copyright and license information, please view the LICENSE ++ * file that was distributed with this source code. ++ */ ++ ++return array( ++ 'root' => array( ++ 'pretty_version' => 'dev-master', ++ 'version' => 'dev-master', ++ 'type' => 'library', ++ // @phpstan-ignore-next-line ++ 'install_path' => $dir . '/./', ++ 'aliases' => array( ++ '1.10.x-dev', ++ ), ++ 'reference' => 'sourceref-by-default', ++ 'name' => '__root__', ++ 'dev' => true, ++ ), ++ 'versions' => array( ++ '__root__' => array( ++ 'pretty_version' => 'dev-master', ++ 'version' => 'dev-master', ++ 'type' => 'library', ++ // @phpstan-ignore-next-line ++ 'install_path' => $dir . '/./', ++ 'aliases' => array( ++ '1.10.x-dev', ++ ), ++ 'reference' => 'sourceref-by-default', ++ 'dev_requirement' => false, ++ ), ++ 'a/provider' => array( ++ 'pretty_version' => '1.1', ++ 'version' => '1.1.0.0', ++ 'type' => 'library', ++ // @phpstan-ignore-next-line ++ 'install_path' => $dir . '/vendor/a/provider', ++ 'aliases' => array(), ++ 'reference' => 'distref-as-no-source', ++ 'dev_requirement' => false, ++ ), ++ 'a/provider2' => array( ++ 'pretty_version' => '1.2', ++ 'version' => '1.2.0.0', ++ 'type' => 'library', ++ // @phpstan-ignore-next-line ++ 'install_path' => $dir . '/vendor/a/provider2', ++ 'aliases' => array( ++ '1.4', ++ ), ++ 'reference' => 'distref-as-installed-from-dist', ++ 'dev_requirement' => false, ++ ), ++ 'b/replacer' => array( ++ 'pretty_version' => '2.2', ++ 'version' => '2.2.0.0', ++ 'type' => 'library', ++ // @phpstan-ignore-next-line ++ 'install_path' => $dir . '/vendor/b/replacer', ++ 'aliases' => array(), ++ 'reference' => null, ++ 'dev_requirement' => false, ++ ), ++ 'c/c' => array( ++ 'pretty_version' => '3.0', ++ 'version' => '3.0.0.0', ++ 'type' => 'library', ++ 'install_path' => '/foo/bar/vendor/c/c', ++ 'aliases' => array(), ++ 'reference' => null, ++ 'dev_requirement' => true, ++ ), ++ 'foo/impl' => array( ++ 'dev_requirement' => false, ++ 'provided' => array( ++ '^1.1', ++ '1.2', ++ '1.4', ++ '2.0', ++ ), ++ ), ++ 'foo/impl2' => array( ++ 'dev_requirement' => false, ++ 'provided' => array( ++ '2.0', ++ ), ++ 'replaced' => array( ++ '2.2', ++ ), ++ ), ++ 'foo/replaced' => array( ++ 'dev_requirement' => false, ++ 'replaced' => array( ++ '^3.0', ++ ), ++ ), ++ 'meta/package' => array( ++ 'pretty_version' => '3.0', ++ 'version' => '3.0.0.0', ++ 'type' => 'metapackage', ++ 'install_path' => null, ++ 'aliases' => array(), ++ 'reference' => null, ++ 'dev_requirement' => false, ++ ) ++ ), ++); ++ diff -Nru composer-2.0.9/debian/patches/series composer-2.0.9/debian/patches/series --- composer-2.0.9/debian/patches/series 2022-05-29 09:03:24.000000000 +0000 +++ composer-2.0.9/debian/patches/series 2024-02-18 08:05:37.000000000 +0000 @@ -10,3 +10,5 @@ 0010-Merge-pull-request-from-GHSA-x7cr-6qr6-2hh6.patch 0011-Update-GitHub-token-pattern.patch 0012-Checkout-ProcessExecutorMock.php-needed-for-updated-.patch +0013-Import-Pcre.patch +0014-Merge-pull-request-from-GHSA-7c6p-848j-wh5h.patch diff -Nru composer-2.0.9/debian/rules composer-2.0.9/debian/rules --- composer-2.0.9/debian/rules 2022-05-29 09:00:09.000000000 +0000 +++ composer-2.0.9/debian/rules 2024-02-18 08:05:37.000000000 +0000 @@ -2,7 +2,7 @@ include /usr/share/dpkg/default.mk UPSTREAM := $(DEB_VERSION_UPSTREAM) -USRDIR := $(CURDIR)/debian/composer/usr +USRDIR := $(CURDIR)/build %: dh $@ --with phpcomposer -XCompiler.php @@ -12,7 +12,19 @@ --output src/Composer/autoload.php \ --template debian/autoload.php.tpl \ src/Composer - mkdir --parents vendor + mkdir --parents vendor build/share/php/data/Composer + # Mimic system path for tests + cp -r src/Composer build/share/php + cp -r LICENSE res build/share/php/data/Composer + ln -s /usr/share/php/Composer/CaBundle build/share/php/Composer + ln -s /usr/share/php/Composer/Semver build/share/php/Composer + ln -s /usr/share/php/Composer/Spdx build/share/php/Composer + ln -s /usr/share/php/Composer/XdebugHandler build/share/php/Composer + ln -s /usr/share/php/JsonSchema build/share/php + ln -s /usr/share/php/Psr build/share/php + ln -s /usr/share/php/React build/share/php + ln -s /usr/share/php/Seld build/share/php + ln -s /usr/share/php/Symfony build/share/php phpab \ --tolerant \ --output vendor/autoload.php \ @@ -27,13 +39,14 @@ tests/Composer/Test override_dh_auto_test: - phpunit --include-path src --verbose --exclude-group remote,git + phpunit --include-path build/share/php --exclude-group remote,git override_dh_installdocs: dh_installdocs -Xdoc/composer execute_before_dh_installman: mkdir --parent $(CURDIR)/debian/tmp + cp -r bin build cd $(USRDIR)/share/php && help2man \ --help-option=\ \ --include=$(CURDIR)/debian/composer.1.in \ @@ -41,5 +54,5 @@ --source="composer $(UPSTREAM)" \ --no-info \ --no-discard-stderr \ - "echo -n 'Usage: composer' && $(CURDIR)/bin/composer --no-ansi | tail -n+10" \ + "echo -n 'Usage: composer' && $(USRDIR)/bin/composer --no-ansi | tail -n+10" \ > $(CURDIR)/debian/tmp/composer.1