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('XSS');">']; 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")">']; 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('xxs')">']; 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=javascript:alert('XSS')>', '<IMG src="alert('XSS')">']; 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=javascript:alert('XSS')>', '<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">']; 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=javascript:alert('XSS')>', '<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">']; 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('XSS');">']; 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	ascript:alert(\'XSS\');">', '<IMG src="alert('XSS');">']; 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
ascript:alert(\'XSS\');">', '<IMG src="alert('XSS');">']; 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
ascript:alert(\'XSS\');">', '<IMG src="alert('XSS');">']; 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="  javascript:alert(\'XSS\');">', '<IMG src="alert('XSS');">']; 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>', '<alert("XSS");//<']; 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('XSS')">']; 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('XSS');">']; 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('XSS')">']; 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('XSS')">']; 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('XSS')">']; 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("XSS")\'>']; 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('XSS');">']; 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">']; 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('XSS');">']; 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('XSS');">']; 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('XSS');"></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('XSS');"></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('XSS')">']; 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('XSS')">']; 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(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('XSS');//">']; 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\')"></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>">', '<?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time"><?import namespace="t" implementation="#default#time2"><t set attributename="innerHTML">alert("XSS")">']; 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\')">']; 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">']; 502 $data[] = ['<SCRIPT =">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '" SRC="http://ha.ckers.org/xss.js">']; 503 $data[] = ['<SCRIPT a=">" \'\' SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '" \'\' SRC="http://ha.ckers.org/xss.js">']; 504 $data[] = ['<SCRIPT "a=\'>\'" SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '\'" SRC="http://ha.ckers.org/xss.js">']; 505 $data[] = ['<SCRIPT a=`>` SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '` SRC="http://ha.ckers.org/xss.js">']; 506 $data[] = ['<SCRIPT a=">\'>" SRC="http://ha.ckers.org/xss.js"></SCRIPT>', '\'>" SRC="http://ha.ckers.org/xss.js">']; 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">']; 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="<script>alert();</script>" />', '<img src="butterfly.jpg" data-caption="alert();" />']; 520 $data[] = ['<img src="butterfly.jpg" data-caption="<EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED>" />', '<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="&lt;script&gt;alert();&lt;/script&gt;" />', '<img src="butterfly.jpg" data-caption="&lt;script&gt;alert();&lt;/script&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