Home | History | Annotate | Download | only in utils
      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>=&{()}", "&#39;&#39;;!--&#34;&#61;&amp;{()}");
     50         sanitize("<img src=javascript:alert(String.fromCharCode(88,83,83))>", "");
     51         sanitize("\\\";alert('XSS');//", "\\&#34;;alert(&#39;XSS&#39;);//");
     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                         "&#34;&gt;\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=\"m&#x61;ilto:hcnidumolu (at) google.com\">", "");
    173         sanitize("<img src=\"m&#x0D;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&#64;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&amp;rewriteMime=image/*\">"); // todo Gmail doesn't escape the = signs
    187                 "<img src=\"//images1-gm-opensocial.googleusercontent.com/gadgets/proxy?" +
    188                         "url&#61;http://foo.bar/baz.png&container&#61;gm&amp;gadget&#61;" +
    189                         "a&amp;rewriteMime&#61;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:&#39;expression; " +
    224 //                        "\\00000acolor:red;\\00000afont-family: completely unsanitized " +
    225 //                        "=(;&#39;\">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(&#39;&#39;)\"></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=\"&#43;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=\"&#96;&#96;onload&#61;alert(1) \" />");
    352         sanitize("<img alt=\"'``onload=alert(1)'\">",
    353                 "<img alt=\"&#39;&#96;&#96;onload&#61;alert(1)&#39; \" />");
    354         sanitize("<img alt=``onload=alert(1)\">", "<img alt=\"&#96;&#96;onload&#61;alert(1) \" />");
    355 
    356         // Make sure we're not fooled by escaped backticks
    357         sanitize("<img alt=\"&#96;&#x0060;onload=alert(1)\">",
    358                 "<img alt=\"&#96;&#96;onload&#61;alert(1) \" />");
    359         sanitize("<img alt=\"&#x000060;&#x000060;onload=alert(1)\">",
    360                 "<img alt=\"&#96;&#96;onload&#61;alert(1) \" />");
    361 
    362         // Misc. dangerous cases:
    363         sanitize("<img alt=`x`onload=alert(1)>", "<img alt=\"&#96;x&#96;onload&#61;alert(1) \" />");
    364         sanitize("<img alt=foo`x`onload=alert(1)>",
    365                 "<img alt=\"foo&#96;x&#96;onload&#61;alert(1) \" />");
    366         sanitize("<img alt=\"`whatever\">Hello world ` onload=alert(1) <br>",
    367                 "<img alt=\"&#96;whatever \" />Hello world &#96; onload&#61;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=\"&#x000060&#x000060onload=alert(1)\">",
    372                 "<img alt=\"&#96;&amp;#x000060onload&#61;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=\"&amp;#x000060&amp;#x000060onload=alert(2)\">",
    377                 "<img alt=\"&amp;#x000060&amp;#x000060onload&#61;alert(2)\" />");
    378         sanitize("<img alt=\"&amp;amp;#x000060&amp;amp;#x000060onload=alert(2)\">",
    379                 "<img alt=\"&amp;amp;#x000060&amp;amp;#x000060onload&#61;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:&#39;courier new&#39; , 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:&#39;a&#39; ,\"></div>");
    442         sanitize("<div style=\"font-family:'',a,\"\",b\"></div>",
    443                 "<div style=\"font-family:, &#39;a&#39; ,\"></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