github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/html/template/content_test.go (about) 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 "bytes" 9 "fmt" 10 "strings" 11 "testing" 12 ) 13 14 func TestTypedContent(t *testing.T) { 15 data := []interface{}{ 16 `<b> "foo%" O'Reilly &bar;`, 17 CSS(`a[href =~ "//example.com"]#foo`), 18 HTML(`Hello, <b>World</b> &tc!`), 19 HTMLAttr(` dir="ltr"`), 20 JS(`c && alert("Hello, World!");`), 21 JSStr(`Hello, World & O'Reilly\x21`), 22 URL(`greeting=H%69&addressee=(World)`), 23 } 24 25 // For each content sensitive escaper, see how it does on 26 // each of the typed strings above. 27 tests := []struct { 28 // A template containing a single {{.}}. 29 input string 30 want []string 31 }{ 32 { 33 `<style>{{.}} { color: blue }</style>`, 34 []string{ 35 `ZgotmplZ`, 36 // Allowed but not escaped. 37 `a[href =~ "//example.com"]#foo`, 38 `ZgotmplZ`, 39 `ZgotmplZ`, 40 `ZgotmplZ`, 41 `ZgotmplZ`, 42 `ZgotmplZ`, 43 }, 44 }, 45 { 46 `<div style="{{.}}">`, 47 []string{ 48 `ZgotmplZ`, 49 // Allowed and HTML escaped. 50 `a[href =~ "//example.com"]#foo`, 51 `ZgotmplZ`, 52 `ZgotmplZ`, 53 `ZgotmplZ`, 54 `ZgotmplZ`, 55 `ZgotmplZ`, 56 }, 57 }, 58 { 59 `{{.}}`, 60 []string{ 61 `<b> "foo%" O'Reilly &bar;`, 62 `a[href =~ "//example.com"]#foo`, 63 // Not escaped. 64 `Hello, <b>World</b> &tc!`, 65 ` dir="ltr"`, 66 `c && alert("Hello, World!");`, 67 `Hello, World & O'Reilly\x21`, 68 `greeting=H%69&addressee=(World)`, 69 }, 70 }, 71 { 72 `<a{{.}}>`, 73 []string{ 74 `ZgotmplZ`, 75 `ZgotmplZ`, 76 `ZgotmplZ`, 77 // Allowed and HTML escaped. 78 ` dir="ltr"`, 79 `ZgotmplZ`, 80 `ZgotmplZ`, 81 `ZgotmplZ`, 82 }, 83 }, 84 { 85 `<a title={{.}}>`, 86 []string{ 87 `<b> "foo%" O'Reilly &bar;`, 88 `a[href =~ "//example.com"]#foo`, 89 // Tags stripped, spaces escaped, entity not re-escaped. 90 `Hello, World &tc!`, 91 ` dir="ltr"`, 92 `c && alert("Hello, World!");`, 93 `Hello, World & O'Reilly\x21`, 94 `greeting=H%69&addressee=(World)`, 95 }, 96 }, 97 { 98 `<a title='{{.}}'>`, 99 []string{ 100 `<b> "foo%" O'Reilly &bar;`, 101 `a[href =~ "//example.com"]#foo`, 102 // Tags stripped, entity not re-escaped. 103 `Hello, World &tc!`, 104 ` dir="ltr"`, 105 `c && alert("Hello, World!");`, 106 `Hello, World & O'Reilly\x21`, 107 `greeting=H%69&addressee=(World)`, 108 }, 109 }, 110 { 111 `<textarea>{{.}}</textarea>`, 112 []string{ 113 `<b> "foo%" O'Reilly &bar;`, 114 `a[href =~ "//example.com"]#foo`, 115 // Angle brackets escaped to prevent injection of close tags, entity not re-escaped. 116 `Hello, <b>World</b> &tc!`, 117 ` dir="ltr"`, 118 `c && alert("Hello, World!");`, 119 `Hello, World & O'Reilly\x21`, 120 `greeting=H%69&addressee=(World)`, 121 }, 122 }, 123 { 124 `<script>alert({{.}})</script>`, 125 []string{ 126 `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, 127 `"a[href =~ \"//example.com\"]#foo"`, 128 `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, 129 `" dir=\"ltr\""`, 130 // Not escaped. 131 `c && alert("Hello, World!");`, 132 // Escape sequence not over-escaped. 133 `"Hello, World & O'Reilly\x21"`, 134 `"greeting=H%69\u0026addressee=(World)"`, 135 }, 136 }, 137 { 138 `<button onclick="alert({{.}})">`, 139 []string{ 140 `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, 141 `"a[href =~ \"//example.com\"]#foo"`, 142 `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, 143 `" dir=\"ltr\""`, 144 // Not JS escaped but HTML escaped. 145 `c && alert("Hello, World!");`, 146 // Escape sequence not over-escaped. 147 `"Hello, World & O'Reilly\x21"`, 148 `"greeting=H%69\u0026addressee=(World)"`, 149 }, 150 }, 151 { 152 `<script>alert("{{.}}")</script>`, 153 []string{ 154 `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, 155 `a[href =~ \x22\/\/example.com\x22]#foo`, 156 `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, 157 ` dir=\x22ltr\x22`, 158 `c \x26\x26 alert(\x22Hello, World!\x22);`, 159 // Escape sequence not over-escaped. 160 `Hello, World \x26 O\x27Reilly\x21`, 161 `greeting=H%69\x26addressee=(World)`, 162 }, 163 }, 164 { 165 `<script type="text/javascript">alert("{{.}}")</script>`, 166 []string{ 167 `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, 168 `a[href =~ \x22\/\/example.com\x22]#foo`, 169 `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, 170 ` dir=\x22ltr\x22`, 171 `c \x26\x26 alert(\x22Hello, World!\x22);`, 172 // Escape sequence not over-escaped. 173 `Hello, World \x26 O\x27Reilly\x21`, 174 `greeting=H%69\x26addressee=(World)`, 175 }, 176 }, 177 { 178 `<script type="text/javascript">alert({{.}})</script>`, 179 []string{ 180 `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, 181 `"a[href =~ \"//example.com\"]#foo"`, 182 `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, 183 `" dir=\"ltr\""`, 184 // Not escaped. 185 `c && alert("Hello, World!");`, 186 // Escape sequence not over-escaped. 187 `"Hello, World & O'Reilly\x21"`, 188 `"greeting=H%69\u0026addressee=(World)"`, 189 }, 190 }, 191 { 192 // Not treated as JS. The output is same as for <div>{{.}}</div> 193 `<script type="text/template">{{.}}</script>`, 194 []string{ 195 `<b> "foo%" O'Reilly &bar;`, 196 `a[href =~ "//example.com"]#foo`, 197 // Not escaped. 198 `Hello, <b>World</b> &tc!`, 199 ` dir="ltr"`, 200 `c && alert("Hello, World!");`, 201 `Hello, World & O'Reilly\x21`, 202 `greeting=H%69&addressee=(World)`, 203 }, 204 }, 205 { 206 `<button onclick='alert("{{.}}")'>`, 207 []string{ 208 `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, 209 `a[href =~ \x22\/\/example.com\x22]#foo`, 210 `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, 211 ` dir=\x22ltr\x22`, 212 `c \x26\x26 alert(\x22Hello, World!\x22);`, 213 // Escape sequence not over-escaped. 214 `Hello, World \x26 O\x27Reilly\x21`, 215 `greeting=H%69\x26addressee=(World)`, 216 }, 217 }, 218 { 219 `<a href="?q={{.}}">`, 220 []string{ 221 `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, 222 `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, 223 `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, 224 `%20dir%3d%22ltr%22`, 225 `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, 226 `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, 227 // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done. 228 `greeting=H%69&addressee=%28World%29`, 229 }, 230 }, 231 { 232 `<style>body { background: url('?img={{.}}') }</style>`, 233 []string{ 234 `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, 235 `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, 236 `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, 237 `%20dir%3d%22ltr%22`, 238 `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, 239 `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, 240 // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done. 241 `greeting=H%69&addressee=%28World%29`, 242 }, 243 }, 244 } 245 246 for _, test := range tests { 247 tmpl := Must(New("x").Parse(test.input)) 248 pre := strings.Index(test.input, "{{.}}") 249 post := len(test.input) - (pre + 5) 250 var b bytes.Buffer 251 for i, x := range data { 252 b.Reset() 253 if err := tmpl.Execute(&b, x); err != nil { 254 t.Errorf("%q with %v: %s", test.input, x, err) 255 continue 256 } 257 if want, got := test.want[i], b.String()[pre:b.Len()-post]; want != got { 258 t.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test.input, x, want, got) 259 continue 260 } 261 } 262 } 263 } 264 265 // Test that we print using the String method. Was issue 3073. 266 type stringer struct { 267 v int 268 } 269 270 func (s *stringer) String() string { 271 return fmt.Sprintf("string=%d", s.v) 272 } 273 274 type errorer struct { 275 v int 276 } 277 278 func (s *errorer) Error() string { 279 return fmt.Sprintf("error=%d", s.v) 280 } 281 282 func TestStringer(t *testing.T) { 283 s := &stringer{3} 284 b := new(bytes.Buffer) 285 tmpl := Must(New("x").Parse("{{.}}")) 286 if err := tmpl.Execute(b, s); err != nil { 287 t.Fatal(err) 288 } 289 var expect = "string=3" 290 if b.String() != expect { 291 t.Errorf("expected %q got %q", expect, b.String()) 292 } 293 e := &errorer{7} 294 b.Reset() 295 if err := tmpl.Execute(b, e); err != nil { 296 t.Fatal(err) 297 } 298 expect = "error=7" 299 if b.String() != expect { 300 t.Errorf("expected %q got %q", expect, b.String()) 301 } 302 } 303 304 // https://golang.org/issue/5982 305 func TestEscapingNilNonemptyInterfaces(t *testing.T) { 306 tmpl := Must(New("x").Parse("{{.E}}")) 307 308 got := new(bytes.Buffer) 309 testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand 310 tmpl.Execute(got, testData) 311 312 // Use this data instead of just hard-coding "<nil>" to avoid 313 // dependencies on the html escaper and the behavior of fmt w.r.t. nil. 314 want := new(bytes.Buffer) 315 data := struct{ E string }{E: fmt.Sprint(nil)} 316 tmpl.Execute(want, data) 317 318 if !bytes.Equal(want.Bytes(), got.Bytes()) { 319 t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes())) 320 } 321 }