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 29package org.owasp.html; 30 31import javax.annotation.Nullable; 32 33import org.junit.Test; 34 35import junit.framework.TestCase; 36 37public 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