Skip to content
Snippets Groups Projects
Commit feebd358 authored by Adam G-H's avatar Adam G-H
Browse files

Issue #3356804 by phenaproxima: Flag a warning during status check if the...

Issue #3356804 by phenaproxima: Flag a warning during status check if the OpenSSL extension is not enabled
parent c47bac33
No related branches found
No related tags found
No related merge requests found
......@@ -194,8 +194,8 @@ services:
class: Drupal\package_manager\Validator\StageNotInActiveValidator
tags:
- { name: event_subscriber }
package_manager.validator.xdebug:
class: Drupal\package_manager\Validator\XdebugValidator
package_manager.validator.php_extensions:
class: Drupal\package_manager\Validator\PhpExtensionsValidator
tags:
- { name: event_subscriber }
package_manager.update_processor:
......
......@@ -10,52 +10,67 @@ use Drupal\package_manager\Event\StatusCheckEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Performs validation if Xdebug is enabled.
* Performs validation if certain PHP extensions are enabled.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
class XdebugValidator implements EventSubscriberInterface {
class PhpExtensionsValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Adds warning if Xdebug is enabled.
* Indicates if a particular PHP extension is loaded.
*
* @param \Drupal\package_manager\Event\StatusCheckEvent $event
* The event object.
* @param string $name
* The name of the PHP extension to check for.
*
* @return bool
* TRUE if the given extension is loaded, FALSE otherwise.
*/
public function validateXdebugOff(StatusCheckEvent $event): void {
$warning = $this->checkForXdebug();
if ($warning) {
$event->addWarning($warning);
final protected function isExtensionLoaded(string $name): bool {
// If and ONLY if we're currently running a test, allow the list of loaded
// extensions to be overridden by a state variable.
if (self::insideTest()) {
// By default, assume OpenSSL is enabled and Xdebug isn't. This allows us
// to run tests in environments that we might not support in production,
// such as Drupal CI.
$loaded_extensions = \Drupal::state()
->get('package_manager_loaded_php_extensions', ['openssl']);
return in_array($name, $loaded_extensions, TRUE);
}
return extension_loaded($name);
}
/**
* Checks if Xdebug is enabled and returns a warning if it is.
* Flags a warning if Xdebug is enabled.
*
* @return array|null
* Returns an array of warnings or null if Xdebug isn't detected.
* @param \Drupal\package_manager\Event\StatusCheckEvent $event
* The event object.
*/
protected function checkForXdebug(): ?array {
// Xdebug is allowed to be enabled while running tests, for debugging
// purposes. It's just not allowed to be enabled while using Package Manager
// in a real environment. Except when specifically testing this validator.
// @see \Drupal\Tests\package_manager\Kernel\XdebugValidatorTest::testXdebugValidation()
// @see \Drupal\Tests\automatic_updates\Kernel\StatusCheck\XdebugValidatorTest::simulateXdebugEnabled()
if (self::insideTest() && !function_exists('xdebug_break_TESTED')) {
return NULL;
public function validateXdebug(StatusCheckEvent $event): void {
if ($this->isExtensionLoaded('xdebug')) {
$event->addWarning([
$this->t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.'),
]);
}
}
if (function_exists('xdebug_break')) {
return [
$this->t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.'),
];
/**
* Flags a warning if the OpenSSL extension is not installed.
*
* @param \Drupal\package_manager\Event\StatusCheckEvent $event
* The event object.
*/
public function validateOpenSsl(StatusCheckEvent $event): void {
if (!$this->isExtensionLoaded('openssl')) {
$message = $this->t('The OpenSSL extension is not enabled, which is a security risk. See <a href=":url">the PHP documentation</a> for information on how to enable this extension.', [
':url' => 'https://www.php.net/manual/en/openssl.installation.php',
]);
$event->addWarning([$message]);
}
return NULL;
}
/**
......@@ -78,7 +93,10 @@ class XdebugValidator implements EventSubscriberInterface {
*/
public static function getSubscribedEvents(): array {
return [
StatusCheckEvent::class => 'validateXdebugOff',
StatusCheckEvent::class => [
['validateXdebug'],
['validateOpenSsl'],
],
];
}
......
<?php
declare(strict_types = 1);
namespace Drupal\Tests\package_manager\Kernel;
use Drupal\package_manager\ValidationResult;
/**
* @covers \Drupal\package_manager\Validator\PhpExtensionsValidator
* @group package_manager
* @internal
*/
class PhpExtensionsValidatorTest extends PackageManagerKernelTestBase {
/**
* Data provider for ::testPhpExtensionsValidation().
*
* @return array[]
* The test cases.
*/
public function providerPhpExtensionsValidation(): array {
return [
'xdebug enabled, openssl installed' => [
['xdebug', 'openssl'],
[
ValidationResult::createWarning([
t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.'),
]),
],
],
'xdebug enabled, openssl not installed' => [
['xdebug'],
[
ValidationResult::createWarning([
t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.'),
]),
ValidationResult::createWarning([
t('The OpenSSL extension is not enabled, which is a security risk. See <a href="https://www.php.net/manual/en/openssl.installation.php">the PHP documentation</a> for information on how to enable this extension.'),
]),
],
],
'xdebug disabled, openssl installed' => [
['openssl'],
[],
],
'xdebug disabled, openssl not installed' => [
[],
[
ValidationResult::createWarning([
t('The OpenSSL extension is not enabled, which is a security risk. See <a href="https://www.php.net/manual/en/openssl.installation.php">the PHP documentation</a> for information on how to enable this extension.'),
]),
],
],
];
}
/**
* Tests that PHP extensions' status are checked by Package Manager.
*
* @param string[] $loaded_extensions
* The names of the PHP extensions that the validator should think are
* loaded.
* @param \Drupal\package_manager\ValidationResult[] $expected_results
* The expected validation results.
*
* @dataProvider providerPhpExtensionsValidation
*/
public function testPhpExtensionsValidation(array $loaded_extensions, array $expected_results): void {
// @see \Drupal\package_manager\Validator\PhpExtensionsValidator::isExtensionLoaded()
$this->container->get('state')
->set('package_manager_loaded_php_extensions', $loaded_extensions);
$this->assertStatusCheckResults($expected_results);
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\package_manager\Kernel;
use Drupal\package_manager\ValidationResult;
/**
* @covers \Drupal\package_manager\Validator\XdebugValidator
* @group package_manager
* @internal
*/
class XdebugValidatorTest extends PackageManagerKernelTestBase {
/**
* Tests warnings and/or errors if Xdebug is enabled.
*/
public function testXdebugValidation(): void {
// Ensure the validator will think Xdebug is enabled.
if (!function_exists('xdebug_break')) {
// @codingStandardsIgnoreLine
eval('function xdebug_break() {}');
// @see \Drupal\package_manager\Validator\XdebugValidator::checkForXdebug()
// @codingStandardsIgnoreLine
eval('function xdebug_break_TESTED() {}');
}
$result = ValidationResult::createWarning([
t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.'),
]);
$this->assertStatusCheckResults([$result]);
}
}
......@@ -402,8 +402,9 @@ class Converter {
}
/**
* Removes lines from the module based on a starting and ending token
* These are lines that are not need in core at all.
* Removes lines from the module based on a starting and ending token.
*
* These are lines that are not needed in core at all.
*
* @param string $core_dir
* The path to the root of Drupal Core.
......
......@@ -4,7 +4,7 @@ declare(strict_types = 1);
namespace Drupal\automatic_updates;
use Drupal\automatic_updates\Validator\XdebugValidator;
use Drupal\automatic_updates\Validator\PhpExtensionsValidator;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
......@@ -17,10 +17,10 @@ class AutomaticUpdatesServiceProvider extends ServiceProviderBase {
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
$service_id = 'package_manager.validator.xdebug';
$service_id = 'package_manager.validator.php_extensions';
if ($container->hasDefinition($service_id)) {
$container->getDefinition($service_id)
->setClass(XdebugValidator::class);
->setClass(PhpExtensionsValidator::class);
}
}
......
......@@ -5,41 +5,32 @@ declare(strict_types = 1);
namespace Drupal\automatic_updates\Validator;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\automatic_updates\CronUpdateStage;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\Validator\XdebugValidator as PackageManagerXdebugValidator;
use Drupal\package_manager\Validator\PhpExtensionsValidator as PackageManagerPhpExtensionsValidator;
/**
* Performs validation if Xdebug is enabled.
* Prevents unattended updates if Xdebug is enabled.
*
* @internal
* This is an internal part of Automatic Updates and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class XdebugValidator extends PackageManagerXdebugValidator implements EventSubscriberInterface {
final class PhpExtensionsValidator extends PackageManagerPhpExtensionsValidator implements EventSubscriberInterface {
/**
* Performs validation if Xdebug is enabled.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event object.
* {@inheritdoc}
*/
public function validateXdebugOff(PreOperationStageEvent $event): void {
$stage = $event->stage;
$warning = $this->checkForXdebug();
if ($warning) {
if ($stage instanceof CronUpdateStage) {
// Cron updates are not allowed if Xdebug is enabled.
$event->addError([$this->t("Xdebug is enabled, currently Cron Updates are not allowed while it is enabled. If Xdebug is not disabled you will not receive security and other updates during cron.")]);
}
elseif ($event instanceof StatusCheckEvent) {
$event->addWarning($warning);
}
public function validateXdebug(PreOperationStageEvent $event): void {
if ($this->isExtensionLoaded('xdebug') && $event->stage instanceof CronUpdateStage) {
$event->addError([$this->t("Xdebug is enabled, currently Cron Updates are not allowed while it is enabled. If Xdebug is not disabled you will not receive security and other updates during cron.")]);
}
elseif ($event instanceof StatusCheckEvent) {
parent::validateXdebug($event);
}
}
......@@ -47,11 +38,10 @@ final class XdebugValidator extends PackageManagerXdebugValidator implements Eve
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
PreCreateEvent::class => 'validateXdebugOff',
PreApplyEvent::class => 'validateXdebugOff',
StatusCheckEvent::class => 'validateXdebugOff',
];
$events = parent::getSubscribedEvents();
$events[PreCreateEvent::class] = 'validateXdebug';
$events[PreApplyEvent::class] = 'validateXdebug';
return $events;
}
}
......@@ -6,23 +6,19 @@ namespace Drupal\Tests\automatic_updates\Kernel\StatusCheck;
use Drupal\automatic_updates\CronUpdateStage;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\StatusCheckTrait;
use Drupal\package_manager\ValidationResult;
use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
use Drupal\Tests\package_manager\Traits\PackageManagerBypassTestTrait;
use ColinODell\PsrTestLogger\TestLogger;
/**
* @covers \Drupal\automatic_updates\Validator\XdebugValidator
* @covers \Drupal\automatic_updates\Validator\PhpExtensionsValidator
* @group automatic_updates
* @internal
*/
class XdebugValidatorTest extends AutomaticUpdatesKernelTestBase {
class PhpExtensionsValidatorTest extends AutomaticUpdatesKernelTestBase {
use PackageManagerBypassTestTrait;
use StringTranslationTrait;
use StatusCheckTrait;
/**
* {@inheritdoc}
......@@ -34,37 +30,30 @@ class XdebugValidatorTest extends AutomaticUpdatesKernelTestBase {
*/
public function testXdebugValidation(): void {
$this->simulateXdebugEnabled();
$message = $this->t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.');
$error = $this->t("Xdebug is enabled, currently Cron Updates are not allowed while it is enabled. If Xdebug is not disabled you will not receive security and other updates during cron.");
$config = $this->config('automatic_updates.settings');
// If cron updates are disabled, the status check message should only be
// a warning.
$config->set('cron', CronUpdateStage::DISABLED)->save();
// Package Manager meekly warns about reduced performance when Xdebug is
// enabled; Automatic Updates will actually prevent unattended updates.
$warning_result = ValidationResult::createWarning([
t('Xdebug is enabled, which may have a negative performance impact on Package Manager and any modules that use it.'),
]);
$error_result = ValidationResult::createError([
t("Xdebug is enabled, currently Cron Updates are not allowed while it is enabled. If Xdebug is not disabled you will not receive security and other updates during cron."),
]);
// Tests that other projects which depend on Package manager also get the
// warning.
$stage = $this->createStage();
$this->assertUpdateStagedTimes(0);
$stage->create();
$stage->require(['drupal/random']);
$this->assertUpdateStagedTimes(1);
$event_dispatcher = \Drupal::service('event_dispatcher');
$result = $this->runStatusCheck($stage, $event_dispatcher);
$this->assertSame($message->getUntranslatedString(), $result[0]->messages[0]->getUntranslatedString());
$stage->destroy(TRUE);
$config = $this->config('automatic_updates.settings');
$result = ValidationResult::createWarning([$message]);
$this->assertCheckerResultsFromManager([$result], TRUE);
// If unattended updates are disabled, we should only see a warning from
// Package Manager.
$config->set('cron', CronUpdateStage::DISABLED)->save();
$this->assertCheckerResultsFromManager([$warning_result], TRUE);
// The parent class' setUp() method simulates an available security update,
// so ensure that the cron update stage will try to update to it.
$config->set('cron', CronUpdateStage::SECURITY)->save();
// If cron updates are enabled the status check message should be an
// error.
$result = ValidationResult::createError([$error]);
$this->assertCheckerResultsFromManager([$result], TRUE);
// If unattended updates are enabled, we should see an error from Automatic
// Updates.
$this->assertCheckerResultsFromManager([$error_result], TRUE);
// Trying to do the update during cron should fail with an error.
$logger = new TestLogger();
......@@ -73,46 +62,42 @@ class XdebugValidatorTest extends AutomaticUpdatesKernelTestBase {
->addLogger($logger);
$this->container->get('cron')->run();
// Assert there was not another update staged during cron.
$this->assertUpdateStagedTimes(1);
$this->assertTrue($logger->hasRecordThatMatches("/$error/", (string) RfcLogLevel::ERROR));
// The update should have been stopped before it started.
$this->assertUpdateStagedTimes(0);
$this->assertTrue($logger->hasRecordThatContains((string) $error_result->messages[0], RfcLogLevel::ERROR));
}
/**
* Tests warnings and/or errors if Xdebug is enabled during pre-apply.
*/
public function testXdebugValidationDuringPreApply(): void {
$listener = function (): void {
$this->simulateXdebugEnabled();
};
$this->addEventTestListener($listener);
$message = "Xdebug is enabled, currently Cron Updates are not allowed while it is enabled. If Xdebug is not disabled you will not receive security and other updates during cron.";
// Xdebug will be enabled during pre-apply.
$this->addEventTestListener($this->simulateXdebugEnabled(...));
// The parent class' setUp() method simulates an available security
// update, so ensure that the cron update stage will try to update to it.
$this->config('automatic_updates.settings')->set('cron', CronUpdateStage::SECURITY)->save();
$this->config('automatic_updates.settings')
->set('cron', CronUpdateStage::SECURITY)
->save();
// Trying to do the update during cron should fail with an error.
$logger = new TestLogger();
$this->container->get('logger.factory')
->get('automatic_updates')
->addLogger($logger);
$this->container->get('cron')->run();
// The update should have been staged, but then stopped with an error.
$this->assertUpdateStagedTimes(1);
$this->assertTrue($logger->hasRecordThatMatches("/$message/", (string) RfcLogLevel::ERROR));
$this->assertTrue($logger->hasRecordThatContains("Xdebug is enabled, currently Cron Updates are not allowed while it is enabled. If Xdebug is not disabled you will not receive security and other updates during cron.", RfcLogLevel::ERROR));
}
/**
* Simulating that xdebug is enabled.
*/
private function simulateXdebugEnabled(): void {
if (!function_exists('xdebug_break')) {
// @codingStandardsIgnoreLine
eval('function xdebug_break() {}');
// @see \Drupal\package_manager\Validator\XdebugValidator::checkForXdebug()
// @codingStandardsIgnoreLine
eval('function xdebug_break_TESTED() {}');
}
// @see \Drupal\package_manager\Validator\PhpExtensionsValidator::isExtensionLoaded()
$this->container->get('state')
->set('package_manager_loaded_php_extensions', ['xdebug', 'openssl']);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment