Skip to content

Commit 3764073

Browse files
committed
Merge branch '2.2' into 2.3
2 parents 1703b74 + 3ebd66b commit 3764073

File tree

6 files changed

+110
-78
lines changed

6 files changed

+110
-78
lines changed

Diff for: doc/06-config.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,15 @@ and **false** to disallow while suppressing further warnings and prompts.
5252
}
5353
```
5454

55-
You can also set the config option itself to `false` to disallow all plugins, or `true` to allow all plugins to run (NOT recommended).
55+
You can also set the config option itself to `false` to disallow all plugins, or `true` to allow all plugins to run (NOT recommended). For example:
56+
57+
```json
58+
{
59+
"config": {
60+
"allow-plugins": false
61+
}
62+
}
63+
```
5664

5765
## use-include-path
5866

Diff for: src/Composer/Factory.php

+11-11
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,17 @@ public function createComposer(IOInterface $io, $localConfig = null, bool $disab
404404
// add installers to the manager (must happen after download manager is created since they read it out of $composer)
405405
$this->createDefaultInstallers($im, $composer, $io, $process);
406406

407+
// init locker if possible
408+
if ($composer instanceof Composer && isset($composerFile)) {
409+
$lockFile = self::getLockFile($composerFile);
410+
if (!$config->get('lock') && file_exists($lockFile)) {
411+
$io->writeError('<warning>'.$lockFile.' is present but ignored as the "lock" config option is disabled.</warning>');
412+
}
413+
414+
$locker = new Package\Locker($io, new JsonFile($config->get('lock') ? $lockFile : Platform::getDevNull(), null, $io), $im, file_get_contents($composerFile), $process);
415+
$composer->setLocker($locker);
416+
}
417+
407418
if ($composer instanceof Composer) {
408419
$globalComposer = null;
409420
if (realpath($config->get('home')) !== $cwd) {
@@ -416,17 +427,6 @@ public function createComposer(IOInterface $io, $localConfig = null, bool $disab
416427
$pm->loadInstalledPlugins();
417428
}
418429

419-
// init locker if possible
420-
if ($composer instanceof Composer && isset($composerFile)) {
421-
$lockFile = self::getLockFile($composerFile);
422-
if (!$config->get('lock') && file_exists($lockFile)) {
423-
$io->writeError('<warning>'.$lockFile.' is present but ignored as the "lock" config option is disabled.</warning>');
424-
}
425-
426-
$locker = new Package\Locker($io, new JsonFile($config->get('lock') ? $lockFile : Platform::getDevNull(), null, $io), $im, file_get_contents($composerFile), $process);
427-
$composer->setLocker($locker);
428-
}
429-
430430
if ($fullLoad) {
431431
$initEvent = new Event(PluginEvents::INIT);
432432
$composer->getEventDispatcher()->dispatch($initEvent->getName(), $initEvent);

Diff for: src/Composer/Installer/PluginInstaller.php

+13
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ public function supports(string $packageType)
4343
return $packageType === 'composer-plugin' || $packageType === 'composer-installer';
4444
}
4545

46+
/**
47+
* @inheritDoc
48+
*/
49+
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null)
50+
{
51+
// fail install process early if it going to fail due to a plugin not being allowed
52+
if ($type === 'install' || $type === 'update') {
53+
$this->composer->getPluginManager()->isPluginAllowed($package->getName(), false);
54+
}
55+
56+
return parent::prepare($type, $package, $prevPackage);
57+
}
58+
4659
/**
4760
* @inheritDoc
4861
*/

Diff for: src/Composer/Package/Locker.php

+10
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,16 @@ public function getAliases(): array
318318
return $lockData['aliases'] ?? array();
319319
}
320320

321+
/**
322+
* @return string
323+
*/
324+
public function getPluginApi()
325+
{
326+
$lockData = $this->getLockData();
327+
328+
return isset($lockData['plugin-api-version']) ? $lockData['plugin-api-version'] : '1.1.0';
329+
}
330+
321331
/**
322332
* @return array<string, mixed>
323333
*/

Diff for: src/Composer/Plugin/PluginManager.php

+65-64
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Composer\IO\IOInterface;
1919
use Composer\Package\BasePackage;
2020
use Composer\Package\CompletePackage;
21+
use Composer\Package\Locker;
2122
use Composer\Package\Package;
2223
use Composer\Package\Version\VersionParser;
2324
use Composer\PartialComposer;
@@ -75,9 +76,8 @@ public function __construct(IOInterface $io, Composer $composer, PartialComposer
7576
$this->globalComposer = $globalComposer;
7677
$this->versionParser = new VersionParser();
7778
$this->disablePlugins = $disablePlugins;
78-
79-
$this->allowPluginRules = $this->parseAllowedPlugins($composer->getConfig()->get('allow-plugins'));
80-
$this->allowGlobalPluginRules = $this->parseAllowedPlugins($globalComposer !== null ? $globalComposer->getConfig()->get('allow-plugins') : false);
79+
$this->allowPluginRules = $this->parseAllowedPlugins($composer->getConfig()->get('allow-plugins'), $composer->getLocker());
80+
$this->allowGlobalPluginRules = $this->parseAllowedPlugins($globalComposer !== null ? $globalComposer->getConfig()->get('allow-plugins') : false, $globalComposer !== null ? $globalComposer->getLocker() : null);
8181
}
8282

8383
/**
@@ -648,12 +648,12 @@ public function getPluginCapabilities($capabilityClassName, array $ctorArgs = ar
648648
}
649649

650650
/**
651-
* @param array<string, bool>|bool|null $allowPluginsConfig
651+
* @param array<string, bool>|bool $allowPluginsConfig
652652
* @return array<non-empty-string, bool>|null
653653
*/
654-
private function parseAllowedPlugins($allowPluginsConfig): ?array
654+
private function parseAllowedPlugins($allowPluginsConfig, ?Locker $locker = null): ?array
655655
{
656-
if (null === $allowPluginsConfig) {
656+
if (array() === $allowPluginsConfig && $locker !== null && $locker->isLocked() && version_compare($locker->getPluginApi(), '2.2.0', '<')) {
657657
return null;
658658
}
659659

@@ -674,22 +674,28 @@ private function parseAllowedPlugins($allowPluginsConfig): ?array
674674
}
675675

676676
/**
677+
* @internal
678+
*
677679
* @param string $package
678680
* @param bool $isGlobalPlugin
679681
* @return bool
680682
*/
681-
private function isPluginAllowed(string $package, bool $isGlobalPlugin): bool
683+
public function isPluginAllowed(string $package, bool $isGlobalPlugin): bool
682684
{
683-
static $warned = array();
684-
$rules = $isGlobalPlugin ? $this->allowGlobalPluginRules : $this->allowPluginRules;
685+
if ($isGlobalPlugin) {
686+
$rules = &$this->allowGlobalPluginRules;
687+
} else {
688+
$rules = &$this->allowPluginRules;
689+
}
685690

691+
// This is a BC mode for lock files created pre-Composer-2.2 where the expectation of
692+
// an allow-plugins config being present cannot be made.
686693
if ($rules === null) {
687694
if (!$this->io->isInteractive()) {
688-
if (!isset($warned['all'])) {
689-
$this->io->writeError('<warning>For additional security you should declare the allow-plugins config with a list of packages names that are allowed to run code. See https://getcomposer.org/allow-plugins</warning>');
690-
$this->io->writeError('<warning>You have until July 2022 to add the setting. Composer will then switch the default behavior to disallow all plugins.</warning>');
691-
$warned['all'] = true;
692-
}
695+
$this->io->writeError('<warning>For additional security you should declare the allow-plugins config with a list of packages names that are allowed to run code. See https://getcomposer.org/allow-plugins</warning>');
696+
$this->io->writeError('<warning>This warning will become an exception once you run composer update!</warning>');
697+
698+
$rules = array('{}' => true);
693699

694700
// if no config is defined we allow all plugins for BC
695701
return true;
@@ -709,59 +715,54 @@ private function isPluginAllowed(string $package, bool $isGlobalPlugin): bool
709715
return false;
710716
}
711717

712-
if (!isset($warned[$package])) {
713-
if ($this->io->isInteractive()) {
714-
$composer = $isGlobalPlugin && $this->globalComposer !== null ? $this->globalComposer : $this->composer;
715-
716-
$this->io->writeError('<warning>'.$package.($isGlobalPlugin ? ' (installed globally)' : '').' contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins</warning>');
717-
$attempts = 0;
718-
while (true) {
719-
// do not allow more than 5 prints of the help message, at some point assume the
720-
// input is not interactive and bail defaulting to a disabled plugin
721-
$default = '?';
722-
if ($attempts > 5) {
723-
$default = 'd';
724-
}
725-
726-
switch ($answer = $this->io->ask('Do you trust "<fg=green;options=bold>'.$package.'</>" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [<comment>y,n,d,?</comment>] ', $default)) {
727-
case 'y':
728-
case 'n':
729-
case 'd':
730-
$allow = $answer === 'y';
731-
732-
// persist answer in current rules to avoid prompting again if the package gets reloaded
733-
if ($isGlobalPlugin) {
734-
$this->allowGlobalPluginRules[BasePackage::packageNameToRegexp($package)] = $allow;
735-
} else {
736-
$this->allowPluginRules[BasePackage::packageNameToRegexp($package)] = $allow;
737-
}
738-
739-
// persist answer in composer.json if it wasn't simply discarded
740-
if ($answer === 'y' || $answer === 'n') {
741-
$composer->getConfig()->getConfigSource()->addConfigSetting('allow-plugins.'.$package, $allow);
742-
}
743-
744-
return $allow;
745-
746-
case '?':
747-
default:
748-
$attempts++;
749-
$this->io->writeError(array(
750-
'y - add package to allow-plugins in composer.json and let it run immediately',
751-
'n - add package (as disallowed) to allow-plugins in composer.json to suppress further prompts',
752-
'd - discard this, do not change composer.json and do not allow the plugin to run',
753-
'? - print help',
754-
));
755-
break;
756-
}
718+
if ($this->io->isInteractive()) {
719+
$composer = $isGlobalPlugin && $this->globalComposer !== null ? $this->globalComposer : $this->composer;
720+
721+
$this->io->writeError('<warning>'.$package.($isGlobalPlugin ? ' (installed globally)' : '').' contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins</warning>');
722+
$attempts = 0;
723+
while (true) {
724+
// do not allow more than 5 prints of the help message, at some point assume the
725+
// input is not interactive and bail defaulting to a disabled plugin
726+
$default = '?';
727+
if ($attempts > 5) {
728+
$this->io->writeError('Too many failed prompts, aborting.');
729+
break;
730+
}
731+
732+
switch ($answer = $this->io->ask('Do you trust "<fg=green;options=bold>'.$package.'</>" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [<comment>y,n,d,?</comment>] ', $default)) {
733+
case 'y':
734+
case 'n':
735+
case 'd':
736+
$allow = $answer === 'y';
737+
738+
// persist answer in current rules to avoid prompting again if the package gets reloaded
739+
$rules[BasePackage::packageNameToRegexp($package)] = $allow;
740+
741+
// persist answer in composer.json if it wasn't simply discarded
742+
if ($answer === 'y' || $answer === 'n') {
743+
$composer->getConfig()->getConfigSource()->addConfigSetting('allow-plugins.'.$package, $allow);
744+
}
745+
746+
return $allow;
747+
748+
case '?':
749+
default:
750+
$attempts++;
751+
$this->io->writeError(array(
752+
'y - add package to allow-plugins in composer.json and let it run immediately',
753+
'n - add package (as disallowed) to allow-plugins in composer.json to suppress further prompts',
754+
'd - discard this, do not change composer.json and do not allow the plugin to run',
755+
'? - print help',
756+
));
757+
break;
757758
}
758-
} else {
759-
$this->io->writeError('<warning>'.$package.($isGlobalPlugin ? ' (installed globally)' : '').' contains a Composer plugin which is blocked by your allow-plugins config. You may add it to the list if you consider it safe. See https://getcomposer.org/allow-plugins</warning>');
760-
$this->io->writeError('<warning>You can run "composer '.($isGlobalPlugin ? 'global ' : '').'config --no-plugins allow-plugins.'.$package.' [true|false]" to enable it (true) or keep it disabled and suppress this warning (false)</warning>');
761759
}
762-
$warned[$package] = true;
763760
}
764761

765-
return false;
762+
throw new \UnexpectedValueException(
763+
$package.($isGlobalPlugin ? ' (installed globally)' : '').' contains a Composer plugin which is blocked by your allow-plugins config. You may add it to the list if you consider it safe.'.PHP_EOL.
764+
'You can run "composer '.($isGlobalPlugin ? 'global ' : '').'config --no-plugins allow-plugins.'.$package.' [true|false]" to enable it (true) or disable it explicitly and suppress this exception (false)'.PHP_EOL.
765+
'See https://getcomposer.org/allow-plugins'
766+
);
766767
}
767768
}

Diff for: src/Composer/Util/GitHub.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ public function authorizeOAuthInteractively(string $originUrl, string $message =
101101
$this->io->writeError(sprintf('Tokens will be stored in plain text in "%s" for future use by Composer.', $this->config->getAuthConfigSource()->getName()));
102102
$this->io->writeError('For additional information, check https://getcomposer.org/doc/articles/authentication-for-private-packages.md#github-oauth');
103103

104-
$token = trim($this->io->askAndHideAnswer('Token (hidden): '));
104+
$token = trim((string) $this->io->askAndHideAnswer('Token (hidden): '));
105105

106-
if (!$token) {
106+
if ($token === '') {
107107
$this->io->writeError('<warning>No token given, aborting.</warning>');
108108
$this->io->writeError('You can also add it manually later by using "composer config --global --auth github-oauth.github.com <token>"');
109109

0 commit comments

Comments
 (0)