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