1<?php
2
3namespace Drupal\Tests\editor\Unit\EditorXssFilter;
4
5use Drupal\editor\EditorXssFilter\Standard;
6use Drupal\Tests\UnitTestCase;
7use Drupal\filter\Plugin\FilterInterface;
8
9/**
10 * @coversDefaultClass \Drupal\editor\EditorXssFilter\Standard
11 * @group editor
12 */
13class StandardTest extends UnitTestCase {
14
15  /**
16   * The mocked text format configuration entity.
17   *
18   * @var \Drupal\filter\Entity\FilterFormat|\PHPUnit\Framework\MockObject\MockObject
19   */
20  protected $format;
21
22  protected function setUp() {
23
24    // Mock text format configuration entity object.
25    $this->format = $this->getMockBuilder('\Drupal\filter\Entity\FilterFormat')
26      ->disableOriginalConstructor()
27      ->getMock();
28    $this->format->expects($this->any())
29      ->method('getFilterTypes')
30      ->will($this->returnValue([FilterInterface::TYPE_HTML_RESTRICTOR]));
31    $restrictions = [
32      'allowed' => [
33        'p' => TRUE,
34        'a' => TRUE,
35        '*' => [
36          'style' => FALSE,
37          'on*' => FALSE,
38        ],
39      ],
40    ];
41    $this->format->expects($this->any())
42      ->method('getHtmlRestrictions')
43      ->will($this->returnValue($restrictions));
44  }
45
46  /**
47   * Provides test data for testFilterXss().
48   *
49   * @see \Drupal\Tests\editor\Unit\editor\EditorXssFilter\StandardTest::testFilterXss()
50   */
51  public function providerTestFilterXss() {
52    $data = [];
53    $data[] = ['<p>Hello, world!</p><unknown>Pink Fairy Armadillo</unknown>', '<p>Hello, world!</p><unknown>Pink Fairy Armadillo</unknown>'];
54    $data[] = ['<p style="color:red">Hello, world!</p><unknown>Pink Fairy Armadillo</unknown>', '<p>Hello, world!</p><unknown>Pink Fairy Armadillo</unknown>'];
55    $data[] = ['<p>Hello, world!</p><unknown>Pink Fairy Armadillo</unknown><script>alert("evil");</script>', '<p>Hello, world!</p><unknown>Pink Fairy Armadillo</unknown>alert("evil");'];
56    $data[] = ['<p>Hello, world!</p><unknown>Pink Fairy Armadillo</unknown><a href="javascript:alert(1)">test</a>', '<p>Hello, world!</p><unknown>Pink Fairy Armadillo</unknown><a href="alert(1)">test</a>'];
57
58    // All cases listed on https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
59
60    // No Filter Evasion.
61    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#No_Filter_Evasion
62    $data[] = ['<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>', ''];
63
64    // Image XSS using the JavaScript directive.
65    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Image_XSS_using_the_JavaScript_directive
66    $data[] = ['<IMG SRC="javascript:alert(\'XSS\');">', '<IMG src="alert(&#039;XSS&#039;);">'];
67
68    // No quotes and no semicolon.
69    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#No_quotes_and_no_semicolon
70    $data[] = ['<IMG SRC=javascript:alert(\'XSS\')>', '<IMG>'];
71
72    // Case insensitive XSS attack vector.
73    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Case_insensitive_XSS_attack_vector
74    $data[] = ['<IMG SRC=JaVaScRiPt:alert(\'XSS\')>', '<IMG>'];
75
76    // HTML entities.
77    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#HTML_entities
78    $data[] = ['<IMG SRC=javascript:alert("XSS")>', '<IMG>'];
79
80    // Grave accent obfuscation.
81    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Grave_accent_obfuscation
82    $data[] = ['<IMG SRC=`javascript:alert("RSnake says, \'XSS\'")`>', '<IMG>'];
83
84    // Malformed A tags.
85    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Malformed_A_tags
86    $data[] = ['<a onmouseover="alert(document.cookie)">xxs link</a>', '<a>xxs link</a>'];
87    $data[] = ['<a onmouseover=alert(document.cookie)>xxs link</a>', '<a>xxs link</a>'];
88
89    // Malformed IMG tags.
90    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Malformed_IMG_tags
91    $data[] = ['<IMG """><SCRIPT>alert("XSS")</SCRIPT>">', '<IMG>alert("XSS")"&gt;'];
92
93    // fromCharCode.
94    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#fromCharCode
95    $data[] = ['<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>', '<IMG src="alert(String.fromCharCode(88,83,83))">'];
96
97    // Default SRC tag to get past filters that check SRC domain.
98    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Default_SRC_tag_to_get_past_filters_that_check_SRC_domain
99    $data[] = ['<IMG SRC=# onmouseover="alert(\'xxs\')">', '<IMG src="#">'];
100
101    // Default SRC tag by leaving it empty.
102    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Default_SRC_tag_by_leaving_it_empty
103    $data[] = ['<IMG SRC= onmouseover="alert(\'xxs\')">', '<IMG nmouseover="alert(&#039;xxs&#039;)">'];
104
105    // Default SRC tag by leaving it out entirely.
106    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Default_SRC_tag_by_leaving_it_out_entirely
107    $data[] = ['<IMG onmouseover="alert(\'xxs\')">', '<IMG>'];
108
109    // Decimal HTML character references.
110    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Decimal_HTML_character_references
111    $data[] = ['<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>', '<IMG src="alert(&#039;XSS&#039;)">'];
112
113    // Decimal HTML character references without trailing semicolons.
114    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Decimal_HTML_character_references_without_trailing_semicolons
115    $data[] = ['<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>', '<IMG src="&amp;#0000106&amp;#0000097&amp;#0000118&amp;#0000097&amp;#0000115&amp;#0000099&amp;#0000114&amp;#0000105&amp;#0000112&amp;#0000116&amp;#0000058&amp;#0000097&amp;#0000108&amp;#0000101&amp;#0000114&amp;#0000116&amp;#0000040&amp;#0000039&amp;#0000088&amp;#0000083&amp;#0000083&amp;#0000039&amp;#0000041">'];
116
117    // Hexadecimal HTML character references without trailing semicolons.
118    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Hexadecimal_HTML_character_references_without_trailing_semicolons
119    $data[] = ['<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>', '<IMG src="&amp;#x6A&amp;#x61&amp;#x76&amp;#x61&amp;#x73&amp;#x63&amp;#x72&amp;#x69&amp;#x70&amp;#x74&amp;#x3A&amp;#x61&amp;#x6C&amp;#x65&amp;#x72&amp;#x74&amp;#x28&amp;#x27&amp;#x58&amp;#x53&amp;#x53&amp;#x27&amp;#x29">'];
120
121    // Embedded tab.
122    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_tab
123    $data[] = ['<IMG SRC="jav  ascript:alert(\'XSS\');">', '<IMG src="alert(&#039;XSS&#039;);">'];
124
125    // Embedded Encoded tab.
126    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_Encoded_tab
127    $data[] = ['<IMG SRC="jav&#x09;ascript:alert(\'XSS\');">', '<IMG src="alert(&#039;XSS&#039;);">'];
128
129    // Embedded newline to break up XSS.
130    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_newline_to_break_up_XSS
131    $data[] = ['<IMG SRC="jav&#x0A;ascript:alert(\'XSS\');">', '<IMG src="alert(&#039;XSS&#039;);">'];
132
133    // Embedded carriage return to break up XSS.
134    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_carriage_return_to_break_up_XSS
135    $data[] = ['<IMG SRC="jav&#x0D;ascript:alert(\'XSS\');">', '<IMG src="alert(&#039;XSS&#039;);">'];
136
137    // Null breaks up JavaScript directive.
138    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Null_breaks_up_JavaScript_directive
139    $data[] = ["<IMG SRC=java\0script:alert(\"XSS\")>", '<IMG>'];
140
141    // Spaces and meta chars before the JavaScript in images for XSS.
142    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Spaces_and_meta_chars_before_the_JavaScript_in_images_for_XSS
143    // @fixme This dataset currently fails under 5.4 because of
144    //   https://www.drupal.org/node/1210798. Restore after it's fixed.
145    if (version_compare(PHP_VERSION, '5.4.0', '<')) {
146      $data[] = ['<IMG SRC=" &#14;  javascript:alert(\'XSS\');">', '<IMG src="alert(&#039;XSS&#039;);">'];
147    }
148
149    // Non-alpha-non-digit XSS.
150    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Non-alpha-non-digit_XSS
151    $data[] = ['<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>', ''];
152    $data[] = ['<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>', '<BODY>'];
153    $data[] = ['<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT>', ''];
154
155    // Extraneous open brackets.
156    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Extraneous_open_brackets
157    $data[] = ['<<SCRIPT>alert("XSS");//<</SCRIPT>', '&lt;alert("XSS");//&lt;'];
158
159    // No closing script tags.
160    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#No_closing_script_tags
161    $data[] = ['<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >', ''];
162
163    // Protocol resolution in script tags.
164    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Protocol_resolution_in_script_tags
165    $data[] = ['<SCRIPT SRC=//ha.ckers.org/.j>', ''];
166
167    // Half open HTML/JavaScript XSS vector.
168    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Half_open_HTML.2FJavaScript_XSS_vector
169    $data[] = ['<IMG SRC="javascript:alert(\'XSS\')"', '<IMG src="alert(&#039;XSS&#039;)">'];
170
171    // Double open angle brackets.
172    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Double_open_angle_brackets
173    // @see http://ha.ckers.org/blog/20060611/hotbot-xss-vulnerability/ to
174    //      understand why this is a vulnerability.
175    $data[] = ['<iframe src=http://ha.ckers.org/scriptlet.html <', '<iframe src="http://ha.ckers.org/scriptlet.html">'];
176
177    // Escaping JavaScript escapes.
178    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Escaping_JavaScript_escapes
179    // This one is irrelevant for Drupal; we *never* output any JavaScript code
180    // that depends on the URL's query string.
181
182    // End title tag.
183    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#End_title_tag
184    $data[] = ['</TITLE><SCRIPT>alert("XSS");</SCRIPT>', '</TITLE>alert("XSS");'];
185
186    // INPUT image.
187    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#INPUT_image
188    $data[] = ['<INPUT TYPE="IMAGE" SRC="javascript:alert(\'XSS\');">', '<INPUT type="IMAGE" src="alert(&#039;XSS&#039;);">'];
189
190    // BODY image.
191    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#BODY_image
192    $data[] = ['<BODY BACKGROUND="javascript:alert(\'XSS\')">', '<BODY background="alert(&#039;XSS&#039;)">'];
193
194    // IMG Dynsrc.
195    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IMG_Dynsrc
196    $data[] = ['<IMG DYNSRC="javascript:alert(\'XSS\')">', '<IMG dynsrc="alert(&#039;XSS&#039;)">'];
197
198    // IMG lowsrc.
199    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IMG_lowsrc
200    $data[] = ['<IMG LOWSRC="javascript:alert(\'XSS\')">', '<IMG lowsrc="alert(&#039;XSS&#039;)">'];
201
202    // List-style-image.
203    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#List-style-image
204    $data[] = ['<STYLE>li {list-style-image: url("javascript:alert(\'XSS\')");}</STYLE><UL><LI>XSS</br>', 'li {list-style-image: url("javascript:alert(\'XSS\')");}<UL><LI>XSS</br>'];
205
206    // VBscript in an image.
207    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#VBscript_in_an_image
208    $data[] = ['<IMG SRC=\'vbscript:msgbox("XSS")\'>', '<IMG src=\'msgbox(&quot;XSS&quot;)\'>'];
209
210    // Livescript (older versions of Netscape only).
211    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Livescript_.28older_versions_of_Netscape_only.29
212    $data[] = ['<IMG SRC="livescript:[code]">', '<IMG src="[code]">'];
213
214    // BODY tag.
215    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#BODY_tag
216    $data[] = ['<BODY ONLOAD=alert(\'XSS\')>', '<BODY>'];
217
218    // Event handlers.
219    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Event_Handlers
220    $events = [
221      'onAbort',
222      'onActivate',
223      'onAfterPrint',
224      'onAfterUpdate',
225      'onBeforeActivate',
226      'onBeforeCopy',
227      'onBeforeCut',
228      'onBeforeDeactivate',
229      'onBeforeEditFocus',
230      'onBeforePaste',
231      'onBeforePrint',
232      'onBeforeUnload',
233      'onBeforeUpdate',
234      'onBegin',
235      'onBlur',
236      'onBounce',
237      'onCellChange',
238      'onChange',
239      'onClick',
240      'onContextMenu',
241      'onControlSelect',
242      'onCopy',
243      'onCut',
244      'onDataAvailable',
245      'onDataSetChanged',
246      'onDataSetComplete',
247      'onDblClick',
248      'onDeactivate',
249      'onDrag',
250      'onDragEnd',
251      'onDragLeave',
252      'onDragEnter',
253      'onDragOver',
254      'onDragDrop',
255      'onDragStart',
256      'onDrop',
257      'onEnd',
258      'onError',
259      'onErrorUpdate',
260      'onFilterChange',
261      'onFinish',
262      'onFocus',
263      'onFocusIn',
264      'onFocusOut',
265      'onHashChange',
266      'onHelp',
267      'onInput',
268      'onKeyDown',
269      'onKeyPress',
270      'onKeyUp',
271      'onLayoutComplete',
272      'onLoad',
273      'onLoseCapture',
274      'onMediaComplete',
275      'onMediaError',
276      'onMessage',
277      'onMousedown',
278      'onMouseEnter',
279      'onMouseLeave',
280      'onMouseMove',
281      'onMouseOut',
282      'onMouseOver',
283      'onMouseUp',
284      'onMouseWheel',
285      'onMove',
286      'onMoveEnd',
287      'onMoveStart',
288      'onOffline',
289      'onOnline',
290      'onOutOfSync',
291      'onPaste',
292      'onPause',
293      'onPopState',
294      'onProgress',
295      'onPropertyChange',
296      'onReadyStateChange',
297      'onRedo',
298      'onRepeat',
299      'onReset',
300      'onResize',
301      'onResizeEnd',
302      'onResizeStart',
303      'onResume',
304      'onReverse',
305      'onRowsEnter',
306      'onRowExit',
307      'onRowDelete',
308      'onRowInserted',
309      'onScroll',
310      'onSeek',
311      'onSelect',
312      'onSelectionChange',
313      'onSelectStart',
314      'onStart',
315      'onStop',
316      'onStorage',
317      'onSyncRestored',
318      'onSubmit',
319      'onTimeError',
320      'onTrackChange',
321      'onUndo',
322      'onUnload',
323      'onURLFlip',
324    ];
325    foreach ($events as $event) {
326      $data[] = ['<p ' . $event . '="javascript:alert(\'XSS\');">Dangerous llama!</p>', '<p>Dangerous llama!</p>'];
327    }
328
329    // BGSOUND.
330    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#BGSOUND
331    $data[] = ['<BGSOUND SRC="javascript:alert(\'XSS\');">', '<BGSOUND src="alert(&#039;XSS&#039;);">'];
332
333    // & JavaScript includes.
334    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#.26_JavaScript_includes
335    $data[] = ['<BR SIZE="&{alert(\'XSS\')}">', '<BR size="">'];
336
337    // STYLE sheet.
338    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_sheet
339    $data[] = ['<LINK REL="stylesheet" HREF="javascript:alert(\'XSS\');">', ''];
340
341    // Remote style sheet.
342    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Remote_style_sheet
343    $data[] = ['<LINK REL="stylesheet" HREF="http://ha.ckers.org/xss.css">', ''];
344
345    // Remote style sheet part 2.
346    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Remote_style_sheet_part_2
347    $data[] = ['<STYLE>@import\'http://ha.ckers.org/xss.css\';</STYLE>', '@import\'http://ha.ckers.org/xss.css\';'];
348
349    // Remote style sheet part 3.
350    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Remote_style_sheet_part_3
351    $data[] = ['<META HTTP-EQUIV="Link" Content="<http://ha.ckers.org/xss.css>; REL=stylesheet">', '<META http-equiv="Link">; REL=stylesheet"&gt;'];
352
353    // Remote style sheet part 4.
354    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Remote_style_sheet_part_4
355    $data[] = ['<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>', 'BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}'];
356
357    // STYLE tags with broken up JavaScript for XSS.
358    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_tags_with_broken_up_JavaScript_for_XSS
359    $data[] = ['<STYLE>@im\port\'\ja\vasc\ript:alert("XSS")\';</STYLE>', '@im\port\'\ja\vasc\ript:alert("XSS")\';'];
360
361    // STYLE attribute using a comment to break up expression.
362    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_attribute_using_a_comment_to_break_up_expression
363    $data[] = ['<IMG STYLE="xss:expr/*XSS*/ession(alert(\'XSS\'))">', '<IMG>'];
364
365    // IMG STYLE with expression.
366    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IMG_STYLE_with_expression
367    $data[] = [
368      'exp/*<A STYLE=\'no\xss:noxss("*//*");
369xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>',
370      'exp/*<A>',
371    ];
372
373    // STYLE tag (Older versions of Netscape only).
374    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_tag_.28Older_versions_of_Netscape_only.29
375    $data[] = ['<STYLE TYPE="text/javascript">alert(\'XSS\');</STYLE>', 'alert(\'XSS\');'];
376
377    // STYLE tag using background-image.
378    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_tag_using_background-image
379    $data[] = ['<STYLE>.XSS{background-image:url("javascript:alert(\'XSS\')");}</STYLE><A CLASS=XSS></A>', '.XSS{background-image:url("javascript:alert(\'XSS\')");}<A class="XSS"></A>'];
380
381    // STYLE tag using background.
382    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_tag_using_background
383    $data[] = ['<STYLE type="text/css">BODY{background:url("javascript:alert(\'XSS\')")}</STYLE>', 'BODY{background:url("javascript:alert(\'XSS\')")}'];
384
385    // Anonymous HTML with STYLE attribute.
386    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Anonymous_HTML_with_STYLE_attribute
387    $data[] = ['<XSS STYLE="xss:expression(alert(\'XSS\'))">', '<XSS>'];
388
389    // Local htc file.
390    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Local_htc_file
391    $data[] = ['<XSS STYLE="behavior: url(xss.htc);">', '<XSS>'];
392
393    // US-ASCII encoding.
394    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#US-ASCII_encoding
395    // This one is irrelevant for Drupal; Drupal *always* outputs UTF-8.
396
397    // META.
398    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#META
399    $data[] = ['<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert(\'XSS\');">', '<META http-equiv="refresh" content="alert(&#039;XSS&#039;);">'];
400
401    // META using data.
402    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#META_using_data
403    $data[] = ['<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">', '<META http-equiv="refresh" content="text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">'];
404
405    // META with additional URL parameter
406    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#META
407    $data[] = ['<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert(\'XSS\');">', '<META http-equiv="refresh" content="//;URL=javascript:alert(&#039;XSS&#039;);">'];
408
409    // IFRAME.
410    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IFRAME
411    $data[] = ['<IFRAME SRC="javascript:alert(\'XSS\');"></IFRAME>', '<IFRAME src="alert(&#039;XSS&#039;);"></IFRAME>'];
412
413    // IFRAME Event based.
414    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IFRAME_Event_based
415    $data[] = ['<IFRAME SRC=# onmouseover="alert(document.cookie)"></IFRAME>', '<IFRAME src="#"></IFRAME>'];
416
417    // FRAME.
418    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#FRAME
419    $data[] = ['<FRAMESET><FRAME SRC="javascript:alert(\'XSS\');"></FRAMESET>', '<FRAMESET><FRAME src="alert(&#039;XSS&#039;);"></FRAMESET>'];
420
421    // TABLE.
422    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#TABLE
423    $data[] = ['<TABLE BACKGROUND="javascript:alert(\'XSS\')">', '<TABLE background="alert(&#039;XSS&#039;)">'];
424
425    // TD.
426    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#TD
427    $data[] = ['<TABLE><TD BACKGROUND="javascript:alert(\'XSS\')">', '<TABLE><TD background="alert(&#039;XSS&#039;)">'];
428
429    // DIV background-image.
430    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#DIV_background-image
431    $data[] = ['<DIV STYLE="background-image: url(javascript:alert(\'XSS\'))">', '<DIV>'];
432
433    // DIV background-image with unicoded XSS exploit.
434    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#DIV_background-image_with_unicoded_XSS_exploit
435    $data[] = ['<DIV STYLE="background-image:\0075\0072\006C\0028\'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029\'\0029">', '<DIV>'];
436
437    // DIV background-image plus extra characters.
438    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#DIV_background-image_plus_extra_characters
439    $data[] = ['<DIV STYLE="background-image: url(&#1;javascript:alert(\'XSS\'))">', '<DIV>'];
440
441    // DIV expression.
442    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#DIV_expression
443    $data[] = ['<DIV STYLE="width: expression(alert(\'XSS\'));">', '<DIV>'];
444
445    // Downlevel-Hidden block.
446    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Downlevel-Hidden_block
447    $data[] = ['<!--[if gte IE 4]>
448 <SCRIPT>alert(\'XSS\');</SCRIPT>
449 <![endif]-->',
450      "\n alert('XSS');\n ",
451    ];
452
453    // BASE tag.
454    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#BASE_tag
455    $data[] = ['<BASE HREF="javascript:alert(\'XSS\');//">', '<BASE href="alert(&#039;XSS&#039;);//">'];
456
457    // OBJECT tag.
458    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#OBJECT_tag
459    $data[] = ['<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>', ''];
460
461    // Using an EMBED tag you can embed a Flash movie that contains XSS.
462    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Using_an_EMBED_tag_you_can_embed_a_Flash_movie_that_contains_XSS
463    $data[] = ['<EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED>', ''];
464
465    // You can EMBED SVG which can contain your XSS vector.
466    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#You_can_EMBED_SVG_which_can_contain_your_XSS_vector
467    $data[] = ['<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>', ''];
468
469    // XML data island with CDATA obfuscation.
470    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#XML_data_island_with_CDATA_obfuscation
471    $data[] = ['<XML ID="xss"><I><B><IMG SRC="javas<!-- -->cript:alert(\'XSS\')"></B></I></XML><SPAN DATASRC="#xss" DATAFLD="B" DATAFORMATAS="HTML"></SPAN>', '<XML id="xss"><I><B><IMG>cript:alert(\'XSS\')"&gt;</B></I></XML><SPAN datasrc="#xss" datafld="B" dataformatas="HTML"></SPAN>'];
472
473    // Locally hosted XML with embedded JavaScript that is generated using an XML data island.
474    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Locally_hosted_XML_with_embedded_JavaScript_that_is_generated_using_an_XML_data_island
475    // This one is irrelevant for Drupal; Drupal disallows XML uploads by
476    // default.
477
478    // HTML+TIME in XML.
479    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#HTML.2BTIME_in_XML
480    $data[] = ['<?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time"><?import namespace="t" implementation="#default#time2"><t:set attributeName="innerHTML" to="XSS<SCRIPT DEFER>alert("XSS")</SCRIPT>">', '&lt;?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time"&gt;&lt;?import namespace="t" implementation="#default#time2"&gt;<t set attributename="innerHTML">alert("XSS")"&gt;'];
481
482    // Assuming you can only fit in a few characters and it filters against ".js".
483    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Assuming_you_can_only_fit_in_a_few_characters_and_it_filters_against_.22.js.22
484    $data[] = ['<SCRIPT SRC="http://ha.ckers.org/xss.jpg"></SCRIPT>', ''];
485
486    // IMG Embedded commands.
487    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IMG_Embedded_commands
488    // This one is irrelevant for Drupal; this is actually a CSRF, for which
489    // Drupal has CSRF protection. See https://www.drupal.org/node/178896.
490
491    // Cookie manipulation.
492    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Cookie_manipulation
493    $data[] = ['<META HTTP-EQUIV="Set-Cookie" Content="USERID=<SCRIPT>alert(\'XSS\')</SCRIPT>">', '<META http-equiv="Set-Cookie">alert(\'XSS\')"&gt;'];
494
495    // UTF-7 encoding.
496    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#UTF-7_encoding
497    // This one is irrelevant for Drupal; Drupal *always* outputs UTF-8.
498
499    // XSS using HTML quote encapsulation.
500    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#XSS_using_HTML_quote_encapsulation
501    $data[] = ['<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '" SRC="http://ha.ckers.org/xss.js"&gt;'];
502    $data[] = ['<SCRIPT =">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '" SRC="http://ha.ckers.org/xss.js"&gt;'];
503    $data[] = ['<SCRIPT a=">" \'\' SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '" \'\' SRC="http://ha.ckers.org/xss.js"&gt;'];
504    $data[] = ['<SCRIPT "a=\'>\'" SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '\'" SRC="http://ha.ckers.org/xss.js"&gt;'];
505    $data[] = ['<SCRIPT a=`>` SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '` SRC="http://ha.ckers.org/xss.js"&gt;'];
506    $data[] = ['<SCRIPT a=">\'>" SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '\'&gt;" SRC="http://ha.ckers.org/xss.js"&gt;'];
507    $data[] = ['<SCRIPT>document.write("<SCRI");</SCRIPT>PT SRC="http://ha.ckers.org/xss.js"></SCRIPT>', 'document.write("<SCRI>PT SRC="http://ha.ckers.org/xss.js"&gt;'];
508
509    // URL string evasion.
510    // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#URL_string_evasion
511    // This one is irrelevant for Drupal; Drupal doesn't forbid linking to some
512    // sites, it only forbids linking to any protocols other than those that are
513    // whitelisted.
514
515    // Test XSS filtering on data-attributes.
516    // @see \Drupal\editor\EditorXssFilter::filterXssDataAttributes()
517
518    // The following two test cases verify that XSS attack vectors are filtered.
519    $data[] = ['<img src="butterfly.jpg" data-caption="&lt;script&gt;alert();&lt;/script&gt;" />', '<img src="butterfly.jpg" data-caption="alert();" />'];
520    $data[] = ['<img src="butterfly.jpg" data-caption="&lt;EMBED SRC=&quot;http://ha.ckers.org/xss.swf&quot; AllowScriptAccess=&quot;always&quot;&gt;&lt;/EMBED&gt;" />', '<img src="butterfly.jpg" data-caption="" />'];
521
522    // When including HTML-tags as visible content, they are double-escaped.
523    // This test case ensures that we leave that content unchanged.
524    $data[] = ['<img src="butterfly.jpg" data-caption="&amp;lt;script&amp;gt;alert();&amp;lt;/script&amp;gt;" />', '<img src="butterfly.jpg" data-caption="&amp;lt;script&amp;gt;alert();&amp;lt;/script&amp;gt;" />'];
525
526    return $data;
527  }
528
529  /**
530   * Tests the method for filtering XSS.
531   *
532   * @param string $input
533   *   The input.
534   * @param string $expected_output
535   *   The expected output.
536   *
537   * @dataProvider providerTestFilterXss
538   */
539  public function testFilterXss($input, $expected_output) {
540    $output = Standard::filterXss($input, $this->format);
541    $this->assertSame($expected_output, $output);
542  }
543
544  /**
545   * Tests removing disallowed tags and XSS prevention.
546   *
547   * \Drupal\Component\Utility\Xss::filter() has the ability to run in blacklist
548   * mode, in which it still applies the exact same filtering, with one
549   * exception: it no longer works with a list of allowed tags, but with a list
550   * of disallowed tags.
551   *
552   * @param string $value
553   *   The value to filter.
554   * @param string $expected
555   *   The string that is expected to be missing.
556   * @param string $message
557   *   The assertion message to display upon failure.
558   * @param array $disallowed_tags
559   *   (optional) The disallowed HTML tags to be passed to \Drupal\Component\Utility\Xss::filter().
560   *
561   * @dataProvider providerTestBlackListMode
562   */
563  public function testBlacklistMode($value, $expected, $message, array $disallowed_tags) {
564    $value = Standard::filter($value, $disallowed_tags);
565    $this->assertSame($expected, $value, $message);
566  }
567
568  /**
569   * Data provider for testBlacklistMode().
570   *
571   * @see testBlacklistMode()
572   *
573   * @return array
574   *   An array of arrays containing the following elements:
575   *     - The value to filter.
576   *     - The value to expect after filtering.
577   *     - The assertion message.
578   *     - (optional) The disallowed HTML tags to be passed to \Drupal\Component\Utility\Xss::filter().
579   */
580  public function providerTestBlackListMode() {
581    return [
582      [
583        '<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
584        '<unknown>Pink Fairy Armadillo</unknown><video src="gerenuk.mp4">alert(0)',
585        'Disallow only the script tag',
586        ['script'],
587      ],
588      [
589        '<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
590        '<unknown>Pink Fairy Armadillo</unknown>alert(0)',
591        'Disallow both the script and video tags',
592        ['script', 'video'],
593      ],
594      // No real use case for this, but it is an edge case we must ensure works.
595      [
596        '<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
597        '<unknown>Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
598        'Disallow no tags',
599        [],
600      ],
601    ];
602  }
603
604}
605