1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package template 6 7 import ( 8 "strconv" 9 "strings" 10 "testing" 11 ) 12 13 func TestEndsWithCSSKeyword(t *testing.T) { 14 tests := []struct { 15 css, kw string 16 want bool 17 }{ 18 {"", "url", false}, 19 {"url", "url", true}, 20 {"URL", "url", true}, 21 {"Url", "url", true}, 22 {"url", "important", false}, 23 {"important", "important", true}, 24 {"image-url", "url", false}, 25 {"imageurl", "url", false}, 26 {"image url", "url", true}, 27 } 28 for _, test := range tests { 29 got := endsWithCSSKeyword([]byte(test.css), test.kw) 30 if got != test.want { 31 t.Errorf("want %t but got %t for css=%v, kw=%v", test.want, got, test.css, test.kw) 32 } 33 } 34 } 35 36 func TestIsCSSNmchar(t *testing.T) { 37 tests := []struct { 38 rune rune 39 want bool 40 }{ 41 {0, false}, 42 {'0', true}, 43 {'9', true}, 44 {'A', true}, 45 {'Z', true}, 46 {'a', true}, 47 {'z', true}, 48 {'_', true}, 49 {'-', true}, 50 {':', false}, 51 {';', false}, 52 {' ', false}, 53 {0x7f, false}, 54 {0x80, true}, 55 {0x1234, true}, 56 {0xd800, false}, 57 {0xdc00, false}, 58 {0xfffe, false}, 59 {0x10000, true}, 60 {0x110000, false}, 61 } 62 for _, test := range tests { 63 got := isCSSNmchar(test.rune) 64 if got != test.want { 65 t.Errorf("%q: want %t but got %t", string(test.rune), test.want, got) 66 } 67 } 68 } 69 70 func TestDecodeCSS(t *testing.T) { 71 tests := []struct { 72 css, want string 73 }{ 74 {``, ``}, 75 {`foo`, `foo`}, 76 {`foo\`, `foo`}, 77 {`foo\\`, `foo\`}, 78 {`\`, ``}, 79 {`\A`, "\n"}, 80 {`\a`, "\n"}, 81 {`\0a`, "\n"}, 82 {`\00000a`, "\n"}, 83 {`\000000a`, "\u0000a"}, 84 {`\1234 5`, "\u1234" + "5"}, 85 {`\1234\20 5`, "\u1234" + " 5"}, 86 {`\1234\A 5`, "\u1234" + "\n5"}, 87 {"\\1234\t5", "\u1234" + "5"}, 88 {"\\1234\n5", "\u1234" + "5"}, 89 {"\\1234\r\n5", "\u1234" + "5"}, 90 {`\12345`, "\U00012345"}, 91 {`\\`, `\`}, 92 {`\\ `, `\ `}, 93 {`\"`, `"`}, 94 {`\'`, `'`}, 95 {`\.`, `.`}, 96 {`\. .`, `. .`}, 97 { 98 `The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3e dog\3c/canine\3e`, 99 "The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>", 100 }, 101 } 102 for _, test := range tests { 103 got1 := string(decodeCSS([]byte(test.css))) 104 if got1 != test.want { 105 t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.css, test.want, got1) 106 } 107 recoded := cssEscaper(got1) 108 if got2 := string(decodeCSS([]byte(recoded))); got2 != test.want { 109 t.Errorf("%q: escape & decode not dual for %q", test.css, recoded) 110 } 111 } 112 } 113 114 func TestHexDecode(t *testing.T) { 115 for i := 0; i < 0x200000; i += 101 /* coprime with 16 */ { 116 s := strconv.FormatInt(int64(i), 16) 117 if got := int(hexDecode([]byte(s))); got != i { 118 t.Errorf("%s: want %d but got %d", s, i, got) 119 } 120 s = strings.ToUpper(s) 121 if got := int(hexDecode([]byte(s))); got != i { 122 t.Errorf("%s: want %d but got %d", s, i, got) 123 } 124 } 125 } 126 127 func TestSkipCSSSpace(t *testing.T) { 128 tests := []struct { 129 css, want string 130 }{ 131 {"", ""}, 132 {"foo", "foo"}, 133 {"\n", ""}, 134 {"\r\n", ""}, 135 {"\r", ""}, 136 {"\t", ""}, 137 {" ", ""}, 138 {"\f", ""}, 139 {" foo", "foo"}, 140 {" foo", " foo"}, 141 {`\20`, `\20`}, 142 } 143 for _, test := range tests { 144 got := string(skipCSSSpace([]byte(test.css))) 145 if got != test.want { 146 t.Errorf("%q: want %q but got %q", test.css, test.want, got) 147 } 148 } 149 } 150 151 func TestCSSEscaper(t *testing.T) { 152 input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" + 153 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + 154 ` !"#$%&'()*+,-./` + 155 `0123456789:;<=>?` + 156 `@ABCDEFGHIJKLMNO` + 157 `PQRSTUVWXYZ[\]^_` + 158 "`abcdefghijklmno" + 159 "pqrstuvwxyz{|}~\x7f" + 160 "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") 161 162 want := ("\\0\x01\x02\x03\x04\x05\x06\x07" + 163 "\x08\\9 \\a\x0b\\c \\d\x0E\x0F" + 164 "\x10\x11\x12\x13\x14\x15\x16\x17" + 165 "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + 166 ` !\22#$%\26\27\28\29*\2b,-.\2f ` + 167 `0123456789\3a\3b\3c=\3e?` + 168 `@ABCDEFGHIJKLMNO` + 169 `PQRSTUVWXYZ[\\]^_` + 170 "`abcdefghijklmno" + 171 `pqrstuvwxyz\7b|\7d~` + "\u007f" + 172 "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E") 173 174 got := cssEscaper(input) 175 if got != want { 176 t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got) 177 } 178 179 got = string(decodeCSS([]byte(got))) 180 if input != got { 181 t.Errorf("decode: want\n\t%q\nbut got\n\t%q", input, got) 182 } 183 } 184 185 func TestCSSValueFilter(t *testing.T) { 186 tests := []struct { 187 css, want string 188 }{ 189 {"", ""}, 190 {"foo", "foo"}, 191 {"0", "0"}, 192 {"0px", "0px"}, 193 {"-5px", "-5px"}, 194 {"1.25in", "1.25in"}, 195 {"+.33em", "+.33em"}, 196 {"100%", "100%"}, 197 {"12.5%", "12.5%"}, 198 {".foo", ".foo"}, 199 {"#bar", "#bar"}, 200 {"corner-radius", "corner-radius"}, 201 {"-moz-corner-radius", "-moz-corner-radius"}, 202 {"#000", "#000"}, 203 {"#48f", "#48f"}, 204 {"#123456", "#123456"}, 205 {"U+00-FF, U+980-9FF", "U+00-FF, U+980-9FF"}, 206 {"color: red", "color: red"}, 207 {"<!--", "ZgotmplZ"}, 208 {"-->", "ZgotmplZ"}, 209 {"<![CDATA[", "ZgotmplZ"}, 210 {"]]>", "ZgotmplZ"}, 211 {"</style", "ZgotmplZ"}, 212 {`"`, "ZgotmplZ"}, 213 {`'`, "ZgotmplZ"}, 214 {"`", "ZgotmplZ"}, 215 {"\x00", "ZgotmplZ"}, 216 {"/* foo */", "ZgotmplZ"}, 217 {"//", "ZgotmplZ"}, 218 {"[href=~", "ZgotmplZ"}, 219 {"expression(alert(1337))", "ZgotmplZ"}, 220 {"-expression(alert(1337))", "ZgotmplZ"}, 221 {"expression", "ZgotmplZ"}, 222 {"Expression", "ZgotmplZ"}, 223 {"EXPRESSION", "ZgotmplZ"}, 224 {"-moz-binding", "ZgotmplZ"}, 225 {"-expr\x00ession(alert(1337))", "ZgotmplZ"}, 226 {`-expr\0ession(alert(1337))`, "ZgotmplZ"}, 227 {`-express\69on(alert(1337))`, "ZgotmplZ"}, 228 {`-express\69 on(alert(1337))`, "ZgotmplZ"}, 229 {`-exp\72 ession(alert(1337))`, "ZgotmplZ"}, 230 {`-exp\52 ession(alert(1337))`, "ZgotmplZ"}, 231 {`-exp\000052 ession(alert(1337))`, "ZgotmplZ"}, 232 {`-expre\0000073sion`, "-expre\x073sion"}, 233 {`@import url evil.css`, "ZgotmplZ"}, 234 } 235 for _, test := range tests { 236 got := cssValueFilter(test.css) 237 if got != test.want { 238 t.Errorf("%q: want %q but got %q", test.css, test.want, got) 239 } 240 } 241 } 242 243 func BenchmarkCSSEscaper(b *testing.B) { 244 for i := 0; i < b.N; i++ { 245 cssEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>") 246 } 247 } 248 249 func BenchmarkCSSEscaperNoSpecials(b *testing.B) { 250 for i := 0; i < b.N; i++ { 251 cssEscaper("The quick, brown fox jumps over the lazy dog.") 252 } 253 } 254 255 func BenchmarkDecodeCSS(b *testing.B) { 256 s := []byte(`The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3edog\3c/canine\3e`) 257 b.ResetTimer() 258 for i := 0; i < b.N; i++ { 259 decodeCSS(s) 260 } 261 } 262 263 func BenchmarkDecodeCSSNoSpecials(b *testing.B) { 264 s := []byte("The quick, brown fox jumps over the lazy dog.") 265 b.ResetTimer() 266 for i := 0; i < b.N; i++ { 267 decodeCSS(s) 268 } 269 } 270 271 func BenchmarkCSSValueFilter(b *testing.B) { 272 for i := 0; i < b.N; i++ { 273 cssValueFilter(` e\78preS\0Sio/**/n(alert(1337))`) 274 } 275 } 276 277 func BenchmarkCSSValueFilterOk(b *testing.B) { 278 for i := 0; i < b.N; i++ { 279 cssValueFilter(`Times New Roman`) 280 } 281 } 282