More updates to stop using dev or alpha or beta versions.
[yaffs-website] / web / modules / contrib / security_review / src / Check.php
diff --git a/web/modules/contrib/security_review/src/Check.php b/web/modules/contrib/security_review/src/Check.php
new file mode 100644 (file)
index 0000000..df18deb
--- /dev/null
@@ -0,0 +1,542 @@
+<?php
+
+namespace Drupal\security_review;
+
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
+use Drupal\Core\Logger\RfcLogLevel;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\user\Entity\User;
+
+/**
+ * Defines a security check.
+ */
+abstract class Check {
+
+  use DependencySerializationTrait;
+  use StringTranslationTrait;
+
+  /**
+   * The configuration storage for this check.
+   *
+   * @var \Drupal\Core\Config\Config $config
+   */
+  protected $config;
+
+  /**
+   * The service container.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface
+   */
+  protected $container;
+
+  /**
+   * Settings handler for this check.
+   *
+   * @var \Drupal\security_review\CheckSettingsInterface $settings
+   */
+  protected $settings;
+
+  /**
+   * The State system.
+   *
+   * @var \Drupal\Core\State\State
+   */
+  protected $state;
+
+  /**
+   * The check's prefix in the State system.
+   *
+   * @var string
+   */
+  protected $statePrefix;
+
+  /**
+   * Initializes the configuration storage and the settings handler.
+   */
+  public function __construct() {
+    $this->container = \Drupal::getContainer();
+
+    $this->config = $this->configFactory()
+      ->getEditable('security_review.check.' . $this->id());
+    $this->settings = new CheckSettings($this, $this->config);
+    $this->state = $this->container->get('state');
+    $this->statePrefix = 'security_review.check.' . $this->id() . '.';
+
+    // Set check ID in config.
+    if ($this->config->get('id') != $this->id()) {
+      $this->config->set('id', $this->id());
+      $this->config->save();
+    }
+  }
+
+  /**
+   * Returns the namespace of the check.
+   *
+   * Usually it's the same as the module's name.
+   *
+   * Naming rules (if overridden):
+   *   - All characters should be lowerspace.
+   *   - Use characters only from the english alphabet.
+   *   - Don't use spaces (use "_" instead).
+   *
+   * @return string
+   *   Machine namespace of the check.
+   */
+  public function getMachineNamespace() {
+    $namespace = strtolower($this->getNamespace());
+    $namespace = preg_replace("/[^a-z0-9 ]/", '', $namespace);
+    $namespace = str_replace(' ', '_', $namespace);
+
+    return $namespace;
+  }
+
+  /**
+   * Returns the namespace of the check.
+   *
+   * Usually it's the same as the module's name.
+   *
+   * @return string
+   *   Human-readable namespace of the check.
+   */
+  public abstract function getNamespace();
+
+  /**
+   * Returns the machine name of the check.
+   *
+   * Naming rules (if overridden):
+   *   - All characters should be lowerspace.
+   *   - Use characters only from the english alphabet.
+   *   - Don't use spaces (use "_" instead).
+   *
+   * @return string
+   *   ID of check.
+   */
+  public function getMachineTitle() {
+    $title = strtolower($this->getTitle());
+    $title = preg_replace("/[^a-z0-9 ]/", '', $title);
+    $title = str_replace(' ', '_', $title);
+
+    return $title;
+  }
+
+  /**
+   * Returns the human-readable title of the check.
+   *
+   * @return string
+   *   Title of check.
+   */
+  public abstract function getTitle();
+
+  /**
+   * Returns the identifier constructed using the namespace and title values.
+   *
+   * @return string
+   *   Unique identifier of the check.
+   */
+  public final function id() {
+    return $this->getMachineNamespace() . '-' . $this->getMachineTitle();
+  }
+
+  /**
+   * Returns whether the findings should be stored or reproduced when needed.
+   *
+   * The only case when this function should return false is if the check can
+   * generate a lot of findings (like the File permissions check for example).
+   * Turning this off for checks that don't generate findings at all or just a
+   * few of them actually means more overhead as the check has to be re-run
+   * in order to get its last result.
+   *
+   * @return bool
+   *   Boolean indicating whether findings will be stored.
+   */
+  public function storesFindings() {
+    return TRUE;
+  }
+
+  /**
+   * Returns the check-specific settings' handler.
+   *
+   * @return \Drupal\security_review\CheckSettingsInterface
+   *   The settings interface of the check.
+   */
+  public function settings() {
+    return $this->settings;
+  }
+
+  /**
+   * The actual procedure of carrying out the check.
+   *
+   * @return \Drupal\security_review\CheckResult
+   *   The result of running the check.
+   */
+  public abstract function run();
+
+  /**
+   * Same as run(), but used in CLI context such as Drush.
+   *
+   * @return \Drupal\security_review\CheckResult
+   *   The result of running the check.
+   */
+  public function runCli() {
+    return $this->run();
+  }
+
+  /**
+   * Returns the check-specific help page.
+   *
+   * @return array
+   *   The render array of the check's help page.
+   */
+  public abstract function help();
+
+  /**
+   * Returns the evaluation page of a result.
+   *
+   * Usually this is a list of the findings and an explanation.
+   *
+   * @param \Drupal\security_review\CheckResult $result
+   *   The check result to evaluate.
+   *
+   * @return array
+   *   The render array of the evaluation page.
+   */
+  public function evaluate(CheckResult $result) {
+    return [];
+  }
+
+  /**
+   * Evaluates a CheckResult and returns a plaintext output.
+   *
+   * @param \Drupal\security_review\CheckResult $result
+   *   The check result to evaluate.
+   *
+   * @return string
+   *   The evaluation string.
+   */
+  public function evaluatePlain(CheckResult $result) {
+    return '';
+  }
+
+  /**
+   * Converts a result integer to a human-readable result message.
+   *
+   * @param int $result_const
+   *   The result integer.
+   *
+   * @return string
+   *   The human-readable result message.
+   */
+  public abstract function getMessage($result_const);
+
+  /**
+   * Returns the last stored result of the check.
+   *
+   * Returns null if no results have been stored yet.
+   *
+   * @param bool $get_findings
+   *   Whether to get the findings too.
+   *
+   * @return \Drupal\security_review\CheckResult|null
+   *   The last stored result (or null).
+   */
+  public function lastResult($get_findings = FALSE) {
+    // Get stored data from State system.
+    $state_prefix = $this->statePrefix . 'last_result.';
+    $result = $this->state->get($state_prefix . 'result');
+    if ($get_findings) {
+      $findings = $this->state->get($state_prefix . 'findings');
+    }
+    else {
+      $findings = [];
+    }
+    $time = $this->state->get($state_prefix . 'time');
+    // Force boolean value.
+    $visible = $this->state->get($state_prefix . 'visible') == TRUE;
+
+    // Check validity of stored data.
+    $valid_result = is_int($result)
+      && $result >= CheckResult::SUCCESS
+      && $result <= CheckResult::INFO;
+    $valid_findings = is_array($findings);
+    $valid_time = is_int($time) && $time > 0;
+
+    // If invalid, return NULL.
+    if (!$valid_result || !$valid_findings || !$valid_time) {
+      return NULL;
+    }
+
+    // Construct the CheckResult.
+    $last_result = new CheckResult($this, $result, $findings, $visible, $time);
+
+    // Do a check run for acquiring findings if required.
+    if ($get_findings && !$this->storesFindings()) {
+      // Run the check to get the findings.
+      $fresh_result = $this->run();
+
+      // If it malfunctioned return the last known good result.
+      if (!($fresh_result instanceof CheckResult)) {
+        return $last_result;
+      }
+
+      if ($fresh_result->result() != $last_result->result()) {
+        // If the result is not the same store the new result and return it.
+        $this->storeResult($fresh_result);
+        $this->securityReview()->logCheckResult($fresh_result);
+        return $fresh_result;
+      }
+      else {
+        // Else return the old result with the fresh one's findings.
+        return CheckResult::combine($last_result, $fresh_result);
+      }
+    }
+
+    return $last_result;
+  }
+
+  /**
+   * Returns the timestamp the check was last run.
+   *
+   * Returns 0 if it has not been run yet.
+   *
+   * @return int
+   *   The timestamp of the last stored result.
+   */
+  public function lastRun() {
+    $last_result_time = $this->state
+      ->get($this->statePrefix . 'last_result.time');
+
+    if (!is_int($last_result_time)) {
+      return 0;
+    }
+    return $last_result_time;
+  }
+
+  /**
+   * Returns whether the check is skipped. Checks are not skipped by default.
+   *
+   * @return bool
+   *   Boolean indicating whether the check is skipped.
+   */
+  public function isSkipped() {
+    $is_skipped = $this->config->get('skipped');
+
+    if (!is_bool($is_skipped)) {
+      return FALSE;
+    }
+    return $is_skipped;
+  }
+
+  /**
+   * Returns the user the check was skipped by.
+   *
+   * Returns null if it hasn't been skipped yet or the user that skipped the
+   * check is not valid anymore.
+   *
+   * @return \Drupal\user\Entity\User|null
+   *   The user the check was last skipped by (or null).
+   */
+  public function skippedBy() {
+    $skipped_by = $this->config->get('skipped_by');
+
+    if (!is_int($skipped_by)) {
+      return NULL;
+    }
+    return User::load($skipped_by);
+  }
+
+  /**
+   * Returns the timestamp the check was last skipped on.
+   *
+   * Returns 0 if it hasn't been skipped yet.
+   *
+   * @return int
+   *   The UNIX timestamp the check was last skipped on (or 0).
+   */
+  public function skippedOn() {
+    $skipped_on = $this->config->get('skipped_on');
+
+    if (!is_int($skipped_on)) {
+      return 0;
+    }
+    return $skipped_on;
+  }
+
+  /**
+   * Enables the check. Has no effect if the check was not skipped.
+   */
+  public function enable() {
+    if ($this->isSkipped()) {
+      $this->config->set('skipped', FALSE);
+      $this->config->save();
+
+      // Log.
+      $context = ['@name' => $this->getTitle()];
+      $this->securityReview()->log($this, '@name check no longer skipped', $context, RfcLogLevel::NOTICE);
+    }
+  }
+
+  /**
+   * Marks the check as skipped.
+   *
+   * It still can be ran manually, but will remain skipped on the Run & Review
+   * page.
+   */
+  public function skip() {
+    if (!$this->isSkipped()) {
+      // Store skip data.
+      $this->config->set('skipped', TRUE);
+      $this->config->set('skipped_by', $this->currentUser()->id());
+      $this->config->set('skipped_on', time());
+      $this->config->save();
+
+      // Log.
+      $context = ['@name' => $this->getTitle()];
+      $this->securityReview()->log($this, '@name check skipped', $context, RfcLogLevel::NOTICE);
+    }
+  }
+
+  /**
+   * Stores a result in the state system.
+   *
+   * @param \Drupal\security_review\CheckResult $result
+   *   The result to store.
+   */
+  public function storeResult(CheckResult $result) {
+    if ($result == NULL) {
+      $context = [
+        '@reviewcheck' => $this->getTitle(),
+        '@namespace' => $this->getNamespace(),
+      ];
+      $this->securityReview()->log($this, 'Unable to store check @reviewcheck for @namespace', $context, RfcLogLevel::CRITICAL);
+      return;
+    }
+
+    $findings = $this->storesFindings() ? $result->findings() : [];
+    $this->state->setMultiple([
+      $this->statePrefix . 'last_result.result' => $result->result(),
+      $this->statePrefix . 'last_result.time' => $result->time(),
+      $this->statePrefix . 'last_result.visible' => $result->isVisible(),
+      $this->statePrefix . 'last_result.findings' => $findings,
+    ]);
+  }
+
+  /**
+   * Creates a new CheckResult for this Check.
+   *
+   * @param int $result
+   *   The result integer (see the constants defined in CheckResult).
+   * @param array $findings
+   *   The findings.
+   * @param bool $visible
+   *   The visibility of the result.
+   * @param int $time
+   *   The time the test was run.
+   *
+   * @return \Drupal\security_review\CheckResult
+   *   The created CheckResult.
+   */
+  public function createResult($result, array $findings = [], $visible = TRUE, $time = NULL) {
+    return new CheckResult($this, $result, $findings, $visible, $time);
+  }
+
+  /**
+   * Returns the Security Review Checklist service.
+   *
+   * @return \Drupal\security_review\Checklist
+   *   Security Review's Checklist service.
+   */
+  protected function checklist() {
+    return $this->container->get('security_review.checklist');
+  }
+
+  /**
+   * Returns the Config factory.
+   *
+   * @return \Drupal\Core\Config\ConfigFactory
+   *   Config factory.
+   */
+  protected function configFactory() {
+    return $this->container->get('config.factory');
+  }
+
+  /**
+   * Returns the service container.
+   *
+   * @return \Symfony\Component\DependencyInjection\ContainerInterface
+   *   Service container.
+   */
+  protected function container() {
+    return $this->container;
+  }
+
+  /**
+   * Returns the current Drupal user.
+   *
+   * @return \Drupal\Core\Session\AccountProxy
+   *   Current Drupal user.
+   */
+  protected function currentUser() {
+    return $this->container->get('current_user');
+  }
+
+  /**
+   * Returns the database connection.
+   *
+   * @return \Drupal\Core\Database\Connection
+   *   Database connection.
+   */
+  protected function database() {
+    return $this->container->get('database');
+  }
+
+  /**
+   * Returns the entity manager.
+   *
+   * @return \Drupal\Core\Entity\EntityManagerInterface
+   *   Entity manager.
+   */
+  protected function entityManager() {
+    return $this->container->get('entity.manager');
+  }
+
+  /**
+   * Returns the Drupal Kernel.
+   *
+   * @return \Drupal\Core\DrupalKernel
+   *   Drupal Kernel.
+   */
+  protected function kernel() {
+    return $this->container->get('kernel');
+  }
+
+  /**
+   * Returns the module handler.
+   *
+   * @return \Drupal\Core\Extension\ModuleHandler
+   *   Module handler.
+   */
+  protected function moduleHandler() {
+    return $this->container->get('module_handler');
+  }
+
+  /**
+   * Returns the Security Review Security service.
+   *
+   * @return \Drupal\security_review\Security
+   *   Security Review's Security service.
+   */
+  protected function security() {
+    return $this->container->get('security_review.security');
+  }
+
+  /**
+   * Returns the Security Review service.
+   *
+   * @return \Drupal\security_review\SecurityReview
+   *   Security Review service.
+   */
+  protected function securityReview() {
+    return $this->container->get('security_review');
+  }
+
+}