1
0
Fork 0

Merge branch '2.2' into main, update baseline (2085, 104)

pull/10729/head
Jordi Boggiano 2022-04-13 16:18:25 +02:00
commit 615530f0a1
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
12 changed files with 167 additions and 26 deletions

View File

@ -62,6 +62,13 @@
* Fixed symlink creation in linux VM guest filesystems to be recognized by Windows (#10592) * Fixed symlink creation in linux VM guest filesystems to be recognized by Windows (#10592)
* Performance improvement in pool optimization step (#10585) * Performance improvement in pool optimization step (#10585)
### [2.2.12] 2022-04-13
* Security: Fixed command injection vulnerability in HgDriver/GitDriver (GHSA-x7cr-6qr6-2hh6 / CVE-2022-24828)
* Fixed curl downloader not retrying when a DNS resolution failure occurs (#10716)
* Fixed composer.lock file still being used/read when the `lock` config option is disabled (#10726)
* Fixed `validate` command checking the lock file even if the `lock` option is disabled (#10723)
### [2.2.11] 2022-04-01 ### [2.2.11] 2022-04-01
* Added missing config.bitbucket-oauth in composer-schema.json * Added missing config.bitbucket-oauth in composer-schema.json
@ -1491,6 +1498,7 @@
[2.3.0]: https://github.com/composer/composer/compare/2.3.0-RC2...2.3.0 [2.3.0]: https://github.com/composer/composer/compare/2.3.0-RC2...2.3.0
[2.3.0-RC2]: https://github.com/composer/composer/compare/2.3.0-RC1...2.3.0-RC2 [2.3.0-RC2]: https://github.com/composer/composer/compare/2.3.0-RC1...2.3.0-RC2
[2.3.0-RC1]: https://github.com/composer/composer/compare/2.2.9...2.3.0-RC1 [2.3.0-RC1]: https://github.com/composer/composer/compare/2.2.9...2.3.0-RC1
[2.2.12]: https://github.com/composer/composer/compare/2.2.11...2.2.12
[2.2.11]: https://github.com/composer/composer/compare/2.2.10...2.2.11 [2.2.11]: https://github.com/composer/composer/compare/2.2.10...2.2.11
[2.2.10]: https://github.com/composer/composer/compare/2.2.9...2.2.10 [2.2.10]: https://github.com/composer/composer/compare/2.2.9...2.2.10
[2.2.9]: https://github.com/composer/composer/compare/2.2.8...2.2.9 [2.2.9]: https://github.com/composer/composer/compare/2.2.8...2.2.9

View File

@ -315,8 +315,8 @@ with other autoloaders.
## autoloader-suffix ## autoloader-suffix
Defaults to `null`. String to be used as a suffix for the generated Composer Defaults to `null`. Non-empty string to be used as a suffix for the generated
autoloader. When null a random one will be generated. Composer autoloader. When null a random one will be generated.
## optimize-autoloader ## optimize-autoloader
@ -396,7 +396,7 @@ in the Composer home, cache, and data directories.
## lock ## lock
Defaults to `true`. If set to `false`, Composer will not create a `composer.lock` Defaults to `true`. If set to `false`, Composer will not create a `composer.lock`
file. file and will ignore it if one is present.
## platform-check ## platform-check

View File

@ -60,14 +60,9 @@ parameters:
count: 1 count: 1
path: ../src/Composer/Autoload/AutoloadGenerator.php path: ../src/Composer/Autoload/AutoloadGenerator.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: ../src/Composer/Autoload/AutoloadGenerator.php
- -
message: "#^Only booleans are allowed in a negated boolean, string given\\.$#" message: "#^Only booleans are allowed in a negated boolean, string given\\.$#"
count: 4 count: 3
path: ../src/Composer/Autoload/AutoloadGenerator.php path: ../src/Composer/Autoload/AutoloadGenerator.php
- -
@ -147,7 +142,7 @@ parameters:
- -
message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#"
count: 2 count: 1
path: ../src/Composer/Autoload/AutoloadGenerator.php path: ../src/Composer/Autoload/AutoloadGenerator.php
- -
@ -1285,6 +1280,11 @@ parameters:
count: 3 count: 3
path: ../src/Composer/Command/UpdateCommand.php path: ../src/Composer/Command/UpdateCommand.php
-
message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#"
count: 1
path: ../src/Composer/Command/ValidateCommand.php
- -
message: "#^Only booleans are allowed in a negated boolean, array\\<Composer\\\\Package\\\\BasePackage\\> given\\.$#" message: "#^Only booleans are allowed in a negated boolean, array\\<Composer\\\\Package\\\\BasePackage\\> given\\.$#"
count: 1 count: 1
@ -1370,11 +1370,6 @@ parameters:
count: 1 count: 1
path: ../src/Composer/Config.php path: ../src/Composer/Config.php
-
message: "#^Only booleans are allowed in an if condition, Composer\\\\IO\\\\IOInterface\\|null given\\.$#"
count: 1
path: ../src/Composer/Config.php
- -
message: "#^Only booleans are allowed in an if condition, bool\\|string given\\.$#" message: "#^Only booleans are allowed in an if condition, bool\\|string given\\.$#"
count: 1 count: 1
@ -2550,11 +2545,21 @@ parameters:
count: 3 count: 3
path: ../src/Composer/Factory.php path: ../src/Composer/Factory.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: ../src/Composer/Factory.php
- -
message: "#^Only booleans are allowed in a negated boolean, string\\|false given\\.$#" message: "#^Only booleans are allowed in a negated boolean, string\\|false given\\.$#"
count: 3 count: 3
path: ../src/Composer/Factory.php path: ../src/Composer/Factory.php
-
message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#"
count: 1
path: ../src/Composer/Factory.php
- -
message: "#^Only booleans are allowed in an if condition, Composer\\\\Util\\\\ProcessExecutor\\|null given\\.$#" message: "#^Only booleans are allowed in an if condition, Composer\\\\Util\\\\ProcessExecutor\\|null given\\.$#"
count: 1 count: 1

View File

@ -159,12 +159,11 @@ class AutoloadGenerator
/** /**
* @param string $targetDir * @param string $targetDir
* @param bool $scanPsrPackages * @param bool $scanPsrPackages
* @param string $suffix
* @return int * @return int
* @throws \Seld\JsonLint\ParsingException * @throws \Seld\JsonLint\ParsingException
* @throws \RuntimeException * @throws \RuntimeException
*/ */
public function dump(Config $config, InstalledRepositoryInterface $localRepo, RootPackageInterface $rootPackage, InstallationManager $installationManager, string $targetDir, bool $scanPsrPackages = false, string $suffix = '') public function dump(Config $config, InstalledRepositoryInterface $localRepo, RootPackageInterface $rootPackage, InstallationManager $installationManager, string $targetDir, bool $scanPsrPackages = false, ?string $suffix = null)
{ {
if ($this->classMapAuthoritative) { if ($this->classMapAuthoritative) {
// Force scanPsrPackages when classmap is authoritative // Force scanPsrPackages when classmap is authoritative
@ -373,16 +372,23 @@ EOF;
} }
$classmapFile .= ");\n"; $classmapFile .= ");\n";
if (!$suffix) { if ('' === $suffix) {
if (!$config->get('autoloader-suffix') && Filesystem::isReadable($vendorPath.'/autoload.php')) { $suffix = null;
}
if (null === $suffix) {
$suffix = $config->get('autoloader-suffix');
// carry over existing autoload.php's suffix if possible and none is configured
if (null === $suffix && Filesystem::isReadable($vendorPath.'/autoload.php')) {
$content = file_get_contents($vendorPath.'/autoload.php'); $content = file_get_contents($vendorPath.'/autoload.php');
if (Preg::isMatch('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) { if (Preg::isMatch('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) {
$suffix = $match[1]; $suffix = $match[1];
} }
} }
if (!$suffix) { // generate one if we still haven't got a suffix
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true)); if (null === $suffix) {
$suffix = md5(uniqid('', true));
} }
} }

View File

@ -45,6 +45,7 @@ class ValidateCommand extends BaseCommand
->setDescription('Validates a composer.json and composer.lock.') ->setDescription('Validates a composer.json and composer.lock.')
->setDefinition(array( ->setDefinition(array(
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'), new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'),
new InputOption('check-lock', null, InputOption::VALUE_NONE, 'Check if lock file is up to date (even when config.lock is false)'),
new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'), new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'),
new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'), new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'),
new InputOption('no-check-version', null, InputOption::VALUE_NONE, 'Do not report a warning if the version field is present'), new InputOption('no-check-version', null, InputOption::VALUE_NONE, 'Do not report a warning if the version field is present'),
@ -92,6 +93,8 @@ EOT
$lockErrors = array(); $lockErrors = array();
$composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins')); $composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins'));
// config.lock = false ~= implicit --no-check-lock; --check-lock overrides
$checkLock = ($checkLock && $composer->getConfig()->get('lock')) || $input->getOption('check-lock');
$locker = $composer->getLocker(); $locker = $composer->getLocker();
if ($locker->isLocked() && !$locker->isFresh()) { if ($locker->isLocked() && !$locker->isFresh()) {
$lockErrors[] = '- The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update <package name>`.'; $lockErrors[] = '- The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update <package name>`.';

View File

@ -427,6 +427,13 @@ class Config
return $protos; return $protos;
case 'autoloader-suffix':
if ($this->config[$key] === '') { // we need to guarantee null or non-empty-string
return null;
}
return $this->process($this->config[$key], $flags);
default: default:
if (!isset($this->config[$key])) { if (!isset($this->config[$key])) {
return null; return null;

View File

@ -418,8 +418,11 @@ class Factory
// init locker if possible // init locker if possible
if ($composer instanceof Composer && isset($composerFile)) { if ($composer instanceof Composer && isset($composerFile)) {
$lockFile = self::getLockFile($composerFile); $lockFile = self::getLockFile($composerFile);
if (!$config->get('lock') && file_exists($lockFile)) {
$io->writeError('<warning>'.$lockFile.' is present but ignored as the "lock" config option is disabled.</warning>');
}
$locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $im, file_get_contents($composerFile), $process); $locker = new Package\Locker($io, new JsonFile($config->get('lock') ? $lockFile : Platform::getDevNull(), null, $io), $im, file_get_contents($composerFile), $process);
$composer->setLocker($locker); $composer->setLocker($locker);
} }

View File

@ -145,6 +145,10 @@ class GitDriver extends VcsDriver
*/ */
public function getFileContent(string $file, string $identifier): ?string public function getFileContent(string $file, string $identifier): ?string
{ {
if (isset($identifier[0]) && $identifier[0] === '-') {
throw new \RuntimeException('Invalid git identifier detected. Identifier must not start with a -, given: ' . $identifier);
}
$resource = sprintf('%s:%s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file)); $resource = sprintf('%s:%s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
$this->process->execute(sprintf('git show %s', $resource), $content, $this->repoDir); $this->process->execute(sprintf('git show %s', $resource), $content, $this->repoDir);
@ -198,7 +202,7 @@ class GitDriver extends VcsDriver
$this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->repoDir); $this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) { foreach ($this->process->splitLines($output) as $branch) {
if ($branch && !Preg::isMatch('{^ *[^/]+/HEAD }', $branch)) { if ($branch && !Preg::isMatch('{^ *[^/]+/HEAD }', $branch)) {
if (Preg::isMatch('{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}', $branch, $match)) { if (Preg::isMatch('{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}', $branch, $match) && $match[1][0] !== '-') {
$branches[$match[1]] = $match[2]; $branches[$match[1]] = $match[2];
} }
} }

View File

@ -126,7 +126,11 @@ class HgDriver extends VcsDriver
*/ */
public function getFileContent(string $file, string $identifier): ?string public function getFileContent(string $file, string $identifier): ?string
{ {
$resource = sprintf('hg cat -r %s %s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file)); if (isset($identifier[0]) && $identifier[0] === '-') {
throw new \RuntimeException('Invalid hg identifier detected. Identifier must not start with a -, given: ' . $identifier);
}
$resource = sprintf('hg cat -r %s -- %s', ProcessExecutor::escape($identifier), ProcessExecutor::escape($file));
$this->process->execute($resource, $content, $this->repoDir); $this->process->execute($resource, $content, $this->repoDir);
if (!trim($content)) { if (!trim($content)) {
@ -186,14 +190,14 @@ class HgDriver extends VcsDriver
$this->process->execute('hg branches', $output, $this->repoDir); $this->process->execute('hg branches', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) { foreach ($this->process->splitLines($output) as $branch) {
if ($branch && Preg::isMatch('(^([^\s]+)\s+\d+:([a-f0-9]+))', $branch, $match)) { if ($branch && Preg::isMatch('(^([^\s]+)\s+\d+:([a-f0-9]+))', $branch, $match) && $match[1][0] !== '-') {
$branches[$match[1]] = $match[2]; $branches[$match[1]] = $match[2];
} }
} }
$this->process->execute('hg bookmarks', $output, $this->repoDir); $this->process->execute('hg bookmarks', $output, $this->repoDir);
foreach ($this->process->splitLines($output) as $branch) { foreach ($this->process->splitLines($output) as $branch) {
if ($branch && Preg::isMatch('(^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$)', $branch, $match)) { if ($branch && Preg::isMatch('(^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$)', $branch, $match) && $match[1][0] !== '-') {
$bookmarks[$match[1]] = $match[2]; $bookmarks[$match[1]] = $match[2];
} }
} }

View File

@ -275,4 +275,16 @@ class Platform
return self::$isVirtualBoxGuest; return self::$isVirtualBoxGuest;
} }
/**
* @return 'NUL'|'/dev/null'
*/
public static function getDevNull(): string
{
if (self::isWindows()) {
return 'NUL';
}
return '/dev/null';
}
} }

View File

@ -8,6 +8,7 @@ use Composer\Repository\Vcs\GitDriver;
use Composer\Test\TestCase; use Composer\Test\TestCase;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\Test\Mock\ProcessExecutorMock;
class GitDriverTest extends TestCase class GitDriverTest extends TestCase
{ {
@ -132,6 +133,48 @@ GIT;
$this->assertSame('main', $driver->getRootIdentifier()); $this->assertSame('main', $driver->getRootIdentifier());
} }
public function testGetBranchesFilterInvalidBranchNames(): void
{
$process = $this->getProcessExecutorMock();
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$driver = new GitDriver(array('url' => 'https://example.org/acme.git'), $io, $this->config, $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(), $process);
$this->setRepoDir($driver, $this->home);
// Branches starting with a - character are not valid git branches names
// Still assert that they get filtered to prevent issues later on
$stdout = <<<GIT
* main 089681446ba44d6d9004350192486f2ceb4eaa06 commit
2.2 12681446ba44d6d9004350192486f2ceb4eaa06 commit
-h 089681446ba44d6d9004350192486f2ceb4eaa06 commit
GIT;
$process
->expects(array(array(
'cmd' => 'git branch --no-color --no-abbrev -v',
'stdout' => $stdout,
)));
$branches = $driver->getBranches();
$this->assertSame(array(
'main' => '089681446ba44d6d9004350192486f2ceb4eaa06',
'2.2' => '12681446ba44d6d9004350192486f2ceb4eaa06',
), $branches);
}
public function testFileGetContentInvalidIdentifier(): void
{
$this->expectException('\RuntimeException');
$process = $this->getProcessExecutorMock();
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$driver = new GitDriver(array('url' => 'https://example.org/acme.git'), $io, $this->config, $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(), $process);
$this->assertNull($driver->getFileContent('file.txt', 'h'));
$driver->getFileContent('file.txt', '-h');
}
private function setRepoDir(GitDriver $driver, string $path): void private function setRepoDir(GitDriver $driver, string $path): void
{ {
$reflectionClass = new \ReflectionClass($driver); $reflectionClass = new \ReflectionClass($driver);

View File

@ -13,6 +13,7 @@
namespace Composer\Test\Repository\Vcs; namespace Composer\Test\Repository\Vcs;
use Composer\Repository\Vcs\HgDriver; use Composer\Repository\Vcs\HgDriver;
use Composer\Test\Mock\ProcessExecutorMock;
use Composer\Test\TestCase; use Composer\Test\TestCase;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Config; use Composer\Config;
@ -67,4 +68,49 @@ class HgDriverTest extends TestCase
array('https://user@bitbucket.org/user/repo'), array('https://user@bitbucket.org/user/repo'),
); );
} }
public function testGetBranchesFilterInvalidBranchNames(): void
{
$process = $this->getProcessExecutorMock();
$driver = new HgDriver(array('url' => 'https://example.org/acme.git'), $this->io, $this->config, $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(), $process);
$stdout = <<<HG_BRANCHES
default 1:dbf6c8acb640
--help 1:dbf6c8acb640
HG_BRANCHES;
$stdout1 = <<<HG_BOOKMARKS
help 1:dbf6c8acb641
--help 1:dbf6c8acb641
HG_BOOKMARKS;
$process
->expects(array(array(
'cmd' => 'hg branches',
'stdout' => $stdout,
), array(
'cmd' => 'hg bookmarks',
'stdout' => $stdout1,
)));
$branches = $driver->getBranches();
$this->assertSame(array(
'help' => 'dbf6c8acb641',
'default' => 'dbf6c8acb640',
), $branches);
}
public function testFileGetContentInvalidIdentifier(): void
{
$this->expectException('\RuntimeException');
$process = $this->getProcessExecutorMock();
$driver = new HgDriver(array('url' => 'https://example.org/acme.git'), $this->io, $this->config, $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(), $process);
$this->assertNull($driver->getFileContent('file.txt', 'h'));
$driver->getFileContent('file.txt', '-h');
}
} }