--- /dev/null
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\security_review\Checks\InputFormats.
+ */
+
+namespace Drupal\security_review\Checks;
+
+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[] = $this->l(
+ $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[] = $this->l(
+ $this->t('Review your text formats.'),
+ Url::fromRoute('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.');
+ }
+ }
+
+}