1 package com.android.mail.utils; 2 3 import android.test.AndroidTestCase; 4 import android.test.suitebuilder.annotation.SmallTest; 5 6 /** 7 * These test cases verify the handling of more advanced cross-site scripting attacks. 8 */ 9 @SmallTest 10 public class AdvancedHtmlSanitizerTest extends AndroidTestCase { 11 public void testSampleEmail() { 12 sanitize("<html>\n" + 13 "<head>\n" + 14 "<title>HTML E-mail</title>\n" + 15 "<script>\n" + 16 "alert(\"I am an alert box!\");\n" + 17 "</script>\n" + 18 "</head>\n" + 19 "<body>\n" + 20 "Body here\n" + 21 "<br />\n" + 22 "<a href=\"http://www.google.com\">Link to Google Search!</a>\n" + 23 "<br />\n" + 24 "<br />\n" + 25 "<a onclick=\"alert('surprise!')\" href=\"#\">I am a link!</a>\n" + 26 "<br />\n" + 27 "Moar body here\n" + 28 "</body>\n" + 29 "</html>" 30 , 31 "\n" + 32 "\n" + 33 "\n" + 34 "\n" + 35 "\n" + 36 "<div>\n" + 37 "Body here\n" + 38 "<br />\n" + 39 "<a href=\"http://www.google.com\">Link to Google Search!</a>\n" + 40 "<br />\n" + 41 "<br />\n" + 42 "<a href=\"#\">I am a link!</a>\n" + 43 "<br />\n" + 44 "Moar body here\n" + 45 "</div>\n"); 46 } 47 48 public void testXSS() { 49 sanitize("'';!--\"<XSS>=&{()}", "'';!--"=&{()}"); 50 sanitize("<img src=javascript:alert(String.fromCharCode(88,83,83))>", ""); 51 sanitize("\\\";alert('XSS');//", "\\";alert('XSS');//"); 52 sanitize("<br size=\"&{alert('XSS')}\">", "<br />"); 53 sanitize("<xss style=\"xss:expression(alert('XSS'))\">", ""); 54 sanitize("<xss style=\"behavior: url(xss.htc);\">", ""); 55 sanitize("scriptalert(XSS)/script", "scriptalert(XSS)/script"); 56 sanitize("<xml><i><b><img src=\"javas<!-- -->cript:alert('XSS')\"></b></i></xml>", 57 "<i><b></b></i>"); 58 sanitize("<xml src=\"xsstest.xml\" id=I></xml>", ""); 59 sanitize("<!--[if gte IE 4]>\n" + 60 " <SCRIPT>alert('XSS');</SCRIPT>\n" + 61 " <![endif]-->", ""); 62 sanitize("<body>\n" + 63 "<?xml:namespace prefix=\"t\" ns=\"urn:schemas-microsoft-com:time\">\n" + 64 "<?import namespace=\"t\" implementation=\"#default#time2\">\n" + 65 "<t:set attributeName=\"innerHTML\" to=\"XSS<script defer>alert(\"XSS\")" + 66 "</script>\">\n" + 67 "</body></html>", 68 "<div>\n" + 69 "\n" + 70 "\n" + 71 "">\n" + 72 "</div>"); 73 } 74 75 /** 76 * Technically, RFC 2392 doesn't limit where CID urls may appear. But, Webview is unhappy 77 * handling them within link tags, so we only allow them in img src attributes until we see a 78 * reason to expand their acceptance. 79 */ 80 public void testCIDurls() { 81 sanitize("<img src=\"http://www.here.com/awesome.png\"/>", 82 "<img src=\"http://www.here.com/awesome.png\" />"); 83 sanitize("<img src=\"https://www.here.com/awesome.png\"/>", 84 "<img src=\"https://www.here.com/awesome.png\" />"); 85 sanitize("<img src=\"cid:ii_145bda161daf6f9c\"/>", 86 "<img src=\"cid:ii_145bda161daf6f9c\" />"); 87 88 sanitize("<a href=\"http://www.here.com/awesome.png\"/>", 89 "<a href=\"http://www.here.com/awesome.png\"></a>"); 90 sanitize("<a href=\"https://www.here.com/awesome.png\"/>", 91 "<a href=\"https://www.here.com/awesome.png\"></a>"); 92 sanitize("<a href=\"cid:ii_145bda161daf6f9c\"/>", ""); 93 } 94 95 // todo the stock CssSchema in OWASP does NOT allow the float property; I experiment with adding 96 // todo it to see how much it beautifies HTML display (the risk seems to be that you can display 97 // todo content outside the bounds of your div and mislead the user with this technique) 98 public void testCSS_float() { 99 sanitize("<div style=\"float:none\"></div>", "<div style=\"float:none\"></div>"); 100 sanitize("<div style=\"float:left\"></div>", "<div style=\"float:left\"></div>"); 101 sanitize("<div style=\"float:right\"></div>", "<div style=\"float:right\"></div>"); 102 sanitize("<div style=\"float:inherit\"></div>", "<div style=\"float:inherit\"></div>"); 103 sanitize("<div style=\"float:initial\"></div>", "<div></div>"); 104 sanitize("<div style=\"float:garbage\"></div>", "<div></div>"); 105 } 106 107 // todo the stock CssSchema in OWASP does NOT allow the display property; I experiment with 108 // todo adding it to see how much it beautifies HTML display (the risk seems to be that you can 109 // todo display content outside the bounds of your div and mislead the user with this technique) 110 public void testCSS_display() { 111 sanitize("<div style=\"display:inline\"></div>", "<div style=\"display:inline\"></div>"); 112 sanitize("<div style=\"display:block\"></div>", "<div style=\"display:block\"></div>"); 113 sanitize("<div style=\"display:flex\"></div>", "<div></div>"); 114 sanitize("<div style=\"display:inline-block\"></div>", 115 "<div style=\"display:inline-block\"></div>"); 116 sanitize("<div style=\"display:inline-flex\"></div>", "<div></div>"); 117 sanitize("<div style=\"display:inline-table\"></div>", 118 "<div style=\"display:inline-table\"></div>"); 119 sanitize("<div style=\"display:list-item\"></div>", 120 "<div style=\"display:list-item\"></div>"); 121 sanitize("<div style=\"display:run-in\"></div>", "<div style=\"display:run-in\"></div>"); 122 sanitize("<div style=\"display:table\"></div>", "<div style=\"display:table\"></div>"); 123 sanitize("<div style=\"display:table-caption\"></div>", 124 "<div style=\"display:table-caption\"></div>"); 125 sanitize("<div style=\"display:table-column-group\"></div>", 126 "<div style=\"display:table-column-group\"></div>"); 127 sanitize("<div style=\"display:table-header-group\"></div>", 128 "<div style=\"display:table-header-group\"></div>"); 129 sanitize("<div style=\"display:table-footer-group\"></div>", 130 "<div style=\"display:table-footer-group\"></div>"); 131 sanitize("<div style=\"display:table-row-group\"></div>", 132 "<div style=\"display:table-row-group\"></div>"); 133 sanitize("<div style=\"display:table-cell\"></div>", 134 "<div style=\"display:table-cell\"></div>"); 135 sanitize("<div style=\"display:table-column\"></div>", 136 "<div style=\"display:table-column\"></div>"); 137 sanitize("<div style=\"display:table-row\"></div>", 138 "<div style=\"display:table-row\"></div>"); 139 sanitize("<div style=\"display:none\"></div>", "<div style=\"display:none\"></div>"); 140 sanitize("<div style=\"display:initial\"></div>", "<div></div>"); 141 sanitize("<div style=\"display:inherit\"></div>", "<div style=\"display:inherit\"></div>"); 142 } 143 144 public void testTrimmingUrls() { 145 // todo Gmail strips the leading space on this href 146 // sanitize("<a href=\"http://www.google.com \">Send mail</a>", 147 // "<a href=\"http://www.google.com\">Send mail</a>"); 148 sanitize("<a href=\" http://www.google.com\">Send mail</a>", "Send mail"); 149 // todo Gmail strips the trailing space on this href 150 // sanitize("<a href=\"http://www.google.com \">Send mail</a> ", 151 // "<a href=\"http://www.google.com\">Send mail</a>"); 152 sanitize("<a href=\"http://www.google.com \">Send mail</a>", 153 "<a href=\"http://www.google.com \">Send mail</a>"); 154 // todo Gmail strips the leading and trailing spaces on this href 155 // sanitize("<a href=\" http://www.google.com \">Send mail</a> ", 156 // "<a href=\"http://www.google.com\">Send mail</a>"); 157 sanitize("<a href=\" http://www.google.com \">Send mail</a>", "Send mail"); 158 } 159 160 public void testDangerousHtml() { 161 // body tag is translated to div tag 162 sanitize("<body dir=\"rtl\" onMouseOVer=\"alert(document.cookie)\">arr</body>", 163 "<div dir=\"rtl\">arr</div>"); 164 sanitize("<DIV ONCLICK=alert(document.cookie) style=color:red>arr</DIV>", 165 "<div style=\"color:red\">arr</div>"); 166 sanitize("<b style=position:absolute;left:0;top:0>arr</b>", "<b>arr</b>"); 167 168 // mailto: URLs on images are too easy to turn into DOS attacks 169 sanitize("<img src=\"mailto:\">", ""); 170 sanitize("<img src=\"mailto:hcnidumolu (at) google.com\">", ""); 171 sanitize("<img src=\"mailto:hcnidumolu (at) google.com\">", ""); 172 sanitize("<img src=\" mailto:hcnidumolu (at) google.com\">", ""); 173 sanitize("<img src=\"mailto:hcnidumolu (at) google.com\">", ""); 174 sanitize("<img src=\"m
ailto:hcnidumolu (at) google.com\">", ""); 175 // todo Gmail doesn't escape the @ sign; OWASP does by default 176 // sanitize("<a href=\"mailto:hcnidumolu (at) google.com\">Send mail </a>", 177 // "<a href=\"mailto:hcnidumolu (at) google.com\">Send mail </a>"); 178 sanitize("<a href=\"mailto:hcnidumolu (at) google.com\">Send mail </a>", 179 "<a href=\"mailto:hcnidumolu@google.com\">Send mail </a>"); 180 } 181 182 public void testSanitizingImgsWithoutSchemes() { 183 sanitize("<img src=\"//images1-gm-opensocial.googleusercontent.com/gadgets/proxy?" + 184 "url=http://foo.bar/baz.png&container=gm&gadget=a&rewriteMime=image/*\">", 185 // "<img src=\"//images1-gm-opensocial.googleusercontent.com/gadgets/proxy?" + 186 // "url=http://foo.bar/baz.png&container=gm&gadget=" + 187 // "a&rewriteMime=image/*\">"); // todo Gmail doesn't escape the = signs 188 "<img src=\"//images1-gm-opensocial.googleusercontent.com/gadgets/proxy?" + 189 "url=http://foo.bar/baz.png&container=gm&gadget=" + 190 "a&rewriteMime=image/*\" />"); 191 } 192 193 public void testAdditionalURISchemes() { 194 // todo Gmail keeps a destinationless link; OWASP strips the a link completely 195 // sanitize("<a href=\"foo:bar\" target=\"_blank\">link1</a>", "<a>link1</a>"); 196 sanitize("<a href=\"foo:bar\" target=\"_blank\">link1</a>", "link1"); 197 // todo Gmail keeps a destinationless a link; OWASP strips the a link completely 198 // sanitize("<a href=\"baz:alanbs (at) google.com\">link2</a>", "<a>link2</a>"); 199 sanitize("<a href=\"baz:alanbs (at) google.com\">link2</a>", "link2"); 200 } 201 202 public void testBackgroundAttribute() { 203 sanitize("<div background=\"http://www.random.org/png\">stuff</div><div>more stuff</div>", 204 "<div background=\"http://www.random.org/png\">stuff</div><div>more stuff</div>"); 205 } 206 207 public void testInputImage() { 208 sanitize("<input type=\"image\" src=\"http://random.org/png\">", 209 "<input type=\"image\" src=\"http://random.org/png\" />"); 210 } 211 212 public void testImplicitInputImage() { 213 // In HTML 4.01, src attribute has meaning only when type="image" (which 214 // is not the default), but this happens in real life. 215 sanitize("<input src=\"http://random.org/png\">", 216 "<input src=\"http://random.org/png\" />"); 217 } 218 219 public void testSerialization() { 220 // N.B. (literal) newlines must not occur in CSS strings. 221 // todo Gmail leaves this CSS style in place and escapes it; OWASP removes it all 222 // sanitize("<a href=\"http://www.google.com\" style=\"font-family: 'expression; " + 223 // "\\a color:red;\\a font-family: completely unsanitized =(;' ;\">asdf</a>", 224 // "<a href=\"http://www.google.com\" style=\"font-family:'expression; " + 225 // "\\00000acolor:red;\\00000afont-family: completely unsanitized " + 226 // "=(;'\">asdf</a>"); 227 sanitize("<a href=\"http://www.google.com\" style=\"font-family: 'expression; " + 228 "\\a color:red;\\a font-family: completely unsanitized =(;' ;\">asdf</a>", 229 "<a href=\"http://www.google.com\">asdf</a>"); 230 } 231 232 public void testNoJS() { 233 // todo Gmail leaves this CSS in place and escapes it; OWASP removes it all 234 // sanitize("<a href=\"http://www.google.com\" style=\"background-image: " + 235 // "url('javascript:alert(1)')\"></a>", 236 // "<a href=\"http://www.google.com\" style=\"background-image:" + 237 // "url('')\"></a>"); 238 sanitize("<a href=\"http://www.google.com\" style=\"background-image: " + 239 "url('javascript:alert(1)')\"></a>", 240 "<a href=\"http://www.google.com\"></a>"); 241 } 242 243 public void testNoStyleElementByDefault() { 244 sanitize("<head><style type='text/css'>verboten { color: red; }</style></head>" + 245 "<body><p>test</p>", 246 "<div><p>test</p></div>"); 247 } 248 249 public void testMessageFormation() { 250 sanitize("<table><tr><td><b>This is a simple message</b></td></tr></table>", 251 "<table><tr><td><b>This is a simple message</b></td></tr></table>"); 252 sanitize("<table><tr><td><b>This is a simple message", 253 "<table><tr><td><b>This is a simple message</b></td></tr></table>"); 254 sanitize("<table><tr>This is a simple message</b></td></tr></table>", 255 "<table><tr><td>This is a simple message</td></tr></table>"); 256 sanitize("This is a simple message</b></td></tr></table>", "This is a simple message"); 257 } 258 259 public void testViolatingTags() { 260 sanitize("<html><head><title>html to ruin your site</title>" 261 + "<meta http-equiv=\"refresh\" content=\"5\" />" 262 + "<link rel=\"stylesheet\" type=\"text/css\" href=\"some site\"/>" 263 + "<style type=\"text/css\"> h1 {color: red}</style>" 264 + "</head><body><script>some script to run</script>" 265 + "<noscript>Please enable Javascript and reload this page." 266 + "Good things abound!</noscript>" 267 + "<noframes>This page requires frames!</noframes>" 268 + "<frameset cols = \"25%, 25%,*\">" 269 + "<frame src=\"site1.htm\" />" 270 + "<frame src=\"site2.htm\" />" 271 + "<frame src=\"site3.htm\" /> " 272 + "</frameset>" 273 + "<table><tr><td>" 274 + "Execute this <applet code=\"some evil site\"></td>" 275 + "</tr></table></body></html>" 276 , 277 "<div>" 278 + "<table><tr><td>" 279 + "Execute this </td>" 280 + "</tr></table></div>" 281 ); 282 283 sanitize("Include this:<br/>" 284 + "<object classid=\"clsid:FOOBAR\" id=\"Slider1\"" 285 + "declare=\"declare\">" 286 + "<param name=\"some param\" value=\"1\" />" 287 + "</object><br/>" 288 + "<form method=\"POST\" action=\"http://www.somesite.com\"" 289 + "onsubmit=\"run some script\">" 290 + "<input type=\"text\" onclick=\"run some script\" id=\"input\"/>" 291 + "<input type=\"submit\" onfocus=\"run some script\" value=\"submit\"/>" 292 + "</form>" 293 , 294 "Include this:<br />" 295 + "<br />" 296 + "<form method=\"POST\" action=\"http://www.somesite.com\">" 297 + "<input type=\"text\" />" 298 + "<input type=\"submit\" value=\"submit\" />" 299 + "</form>" 300 ); 301 } 302 303 public void testLinks() { 304 sanitize("<a href=\"http://www.somesite.com\" target=\"_self\" " 305 + "onclick=\"run some script\" onmouseover=\"run some script\">" 306 + "click here</a>" 307 + "<a href=\"javascript:run some script\">here</a>" 308 + "<a href=\"someinternalpage.htm\">or here</a>" 309 , 310 "<a href=\"http://www.somesite.com\">" 311 + "click here</a>" 312 + "here" 313 + "<a href=\"someinternalpage.htm\">or here</a>" 314 ); 315 } 316 317 public void testExternalLinks() { 318 sanitize("This is a test <a href=http://google.com>here</a> " 319 + "<img src=\"http://google.com/bogus.jpg\">" 320 + "<img src=\"//google.com/bogus2.jpg\">" 321 + "<img src=\"google.com/bogus3.jpg\">" 322 + "<script>Hello</script> " 323 + "<frameset><frame src=foo name=onlyFrame>hey</frame></frameset>" 324 , 325 "This is a test <a href=\"http://google.com\">here</a> " 326 + "<img src=\"http://google.com/bogus.jpg\" />" 327 + "<img src=\"//google.com/bogus2.jpg\" />" 328 + "<img src=\"google.com/bogus3.jpg\" /> " 329 ); 330 } 331 332 public void testNewHtmlWhitelist() { 333 sanitize("<a href=http://google.com/boguslink>link</a>" 334 + "<b>BOLD</b>" 335 + "<i>italics</i>" 336 + "<u>underlined</u>" 337 + "<br/>break<br>break" 338 + "<font size=+1>Big_font_gone</font>" 339 , 340 "<a href=\"http://google.com/boguslink\">link</a>" 341 + "<b>BOLD</b>" 342 + "<i>italics</i>" 343 + "<u>underlined</u>" 344 + "<br />break<br />break" 345 + "<font size=\"+1\">Big_font_gone</font>" 346 ); 347 } 348 349 public void testRemoveBackticksInAttributes() { 350 // IE treats backticks as quotes when re-serializing, but not when parsing 351 sanitize("<img alt=\"``onload=alert(1)\">", 352 "<img alt=\"``onload=alert(1) \" />"); 353 sanitize("<img alt=\"'``onload=alert(1)'\">", 354 "<img alt=\"'``onload=alert(1)' \" />"); 355 sanitize("<img alt=``onload=alert(1)\">", "<img alt=\"``onload=alert(1) \" />"); 356 357 // Make sure we're not fooled by escaped backticks 358 sanitize("<img alt=\"``onload=alert(1)\">", 359 "<img alt=\"``onload=alert(1) \" />"); 360 sanitize("<img alt=\"``onload=alert(1)\">", 361 "<img alt=\"``onload=alert(1) \" />"); 362 363 // Misc. dangerous cases: 364 sanitize("<img alt=`x`onload=alert(1)>", "<img alt=\"`x`onload=alert(1) \" />"); 365 sanitize("<img alt=foo`x`onload=alert(1)>", 366 "<img alt=\"foo`x`onload=alert(1) \" />"); 367 sanitize("<img alt=\"`whatever\">Hello world ` onload=alert(1) <br>", 368 "<img alt=\"`whatever \" />Hello world ` onload=alert(1) <br />"); 369 370 // The tokenizer doesn't see these as entities because they lack a trailing semicolon, so it 371 // escapes the leading ampersands. 372 sanitize("<img alt=\"``onload=alert(1)\">", 373 "<img alt=\"`&#x000060onload=alert(1) \" />"); 374 375 // Here there are no actual backticks, though there would be if we (or IE) did repeated 376 // unescaping. 377 sanitize("<img alt=\"&#x000060&#x000060onload=alert(2)\">", 378 "<img alt=\"&#x000060&#x000060onload=alert(2)\" />"); 379 sanitize("<img alt=\"&amp;#x000060&amp;#x000060onload=alert(2)\">", 380 "<img alt=\"&amp;#x000060&amp;#x000060onload=alert(2)\" />"); 381 } 382 383 public void testMakeSafeStyle() { 384 sanitize("<div style=\"color:red\"></div>", "<div style=\"color:red\"></div>"); 385 sanitize("<div style=\"color:r\\ne\\t d d\\r\\n\"></div>", "<div></div>"); 386 sanitize("<div style=\"font-size:13.5pt; color:#804000 \"></div>", 387 "<div style=\"font-size:13.5pt;color:#804000\"></div>"); 388 sanitize("<div style=\"color:red;color\"></div>", "<div style=\"color:red\"></div>"); 389 sanitize("<div style=\"color:red;color:a:b\"></div>", "<div style=\"color:red\"></div>"); 390 sanitize("<div style=\"color:url(foo)\"></div>", "<div></div>"); 391 sanitize("<div style=\"color:white; list-style:url(foo.gif);\"></div>", 392 "<div style=\"color:white\"></div>"); 393 sanitize("<div style=\"color:rgb(255, 0, 0)\"></div>", 394 "<div style=\"color:rgb( 255 , 0 , 0 )\"></div>"); 395 sanitize("<div style=\"background-color:rgb(80%,92%,18%)\"></div>", 396 "<div style=\"background-color:rgb( 80% , 92% , 18% )\"></div>"); 397 sanitize("<div style=\"border-left:1px rgb(0,255,0) solid\"></div>", 398 "<div style=\"border-left:1px rgb( 0 , 255 , 0 ) solid\"></div>"); 399 sanitize("<div style=\"background:rgb(0,255,0) url(foo) no-repeat top\"></div>", 400 "<div style=\"background:rgb( 0 , 255 , 0 ) no-repeat top\"></div>"); 401 sanitize("<div style=\"display:none; border-color: #ffeeff \"></div>", 402 "<div style=\"display:none;border-color:#ffeeff\"></div>"); 403 404 // check for CSS3 border-radius 405 sanitize("<div style=\"border-radius:10px\"></div>", 406 "<div style=\"border-radius:10px\"></div>"); 407 sanitize("<div style=\"border-bottom-left-radius:10px\"></div>", 408 "<div style=\"border-bottom-left-radius:10px\"></div>"); 409 sanitize("<div style=\"border-bottom-right-radius:10px\"></div>", 410 "<div style=\"border-bottom-right-radius:10px\"></div>"); 411 sanitize("<div style=\"border-top-left-radius:10px\"></div>", 412 "<div style=\"border-top-left-radius:10px\"></div>"); 413 sanitize("<div style=\"border-top-right-radius:10px\"></div>", 414 "<div style=\"border-top-right-radius:10px\"></div>"); 415 416 // allow positive margins 417 sanitize("<div style=\"margin:10 0 10 0\"></div>", 418 "<div style=\"margin:10 0 10 0\"></div>"); 419 sanitize("<div style=\"margin-left:40px\"></div>", 420 "<div style=\"margin-left:40px\"></div>"); 421 422 // negative margin would allow it to slip out of the box 423 sanitize("<div style=\"margin-left:-10\"></div>", "<div></div>"); 424 425 // allow positive text-ident 426 sanitize("<div style=\"text-indent:10\"></div>", "<div style=\"text-indent:10\"></div>"); 427 sanitize("<div style=\"text-indent:0\"></div>", "<div style=\"text-indent:0\"></div>"); 428 429 // todo Gmail disallows negative text-indents; OWASP is fine with them 430 // negative text-indent would allow it to slip out of the box 431 // sanitize("<div style=\"text-indent:-10\"></div>", "<div></div>"); 432 sanitize("<div style=\"text-indent:-10\"></div>", "<div style=\"text-indent:-10\"></div>"); 433 } 434 435 public void testMakeSafeStyleWithQuotedStrings() { 436 sanitize("<div style=\"font-family:'courier new',monospace;font-size:x-small\"></div>", 437 "<div style=\"font-family:'courier new' , monospace;font-size:x-small\">" + 438 "</div>"); 439 sanitize("<div style=\"font-family:\"courier new\",monospace\"></div>", "<div></div>"); 440 sanitize("<div style=\"font-family:''\"></div>", "<div></div>"); 441 sanitize("<div style=\"font-family:a,''\"></div>", 442 "<div style=\"font-family:'a' ,\"></div>"); 443 sanitize("<div style=\"font-family:'',a,\"\",b\"></div>", 444 "<div style=\"font-family:, 'a' ,\"></div>"); 445 446 sanitize("<div style=\"font-family:'\"></div>", "<div></div>"); 447 sanitize("<div style=\"font-family: 'courier new\",monospace;'\"></div>", "<div></div>"); 448 sanitize("<div style=\"font-family: \"courier new',monospace;\"></div>", "<div></div>"); 449 } 450 451 public void testSeriouslyNoBackgroundImages() { 452 sanitize("<div style=\"background:url('http://www.here.com/awesome.png')\"></div>", 453 "<div></div>"); 454 sanitize("<div style=\"background-image:url('http://www.here.com/awesome.png')\"></div>", 455 "<div></div>"); 456 457 sanitize("<div style=\"background:url('javascript:evil()')\"></div>", "<div></div>"); 458 sanitize("<div style=\"background-image:url('javascript:evil()')\"></div>", "<div></div>"); 459 } 460 461 public void testExpression() { 462 sanitize("<div style=\"width: expression(alert(1))\"></div>", "<div></div>"); 463 } 464 465 public void testStrayUrlConsideredHarmful() { 466 sanitize("<div style=\"float:url(\"></div>", "<div></div>"); 467 sanitize("<div style=\"float:\\075\\0072\\006C\\0028\"></div>", "<div></div>"); 468 } 469 470 public void testObjectionableFunctions() { 471 sanitize("<div style=\"ex\\pression(123)\"></div>", "<div></div>"); 472 sanitize("<div style=\"_expression(123)\"></div>", "<div></div>"); 473 sanitize("<div style=\"(123)\"></div>", "<div></div>"); 474 sanitize("<div style=\"funkyFunction(123)\"></div>", "<div></div>"); 475 476 sanitize("<div style=\"color:expression(alert('xss'))\"></div>", "<div></div>"); 477 sanitize("<div style=\"color:expression(alert\\000028\\000027xss\\000027\\000029)\"></div>", 478 "<div></div>"); 479 sanitize("<div style=\"color:expression\\000028alert\\000028\\000027xss\\000027" + 480 "\\000029\\000029\"></div>", "<div></div>"); 481 sanitize("<div style=\"color:expressio\\00006E\\000028alert\\000028\\000027xss\\000027" + 482 "\\000029\\000029\"\"></div>", "<div></div>"); 483 sanitize("<div style=\"color:expression\\(alert\\)\"></div>", "<div></div>"); 484 } 485 486 public void testAbsolutePositionBanned() { 487 sanitize("<div style=\"position: absolute\"></div>", "<div></div>"); 488 } 489 490 public void testNoTextShadow() { 491 // todo Gmail disallows this text-shadow; OWASP is fine with it 492 // sanitize("<div style=\"text-shadow: red -50px -100px 0px\"></div>", "<div></div>"); 493 sanitize("<div style=\"text-shadow: red -50px -100px 0px\"></div>", 494 "<div style=\"text-shadow:red -50px -100px 0px\"></div>"); 495 } 496 497 public void testToStyle() { 498 sanitize("<div style=\"color:red\"></div>", "<div style=\"color:red\"></div>"); 499 sanitize("<div style=\"color: red\"></div>", "<div style=\"color:red\"></div>"); 500 sanitize("<div style=\"color :red\"></div>", "<div style=\"color:red\"></div>"); 501 sanitize("<div style=\"color :red; font-size:13.5pt;\"></div>", 502 "<div style=\"color:red;font-size:13.5pt\"></div>"); 503 sanitize("<div style=\"content:'\"'\"></div>", "<div></div>"); 504 } 505 506 public void testNoColorStrings() { 507 sanitize("<div style=\"color: 'red';\"></div>", "<div></div>"); 508 } 509 510 public void testTolerateMalformedBorder() { 511 sanitize("<div style=\"border-left-: solid thin red\"></div>", "<div></div>"); 512 } 513 514 public void testRgba() { 515 sanitize("<div style=\"color:rgba(255, 0, 0, 0.5)\"></div>", 516 "<div style=\"color:rgba( 255 , 0 , 0 , 0.5 )\"></div>"); 517 } 518 519 public void testFontStyle() { 520 // todo Gmail accepts !important while OWASP discards it; this is only beauty, not security 521 // sanitize("<div style=\"font-style:normal!important\"></div>", 522 // "<div style=\"font-style:normal!important\"></div>"); 523 sanitize("<div style=\"font-style:normal!important\"></div>", 524 "<div style=\"font-style:normal\"></div>"); 525 // todo Gmail accepts !important while OWASP discards it; this is only beauty, not security 526 // sanitize("<div style=\"font-style:oblique !important\"></div>", 527 // "<div style=\"font-style:oblique!important\"></div>"); 528 sanitize("<div style=\"font-style:oblique !important\"></div>", 529 "<div style=\"font-style:oblique\"></div>"); 530 sanitize("<div style=\"font-style:italic\"></div>", 531 "<div style=\"font-style:italic\"></div>"); 532 } 533 534 private void sanitize(String dirtyHTML, String expectedHTML) { 535 final String cleansedHTML = HtmlSanitizer.sanitizeHtml(dirtyHTML); 536 assertEquals(expectedHTML, cleansedHTML); 537 } 538 } 539