MDL-81616 upgrade: add the 4.4.0 separation line to all upgrade scripts
[moodle.git] / lib / tests / formatting_test.php
blob7e971d105ede7fc78527c2e6ba633292e5771e75
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 namespace core;
19 /**
20 * Tests for Moodle's String Formatter.
22 * @package core
23 * @copyright 2023 Andrew Nicols <andrew@nicols.co.uk>
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 * @covers \core\formatting
26 * @coversDefaultClass \core\formatting
28 class formatting_test extends \advanced_testcase {
29 /**
30 * @covers ::format_string
32 public function test_format_string_striptags_cfg(): void {
33 global $CFG;
35 $this->resetAfterTest();
37 $formatting = new formatting();
39 // Check < and > signs.
40 $CFG->formatstringstriptags = false;
41 $this->assertSame('x &lt; 1', $formatting->format_string('x < 1'));
42 $this->assertSame('x &gt; 1', $formatting->format_string('x > 1'));
43 $this->assertSame('x &lt; 1 and x &gt; 0', $formatting->format_string('x < 1 and x > 0'));
45 $CFG->formatstringstriptags = true;
46 $this->assertSame('x &lt; 1', $formatting->format_string('x < 1'));
47 $this->assertSame('x &gt; 1', $formatting->format_string('x > 1'));
48 $this->assertSame('x &lt; 1 and x &gt; 0', $formatting->format_string('x < 1 and x > 0'));
51 /**
52 * @covers ::format_string
54 public function test_format_string_striptags_prop(): void {
55 $formatting = new formatting();
57 // Check < and > signs.
58 $formatting->set_striptags(false);
59 $this->assertSame('x &lt; 1', $formatting->format_string('x < 1'));
60 $this->assertSame('x &gt; 1', $formatting->format_string('x > 1'));
61 $this->assertSame('x &lt; 1 and x &gt; 0', $formatting->format_string('x < 1 and x > 0'));
63 $formatting->set_striptags(true);
64 $this->assertSame('x &lt; 1', $formatting->format_string('x < 1'));
65 $this->assertSame('x &gt; 1', $formatting->format_string('x > 1'));
66 $this->assertSame('x &lt; 1 and x &gt; 0', $formatting->format_string('x < 1 and x > 0'));
69 /**
70 * @covers ::format_string
71 * @dataProvider format_string_provider
72 * @param string $expected
73 * @param mixed $input
74 * @param array $options
76 public function test_format_string_values(
77 string $expected,
78 array $params,
79 ): void {
80 $formatting = new formatting();
81 $this->assertSame(
82 $expected,
83 $formatting->format_string(...$params),
87 /**
88 * Data provider for format_string tests.
90 * @return array
92 public static function format_string_provider(): array {
93 return [
94 // Ampersands.
96 'expected' => "&amp; &amp;&amp;&amp;&amp;&amp; &amp;&amp;",
97 'params' => ["& &&&&& &&"],
100 'expected' => "ANother &amp; &amp;&amp;&amp;&amp;&amp; Category",
101 'params' => ["ANother & &&&&& Category"],
104 'expected' => "ANother &amp; &amp;&amp;&amp;&amp;&amp; Category",
105 'params' => [
106 'string' => "ANother & &&&&& Category",
107 'striplinks' => true,
111 'expected' => "Nick's Test Site &amp; Other things",
112 'params' => [
113 'string' => "Nick's Test Site & Other things",
114 'striplinks' => true,
118 'expected' => "& < > \" '",
119 'params' => [
120 'string' => "& < > \" '",
121 'striplinks' => true,
122 'escape' => false,
126 // String entities.
128 'expected' => "&quot;",
129 'params' => ["&quot;"],
132 // Digital entities.
134 'expected' => "&11234;",
135 'params' => ["&11234;"],
138 // Unicode entities.
140 'expected' => "&#4475;",
141 'params' => ["&#4475;"],
144 // Nulls.
145 ['', [null]],
147 'expected' => '',
148 'params' => [
149 'string' => null,
150 'striplinks' => true,
151 'escape' => false,
158 * The format string static caching should include the filters option to make
159 * sure filters are correctly applied when requested.
161 public function test_format_string_static_caching_with_filters(): void {
162 global $CFG;
164 $this->resetAfterTest(true);
165 $this->setAdminUser();
166 $generator = $this->getDataGenerator();
167 $course = $generator->create_course();
168 $user = $generator->create_user();
170 $rawstring = '<span lang="en" class="multilang">English</span><span lang="ca" class="multilang">Catalan</span>';
171 $expectednofilter = strip_tags($rawstring);
172 $expectedfilter = 'English';
173 $context = \core\context\course::instance($course->id);
174 $options = [
175 'striplinks' => true,
176 'context' => $context,
177 'escape' => true,
178 'filter' => false,
181 $this->setUser($user);
183 $formatting = new formatting();
185 // Format the string without filters. It should just strip the
186 // links.
187 $nofilterresult = $formatting->format_string($rawstring, ...$options);
188 $this->assertEquals($expectednofilter, $nofilterresult);
190 // Add the multilang filter. Make sure it's enabled globally.
191 $CFG->stringfilters = 'multilang';
192 filter_set_global_state('multilang', TEXTFILTER_ON);
193 filter_set_local_state('multilang', $context->id, TEXTFILTER_ON);
195 // Even after setting the filters, no filters are applied yet.
196 $nofilterresult = $formatting->format_string($rawstring,...$options);
197 $this->assertEquals($expectednofilter, $nofilterresult);
199 // Apply the filter as an option.
200 $options['filter'] = true;
201 $filterresult = $formatting->format_string($rawstring, ...$options);
202 $this->assertMatchesRegularExpression("/$expectedfilter/", $filterresult);
204 // Apply it as a formatting setting.
205 unset($options['filter']);
206 $formatting->set_filterall(true);
207 $filterresult = $formatting->format_string($rawstring, ...$options);
208 $this->assertMatchesRegularExpression("/$expectedfilter/", $filterresult);
210 // Unset it and we do not filter.
211 $formatting->set_filterall(false);
212 $nofilterresult = $formatting->format_string($rawstring, ...$options);
213 $this->assertEquals($expectednofilter, $nofilterresult);
215 // Set it again.
216 $formatting->set_filterall(true);
217 filter_set_local_state('multilang', $context->id, TEXTFILTER_OFF);
219 // Confirm that we get back the cached string. The result should be
220 // the same as the filtered text above even though we've disabled the
221 // multilang filter in between.
222 $cachedresult = $formatting->format_string($rawstring, ...$options);
223 $this->assertMatchesRegularExpression("/$expectedfilter/", $cachedresult);
227 * Test trust option of format_text().
229 * @covers ::format_text
230 * @dataProvider format_text_trusted_provider
232 public function test_format_text_trusted(
233 $expected,
234 int $enabletrusttext,
235 mixed $input,
236 // Yes... FORMAT_ constants are strings of ints.
237 string $format,
238 array $options = [],
239 ): void {
240 global $CFG;
241 $this->resetAfterTest();
243 $CFG->enabletrusttext = $enabletrusttext;
245 $formatter = new formatting();
246 $this->assertEquals(
247 $expected,
248 $formatter->format_text($input, $format, ...$options),
252 public static function format_text_trusted_provider(): array {
253 $text = "lala <object>xx</object>";
254 return [
256 s($text),
258 $text,
259 FORMAT_PLAIN,
260 ['trusted' => true],
263 "<p>lala xx</p>\n",
265 $text,
266 FORMAT_MARKDOWN,
267 ['trusted' => true],
270 '<div class="text_to_html">lala xx</div>',
272 $text,
273 FORMAT_MOODLE,
274 ['trusted' => true],
277 'lala xx',
279 $text,
280 FORMAT_HTML,
281 ['trusted' => true],
285 s($text),
287 $text,
288 FORMAT_PLAIN,
289 ['trusted' => false],
292 "<p>lala xx</p>\n",
294 $text,
295 FORMAT_MARKDOWN,
296 ['trusted' => false],
299 '<div class="text_to_html">lala xx</div>',
301 $text,
302 FORMAT_MOODLE,
303 ['trusted' => false],
306 'lala xx',
308 $text,
309 FORMAT_HTML,
310 ['trusted' => false],
314 s($text),
316 $text,
317 FORMAT_PLAIN,
318 ['trusted' => true],
321 "<p>lala xx</p>\n",
323 $text,
324 FORMAT_MARKDOWN,
325 ['trusted' => true],
328 '<div class="text_to_html">lala <object>xx</object></div>',
330 $text,
331 FORMAT_MOODLE,
332 ['trusted' => true],
335 'lala <object>xx</object>',
337 $text,
338 FORMAT_HTML,
339 ['trusted' => true],
343 s($text),
345 $text,
346 FORMAT_PLAIN,
347 ['trusted' => false],
350 "<p>lala xx</p>\n",
352 $text,
353 FORMAT_MARKDOWN,
354 ['trusted' => false],
357 '<div class="text_to_html">lala xx</div>',
359 $text,
360 FORMAT_MOODLE,
361 ['trusted' => false],
364 'lala xx',
366 $text,
367 FORMAT_HTML,
368 ['trusted' => false],
372 "<p>lala <object>xx</object></p>\n",
374 $text,
375 FORMAT_MARKDOWN,
376 ['trusted' => true, 'clean' => false],
379 "<p>lala <object>xx</object></p>\n",
381 $text,
382 FORMAT_MARKDOWN,
383 ['trusted' => false, 'clean' => false],
388 public function test_format_text_format_html(): void {
389 $this->resetAfterTest();
390 $formatter = new formatting();
392 filter_set_global_state('emoticon', TEXTFILTER_ON);
393 $this->assertMatchesRegularExpression(
394 '~^<p><img class="icon emoticon" alt="smile" title="smile" ' .
395 'src="https://www.example.com/moodle/theme/image.php/boost/core/1/s/smiley" /></p>$~',
396 $formatter->format_text('<p>:-)</p>', FORMAT_HTML)
400 public function test_format_text_format_html_no_filters(): void {
401 $this->resetAfterTest();
402 $formatter = new formatting();
404 filter_set_global_state('emoticon', TEXTFILTER_ON);
405 $this->assertEquals(
406 '<p>:-)</p>',
407 $formatter->format_text(
408 '<p>:-)</p>',
409 FORMAT_HTML,
410 filter: false,
415 public function test_format_text_format_plain(): void {
416 // Note FORMAT_PLAIN does not filter ever, no matter we ask for filtering.
417 $this->resetAfterTest();
418 $formatter = new formatting();
420 filter_set_global_state('emoticon', TEXTFILTER_ON);
421 $this->assertEquals(
422 ':-)',
423 $formatter->format_text(':-)', FORMAT_PLAIN)
427 public function test_format_text_format_plain_no_filters(): void {
428 $this->resetAfterTest();
429 $formatter = new formatting();
431 filter_set_global_state('emoticon', TEXTFILTER_ON);
432 $this->assertEquals(
433 ':-)',
434 $formatter->format_text(
435 ':-)',
436 FORMAT_PLAIN,
437 filter: false,
442 public function test_format_text_format_markdown(): void {
443 $this->resetAfterTest();
444 $formatter = new formatting();
446 filter_set_global_state('emoticon', TEXTFILTER_ON);
447 $this->assertMatchesRegularExpression(
448 '~^<p><em><img class="icon emoticon" alt="smile" title="smile" ' .
449 'src="https://www.example.com/moodle/theme/image.php/boost/core/1/s/smiley" />' .
450 '</em></p>\n$~',
451 $formatter->format_text('*:-)*', FORMAT_MARKDOWN)
455 public function test_format_text_format_markdown_nofilter(): void {
456 $this->resetAfterTest();
457 $formatter = new formatting();
459 filter_set_global_state('emoticon', TEXTFILTER_ON);
460 $this->assertEquals(
461 "<p><em>:-)</em></p>\n",
462 $formatter->format_text('*:-)*', FORMAT_MARKDOWN, filter: false)
466 public function test_format_text_format_moodle(): void {
467 $this->resetAfterTest();
468 $formatter = new formatting();
470 filter_set_global_state('emoticon', TEXTFILTER_ON);
471 $this->assertMatchesRegularExpression(
472 '~^<div class="text_to_html"><p>' .
473 '<img class="icon emoticon" alt="smile" title="smile" ' .
474 'src="https://www.example.com/moodle/theme/image.php/boost/core/1/s/smiley" /></p></div>$~',
475 $formatter->format_text('<p>:-)</p>', FORMAT_MOODLE)
479 public function test_format_text_format_moodle_no_filters(): void {
480 $this->resetAfterTest();
481 $formatter = new formatting();
483 filter_set_global_state('emoticon', TEXTFILTER_ON);
484 $this->assertEquals(
485 '<div class="text_to_html"><p>:-)</p></div>',
486 $formatter->format_text('<p>:-)</p>', FORMAT_MOODLE, filter: false)
491 * Make sure that nolink tags and spans prevent linking in filters that support it.
493 public function test_format_text_nolink(): void {
494 global $CFG;
495 $this->resetAfterTest();
496 $formatter = new formatting();
498 filter_set_global_state('activitynames', TEXTFILTER_ON);
500 $course = $this->getDataGenerator()->create_course();
501 $context = \context_course::instance($course->id);
502 $page = $this->getDataGenerator()->create_module(
503 'page',
504 ['course' => $course->id, 'name' => 'Test 1'],
506 $cm = get_coursemodule_from_instance('page', $page->id, $page->course, false, MUST_EXIST);
507 $pageurl = $CFG->wwwroot . '/mod/page/view.php?id=' . $cm->id;
509 $this->assertSame(
510 '<p>Read <a class="autolink" title="Test 1" href="' . $pageurl . '">Test 1</a>.</p>',
511 $formatter->format_text('<p>Read Test 1.</p>', FORMAT_HTML, context: $context),
514 $this->assertSame(
515 '<p>Read <a class="autolink" title="Test 1" href="' . $pageurl . '">Test 1</a>.</p>',
516 $formatter->format_text(
517 '<p>Read Test 1.</p>',
518 FORMAT_HTML,
519 context: $context,
520 clean: false,
524 $this->assertSame(
525 '<p>Read Test 1.</p>',
526 $formatter->format_text(
527 '<p><nolink>Read Test 1.</nolink></p>',
528 FORMAT_HTML,
529 context: $context,
530 clean: true,
534 $this->assertSame(
535 '<p>Read Test 1.</p>',
536 $formatter->format_text(
537 '<p><nolink>Read Test 1.</nolink></p>',
538 FORMAT_HTML,
539 context: $context,
540 clean: false,
544 $this->assertSame(
545 '<p><span class="nolink">Read Test 1.</span></p>',
546 $formatter->format_text(
547 '<p><span class="nolink">Read Test 1.</span></p>',
548 FORMAT_HTML,
549 context: $context,
554 public function test_format_text_overflowdiv(): void {
555 $formatter = new formatting();
557 $this->assertEquals(
558 '<div class="no-overflow"><p>Hello world</p></div>',
559 $formatter->format_text(
560 '<p>Hello world</p>',
561 FORMAT_HTML,
562 overflowdiv: true,
568 * Test adding blank target attribute to links
570 * @dataProvider format_text_blanktarget_testcases
571 * @param string $link The link to add target="_blank" to
572 * @param string $expected The expected filter value
574 public function test_format_text_blanktarget($link, $expected): void {
575 $formatter = new formatting();
576 $actual = $formatter->format_text(
577 $link,
578 FORMAT_MOODLE,
579 blanktarget: true,
580 filter: false,
581 clean: false,
583 $this->assertEquals($expected, $actual);
587 * Data provider for the test_format_text_blanktarget testcase
589 * @return array of testcases
591 public static function format_text_blanktarget_testcases(): array {
592 return [
593 'Simple link' => [
594 '<a href="https://www.youtube.com/watch?v=JeimE8Wz6e4">Hey, that\'s pretty good!</a>',
595 '<div class="text_to_html"><a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" target="_blank"' .
596 ' rel="noreferrer">Hey, that\'s pretty good!</a></div>',
598 'Link with rel' => [
599 '<a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" rel="nofollow">Hey, that\'s pretty good!</a>',
600 '<div class="text_to_html"><a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" rel="nofollow noreferrer"' .
601 ' target="_blank">Hey, that\'s pretty good!</a></div>',
603 'Link with rel noreferrer' => [
604 '<a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" rel="noreferrer">Hey, that\'s pretty good!</a>',
605 '<div class="text_to_html"><a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" rel="noreferrer"' .
606 ' target="_blank">Hey, that\'s pretty good!</a></div>',
608 'Link with target' => [
609 '<a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" target="_self">Hey, that\'s pretty good!</a>',
610 '<div class="text_to_html"><a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" target="_self">' .
611 'Hey, that\'s pretty good!</a></div>',
613 'Link with target blank' => [
614 '<a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" target="_blank">Hey, that\'s pretty good!</a>',
615 '<div class="text_to_html"><a href="https://www.youtube.com/watch?v=JeimE8Wz6e4" target="_blank"' .
616 ' rel="noreferrer">Hey, that\'s pretty good!</a></div>',
618 'Link with Frank\'s casket inscription' => [
619 // phpcs:ignore moodle.Files.LineLength
620 '<a href="https://en.wikipedia.org/wiki/Franks_Casket">ᚠᛁᛋᚳ᛫ᚠᛚᚩᛞᚢ᛫ᚪᚻᚩᚠᚩᚾᚠᛖᚱᚷ ᛖᚾᛒᛖᚱᛁᚷ ᚹᚪᚱᚦᚷᚪ᛬ᛋᚱᛁᚳᚷᚱᚩᚱᚾᚦᚫᚱᚻᛖᚩᚾᚷᚱᛖᚢᛏᚷᛁᛋᚹᚩᛗ ᚻ' .
621 'ᚱᚩᚾᚫᛋᛒᚪᚾ ᛗᚫᚷᛁᚠᛁᛋᚳ᛫ᚠᛚᚩᛞᚢ᛫ᚪᚻᚩᚠᚩᚾᚠᛖᚱᚷ ᛖᚾᛒᛖᚱᛁᚷ ᚹᚪᚱᚦᚷᚪ᛬ᛋᚱᛁᚳᚷᚱᚩᚱᚾᚦᚫᚱᚻᛖᚩᚾᚷᚱᛖᚢᛏᚷᛁᛋᚹᚩᛗ ᚻᚱᚩᚾᚫᛋᛒᚪᚾ ᛗᚫᚷᛁ</a>',
622 '<div class="text_to_html"><a href="https://en.wikipedia.org/wiki/Franks_Casket" target="_blank" ' .
623 // phpcs:ignore moodle.Files.LineLength
624 'rel="noreferrer">ᚠᛁᛋᚳ᛫ᚠᛚᚩᛞᚢ᛫ᚪᚻᚩᚠᚩᚾᚠᛖᚱᚷ ᛖᚾᛒᛖᚱᛁᚷ ᚹᚪᚱᚦᚷᚪ᛬ᛋᚱᛁᚳᚷᚱᚩᚱᚾᚦᚫᚱᚻᛖᚩᚾᚷᚱᛖᚢᛏᚷᛁᛋᚹᚩᛗ ᚻᚱᚩᚾᚫᛋᛒᚪᚾ ᛗᚫᚷᛁᚠᛁᛋᚳ᛫ᚠᛚᚩᛞᚢ᛫ᚪᚻᚩᚠᚩᚾᚠᛖᚱᚷ ᛖᚾ' .
625 'ᛒᛖᚱᛁᚷ ᚹᚪᚱᚦᚷᚪ᛬ᛋᚱᛁᚳᚷᚱᚩᚱᚾᚦᚫᚱᚻᛖᚩᚾᚷᚱᛖᚢᛏᚷᛁᛋᚹᚩᛗ ᚻᚱᚩᚾᚫᛋᛒᚪᚾ ᛗᚫᚷᛁ</a></div>',
627 'No link' => [
628 'Some very boring text written with the Latin script',
629 '<div class="text_to_html">Some very boring text written with the Latin script</div>',
631 'No link with Thror\'s map runes' => [
632 // phpcs:ignore moodle.Files.LineLength
633 'ᛋᛏᚫᚾᛞ ᛒᚣ ᚦᛖ ᚷᚱᛖᚣ ᛋᛏᚩᚾᛖ ᚻᚹᛁᛚᛖ ᚦᛖ ᚦᚱᚢᛋᚻ ᚾᚩᚳᛋ ᚫᚾᛞ ᚦᛖ ᛋᛖᛏᛏᛁᚾᚷ ᛋᚢᚾ ᚹᛁᚦ ᚦᛖ ᛚᚫᛋᛏ ᛚᛁᚷᚻᛏ ᚩᚠ ᛞᚢᚱᛁᚾᛋ ᛞᚫᚣ ᚹᛁᛚᛚ ᛋᚻᛁᚾᛖ ᚢᛈᚩᚾ ᚦᛖ ᚳᛖᚣᚻᚩᛚᛖ',
634 // phpcs:ignore moodle.Files.LineLength
635 '<div class="text_to_html">ᛋᛏᚫᚾᛞ ᛒᚣ ᚦᛖ ᚷᚱᛖᚣ ᛋᛏᚩᚾᛖ ᚻᚹᛁᛚᛖ ᚦᛖ ᚦᚱᚢᛋᚻ ᚾᚩᚳᛋ ᚫᚾᛞ ᚦᛖ ᛋᛖᛏᛏᛁᚾᚷ ᛋᚢᚾ ᚹᛁᚦ ᚦᛖ ᛚᚫᛋᛏ ᛚᛁᚷᚻᛏ ᚩᚠ ᛞᚢᚱᛁᚾᛋ ᛞᚫᚣ ᚹ' .
636 'ᛁᛚᛚ ᛋᚻᛁᚾᛖ ᚢᛈᚩᚾ ᚦᛖ ᚳᛖᚣᚻᚩᛚᛖ</div>',
642 * Test ability to force cleaning of otherwise non-cleaned content.
644 * @dataProvider format_text_cleaning_testcases
646 * @param string $input Input text
647 * @param string $nocleaned Expected output of format_text() with noclean=true
648 * @param string $cleaned Expected output of format_text() with noclean=false
650 public function test_format_text_cleaning($input, $nocleaned, $cleaned): void {
651 $formatter = new formatting();
653 $formatter->set_forceclean(false);
654 $actual = $formatter->format_text($input, FORMAT_HTML, filter: false, clean: true);
655 $this->assertEquals($cleaned, $actual);
657 $formatter->set_forceclean(true);
658 $actual = $formatter->format_text($input, FORMAT_HTML, filter: false, clean: true);
659 $this->assertEquals($cleaned, $actual);
661 $formatter->set_forceclean(false);
662 $actual = $formatter->format_text($input, FORMAT_HTML, filter: false, clean: false);
663 $this->assertEquals($nocleaned, $actual);
665 $formatter->set_forceclean(true);
666 $actual = $formatter->format_text($input, FORMAT_HTML, filter: false, clean: false);
667 $this->assertEquals($cleaned, $actual);
671 * Data provider for the test_format_text_cleaning testcase
673 * @return array of testcases (string)testcasename => [(string)input, (string)nocleaned, (string)cleaned]
675 public static function format_text_cleaning_testcases(): array {
676 return [
677 'JavaScript' => [
678 'Hello <script type="text/javascript">alert("XSS");</script> world',
679 'Hello <script type="text/javascript">alert("XSS");</script> world',
680 'Hello world',
682 'Inline frames' => [
683 'Let us go phishing! <iframe src="https://1.2.3.4/google.com"></iframe>',
684 'Let us go phishing! <iframe src="https://1.2.3.4/google.com"></iframe>',
685 'Let us go phishing! ',
687 'Malformed A tags' => [
688 '<a onmouseover="alert(document.cookie)">xxs link</a>',
689 '<a onmouseover="alert(document.cookie)">xxs link</a>',
690 '<a>xxs link</a>',
692 'Malformed IMG tags' => [
693 '<IMG """><SCRIPT>alert("XSS")</SCRIPT>">',
694 '<IMG """><SCRIPT>alert("XSS")</SCRIPT>">',
695 '"&gt;',
697 'On error alert' => [
698 '<IMG SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>',
699 '<IMG SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>',
700 '<img src="/" alt="" />',
702 'IMG onerror and javascript alert encode' => [
703 '<img src=x onerror="&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000083&#0000083&#0000039&#0000041">',
704 '<img src=x onerror="&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000083&#0000083&#0000039&#0000041">',
705 '<img src="x" alt="x" />',
707 'DIV background-image' => [
708 '<DIV STYLE="background-image: url(javascript:alert(\'XSS\'))">',
709 '<DIV STYLE="background-image: url(javascript:alert(\'XSS\'))">',
710 '<div></div>',