Last updated 16 July 2015. Created on 18 June 2015.
Edited by banviktor, AohRveTPV. Log in to edit this page.

This is the documentation of the API of Security Review for Drupal 8.

Table of contents


Defining a security check

This part of the documentation lets the developer understand the behavior of the module. If anything's unclear it is recommended to look at the example at the bottom of the page.

To define a security check for Security Review, one has to create a class that extends Drupal\security_review\Check.
The functions that must be overridden are the following:

  • getNamespace()
  • getTitle()
  • run()
  • help()
  • getMessage()

Identifiers

There are 5 kinds of identifiers for a given check:

  • namespace
  • machine namespace
  • title
  • machine title
  • id

The 'namespace' must be manually set for each check by overriding the getNamespace() method. This is the human-readable namespace of the check (usually the module's name).

The 'machine namespace' is the version of namespace that is used internally. If getMachineNamespace() isn't overridden, then it is produced from the human-readable namespace by removing any non-alphanumeric characters and replacing spaces with underscores. When overriding getMachineNamespace() this rule must be followed.

The 'title' must be manually set for each check by overriding the getTitle() method. This is the human-readable title of the check.

The 'machine title' has the same relationship to 'title' as 'machine namespace' has to 'namespace'. The machine title should be unique to the namespace. This might only be achievable by overriding getMachineTitle().

The 'id' is only used internally and cannot be overridden. It's constructed by taking the 'machine namespace' and 'machine title' and putting a hyphen between them.

Action and messages

The part where the actual security check happens is the run() method. This method must be overridden, and should always return an instance of Drupal\security_review\CheckResult.

Instantiating a CheckResult:

CheckResult defines one constructor: (Check $check, $result, array $findings, $time = NULL)

  • $check
    The Check that is responsible for the result
  • $result
    ​An integer that defines the outcome of the check (see CheckResult constants):
    • CheckResult::SUCCESS
      for a successful check
    • CheckResult::FAIL
      for a failed check
    • CheckResult::WARN
      for a check that only raised a warning
    • CheckResult::INFO
      general result for providing information
    • CheckResult::HIDE
      for a check result that should not be shown
    • Do NOT use the SKIPPED constant! That is reserved for internal usage.
  • $findings
    An array of findings that can be evaluated. It can be empty.
  • $time
    Timestamp indicating the time when the result was produced. If left null it will be the current time.

NOTE:
It's easier to instantiate a result with Check's createResult() method. It has the same parameters as the constructor for CheckResult, except the $check is left out (set to $this).

Human-readable messages for each result integer:

Must be defined by overriding the getMessage() method. The implementation is usually a switch-case. For more details take a look at Security Review's own Check implementations.

Help page

Every Check can have its own help page by overriding the help() method. This should return a render array.

See render arrays.

Evaluation page (optional)

The evaluation page is for providing an evaluation of a CheckResult produced by the Check. Overriding this is optional, the default implementation returns an empty array. If one chooses to override evaluate(), the function must return a render array.

See render arrays.

Check-specific settings (optional)

If the Check requires storage for settings, it can be accessed via $this->settings(). This method returns a Drupal\security_review\CheckSettingsInterface. It has get() and set() methods for accessing the stored configuration, and buildForm(), submitForm(), validateForm() for form building. By default Check's implementation contains a Drupal\security_review\CheckSettings, which stores the values in the Configuration system, and does nothing in its form building methods. Usually it's enough to extend this class if the Check needs separate settings on the Security Review settings page.

When using check-specific settings it's recommended to define a configuration schema to store the values in their correct types. The schema to declare is called security_review.check_settings.[id of check] .


Hooks

hook_security_review_checks()

To let Security Review know of the checks defined in the module it has to implement hook_security_review_checks(). This hook is fairly simple. It has to return an array of check instances.

For example implementations see security_review.api.php and security_review.module and the example below.

hook_security_review_log()

Provides logging functions for various events:
Check skipped / enabled
Check ran
Check gave a NULL result

For example implementations see security_review.api.php and security_review.module.


Alterable variables

To understand what alterable variables are, take a look at ModuleHandler::alter(). To modify an alterable variable you have to implement hook_[TYPE]_alter.
An example:

//...
/**
 * Implements hook_security_review_unsafe_extensions_alter().
 */
function my_module_security_review_unsafe_extensions_alter(array &$variable)
{
  // Add the .reg file extension to the list of unsafe extensions.
  $variable[] = 'reg';
}

security_review_unsafe_tags

The list of HTML tags considered to be unsafe. See OWASP XSS Filter Evasion Cheat Sheet.

Default variable content is at Security::unsafeTags().

security_review_unsafe_extensions

The list of file extensions considered to be unsafe for upload. Untrusted users should not be allowed to upload files of these extensions.

Default variable content is at Security::unsafeExtensions().

security_review_file_ignore

The list of relative and absolute paths to ignore when running the File permissions check.

Default variable content is at FilePermissions::run().

security_review_temporary_files

The list of files to check for the Temporary files security check.

Default variable definition is at TemporaryFiles::run().


Drush usage

Run the checklist via Drush with the drush security-review command. Consult the Drush help on the security-review command for more information.


Example

In this part we will be going through the whole process of defining a security check starting from scratch.

Let's call our module Security Review Extender and start off with a folder named secrev_extender First we are going to need a .info.yml file.

/secrev_extender.info.yml

name: Security Review Extender
type: module
description: 'Provides additonal security checks for Security Review.'
core: 8.x
# This module functions well on its own, but is not really useful. If your module's only purpose is to define security
# checks for Security Review, then feel free to add security_review as a dependency.
#dependencies:
#  - security_review

Now that we have an empty module, let's start defining a check. Our security check will be called MyCustomCheck. Let's see the example code:

/src/Checks/MyCustomCheck.php


namespace Drupal\secrev_extender\Checks;

use Drupal\security_review\Check;
use Drupal\security_review\CheckResult;

class MyCustomCheck extends Check {
  public function getNamespace() {
    return "Security Review Extender";
  }

  public function getMachineNamespace() {
    // It is not necessary to override this function, but here is an example if you decide to do so.
    return "secrev_extender";
  }

  public function getTitle() {
    return "My Custom Check";
    // This means the machine title will be 'my_custom_check'.
  }

  public function run() {
    // A relatively common structure of a security check.
    $result = CheckResult::SUCCESS;
    $findings = array();

    // The actual security check happens here, possibly modifying $result and $findings as it proceeds.

    return $this->createResult($result, $findings);
  }

  public function help() {
    // Plain HTML output for simplicity.
    $output = '

'; $output .= t('Here you should provide information about what your check does, why it is important.'); $output .= '

'; return array( '#type' => 'markup', '#markup' => $output ); } public function getMessage($resultConst) { // It's enough to handle the constants that are actually used by run(). switch($resultConst) { case CheckResult::SUCCESS: return "The check was successful!"; case CheckResult::FAIL: return "The check failed!"; case CheckResult::WARN: return "The check was almost successful!"; case CheckResult::INFO: return "Just providing some information."; default: return "Unexpected result."; // Normally this shouldn't happen. } } }

The check is finished! The only task remaining is to implement hook_security_review_checks(), so let's see the .module file.

/secrev_extender.module


use Drupal\security_review\Check;
use Drupal\secrev_extender\Checks\MyCustomCheck;

/**
 * Implements hook_security_review_checks().
 */
function secrev_extender_security_review_checks(){
  return array(
    new MyCustomCheck()
  );
}

The module is ready. Enable it and try it!

The following steps explain how check-specific settings work, so everything below this line is optional, and only needed in particular situations.

If your check has to work based on some kind of settings, you should define a configuration schema for it first. In this example we are going to store an integer called some_integer.

/config/schema/secrev_extender.schema.yml

security_review.check_settings.secrev_extender-my_custom_check:
  type: mapping
  mapping:
    some_integer:
      type: integer

To have a form that can modify these settings, you have to override the buildForm() and submitForm() of CheckSettings. To do this we are going to create a class that extends CheckSettings, and we are going to replace MyCustomCheck's $settings to an instance of our new settings class.

/src/CheckSettings/MyCustomCheckSettings.php


namespace Drupal\secrev_extender\CheckSettings;

use Drupal\security_review\CheckSettings;

class MyCustomCheckSettings extends CheckSettings {

  public function buildForm()
  {
    $form = array();

    /*
    $form['some_integer'] = array(
      '#type' => 'radios',
      '#title' => t('Check result'),
      '#description' => t('Sets the value of some_integer.'),
      '#options' => array(
        1 => 'One pass',
        2 => 'Two passes'
      ),
      '#default_value' => $this->get('some_integer', 1),
    );
    */

    return $form;
  }

  public function submitForm(array &$form, $values)
  {
    //$this->set('some_integer', $values['some_integer']);
  }
}

/src/Checks/MyCustomCheck.php

//...
use Drupal\secrev_extender\CheckSettings\MyCustomCheckSettings;
//...
  public function __construct() {
    parent::__construct();

    $this->settings = new MyCustomCheckSettings($this, $this->config);
  }
}

Additional notes

You can provide default values for the check-specific settings using the /config/install directory. In this case it would look like this:

/config/install/security_review.check.secrev_extender-my_custom_check.yml

# The ID must be set in this case.
id: secrev_extender-my_custom_check
settings:
  some_integer: 1

This is all nice and comfortable, but because this item starts with security_review., Drupal only deletes it when Security Review gets uninstalled. If this module gets uninstalled, it won't be installable while this configuration item exists. Security Review has a workaround for this and monitors module uninstalls and automatically deletes orphaned check data, BUT if Security Review is not installed when your module gets uninstalled then this configuration item will remain stored. This can be a problem if your module has other functions than defining checks for Security Review and they might not be used simultaneously. In this case your module should manually delete these items on uninstall. Here's an example how that might be done:

/secrev_extender.module

//...
/**
 * Implements hook_uninstall().
 */
function secrev_extender_uninstall()
{
  // Useful if the module has functions other than defining checks for Security Review.
  foreach(secrev_extender_security_review_checks() as $check){
    /** @var Check $check */
    $config = \Drupal::configFactory()->getEditable('security_review.check.' . $check->id());
    $config->delete();
  }
}

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.