Home | History | Annotate | Download | only in html
      1 // Copyright (c) 2011, Mike Samuel
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions
      6 // are met:
      7 //
      8 // Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 // Redistributions in binary form must reproduce the above copyright
     11 // notice, this list of conditions and the following disclaimer in the
     12 // documentation and/or other materials provided with the distribution.
     13 // Neither the name of the OWASP nor the names of its contributors may
     14 // be used to endorse or promote products derived from this software
     15 // without specific prior written permission.
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     20 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     26 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27 // POSSIBILITY OF SUCH DAMAGE.
     28 
     29 package org.owasp.html;
     30 
     31 import org.junit.Test;
     32 
     33 import junit.framework.TestCase;
     34 
     35 public class SanitizersTest extends TestCase {
     36 
     37   @Test
     38   public static final void testFormatting() {
     39     assertEquals("", Sanitizers.FORMATTING.sanitize(null));
     40     assertEquals("", Sanitizers.FORMATTING.sanitize(""));
     41     assertEquals(
     42         "Hello, World!",
     43         Sanitizers.FORMATTING.sanitize("Hello, World!"));
     44     assertEquals(
     45         "Hello, <b>World</b>!",
     46         Sanitizers.FORMATTING.sanitize("Hello, <b>World</b>!"));
     47     assertEquals(
     48         "Hello, <b>World</b>!",
     49         Sanitizers.FORMATTING.sanitize(
     50             "<p>Hello, <b onclick=alert(1337)>World</b>!</p>"));
     51   }
     52 
     53   @Test
     54   public static final void testBlockElements() {
     55     assertEquals("", Sanitizers.BLOCKS.sanitize(null));
     56     assertEquals(
     57         "Hello, World!",
     58         Sanitizers.BLOCKS.sanitize("Hello, World!"));
     59     assertEquals(
     60         "Hello, World!",
     61         Sanitizers.BLOCKS.sanitize("Hello, <b>World</b>!"));
     62     assertEquals(
     63         "<p>Hello, World!</p>",
     64         Sanitizers.BLOCKS.sanitize(
     65             "<p onclick=alert(1337)>Hello, <b>World</b>!</p>"));
     66   }
     67 
     68   @Test
     69   public static final void testBlockAndFormattingElements() {
     70     PolicyFactory s = Sanitizers.BLOCKS.and(Sanitizers.FORMATTING);
     71     PolicyFactory r1 = Sanitizers.BLOCKS.and(Sanitizers.FORMATTING)
     72         .and(Sanitizers.BLOCKS);
     73     PolicyFactory r2 = Sanitizers.BLOCKS.and(Sanitizers.FORMATTING)
     74         .and(Sanitizers.FORMATTING);
     75     for (PolicyFactory f : new PolicyFactory[] { s, r1, r2 }) {
     76       assertEquals("", f.sanitize(null));
     77       assertEquals("Hello, World!", f.sanitize("Hello, World!"));
     78       assertEquals("Hello, <b>World</b>!", f.sanitize("Hello, <b>World</b>!"));
     79       assertEquals(
     80           "<p>Hello, <b>World</b>!</p>",
     81           f.sanitize("<p onclick=alert(1337)>Hello, <b>World</b>!</p>"));
     82     }
     83   }
     84 
     85   @Test
     86   public static final void testStylesAndFormatting() {
     87     PolicyFactory sanitizer = Sanitizers.FORMATTING
     88       .and(Sanitizers.BLOCKS).and(Sanitizers.STYLES).and(Sanitizers.LINKS);
     89     String input = "<span style=\"font-weight:bold;"
     90       + "text-decoration:underline;background-color:yellow\""
     91       + ">aaaaaaaaaaaaaaaaaaaaaaa</span>";
     92     String got = sanitizer.sanitize(input);
     93     String want = input;
     94     assertEquals(want, got);
     95   }
     96 
     97   @Test
     98   public static final void testAndIntersects() {
     99     PolicyFactory restrictedLink = new HtmlPolicyBuilder()
    100        .allowElements("a")
    101        .allowUrlProtocols("https")
    102        .allowAttributes("href", "title").onElements("a")
    103        .toFactory();
    104     PolicyFactory inline = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
    105     String inputHtml =
    106         "<a href='http://foo.com/'>Hello, <b>World</b></a>"
    107         + "<a title='!' href='https://foo.com/#!'>!</a>";
    108     PolicyFactory and1 = restrictedLink.and(inline);
    109     PolicyFactory and2 = inline.and(restrictedLink);
    110     assertEquals(
    111         "https-only links",
    112         "Hello, World<a title=\"!\" href=\"https://foo.com/#!\">!</a>",
    113         restrictedLink.sanitize(inputHtml));
    114     assertEquals(
    115         "inline els",
    116         "<a href=\"http://foo.com/\" rel=\"nofollow\">Hello, <b>World</b></a>"
    117         + "<a href=\"https://foo.com/#!\" rel=\"nofollow\">!</a>",
    118         inline.sanitize(inputHtml));
    119     assertEquals(
    120         "https-only links and inline els",
    121         "Hello, <b>World</b>"
    122         + "<a title=\"!\" href=\"https://foo.com/#!\" rel=\"nofollow\">!</a>",
    123         and1.sanitize(inputHtml));
    124     assertEquals(
    125         "inline els and https-only links",
    126         "Hello, <b>World</b>"
    127         + "<a title=\"!\" href=\"https://foo.com/#!\" rel=\"nofollow\">!</a>",
    128         and2.sanitize(inputHtml));
    129   }
    130 
    131   @Test
    132   public static final void testImages() {
    133     PolicyFactory s = Sanitizers.IMAGES;
    134     assertEquals(
    135         "foo", s.sanitize("<a href=\"javascript:alert(1337)\">foo</a>"));
    136     assertEquals(
    137         "<img src=\"foo.gif\" />", s.sanitize("<img src=\"foo.gif\">"));
    138     assertEquals(
    139         "", s.sanitize("<img src=\"javascript://alert(1337)\">"));
    140     assertEquals(
    141         "<img src=\"x.gif\" alt=\"y\""
    142         + " width=\"96\" height=\"64\" border=\"0\" />",
    143         s.sanitize(
    144             "<img src=\"x.gif\" alt=\"y\" width=96 height=64 border=0>"));
    145     assertEquals(
    146         "<img src=\"x.png\" alt=\"y\" height=\"64\" border=\"0\" />",
    147         s.sanitize(
    148             "<img src=\"x.png\" alt=\"y\" width=\"widgy\" height=64 border=0>")
    149         );
    150   }
    151 
    152   @Test
    153   public static final void testLinks() {
    154     PolicyFactory s = Sanitizers.LINKS;
    155     assertEquals(
    156         "<a href=\"foo.html\" rel=\"nofollow\">Link text</a>",
    157         s.sanitize("<a href=\"foo.html\">Link text</a>"));
    158     assertEquals(
    159         "<a href=\"foo.html\" rel=\"nofollow\">Link text</a>",
    160         s.sanitize(
    161             "<a href=\"foo.html\" onclick=\"alert(1337)\">Link text</a>"));
    162     assertEquals(
    163         "<a href=\"http://example.com/x.html\" rel=\"nofollow\">Link text</a>",
    164         s.sanitize(
    165             "<a href=\"http://example.com/x.html\""
    166             + " onclick=\"alert(1337)\">Link text</a>"));
    167     assertEquals(
    168         "<a href=\"https://example.com/x.html\" rel=\"nofollow\">Link text</a>",
    169         s.sanitize(
    170             "<a href=\"https://example.com/x.html\""
    171             + " onclick=\"alert(1337)\">Link text</a>"));
    172     assertEquals(
    173         "<a href=\"HTTPS://example.com/x.html\" rel=\"nofollow\">Link text</a>",
    174         s.sanitize(
    175             "<a href=\"HTTPS://example.com/x.html\""
    176             + " onclick=\"alert(1337)\">Link text</a>"));
    177     assertEquals(
    178         "<a href=\"//example.com/x.html\" rel=\"nofollow\">Link text</a>",
    179         s.sanitize(
    180             "<a href=\"//example.com/x.html\""
    181             + " onclick=\"alert(1337)\">Link text</a>"));
    182     assertEquals(
    183         "Link text",
    184         s.sanitize(
    185             "<a href=\"javascript:alert(1337).html\""
    186             + " onclick=\"alert(1337)\">Link text</a>"));
    187     // Not a link.  Instead, an attempt to intercept URL references that has
    188     // not been explicitly allowed.
    189     assertEquals(
    190         "Header text",
    191         s.sanitize("<a name=\"header\" id=\"header\">Header text</a>"));
    192   }
    193 
    194   @Test
    195   public static final void testExplicitlyAllowedProtocolsAreCaseInsensitive() {
    196     // Issue 24.
    197     PolicyFactory s = new HtmlPolicyBuilder()
    198         .allowElements("a")
    199         .allowAttributes("href").onElements("a")
    200         .allowStandardUrlProtocols()
    201         .allowUrlProtocols("file")  // Don't try this at home
    202         .toFactory();
    203     String input = (
    204         "<a href='file:///etc/passwd'>Copy and paste this into email</a>"
    205         + "<a href='FILE:///etc/passwd'>Or this one</a>"
    206         + "<a href='F\u0130LE:///etc/passwd'>not with Turkish dotted I's</a>"
    207         + "<a href='fail:///etc/passed'>The fail protocol needs to happen</a>");
    208     String want = (
    209         "<a href=\"file:///etc/passwd\">Copy and paste this into email</a>"
    210         + "<a href=\"FILE:///etc/passwd\">Or this one</a>"
    211         + "not with Turkish dotted I&#39;s"
    212         + "The fail protocol needs to happen");
    213     assertEquals(want, s.sanitize(input));
    214   }
    215 
    216   @Test
    217   public static final void testIssue9StylesInTables() {
    218     String input = ""
    219         + "<table style=\"color: rgb(0, 0, 0);"
    220         + " font-family: Arial, Geneva, sans-serif;\">"
    221         + "<tbody>"
    222         + "<tr>"
    223         + "<th>Column One</th><th>Column Two</th>"
    224         + "</tr>"
    225         + "<tr>"
    226         + "<td align=\"center\""
    227         + " style=\"background-color: rgb(255, 255, 254);\">"
    228         + "<font size=\"2\">Size 2</font></td>"
    229         + "<td align=\"center\""
    230         + " style=\"background-color: rgb(255, 255, 254);\">"
    231         + "<font size=\"7\">Size 7</font></td>"
    232         + "</tr>"
    233         + "</tbody>"
    234         + "</table>";
    235     PolicyFactory s = new HtmlPolicyBuilder()
    236         .allowElements("table", "tbody", "thead", "tr", "td", "th")
    237         .allowCommonBlockElements()
    238         .allowCommonInlineFormattingElements()
    239         .allowStyling()
    240         .allowAttributes("align").matching(true, "left", "center", "right")
    241           .onElements("table", "tr", "td", "th")
    242         .allowAttributes("size").onElements("font", "img")
    243         .toFactory();
    244     String sanitized = ""
    245         + "<table style=\"color:rgb( 0 , 0 , 0 );"
    246         + "font-family:&#39;arial&#39; , &#39;geneva&#39; , sans-serif\">"
    247         + "<tbody>"
    248         + "<tr>"
    249         + "<th>Column One</th><th>Column Two</th>"
    250         + "</tr>"
    251         + "<tr>"
    252         + "<td align=\"center\""
    253         + " style=\"background-color:rgb( 255 , 255 , 254 )\">"
    254         + "<font size=\"2\">Size 2</font></td>"
    255         + "<td align=\"center\""
    256         + " style=\"background-color:rgb( 255 , 255 , 254 )\">"
    257         + "<font size=\"7\">Size 7</font></td>"
    258         + "</tr>"
    259         + "</tbody>"
    260         + "</table>";
    261     assertEquals(sanitized, s.sanitize(input));
    262   }
    263 
    264   @Test
    265   public static final void testSkipIfEmptyUnionsProperly() {
    266     // Issue 23
    267     PolicyFactory extras = new HtmlPolicyBuilder()
    268         .allowWithoutAttributes("span", "div")
    269         .allowElements("span", "div", "textarea")
    270         // This is not the proper way to require the attribute disabled on
    271         // textareas.  This is a test.  This is only a test.
    272         .allowAttributes("disabled").onElements("textarea")
    273         .disallowWithoutAttributes("textarea")
    274         .toFactory();
    275     PolicyFactory policy = Sanitizers.FORMATTING
    276         .and(Sanitizers.BLOCKS)
    277         .and(Sanitizers.IMAGES)
    278         .and(Sanitizers.STYLES)
    279         .and(extras);
    280     String input =
    281         "<textarea>text</textarea><textarea disabled></textarea>"
    282         + "<div onclick='redirect()'><span>Styled by span</span></div>";
    283     String want = "text<textarea disabled=\"disabled\"></textarea>"
    284         + "<div><span>Styled by span</span></div>";
    285     assertEquals(want, policy.sanitize(input));
    286   }
    287 }
    288