diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 774370b51..0679bb46f 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -220,7 +220,7 @@ class Application extends BaseApplication // Clobber sudo credentials if COMPOSER_ALLOW_SUPERUSER is not set before loading plugins if ($needsSudoCheck) { - $isNonAllowedRoot = function_exists('posix_getuid') && posix_getuid() === 0; + $isNonAllowedRoot = $this->isRunningAsRoot(); if ($isNonAllowedRoot) { if ($uid = (int) Platform::getEnv('SUDO_UID')) { @@ -476,6 +476,12 @@ class Application extends BaseApplication $io->writeError('Check https://getcomposer.org/doc/06-config.md#process-timeout for details', true, IOInterface::QUIET); } + if ($this->getDisablePluginsByDefault() && $this->isRunningAsRoot() && !$this->io->isInteractive()) { + $io->writeError('Plugins have been disabled automatically as you are running as root, this may be the cause of the following exception. See also https://getcomposer.org/root', true, IOInterface::QUIET); + } elseif ($exception instanceof CommandNotFoundException && $this->getDisablePluginsByDefault()) { + $io->writeError('Plugins have been disabled, which may be why some commands are missing, unless you made a typo', true, IOInterface::QUIET); + } + $hints = HttpDownloader::getExceptionHints($exception); if (null !== $hints && count($hints) > 0) { foreach ($hints as $hint) { @@ -678,4 +684,9 @@ class Application extends BaseApplication return $config->get('use-parent-dir'); } + + private function isRunningAsRoot(): bool + { + return function_exists('posix_getuid') && posix_getuid() === 0; + } } diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 74f356826..27afa7c3a 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -94,8 +94,8 @@ class InstallationManager /** * Disables plugins. * - * We prevent any plugins from being instantiated by simply - * deactivating the installer for them. This ensure that no third-party + * We prevent any plugins from being instantiated by + * disabling the PluginManager. This ensures that no third-party * code is ever executed. */ public function disablePlugins(): void @@ -105,7 +105,7 @@ class InstallationManager continue; } - unset($this->installers[$i]); + $installer->disablePlugins(); } } diff --git a/src/Composer/Installer/PluginInstaller.php b/src/Composer/Installer/PluginInstaller.php index 3a982f13b..58ba0d7d7 100644 --- a/src/Composer/Installer/PluginInstaller.php +++ b/src/Composer/Installer/PluginInstaller.php @@ -43,6 +43,11 @@ class PluginInstaller extends LibraryInstaller return $packageType === 'composer-plugin' || $packageType === 'composer-installer'; } + public function disablePlugins(): void + { + $this->getPluginManager()->disablePlugins(); + } + /** * @inheritDoc */ diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 731a5aba0..f594478a6 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -153,6 +153,7 @@ class PluginManager public function registerPackage(PackageInterface $package, bool $failOnMissingClasses = false, bool $isGlobalPlugin = false): void { if ($this->arePluginsDisabled($isGlobalPlugin ? 'global' : 'local')) { + $this->io->writeError('The "'.$package->getName().'" plugin was not loaded as plugins are disabled.'); return; } @@ -656,6 +657,14 @@ class PluginManager return $this->disablePlugins === true || $this->disablePlugins === $type; } + /** + * @internal + */ + public function disablePlugins(): void + { + $this->disablePlugins = true; + } + /** * @internal */