Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Tests\Component\Utility;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Component\Utility\Html;
|
Chris@0
|
6 use Drupal\Component\Utility\UrlHelper;
|
Chris@0
|
7 use Drupal\Component\Utility\Xss;
|
Chris@0
|
8 use PHPUnit\Framework\TestCase;
|
Chris@0
|
9
|
Chris@0
|
10 /**
|
Chris@0
|
11 * XSS Filtering tests.
|
Chris@0
|
12 *
|
Chris@0
|
13 * @group Utility
|
Chris@0
|
14 *
|
Chris@0
|
15 * @coversDefaultClass \Drupal\Component\Utility\Xss
|
Chris@0
|
16 *
|
Chris@0
|
17 * Script injection vectors mostly adopted from http://ha.ckers.org/xss.html.
|
Chris@0
|
18 *
|
Chris@0
|
19 * Relevant CVEs:
|
Chris@0
|
20 * - CVE-2002-1806, ~CVE-2005-0682, ~CVE-2005-2106, CVE-2005-3973,
|
Chris@0
|
21 * CVE-2006-1226 (= rev. 1.112?), CVE-2008-0273, CVE-2008-3740.
|
Chris@0
|
22 */
|
Chris@0
|
23 class XssTest extends TestCase {
|
Chris@0
|
24
|
Chris@0
|
25 /**
|
Chris@0
|
26 * {@inheritdoc}
|
Chris@0
|
27 */
|
Chris@0
|
28 protected function setUp() {
|
Chris@0
|
29 parent::setUp();
|
Chris@0
|
30
|
Chris@0
|
31 $allowed_protocols = [
|
Chris@0
|
32 'http',
|
Chris@0
|
33 'https',
|
Chris@0
|
34 'ftp',
|
Chris@0
|
35 'news',
|
Chris@0
|
36 'nntp',
|
Chris@0
|
37 'telnet',
|
Chris@0
|
38 'mailto',
|
Chris@0
|
39 'irc',
|
Chris@0
|
40 'ssh',
|
Chris@0
|
41 'sftp',
|
Chris@0
|
42 'webcal',
|
Chris@0
|
43 'rtsp',
|
Chris@0
|
44 ];
|
Chris@0
|
45 UrlHelper::setAllowedProtocols($allowed_protocols);
|
Chris@0
|
46 }
|
Chris@0
|
47
|
Chris@0
|
48 /**
|
Chris@0
|
49 * Tests limiting allowed tags and XSS prevention.
|
Chris@0
|
50 *
|
Chris@0
|
51 * XSS tests assume that script is disallowed by default and src is allowed
|
Chris@0
|
52 * by default, but on* and style attributes are disallowed.
|
Chris@0
|
53 *
|
Chris@0
|
54 * @param string $value
|
Chris@0
|
55 * The value to filter.
|
Chris@0
|
56 * @param string $expected
|
Chris@0
|
57 * The expected result.
|
Chris@0
|
58 * @param string $message
|
Chris@0
|
59 * The assertion message to display upon failure.
|
Chris@0
|
60 * @param array $allowed_tags
|
Chris@0
|
61 * (optional) The allowed HTML tags to be passed to \Drupal\Component\Utility\Xss::filter().
|
Chris@0
|
62 *
|
Chris@0
|
63 * @dataProvider providerTestFilterXssNormalized
|
Chris@0
|
64 */
|
Chris@0
|
65 public function testFilterXssNormalized($value, $expected, $message, array $allowed_tags = NULL) {
|
Chris@0
|
66 if ($allowed_tags === NULL) {
|
Chris@0
|
67 $value = Xss::filter($value);
|
Chris@0
|
68 }
|
Chris@0
|
69 else {
|
Chris@0
|
70 $value = Xss::filter($value, $allowed_tags);
|
Chris@0
|
71 }
|
Chris@0
|
72 $this->assertNormalized($value, $expected, $message);
|
Chris@0
|
73 }
|
Chris@0
|
74
|
Chris@0
|
75 /**
|
Chris@0
|
76 * Data provider for testFilterXssNormalized().
|
Chris@0
|
77 *
|
Chris@0
|
78 * @see testFilterXssNormalized()
|
Chris@0
|
79 *
|
Chris@0
|
80 * @return array
|
Chris@0
|
81 * An array of arrays containing strings:
|
Chris@0
|
82 * - The value to filter.
|
Chris@0
|
83 * - The value to expect after filtering.
|
Chris@0
|
84 * - The assertion message.
|
Chris@0
|
85 * - (optional) The allowed HTML HTML tags array that should be passed to
|
Chris@0
|
86 * \Drupal\Component\Utility\Xss::filter().
|
Chris@0
|
87 */
|
Chris@0
|
88 public function providerTestFilterXssNormalized() {
|
Chris@0
|
89 return [
|
Chris@0
|
90 [
|
Chris@0
|
91 "Who's Online",
|
Chris@0
|
92 "who's online",
|
Chris@0
|
93 'HTML filter -- html entity number',
|
Chris@0
|
94 ],
|
Chris@0
|
95 [
|
Chris@0
|
96 "Who&#039;s Online",
|
Chris@0
|
97 "who's online",
|
Chris@0
|
98 'HTML filter -- encoded html entity number',
|
Chris@0
|
99 ],
|
Chris@0
|
100 [
|
Chris@0
|
101 "Who&amp;#039; Online",
|
Chris@0
|
102 "who&#039; online",
|
Chris@0
|
103 'HTML filter -- double encoded html entity number',
|
Chris@0
|
104 ],
|
Chris@0
|
105 // Custom elements with dashes in the tag name.
|
Chris@0
|
106 [
|
Chris@0
|
107 "<test-element></test-element>",
|
Chris@0
|
108 "<test-element></test-element>",
|
Chris@0
|
109 'Custom element with dashes in tag name.',
|
Chris@0
|
110 ['test-element'],
|
Chris@0
|
111 ],
|
Chris@0
|
112 ];
|
Chris@0
|
113 }
|
Chris@0
|
114
|
Chris@0
|
115 /**
|
Chris@0
|
116 * Tests limiting to allowed tags and XSS prevention.
|
Chris@0
|
117 *
|
Chris@0
|
118 * XSS tests assume that script is disallowed by default and src is allowed
|
Chris@0
|
119 * by default, but on* and style attributes are disallowed.
|
Chris@0
|
120 *
|
Chris@0
|
121 * @param string $value
|
Chris@0
|
122 * The value to filter.
|
Chris@0
|
123 * @param string $expected
|
Chris@0
|
124 * The string that is expected to be missing.
|
Chris@0
|
125 * @param string $message
|
Chris@0
|
126 * The assertion message to display upon failure.
|
Chris@0
|
127 * @param array $allowed_tags
|
Chris@0
|
128 * (optional) The allowed HTML tags to be passed to \Drupal\Component\Utility\Xss::filter().
|
Chris@0
|
129 *
|
Chris@0
|
130 * @dataProvider providerTestFilterXssNotNormalized
|
Chris@0
|
131 */
|
Chris@0
|
132 public function testFilterXssNotNormalized($value, $expected, $message, array $allowed_tags = NULL) {
|
Chris@0
|
133 if ($allowed_tags === NULL) {
|
Chris@0
|
134 $value = Xss::filter($value);
|
Chris@0
|
135 }
|
Chris@0
|
136 else {
|
Chris@0
|
137 $value = Xss::filter($value, $allowed_tags);
|
Chris@0
|
138 }
|
Chris@0
|
139 $this->assertNotNormalized($value, $expected, $message);
|
Chris@0
|
140 }
|
Chris@0
|
141
|
Chris@0
|
142 /**
|
Chris@0
|
143 * Data provider for testFilterXssNotNormalized().
|
Chris@0
|
144 *
|
Chris@0
|
145 * @see testFilterXssNotNormalized()
|
Chris@0
|
146 *
|
Chris@0
|
147 * @return array
|
Chris@0
|
148 * An array of arrays containing the following elements:
|
Chris@0
|
149 * - The value to filter.
|
Chris@0
|
150 * - The value to expect that's missing after filtering.
|
Chris@0
|
151 * - The assertion message.
|
Chris@0
|
152 * - (optional) The allowed HTML HTML tags array that should be passed to
|
Chris@0
|
153 * \Drupal\Component\Utility\Xss::filter().
|
Chris@0
|
154 */
|
Chris@0
|
155 public function providerTestFilterXssNotNormalized() {
|
Chris@0
|
156 $cases = [
|
Chris@0
|
157 // Tag stripping, different ways to work around removal of HTML tags.
|
Chris@0
|
158 [
|
Chris@0
|
159 '<script>alert(0)</script>',
|
Chris@0
|
160 'script',
|
Chris@0
|
161 'HTML tag stripping -- simple script without special characters.',
|
Chris@0
|
162 ],
|
Chris@0
|
163 [
|
Chris@0
|
164 '<script src="http://www.example.com" />',
|
Chris@0
|
165 'script',
|
Chris@0
|
166 'HTML tag stripping -- empty script with source.',
|
Chris@0
|
167 ],
|
Chris@0
|
168 [
|
Chris@0
|
169 '<ScRipt sRc=http://www.example.com/>',
|
Chris@0
|
170 'script',
|
Chris@0
|
171 'HTML tag stripping evasion -- varying case.',
|
Chris@0
|
172 ],
|
Chris@0
|
173 [
|
Chris@0
|
174 "<script\nsrc\n=\nhttp://www.example.com/\n>",
|
Chris@0
|
175 'script',
|
Chris@0
|
176 'HTML tag stripping evasion -- multiline tag.',
|
Chris@0
|
177 ],
|
Chris@0
|
178 [
|
Chris@0
|
179 '<script/a src=http://www.example.com/a.js></script>',
|
Chris@0
|
180 'script',
|
Chris@0
|
181 'HTML tag stripping evasion -- non whitespace character after tag name.',
|
Chris@0
|
182 ],
|
Chris@0
|
183 [
|
Chris@0
|
184 '<script/src=http://www.example.com/a.js></script>',
|
Chris@0
|
185 'script',
|
Chris@0
|
186 'HTML tag stripping evasion -- no space between tag and attribute.',
|
Chris@0
|
187 ],
|
Chris@0
|
188 // Null between < and tag name works at least with IE6.
|
Chris@0
|
189 [
|
Chris@0
|
190 "<\0scr\0ipt>alert(0)</script>",
|
Chris@0
|
191 'ipt',
|
Chris@0
|
192 'HTML tag stripping evasion -- breaking HTML with nulls.',
|
Chris@0
|
193 ],
|
Chris@0
|
194 [
|
Chris@0
|
195 "<scrscriptipt src=http://www.example.com/a.js>",
|
Chris@0
|
196 'script',
|
Chris@0
|
197 'HTML tag stripping evasion -- filter just removing "script".',
|
Chris@0
|
198 ],
|
Chris@0
|
199 [
|
Chris@0
|
200 '<<script>alert(0);//<</script>',
|
Chris@0
|
201 'script',
|
Chris@0
|
202 'HTML tag stripping evasion -- double opening brackets.',
|
Chris@0
|
203 ],
|
Chris@0
|
204 [
|
Chris@0
|
205 '<script src=http://www.example.com/a.js?<b>',
|
Chris@0
|
206 'script',
|
Chris@0
|
207 'HTML tag stripping evasion -- no closing tag.',
|
Chris@0
|
208 ],
|
Chris@0
|
209 // DRUPAL-SA-2008-047: This doesn't seem exploitable, but the filter should
|
Chris@0
|
210 // work consistently.
|
Chris@0
|
211 [
|
Chris@0
|
212 '<script>>',
|
Chris@0
|
213 'script',
|
Chris@0
|
214 'HTML tag stripping evasion -- double closing tag.',
|
Chris@0
|
215 ],
|
Chris@0
|
216 [
|
Chris@0
|
217 '<script src=//www.example.com/.a>',
|
Chris@0
|
218 'script',
|
Chris@0
|
219 'HTML tag stripping evasion -- no scheme or ending slash.',
|
Chris@0
|
220 ],
|
Chris@0
|
221 [
|
Chris@0
|
222 '<script src=http://www.example.com/.a',
|
Chris@0
|
223 'script',
|
Chris@0
|
224 'HTML tag stripping evasion -- no closing bracket.',
|
Chris@0
|
225 ],
|
Chris@0
|
226 [
|
Chris@0
|
227 '<script src=http://www.example.com/ <',
|
Chris@0
|
228 'script',
|
Chris@0
|
229 'HTML tag stripping evasion -- opening instead of closing bracket.',
|
Chris@0
|
230 ],
|
Chris@0
|
231 [
|
Chris@0
|
232 '<nosuchtag attribute="newScriptInjectionVector">',
|
Chris@0
|
233 'nosuchtag',
|
Chris@0
|
234 'HTML tag stripping evasion -- unknown tag.',
|
Chris@0
|
235 ],
|
Chris@0
|
236 [
|
Chris@0
|
237 '<t:set attributeName="innerHTML" to="<script defer>alert(0)</script>">',
|
Chris@0
|
238 't:set',
|
Chris@0
|
239 'HTML tag stripping evasion -- colon in the tag name (namespaces\' tricks).',
|
Chris@0
|
240 ],
|
Chris@0
|
241 [
|
Chris@0
|
242 '<img """><script>alert(0)</script>',
|
Chris@0
|
243 'script',
|
Chris@0
|
244 'HTML tag stripping evasion -- a malformed image tag.',
|
Chris@0
|
245 ['img'],
|
Chris@0
|
246 ],
|
Chris@0
|
247 [
|
Chris@0
|
248 '<blockquote><script>alert(0)</script></blockquote>',
|
Chris@0
|
249 'script',
|
Chris@0
|
250 'HTML tag stripping evasion -- script in a blockqoute.',
|
Chris@0
|
251 ['blockquote'],
|
Chris@0
|
252 ],
|
Chris@0
|
253 [
|
Chris@0
|
254 "<!--[if true]><script>alert(0)</script><![endif]-->",
|
Chris@0
|
255 'script',
|
Chris@0
|
256 'HTML tag stripping evasion -- script within a comment.',
|
Chris@0
|
257 ],
|
Chris@0
|
258 // Dangerous attributes removal.
|
Chris@0
|
259 [
|
Chris@0
|
260 '<p onmouseover="http://www.example.com/">',
|
Chris@0
|
261 'onmouseover',
|
Chris@0
|
262 'HTML filter attributes removal -- events, no evasion.',
|
Chris@0
|
263 ['p'],
|
Chris@0
|
264 ],
|
Chris@0
|
265 [
|
Chris@0
|
266 '<li style="list-style-image: url(javascript:alert(0))">',
|
Chris@0
|
267 'style',
|
Chris@0
|
268 'HTML filter attributes removal -- style, no evasion.',
|
Chris@0
|
269 ['li'],
|
Chris@0
|
270 ],
|
Chris@0
|
271 [
|
Chris@0
|
272 '<img onerror =alert(0)>',
|
Chris@0
|
273 'onerror',
|
Chris@0
|
274 'HTML filter attributes removal evasion -- spaces before equals sign.',
|
Chris@0
|
275 ['img'],
|
Chris@0
|
276 ],
|
Chris@0
|
277 [
|
Chris@0
|
278 '<img onabort!#$%&()*~+-_.,:;?@[/|\]^`=alert(0)>',
|
Chris@0
|
279 'onabort',
|
Chris@0
|
280 'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.',
|
Chris@0
|
281 ['img'],
|
Chris@0
|
282 ],
|
Chris@0
|
283 [
|
Chris@0
|
284 '<img oNmediAError=alert(0)>',
|
Chris@0
|
285 'onmediaerror',
|
Chris@0
|
286 'HTML filter attributes removal evasion -- varying case.',
|
Chris@0
|
287 ['img'],
|
Chris@0
|
288 ],
|
Chris@0
|
289 // Works at least with IE6.
|
Chris@0
|
290 [
|
Chris@0
|
291 "<img o\0nfocus\0=alert(0)>",
|
Chris@0
|
292 'focus',
|
Chris@0
|
293 'HTML filter attributes removal evasion -- breaking with nulls.',
|
Chris@0
|
294 ['img'],
|
Chris@0
|
295 ],
|
Chris@0
|
296 // Only whitelisted scheme names allowed in attributes.
|
Chris@0
|
297 [
|
Chris@0
|
298 '<img src="javascript:alert(0)">',
|
Chris@0
|
299 'javascript',
|
Chris@0
|
300 'HTML scheme clearing -- no evasion.',
|
Chris@0
|
301 ['img'],
|
Chris@0
|
302 ],
|
Chris@0
|
303 [
|
Chris@0
|
304 '<img src=javascript:alert(0)>',
|
Chris@0
|
305 'javascript',
|
Chris@0
|
306 'HTML scheme clearing evasion -- no quotes.',
|
Chris@0
|
307 ['img'],
|
Chris@0
|
308 ],
|
Chris@0
|
309 // A bit like CVE-2006-0070.
|
Chris@0
|
310 [
|
Chris@0
|
311 '<img src="javascript:confirm(0)">',
|
Chris@0
|
312 'javascript',
|
Chris@0
|
313 'HTML scheme clearing evasion -- no alert ;)',
|
Chris@0
|
314 ['img'],
|
Chris@0
|
315 ],
|
Chris@0
|
316 [
|
Chris@0
|
317 '<img src=`javascript:alert(0)`>',
|
Chris@0
|
318 'javascript',
|
Chris@0
|
319 'HTML scheme clearing evasion -- grave accents.',
|
Chris@0
|
320 ['img'],
|
Chris@0
|
321 ],
|
Chris@0
|
322 [
|
Chris@0
|
323 '<img dynsrc="javascript:alert(0)">',
|
Chris@0
|
324 'javascript',
|
Chris@0
|
325 'HTML scheme clearing -- rare attribute.',
|
Chris@0
|
326 ['img'],
|
Chris@0
|
327 ],
|
Chris@0
|
328 [
|
Chris@0
|
329 '<table background="javascript:alert(0)">',
|
Chris@0
|
330 'javascript',
|
Chris@0
|
331 'HTML scheme clearing -- another tag.',
|
Chris@0
|
332 ['table'],
|
Chris@0
|
333 ],
|
Chris@0
|
334 [
|
Chris@0
|
335 '<base href="javascript:alert(0);//">',
|
Chris@0
|
336 'javascript',
|
Chris@0
|
337 'HTML scheme clearing -- one more attribute and tag.',
|
Chris@0
|
338 ['base'],
|
Chris@0
|
339 ],
|
Chris@0
|
340 [
|
Chris@0
|
341 '<img src="jaVaSCriPt:alert(0)">',
|
Chris@0
|
342 'javascript',
|
Chris@0
|
343 'HTML scheme clearing evasion -- varying case.',
|
Chris@0
|
344 ['img'],
|
Chris@0
|
345 ],
|
Chris@0
|
346 [
|
Chris@0
|
347 '<img src=javascript:alert(0)>',
|
Chris@0
|
348 'javascript',
|
Chris@0
|
349 'HTML scheme clearing evasion -- UTF-8 decimal encoding.',
|
Chris@0
|
350 ['img'],
|
Chris@0
|
351 ],
|
Chris@0
|
352 [
|
Chris@0
|
353 '<img src=javascript:alert(0)>',
|
Chris@0
|
354 'javascript',
|
Chris@0
|
355 'HTML scheme clearing evasion -- long UTF-8 encoding.',
|
Chris@0
|
356 ['img'],
|
Chris@0
|
357 ],
|
Chris@0
|
358 [
|
Chris@0
|
359 '<img src=javascript:alert(0)>',
|
Chris@0
|
360 'javascript',
|
Chris@0
|
361 'HTML scheme clearing evasion -- UTF-8 hex encoding.',
|
Chris@0
|
362 ['img'],
|
Chris@0
|
363 ],
|
Chris@0
|
364 [
|
Chris@0
|
365 "<img src=\"jav\tascript:alert(0)\">",
|
Chris@0
|
366 'script',
|
Chris@0
|
367 'HTML scheme clearing evasion -- an embedded tab.',
|
Chris@0
|
368 ['img'],
|
Chris@0
|
369 ],
|
Chris@0
|
370 [
|
Chris@0
|
371 '<img src="jav	ascript:alert(0)">',
|
Chris@0
|
372 'script',
|
Chris@0
|
373 'HTML scheme clearing evasion -- an encoded, embedded tab.',
|
Chris@0
|
374 ['img'],
|
Chris@0
|
375 ],
|
Chris@0
|
376 [
|
Chris@0
|
377 '<img src="jav
ascript:alert(0)">',
|
Chris@0
|
378 'script',
|
Chris@0
|
379 'HTML scheme clearing evasion -- an encoded, embedded newline.',
|
Chris@0
|
380 ['img'],
|
Chris@0
|
381 ],
|
Chris@0
|
382 // With 
 this test would fail, but the entity gets turned into
|
Chris@0
|
383 // &#xD;, so it's OK.
|
Chris@0
|
384 [
|
Chris@0
|
385 '<img src="jav
ascript:alert(0)">',
|
Chris@0
|
386 'script',
|
Chris@0
|
387 'HTML scheme clearing evasion -- an encoded, embedded carriage return.',
|
Chris@0
|
388 ['img'],
|
Chris@0
|
389 ],
|
Chris@0
|
390 [
|
Chris@0
|
391 "<img src=\"\n\n\nj\na\nva\ns\ncript:alert(0)\">",
|
Chris@0
|
392 'cript',
|
Chris@0
|
393 'HTML scheme clearing evasion -- broken into many lines.',
|
Chris@0
|
394 ['img'],
|
Chris@0
|
395 ],
|
Chris@0
|
396 [
|
Chris@0
|
397 "<img src=\"jav\0a\0\0cript:alert(0)\">",
|
Chris@0
|
398 'cript',
|
Chris@0
|
399 'HTML scheme clearing evasion -- embedded nulls.',
|
Chris@0
|
400 ['img'],
|
Chris@0
|
401 ],
|
Chris@0
|
402 [
|
Chris@0
|
403 '<img src="vbscript:msgbox(0)">',
|
Chris@0
|
404 'vbscript',
|
Chris@0
|
405 'HTML scheme clearing evasion -- another scheme.',
|
Chris@0
|
406 ['img'],
|
Chris@0
|
407 ],
|
Chris@0
|
408 [
|
Chris@0
|
409 '<img src="nosuchscheme:notice(0)">',
|
Chris@0
|
410 'nosuchscheme',
|
Chris@0
|
411 'HTML scheme clearing evasion -- unknown scheme.',
|
Chris@0
|
412 ['img'],
|
Chris@0
|
413 ],
|
Chris@0
|
414 // Netscape 4.x javascript entities.
|
Chris@0
|
415 [
|
Chris@0
|
416 '<br size="&{alert(0)}">',
|
Chris@0
|
417 'alert',
|
Chris@0
|
418 'Netscape 4.x javascript entities.',
|
Chris@0
|
419 ['br'],
|
Chris@0
|
420 ],
|
Chris@0
|
421 // DRUPAL-SA-2008-006: Invalid UTF-8, these only work as reflected XSS with
|
Chris@0
|
422 // Internet Explorer 6.
|
Chris@0
|
423 [
|
Chris@0
|
424 "<p arg=\"\xe0\">\" style=\"background-image: url(javascript:alert(0));\"\xe0<p>",
|
Chris@0
|
425 'style',
|
Chris@0
|
426 'HTML filter -- invalid UTF-8.',
|
Chris@0
|
427 ['p'],
|
Chris@0
|
428 ],
|
Chris@0
|
429 ];
|
Chris@0
|
430 // @fixme This dataset currently fails under 5.4 because of
|
Chris@0
|
431 // https://www.drupal.org/node/1210798. Restore after its fixed.
|
Chris@0
|
432 if (version_compare(PHP_VERSION, '5.4.0', '<')) {
|
Chris@0
|
433 $cases[] = [
|
Chris@0
|
434 '<img src="  javascript:alert(0)">',
|
Chris@0
|
435 'javascript',
|
Chris@0
|
436 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.',
|
Chris@0
|
437 ['img'],
|
Chris@0
|
438 ];
|
Chris@0
|
439 }
|
Chris@0
|
440 return $cases;
|
Chris@0
|
441 }
|
Chris@0
|
442
|
Chris@0
|
443 /**
|
Chris@0
|
444 * Checks that invalid multi-byte sequences are rejected.
|
Chris@0
|
445 *
|
Chris@0
|
446 * @param string $value
|
Chris@0
|
447 * The value to filter.
|
Chris@0
|
448 * @param string $expected
|
Chris@0
|
449 * The expected result.
|
Chris@0
|
450 * @param string $message
|
Chris@0
|
451 * The assertion message to display upon failure.
|
Chris@0
|
452 *
|
Chris@0
|
453 * @dataProvider providerTestInvalidMultiByte
|
Chris@0
|
454 */
|
Chris@0
|
455 public function testInvalidMultiByte($value, $expected, $message) {
|
Chris@0
|
456 $this->assertEquals(Xss::filter($value), $expected, $message);
|
Chris@0
|
457 }
|
Chris@0
|
458
|
Chris@0
|
459 /**
|
Chris@0
|
460 * Data provider for testInvalidMultiByte().
|
Chris@0
|
461 *
|
Chris@0
|
462 * @see testInvalidMultiByte()
|
Chris@0
|
463 *
|
Chris@0
|
464 * @return array
|
Chris@0
|
465 * An array of arrays containing strings:
|
Chris@0
|
466 * - The value to filter.
|
Chris@0
|
467 * - The value to expect after filtering.
|
Chris@0
|
468 * - The assertion message.
|
Chris@0
|
469 */
|
Chris@0
|
470 public function providerTestInvalidMultiByte() {
|
Chris@0
|
471 return [
|
Chris@0
|
472 ["Foo\xC0barbaz", '', 'Xss::filter() accepted invalid sequence "Foo\xC0barbaz"'],
|
Chris@0
|
473 ["Fooÿñ", "Fooÿñ", 'Xss::filter() rejects valid sequence Fooÿñ"'],
|
Chris@0
|
474 ["\xc0aaa", '', 'HTML filter -- overlong UTF-8 sequences.'],
|
Chris@0
|
475 ];
|
Chris@0
|
476 }
|
Chris@0
|
477
|
Chris@0
|
478 /**
|
Chris@0
|
479 * Checks that strings starting with a question sign are correctly processed.
|
Chris@0
|
480 */
|
Chris@0
|
481 public function testQuestionSign() {
|
Chris@0
|
482 $value = Xss::filter('<?xml:namespace ns="urn:schemas-microsoft-com:time">');
|
Chris@0
|
483 $this->assertTrue(stripos($value, '<?xml') === FALSE, 'HTML tag stripping evasion -- starting with a question sign (processing instructions).');
|
Chris@0
|
484 }
|
Chris@0
|
485
|
Chris@0
|
486 /**
|
Chris@0
|
487 * Check that strings in HTML attributes are correctly processed.
|
Chris@0
|
488 *
|
Chris@0
|
489 * @covers ::attributes
|
Chris@0
|
490 * @dataProvider providerTestAttributes
|
Chris@0
|
491 */
|
Chris@0
|
492 public function testAttribute($value, $expected, $message, $allowed_tags = NULL) {
|
Chris@0
|
493 $value = Xss::filter($value, $allowed_tags);
|
Chris@0
|
494 $this->assertEquals($expected, $value, $message);
|
Chris@0
|
495 }
|
Chris@0
|
496
|
Chris@0
|
497 /**
|
Chris@0
|
498 * Data provider for testFilterXssAdminNotNormalized().
|
Chris@0
|
499 */
|
Chris@0
|
500 public function providerTestAttributes() {
|
Chris@0
|
501 return [
|
Chris@0
|
502 [
|
Chris@0
|
503 '<img src="http://example.com/foo.jpg" title="Example: title" alt="Example: alt">',
|
Chris@0
|
504 '<img src="http://example.com/foo.jpg" title="Example: title" alt="Example: alt">',
|
Chris@0
|
505 'Image tag with alt and title attribute',
|
Chris@17
|
506 ['img'],
|
Chris@0
|
507 ],
|
Chris@0
|
508 [
|
Chris@0
|
509 '<a href="https://www.drupal.org/" rel="dc:publisher">Drupal</a>',
|
Chris@0
|
510 '<a href="https://www.drupal.org/" rel="dc:publisher">Drupal</a>',
|
Chris@0
|
511 'Link tag with rel attribute',
|
Chris@17
|
512 ['a'],
|
Chris@0
|
513 ],
|
Chris@0
|
514 [
|
Chris@0
|
515 '<span property="dc:subject">Drupal 8: The best release ever.</span>',
|
Chris@0
|
516 '<span property="dc:subject">Drupal 8: The best release ever.</span>',
|
Chris@0
|
517 'Span tag with property attribute',
|
Chris@17
|
518 ['span'],
|
Chris@0
|
519 ],
|
Chris@0
|
520 [
|
Chris@0
|
521 '<img src="http://example.com/foo.jpg" data-caption="Drupal 8: The best release ever.">',
|
Chris@0
|
522 '<img src="http://example.com/foo.jpg" data-caption="Drupal 8: The best release ever.">',
|
Chris@0
|
523 'Image tag with data attribute',
|
Chris@17
|
524 ['img'],
|
Chris@0
|
525 ],
|
Chris@0
|
526 [
|
Chris@0
|
527 '<a data-a2a-url="foo"></a>',
|
Chris@0
|
528 '<a data-a2a-url="foo"></a>',
|
Chris@0
|
529 'Link tag with numeric data attribute',
|
Chris@17
|
530 ['a'],
|
Chris@0
|
531 ],
|
Chris@0
|
532 ];
|
Chris@0
|
533 }
|
Chris@0
|
534
|
Chris@0
|
535 /**
|
Chris@0
|
536 * Checks that \Drupal\Component\Utility\Xss::filterAdmin() correctly strips unallowed tags.
|
Chris@0
|
537 */
|
Chris@0
|
538 public function testFilterXSSAdmin() {
|
Chris@0
|
539 $value = Xss::filterAdmin('<style /><iframe /><frame /><frameset /><meta /><link /><embed /><applet /><param /><layer />');
|
Chris@0
|
540 $this->assertEquals($value, '', 'Admin HTML filter -- should never allow some tags.');
|
Chris@0
|
541 }
|
Chris@0
|
542
|
Chris@0
|
543 /**
|
Chris@0
|
544 * Tests the loose, admin HTML filter.
|
Chris@0
|
545 *
|
Chris@0
|
546 * @param string $value
|
Chris@0
|
547 * The value to filter.
|
Chris@0
|
548 * @param string $expected
|
Chris@0
|
549 * The expected result.
|
Chris@0
|
550 * @param string $message
|
Chris@0
|
551 * The assertion message to display upon failure.
|
Chris@0
|
552 *
|
Chris@0
|
553 * @dataProvider providerTestFilterXssAdminNotNormalized
|
Chris@0
|
554 */
|
Chris@0
|
555 public function testFilterXssAdminNotNormalized($value, $expected, $message) {
|
Chris@0
|
556 $this->assertNotNormalized(Xss::filterAdmin($value), $expected, $message);
|
Chris@0
|
557 }
|
Chris@0
|
558
|
Chris@0
|
559 /**
|
Chris@0
|
560 * Data provider for testFilterXssAdminNotNormalized().
|
Chris@0
|
561 *
|
Chris@0
|
562 * @see testFilterXssAdminNotNormalized()
|
Chris@0
|
563 *
|
Chris@0
|
564 * @return array
|
Chris@0
|
565 * An array of arrays containing strings:
|
Chris@0
|
566 * - The value to filter.
|
Chris@0
|
567 * - The value to expect after filtering.
|
Chris@0
|
568 * - The assertion message.
|
Chris@0
|
569 */
|
Chris@0
|
570 public function providerTestFilterXssAdminNotNormalized() {
|
Chris@0
|
571 return [
|
Chris@0
|
572 // DRUPAL-SA-2008-044
|
Chris@0
|
573 ['<object />', 'object', 'Admin HTML filter -- should not allow object tag.'],
|
Chris@0
|
574 ['<script />', 'script', 'Admin HTML filter -- should not allow script tag.'],
|
Chris@0
|
575 ];
|
Chris@0
|
576 }
|
Chris@0
|
577
|
Chris@0
|
578 /**
|
Chris@0
|
579 * Asserts that a text transformed to lowercase with HTML entities decoded does contain a given string.
|
Chris@0
|
580 *
|
Chris@0
|
581 * Otherwise fails the test with a given message, similar to all the
|
Chris@0
|
582 * SimpleTest assert* functions.
|
Chris@0
|
583 *
|
Chris@0
|
584 * Note that this does not remove nulls, new lines and other characters that
|
Chris@0
|
585 * could be used to obscure a tag or an attribute name.
|
Chris@0
|
586 *
|
Chris@0
|
587 * @param string $haystack
|
Chris@0
|
588 * Text to look in.
|
Chris@0
|
589 * @param string $needle
|
Chris@0
|
590 * Lowercase, plain text to look for.
|
Chris@0
|
591 * @param string $message
|
Chris@0
|
592 * (optional) Message to display if failed. Defaults to an empty string.
|
Chris@0
|
593 * @param string $group
|
Chris@0
|
594 * (optional) The group this message belongs to. Defaults to 'Other'.
|
Chris@0
|
595 */
|
Chris@0
|
596 protected function assertNormalized($haystack, $needle, $message = '', $group = 'Other') {
|
Chris@0
|
597 $this->assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) !== FALSE, $message, $group);
|
Chris@0
|
598 }
|
Chris@0
|
599
|
Chris@0
|
600 /**
|
Chris@0
|
601 * Asserts that text transformed to lowercase with HTML entities decoded does not contain a given string.
|
Chris@0
|
602 *
|
Chris@0
|
603 * Otherwise fails the test with a given message, similar to all the
|
Chris@0
|
604 * SimpleTest assert* functions.
|
Chris@0
|
605 *
|
Chris@0
|
606 * Note that this does not remove nulls, new lines, and other character that
|
Chris@0
|
607 * could be used to obscure a tag or an attribute name.
|
Chris@0
|
608 *
|
Chris@0
|
609 * @param string $haystack
|
Chris@0
|
610 * Text to look in.
|
Chris@0
|
611 * @param string $needle
|
Chris@0
|
612 * Lowercase, plain text to look for.
|
Chris@0
|
613 * @param string $message
|
Chris@0
|
614 * (optional) Message to display if failed. Defaults to an empty string.
|
Chris@0
|
615 * @param string $group
|
Chris@0
|
616 * (optional) The group this message belongs to. Defaults to 'Other'.
|
Chris@0
|
617 */
|
Chris@0
|
618 protected function assertNotNormalized($haystack, $needle, $message = '', $group = 'Other') {
|
Chris@0
|
619 $this->assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) === FALSE, $message, $group);
|
Chris@0
|
620 }
|
Chris@0
|
621
|
Chris@0
|
622 }
|