diff --git a/CHANGELOG.md b/CHANGELOG.md index c9fe7206f..f44dd02cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,15 @@ * Fixed symlink creation in linux VM guest filesystems to be recognized by Windows (#10592) * Performance improvement in pool optimization step (#10585) +### [2.2.15] 2022-07-01 + + * Fixed support for `cache-read-only` where the filesystem is not writable (#10906) + * Fixed type error when using `allow-plugins: true` (#10909) + * Fixed @putenv scripts receiving arguments passed to the command (#10846) + * Fixed support for spaces in paths with binary proxies on Windows (#10836) + * Fixed type error in GitDownloader if branches cannot be listed (#10888) + * Fixed RootPackageInterface issue on PHP 5.3.3 (#10895) + ### [2.2.14] 2022-06-06 * Fixed handling of broken symlinks when checking whether a package is still installed (#6708) @@ -1552,6 +1561,7 @@ [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-RC1]: https://github.com/composer/composer/compare/2.2.9...2.3.0-RC1 +[2.2.15]: https://github.com/composer/composer/compare/2.2.14...2.2.15 [2.2.14]: https://github.com/composer/composer/compare/2.2.13...2.2.14 [2.2.13]: https://github.com/composer/composer/compare/2.2.12...2.2.13 [2.2.12]: https://github.com/composer/composer/compare/2.2.11...2.2.12 diff --git a/doc/06-config.md b/doc/06-config.md index 5490faa7e..47e1a6f5e 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -26,8 +26,7 @@ helper is available: ## allow-plugins -Defaults to `null` (allow all plugins implicitly) for backwards compatibility until July 2022. -At that point the default will become `{}` and plugins will not load anymore unless allowed. +Defaults to `{}` which does not allow any plugins to be loaded. As of Composer 2.2.0, the `allow-plugins` option adds a layer of security allowing you to restrict which Composer plugins are able to execute code during diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index c6e2585aa..4a2c762a4 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -98,10 +98,13 @@ class Cache $this->enabled = true; if ( - (!is_dir($this->root) && !Silencer::call('mkdir', $this->root, 0777, true)) - || !is_writable($this->root) + !$this->readOnly + && ( + (!is_dir($this->root) && !Silencer::call('mkdir', $this->root, 0777, true)) + || !is_writable($this->root) + ) ) { - $this->io->writeError('Cannot create cache directory ' . $this->root . ', or directory is not writable. Proceeding without cache'); + $this->io->writeError('Cannot create cache directory ' . $this->root . ', or directory is not writable. Proceeding without cache. See also cache-read-only config if your filesystem is read-only.'); $this->enabled = false; } } @@ -258,7 +261,7 @@ class Cache */ public function remove(string $file) { - if ($this->isEnabled()) { + if ($this->isEnabled() && !$this->readOnly) { $file = Preg::replace('{[^'.$this->allowlist.']}i', '-', $file); if (file_exists($this->root . $file)) { return $this->filesystem->unlink($this->root . $file); @@ -273,7 +276,7 @@ class Cache */ public function clear() { - if ($this->isEnabled()) { + if ($this->isEnabled() && !$this->readOnly) { $this->filesystem->emptyDirectory($this->root); return true; @@ -307,7 +310,7 @@ class Cache */ public function gc(int $ttl, int $maxSize) { - if ($this->isEnabled()) { + if ($this->isEnabled() && !$this->readOnly) { $expire = new \DateTime(); $expire->modify('-'.$ttl.' seconds'); diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 5a0ded2a1..30155452d 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -34,7 +34,7 @@ class Config public static $defaultConfig = array( 'process-timeout' => 300, 'use-include-path' => false, - 'allow-plugins' => null, // null for BC for now, will become array() after July 2022 + 'allow-plugins' => array(), 'use-parent-dir' => 'prompt', 'preferred-install' => 'dist', 'notify-on-install' => true, @@ -120,11 +120,6 @@ class Config // load defaults $this->config = static::$defaultConfig; - // TODO after July 2022 remove this and update the default value above in self::$defaultConfig + remove note from 06-config.md - if (strtotime('2022-07-01') < time()) { - $this->config['allow-plugins'] = array(); - } - $this->repositories = static::$defaultRepositories; $this->useEnvironment = (bool) $useEnvironment; $this->baseDir = is_string($baseDir) && '' !== $baseDir ? $baseDir : null; @@ -186,7 +181,7 @@ class Config if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic', 'bearer'), true) && isset($this->config[$key])) { $this->config[$key] = array_merge($this->config[$key], $val); $this->setSourceOfConfigValue($val, $key, $source); - } elseif (in_array($key, array('allow-plugins'), true) && isset($this->config[$key]) && is_array($this->config[$key])) { + } elseif (in_array($key, array('allow-plugins'), true) && isset($this->config[$key]) && is_array($this->config[$key]) && is_array($val)) { // merging $val first to get the local config on top of the global one, then appending the global config, // then merging local one again to make sure the values from local win over global ones for keys present in both $this->config[$key] = array_merge($val, $this->config[$key], $val); diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 855f4c2ef..69a64397b 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -408,4 +408,34 @@ class ConfigTest extends TestCase $this->assertCount(0, $value); } } + + public function testMergesPluginConfig() + { + $config = new Config(false); + $config->merge(array('config' => array('allow-plugins' => array('some/plugin' => true)))); + $this->assertEquals(array('some/plugin' => true), $config->get('allow-plugins')); + + $config->merge(array('config' => array('allow-plugins' => array('another/plugin' => true)))); + $this->assertEquals(array('some/plugin' => true, 'another/plugin' => true), $config->get('allow-plugins')); + } + + public function testOverridesGlobalBooleanPluginsConfig() + { + $config = new Config(false); + $config->merge(array('config' => array('allow-plugins' => true))); + $this->assertEquals(true, $config->get('allow-plugins')); + + $config->merge(array('config' => array('allow-plugins' => array('another/plugin' => true)))); + $this->assertEquals(array('another/plugin' => true), $config->get('allow-plugins')); + } + + public function testAllowsAllPluginsFromLocalBoolean() + { + $config = new Config(false); + $config->merge(array('config' => array('allow-plugins' => array('some/plugin' => true)))); + $this->assertEquals(array('some/plugin' => true), $config->get('allow-plugins')); + + $config->merge(array('config' => array('allow-plugins' => true))); + $this->assertEquals(true, $config->get('allow-plugins')); + } }