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