Including security review as a submodule - with patched for Yaffs.
[yaffs-website] / web / modules / contrib / security_review / src / Checks / InputFormats.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\security_review\Checks\InputFormats.
6  */
7
8 namespace Drupal\security_review\Checks;
9
10 use Drupal\Core\Url;
11 use Drupal\security_review\Check;
12 use Drupal\security_review\CheckResult;
13
14 /**
15  * Checks for vulnerabilities related to input formats.
16  *
17  * Checks for formats that either do not have HTML filter that can be used by
18  * untrusted users, or if they do check if unsafe tags are allowed.
19  */
20 class InputFormats extends Check {
21
22   /**
23    * {@inheritdoc}
24    */
25   public function getNamespace() {
26     return 'Security Review';
27   }
28
29   /**
30    * {@inheritdoc}
31    */
32   public function getTitle() {
33     return 'Text formats';
34   }
35
36   /**
37    * {@inheritdoc}
38    */
39   public function getMachineTitle() {
40     return 'input_formats';
41   }
42
43   /**
44    * {@inheritdoc}
45    */
46   public function run() {
47     // If filter is not enabled return with INFO.
48     if (!$this->moduleHandler()->moduleExists('filter')) {
49       return $this->createResult(CheckResult::INFO);
50     }
51
52     $result = CheckResult::SUCCESS;
53     $findings = [];
54
55     $formats = filter_formats();
56     $untrusted_roles = $this->security()->untrustedRoles();
57     $unsafe_tags = $this->security()->unsafeTags();
58
59     foreach ($formats as $format) {
60       $format_roles = array_keys(filter_get_roles_by_format($format));
61       $intersect = array_intersect($format_roles, $untrusted_roles);
62
63       if (!empty($intersect)) {
64         // Untrusted users can use this format.
65         // Check format for enabled HTML filter.
66         $filter_html_enabled = FALSE;
67         if ($format->filters()->has('filter_html')) {
68           $filter_html_enabled = $format->filters('filter_html')
69             ->getConfiguration()['status'];
70         }
71         $filter_html_escape_enabled = FALSE;
72         if ($format->filters()->has('filter_html_escape')) {
73           $filter_html_escape_enabled = $format->filters('filter_html_escape')
74             ->getConfiguration()['status'];
75         }
76
77         if ($filter_html_enabled) {
78           $filter = $format->filters('filter_html');
79
80           // Check for unsafe tags in allowed tags.
81           $allowed_tags = array_keys($filter->getHTMLRestrictions()['allowed']);
82           foreach (array_intersect($allowed_tags, $unsafe_tags) as $tag) {
83             // Found an unsafe tag.
84             $findings['tags'][$format->id()] = $tag;
85           }
86         }
87         elseif (!$filter_html_escape_enabled) {
88           // Format is usable by untrusted users but does not contain the HTML
89           // Filter or the HTML escape.
90           $findings['formats'][$format->id()] = $format->label();
91         }
92       }
93     }
94
95     if (!empty($findings)) {
96       $result = CheckResult::FAIL;
97     }
98     return $this->createResult($result, $findings);
99   }
100
101   /**
102    * {@inheritdoc}
103    */
104   public function help() {
105     $paragraphs = [];
106     $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.");
107     $paragraphs[] = $this->l(
108       $this->t("Read more about Drupal's input formats in the handbooks."),
109       Url::fromUri('http://drupal.org/node/224921')
110     );
111
112     return [
113       '#theme' => 'check_help',
114       '#title' => $this->t('Allowed HTML tags in text formats'),
115       '#paragraphs' => $paragraphs,
116     ];
117   }
118
119   /**
120    * {@inheritdoc}
121    */
122   public function evaluate(CheckResult $result) {
123     $output = [];
124
125     if (!empty($result->findings()['tags'])) {
126       $paragraphs = [];
127       $paragraphs[] = $this->l(
128         $this->t('Review your text formats.'),
129         Url::fromRoute('filter.admin_overview')
130       );
131       $paragraphs[] = $this->t('It is recommended you remove the following tags from roles accessible by untrusted users.');
132       $output[] = [
133         '#theme' => 'check_evaluation',
134         '#paragraphs' => $paragraphs,
135         '#items' => $result->findings()['tags'],
136       ];
137     }
138
139     if (!empty($result->findings()['formats'])) {
140       $paragraphs = [];
141       $paragraphs[] = $this->t('The following formats are usable by untrusted roles and do not filter or escape allowed HTML tags.');
142       $output[] = [
143         '#theme' => 'check_evaluation',
144         '#paragraphs' => $paragraphs,
145         '#items' => $result->findings()['formats'],
146       ];
147     }
148
149     return $output;
150   }
151
152   /**
153    * {@inheritdoc}
154    */
155   public function evaluatePlain(CheckResult $result) {
156     $output = '';
157
158     if (!empty($result->findings()['tags'])) {
159       $output .= $this->t('Tags') . "\n";
160       foreach ($result->findings()['tags'] as $tag) {
161         $output .= "\t$tag\n";
162       }
163     }
164
165     if (!empty($result->findings()['formats'])) {
166       $output .= $this->t('Formats') . "\n";
167       foreach ($result->findings()['formats'] as $format) {
168         $output .= "\t$format\n";
169       }
170     }
171
172     return $output;
173   }
174
175   /**
176    * {@inheritdoc}
177    */
178   public function getMessage($result_const) {
179     switch ($result_const) {
180       case CheckResult::SUCCESS:
181         return $this->t('Untrusted users are not allowed to input dangerous HTML tags.');
182
183       case CheckResult::FAIL:
184         return $this->t('Untrusted users are allowed to input dangerous HTML tags.');
185
186       case CheckResult::INFO:
187         return $this->t('Module filter is not enabled.');
188
189       default:
190         return $this->t('Unexpected result.');
191     }
192   }
193
194 }