You are here

InputFormats.php in Security Review 8

File

src/Checks/InputFormats.php
View source
<?php

namespace Drupal\security_review\Checks;

use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\security_review\Check;
use Drupal\security_review\CheckResult;

/**
 * Checks for vulnerabilities related to input formats.
 *
 * Checks for formats that either do not have HTML filter that can be used by
 * untrusted users, or if they do check if unsafe tags are allowed.
 */
class InputFormats extends Check {

  /**
   * {@inheritdoc}
   */
  public function getNamespace() {
    return 'Security Review';
  }

  /**
   * {@inheritdoc}
   */
  public function getTitle() {
    return 'Text formats';
  }

  /**
   * {@inheritdoc}
   */
  public function getMachineTitle() {
    return 'input_formats';
  }

  /**
   * {@inheritdoc}
   */
  public function run() {

    // If filter is not enabled return with INFO.
    if (!$this
      ->moduleHandler()
      ->moduleExists('filter')) {
      return $this
        ->createResult(CheckResult::INFO);
    }
    $result = CheckResult::SUCCESS;
    $findings = [];
    $formats = filter_formats();
    $untrusted_roles = $this
      ->security()
      ->untrustedRoles();
    $unsafe_tags = $this
      ->security()
      ->unsafeTags();
    foreach ($formats as $format) {
      $format_roles = array_keys(filter_get_roles_by_format($format));
      $intersect = array_intersect($format_roles, $untrusted_roles);
      if (!empty($intersect)) {

        // Untrusted users can use this format.
        // Check format for enabled HTML filter.
        $filter_html_enabled = FALSE;
        if ($format
          ->filters()
          ->has('filter_html')) {
          $filter_html_enabled = $format
            ->filters('filter_html')
            ->getConfiguration()['status'];
        }
        $filter_html_escape_enabled = FALSE;
        if ($format
          ->filters()
          ->has('filter_html_escape')) {
          $filter_html_escape_enabled = $format
            ->filters('filter_html_escape')
            ->getConfiguration()['status'];
        }
        if ($filter_html_enabled) {
          $filter = $format
            ->filters('filter_html');

          // Check for unsafe tags in allowed tags.
          $allowed_tags = array_keys($filter
            ->getHTMLRestrictions()['allowed']);
          foreach (array_intersect($allowed_tags, $unsafe_tags) as $tag) {

            // Found an unsafe tag.
            $findings['tags'][$format
              ->id()] = $tag;
          }
        }
        elseif (!$filter_html_escape_enabled) {

          // Format is usable by untrusted users but does not contain the HTML
          // Filter or the HTML escape.
          $findings['formats'][$format
            ->id()] = $format
            ->label();
        }
      }
    }
    if (!empty($findings)) {
      $result = CheckResult::FAIL;
    }
    return $this
      ->createResult($result, $findings);
  }

  /**
   * {@inheritdoc}
   */
  public function help() {
    $paragraphs = [];
    $paragraphs[] = $this
      ->t("Certain HTML tags can allow an attacker to take control of your site. Drupal's input format system makes use of a set filters to run on incoming text. The 'HTML Filter' strips out harmful tags and Javascript events and should be used on all formats accessible by untrusted users.");
    $paragraphs[] = new Link($this
      ->t("Read more about Drupal's input formats in the handbooks."), Url::fromUri('http://drupal.org/node/224921'));
    return [
      '#theme' => 'check_help',
      '#title' => $this
        ->t('Allowed HTML tags in text formats'),
      '#paragraphs' => $paragraphs,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function evaluate(CheckResult $result) {
    $output = [];
    if (!empty($result
      ->findings()['tags'])) {
      $paragraphs = [];
      $paragraphs[] = Link::createFromRoute($this
        ->t('Review your text formats.'), 'filter.admin_overview');
      $paragraphs[] = $this
        ->t('It is recommended you remove the following tags from roles accessible by untrusted users.');
      $output[] = [
        '#theme' => 'check_evaluation',
        '#paragraphs' => $paragraphs,
        '#items' => $result
          ->findings()['tags'],
      ];
    }
    if (!empty($result
      ->findings()['formats'])) {
      $paragraphs = [];
      $paragraphs[] = $this
        ->t('The following formats are usable by untrusted roles and do not filter or escape allowed HTML tags.');
      $output[] = [
        '#theme' => 'check_evaluation',
        '#paragraphs' => $paragraphs,
        '#items' => $result
          ->findings()['formats'],
      ];
    }
    return $output;
  }

  /**
   * {@inheritdoc}
   */
  public function evaluatePlain(CheckResult $result) {
    $output = '';
    if (!empty($result
      ->findings()['tags'])) {
      $output .= $this
        ->t('Tags') . "\n";
      foreach ($result
        ->findings()['tags'] as $tag) {
        $output .= "\t{$tag}\n";
      }
    }
    if (!empty($result
      ->findings()['formats'])) {
      $output .= $this
        ->t('Formats') . "\n";
      foreach ($result
        ->findings()['formats'] as $format) {
        $output .= "\t{$format}\n";
      }
    }
    return $output;
  }

  /**
   * {@inheritdoc}
   */
  public function getMessage($result_const) {
    switch ($result_const) {
      case CheckResult::SUCCESS:
        return $this
          ->t('Untrusted users are not allowed to input dangerous HTML tags.');
      case CheckResult::FAIL:
        return $this
          ->t('Untrusted users are allowed to input dangerous HTML tags.');
      case CheckResult::INFO:
        return $this
          ->t('Module filter is not enabled.');
      default:
        return $this
          ->t('Unexpected result.');
    }
  }

}

Classes

Namesort descending Description
InputFormats Checks for vulnerabilities related to input formats.