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 javax.annotation.Nullable;
     32 
     33 import org.junit.Test;
     34 
     35 import junit.framework.TestCase;
     36 
     37 public class StylingPolicyTest extends TestCase {
     38   @Test
     39   public static final void testNothingToOutput() {
     40     assertSanitizedCss(null, "");
     41     assertSanitizedCss(null, "/** no CSS here */");
     42     assertSanitizedCss(null, "/* props: disabled; font-weight: bold */");
     43     assertSanitizedCss(null, "position: fixed");
     44     assertSanitizedCss(
     45         null, "background: url('javascript:alert%281337%29')");
     46   }
     47 
     48   @Test
     49   public static final void testColors() {
     50     assertSanitizedCss("color:red", "color: red");
     51     assertSanitizedCss("background-color:#f00", "background-color: #f00");
     52     assertSanitizedCss("background:#f00", "background: #f00");
     53     assertSanitizedCss("color:#f00", "color: #F00");
     54     assertSanitizedCss(null, "color: #F000");
     55     assertSanitizedCss("color:#ff0000", "color: #ff0000");
     56     assertSanitizedCss("color:rgb( 255 , 0 , 0 )", "color: rgb(255, 0, 0)");
     57     assertSanitizedCss("background:rgb( 100% , 0 , 0 )",
     58                        "background: rgb(100%, 0, 0)");
     59     assertSanitizedCss(
     60         "color:rgba( 100% , 0 , 0 , 100% )", "color: RGBA(100%, 0, 0, 100%)");
     61     assertSanitizedCss(null, "color: transparent");
     62     assertSanitizedCss(null, "color: bogus");
     63     assertSanitizedCss(null, "color: expression(alert(1337))");
     64     assertSanitizedCss(null, "color: 000");
     65     assertSanitizedCss(null, "background-color: 000");
     66     // Not colors.
     67     assertSanitizedCss(null, "background: \"pwned.jpg\"");
     68     assertSanitizedCss(null, "background: url(pwned.jpg)");
     69     assertSanitizedCss(null, "color:#urlabc");
     70     assertSanitizedCss(null, "color:#urlabcd");
     71   }
     72 
     73   @Test
     74   public static final void testFontWeight() {
     75     assertSanitizedCss(
     76         "font-weight:bold", "font-weight: bold");
     77     assertSanitizedCss(
     78         "font:bold", "font: bold");
     79     assertSanitizedCss(
     80         "font:bolder", "font: Bolder");
     81     assertSanitizedCss(
     82         "font-weight:800", "font-weight: 800");
     83     assertSanitizedCss(
     84         null, "font-weight: expression(alert(1337))");
     85     assertSanitizedCss(
     86         "font:'evil'",
     87         "font: 3execute evil");
     88   }
     89 
     90   @Test
     91   public static final void testFontStyle() {
     92     assertSanitizedCss(
     93         "font-style:italic", "font-style: Italic");
     94     assertSanitizedCss(
     95         "font:italic", "font: italic");
     96     assertSanitizedCss(
     97         "font:oblique", "font: Oblique");
     98     assertSanitizedCss(
     99         null, "font-style: expression(alert(1337))");
    100   }
    101 
    102   @Test
    103   public static final void testFontFace() {
    104     assertSanitizedCss(
    105         "font:'arial' , 'helvetica'", "font: Arial, Helvetica");
    106     assertSanitizedCss(
    107         "font-family:'arial' , 'helvetica' , sans-serif",
    108         "Font-family: Arial, Helvetica, sans-serif");
    109     assertSanitizedCss(
    110         "font-family:'monospace' , sans-serif",
    111         "Font-family: \"Monospace\", Sans-serif");
    112     assertSanitizedCss(
    113         "font:'arial bold' , 'helvetica' , monospace",
    114         "FONT: \"Arial Bold\", Helvetica, monospace");
    115     assertSanitizedCss(
    116         "font-family:'arial bold' , 'helvetica'",
    117         "font-family: \"Arial Bold\", Helvetica");
    118     assertSanitizedCss(
    119         "font-family:'arial bold' , 'helvetica'",
    120         "font-family: 'Arial Bold', Helvetica");
    121     assertSanitizedCss(
    122         "font-family:'evil'",
    123         "font-family: 3execute evil");
    124     assertSanitizedCss(
    125         "font-family:'arial bold' , , , 'helvetica' , sans-serif",
    126         "font-family: 'Arial Bold',,\"\",Helvetica,sans-serif");
    127   }
    128 
    129   @Test
    130   public static final void testFont() {
    131     assertSanitizedCss(
    132         "font:'arial' 12pt bold oblique",
    133         "font: Arial 12pt bold oblique");
    134     assertSanitizedCss(
    135         "font:'times new roman' 24px bolder",
    136         "font: \"Times New Roman\" 24px bolder");
    137     assertSanitizedCss("font:24px", "font: 24px");
    138     // Non-ascii characters discarded.
    139     assertSanitizedCss(null, "font: 24ex\\pression");
    140     // Harmless garbage.
    141     assertSanitizedCss(
    142         "font:24ex 'pression'", "font: 24ex\0pression");
    143     assertSanitizedCss(
    144         null, "font: expression(arial)");
    145     assertSanitizedCss(
    146         null, "font: rgb(\"expression(alert(1337))//\")");
    147     assertSanitizedCss("font-size:smaller", "font-size: smaller");
    148     assertSanitizedCss("font:smaller", "font: smaller");
    149   }
    150 
    151   @Test
    152   public static final void testBidiAndAlignmentAttributes() {
    153     assertSanitizedCss(
    154         "text-align:left;unicode-bidi:embed;direction:ltr",
    155         "Text-align: left; Unicode-bidi: Embed; Direction: LTR;");
    156     assertSanitizedCss(
    157         null, "text-align:expression(left())");
    158     assertSanitizedCss(null, "text-align: bogus");
    159     assertSanitizedCss("unicode-bidi:embed", "unicode-bidi:embed");
    160     assertSanitizedCss(null, "unicode-bidi:expression(embed)");
    161     assertSanitizedCss(null, "unicode-bidi:bogus");
    162     assertSanitizedCss(null, "direction:expression(ltr())");
    163   }
    164 
    165   @Test
    166   public static final void testTextDecoration() {
    167     assertSanitizedCss(
    168         "text-decoration:underline",
    169         "Text-Decoration: Underline");
    170     assertSanitizedCss(
    171         "text-decoration:overline",
    172         "text-decoration: overline");
    173     assertSanitizedCss(
    174         "text-decoration:line-through",
    175         "text-decoration: line-through");
    176     assertSanitizedCss(
    177         null,
    178         "text-decoration: expression(document.location=42)");
    179   }
    180 
    181   @Test
    182   public static final void testBoxProperties() {
    183     // http://www.w3.org/TR/CSS2/box.html
    184     assertSanitizedCss("height:0", "height:0");
    185     assertSanitizedCss("width:0", "width:0");
    186     assertSanitizedCss("width:20px", "width:20px");
    187     assertSanitizedCss("width:20", "width:20");
    188     assertSanitizedCss("width:100%", "width:100%");
    189     assertSanitizedCss("height:6in", "height:6in");
    190     assertSanitizedCss(null, "width:-20");
    191     assertSanitizedCss(null, "width:url('foo')");
    192     assertSanitizedCss(null, "height:6fixed");
    193     assertSanitizedCss("margin:2 2 2 2", "margin:2 2 2 2");
    194     assertSanitizedCss("margin:2 2 2", "margin:2 2 2");
    195     assertSanitizedCss("padding:2 2", "padding:2 2");
    196     assertSanitizedCss("margin:2", "margin:2");
    197     assertSanitizedCss("margin:2px 4px 6px 8px", "margin:2px 4px 6px 8px");
    198     assertSanitizedCss("padding:0 4px 6px", "padding:0 4px 6px");
    199     assertSanitizedCss("margin:2px 4px 6px 4px", "margin:2px 4px 6px 4px");
    200     assertSanitizedCss("margin:0 4px", "margin:0 4px");
    201     assertSanitizedCss("margin:0 4px", "margin:0 4 px");
    202     assertSanitizedCss("padding-left:4px", "padding-left:4px");
    203     assertSanitizedCss("padding-left:0.4em;padding-top:2px;margin-bottom:3px",
    204                        "padding-left:0.4em;padding-top:2px;margin-bottom:3px");
    205     assertSanitizedCss("padding:0 1em 0.5in 1.5cm",
    206                        "padding:00. 1EM +00.5 In 1.50cm");
    207     // Mixed.
    208     assertSanitizedCss("margin:1em;margin-top:0.25em",
    209                        "margin:1em; margin-top:.25em");
    210   }
    211 
    212   @Test
    213   public static final void testUrls() throws Exception {
    214     // Test that a long URL does not blow out the stack or consume quadratic
    215     // amounts of processor as when the CSS lexer was implemented as a bunch of
    216     // regular expressions.
    217     String longUrl = ""
    218         + "background-image:url(data:image/gif;base64,"
    219         + "R0lGODlhMgCaAPf/AO5ZOuRpTfXSyvro5Pz08uCEb+ShkeummPOMdPOqmdZdQvbEud1"
    220         + "UNuqmlvqbhchNMfnTyvXPxu6pmtFkTeJ6Y9JxXPR8YNRSNvyunP3Mwd1zW+iCau9dPt"
    221         + "BOMe69sutwVPGGbuFcPvmRefqAZuhiQ/rJv9pFJf3h2up0WttCItmBbv3r5/26q/bc1"
    222         + "v3XzvHOx8tML/nf2cpAI+hfQf3GufVbOvyrmvCkk/7r5vnm4t+BbPism/apmN9DI+hN"
    223         + "LrkrD/x4V98+HeFBIPt0U/x2VflaOfdwT/lzUvhYN/p2VfhWNd47Gvx5WPtyUflcO+J"
    224         + "EI/VsS/FjQvRpSPtwT/tuTebm5vpmRfppSPlePeRIJ8XFxdRQNPpsS8I9IYyMjOhQL+"
    225         + "xYN+ZMK/liQflgP+1dO+pUM//188RBJZGRkff39/718vFZOcA/JtVXO8VEKPvVzeuhk"
    226         + "Pynk/ermsdHK/7Yz/ehjvi2p9Z4ZPre2NRDJPVkRPzz8d9NLdR1YNM2F/SkksBBKOdX"
    227         + "OfaHbdhsVPFbO81cQs1UOO2HcPNTM/708dd7Zt1GJuFRMd1JKfBiQvyNcuF1XfqOd+6"
    228         + "gj+SDbPuJb/re1/TUzeReP+mHce1zWeOdjthYPOaSgO2LdOyllPFzWPezpPe7rfVzVO"
    229         + "6Hb/vz8f7f2eGJdOKMee54XeBiR/Z5XeNlSfyVfPJtT++ikf7i3POrnPGun9+VhOVSM"
    230         + "vzo499/as1EJup5YPvUy+abi+tjRPNrS91aPfCCafyxn/SCaNVZPeu7r+B+ad1WOOuy"
    231         + "pOm3rPt6W/OikPvq5vCmlt6Qf/ygivp2V/OTfPrb1eKfkP7Z0OyBaeibiuieju+ciu2"
    232         + "ejeKCbOatn+2YhOFVN+eRfedWNvWplvLRyuF4Xs5kTdlOL+3Eu+1mR/zp5fzq5dVMLt"
    233         + "JTOPvDtvKgjvy1pe9yVchFKeafj+WYh/nBs/HIvv3Ctd9YONdVOM5BI8pXP/jUzOqKd"
    234         + "OiMd9toUN1iSONuU8HBwYmJifX19f///////yH/C1hNUCBEYXRhWE1QPD94cGFja2V0"
    235         + "IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1"
    236         + "wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIE"
    237         + "NvcmUgNS4wLWMwNjEgNjQuMTQwOTQ5LCAyMDEwLzEyLzA3LTEwOjU3OjAxICAgICAgI"
    238         + "CAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIv"
    239         + "MjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB"
    240         + "4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbn"
    241         + "M6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZ"
    242         + "VJlZiMiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1w"
    243         + "TU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOkEyRDgxODE2MkMyMDY4MTE4NzF"
    244         + "GRDNDMzU5QkE3OTE3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkMzMjA1M0I4Qk"
    245         + "M4RjExRTBCRDBEQkE0MTlGMTc4MDZGIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkO"
    246         + "jlFQzFFMTZFQkM4RDExRTBCRDBEQkE0MTlGMTc4MDZGIiB4bXA6Q3JlYXRvclRvb2w9"
    247         + "IkFkb2JlIFBob3Rvc2hvcCBDUzUuMSBNYWNpbnRvc2giPiA8eG1wTU06RGVyaXZlZEZ"
    248         + "yb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMjFGMUIwQjMyMjA2ODExODcxRk"
    249         + "QzQzM1OUJBNzkxNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBMkQ4MTgxNjJDM"
    250         + "jA2ODExODcxRkQzQzM1OUJBNzkxNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6"
    251         + "UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fT"
    252         + "z8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDws"
    253         + "HAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj"
    254         + "46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15d"
    255         + "XFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCs"
    256         + "qKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAP"
    257         + "8ALAAAAAAyAJoAAAj/AP+h8EGwoMGDCBMqXOgDxb8ZUphInEixosWLGDMykTLDB5CPI"
    258         + "EOKHEmypEmQBImoXMmypcuXMGOuJDikps2bOHPq3MnTJsEmQJv4G0rUX9CjSJMqXXqU"
    259         + "4JSnRaM+nWKMxtBXy6Y8gvZoqtevYL8SpEK2KJWiCsjSMIPNlCUC6lj5u7eLrN27ePO"
    260         + "SJcilb1EuaPvSQaZBnAUJGkT487DCTBwulOj4M8OCSxw6vvwxYzH5cV8uBK+IHiq69F"
    261         + "Bgoh0MPcHCAgnF7+zFIKArw4kAufxFw+AvBixiODo18IeqNEEryItaKdoGuRUEHgj4I"
    262         + "6Aqkr8CIeT422QF0wEX13f4/xvEi1y9A9ob6EF+PDnR5USbW8EQydElaf4aWC/giHcb"
    263         + "eGbYkcB12rWhhxkCSJBAAgXMwJ4PYkQYVVEXRIgDHp+IMYI/7Rzijw4c2ODPBf4kIw8"
    264         + "1H/IwohgZCJDKBxAcwkGEBI1h4xgTjniBjSMMQBQuF4DwYYgjpuMPBKX4c4qKO44wzl"
    265         + "AlDMOBjQRhYaWVW2Sp5RZXYkEIJLUosAUDWACwBQBYBMLlLfgwwMCZalr5pQZbZHMlQ"
    266         + "U7kqeeefOq5BgA19LnnGoEK6sQaa+xJUBGMNuroo5BGKumkjRKExKWYZqrpppx26imm"
    267         + "PsyAiBKklmrqqaimquqqSiAyw0MMxf8q66yv/lPID7jmquuuvPbq668/FPIPGyGcZOy"
    268         + "xyIbAxg9JNOvss9BGK+201DqL6xHYZqvtttx26+232eJqxLjklmvuueimqy65uELhLh"
    269         + "Q5vivvvPTWa6+8uEqhb45D6SuFKOes5oAUgrggiL8IJ6xwwrhG4bBZRRnisDtqaKNCN"
    270         + "wQEgMB1Mzjs8ccgh+xwww8TBRhREkfxxgCDXIJCA4NsHAw5atQRxS9v+KOGHVHU8YZ4"
    271         + "rdihs80e40rG0aSVdsVQDxzdzFB4gPLBMKP4E441LewRTwmVKCCLP95w408LnlwzziQ"
    272         + "G+KPP0WTgCsbbyhUFw9tgbFAMKf7s8YGQimz/cYM/cwtjAAT+KPL3BB0MIIABfz+zzd"
    273         + "u4liF53ETNIfkfvTBSjjL+aBKNP3cw8oc/c4SSSCxwgO4K6bMk8gI7cMBxBziS4/rF7"
    274         + "fz648bttLSwyheZ+KMMBf70wccxuvszzTqcFC+J7l8s8II5+URAAR+34xrG9mHkeMYZ"
    275         + "23+QA1ERnEF8H42g488ZsPgTgTP+qFDN+mGIP9QCtjSyPa5Z9N9/FwAMYBf8l4VFTKA"
    276         + "CD+iCDLJggi6YIAt5GCA+6CEDGTgwgv0z4De6MA//4eoJIAyhCEcYwhSkoAckFOEJUw"
    277         + "hCE4oQV0KIoQxnSMMa2vCGOJQhroLAwx768IdADKIQ/4fYwx8Awg9LSKISl8jEJjrxi"
    278         + "VBcgh8AMSxgWfGKWGTDP/6hhX148YtgDKMYx0jGMu5DC1zsR+7WyMY2FqUfXXSjHOc4"
    279         + "IS/S8Y5ztCMe97hGPfLxj1HxIyAHKchB/rGQhtwjIhN5x0UyMo/7eOQhIylJRVKyko2"
    280         + "8JCYhuclMdpKOjvxkjkIpykBqspS5IyUqh6LKVbYSla8sZSxFOctP1rKTt9xkLjG5y0"
    281         + "r2UpJnTMMqc5cGNHbRjGVU4xuRSUY0bvGZ0IymNLe4D2X6ox/7mKY2t8lNblbzmtnsp"
    282         + "jjHuc1IhpOc6EznP86pzmiigR/wjKc850nPetrznvxAwz+8UP+FfvjznwANqEAHStCC"
    283         + "9qMKXuCHQRfK0Ib6E54OjahE/wnRiVqUoRW9qEYHmtGNevShCv2oSDsqUo2StKQWPSl"
    284         + "KJarSlTq0pS7FaEhjOlGY0rSgNr0pR2eq04bmtKcA/SlQQTpUn/K0qAQV6lCVClSm9t"
    285         + "SpOoXqTaVKU6rG1KouxepKtYpSfvATqQVF6D7xic9+ArQKZLWnF9oZTX6Y9aD8YKtc2"
    286         + "9pPtM71rs90a1zxyte98vWZx2SmYL/oTHRqwZqPhGM6T/lIdoqTsYx0bDchm0jJehOX"
    287         + "i8UsOb+py80i1pfj5KxmvflZXoa2tMD0rC3RKVrQsna0oYXtY2U7WdplclMLwqxkMdU"
    288         + "Z2MEKtrDofGdahytPfabzqzEVKzqPutV0Mrerzo1qdKc63aou961XJadbnzrO7XK3m9"
    289         + "79Lnixa93ukjer15XuctWrXfZ2173ifG5J/TpO5LpUuehMKHH3u1ZxBgQAOw==);";
    290     assertSanitizedCss(null, longUrl);
    291   }
    292 
    293   private static void assertSanitizedCss(
    294       @Nullable String expectedCss, String css) {
    295     StylingPolicy stylingPolicy = new StylingPolicy(CssSchema.DEFAULT);
    296     assertEquals(expectedCss, stylingPolicy.sanitizeCssProperties(css));
    297   }
    298 }
    299