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.
14 func TestTypedContent(t
*testing
.T
) {
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\u0021`),
22 URL(`greeting=H%69,&addressee=(World)`),
23 Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`),
27 // For each content sensitive escaper, see how it does on
28 // each of the typed strings above.
30 // A template containing a single {{.}}.
35 `<style>{{.}} { color: blue }</style>`,
38 // Allowed but not escaped.
39 `a[href =~ "//example.com"]#foo`,
50 `<div style="{{.}}">`,
53 // Allowed and HTML escaped.
54 `a[href =~ "//example.com"]#foo`,
67 `<b> "foo%" O'Reilly &bar;`,
68 `a[href =~ "//example.com"]#foo`,
70 `Hello, <b>World</b> &tc!`,
72 `c && alert("Hello, World!");`,
73 `Hello, World & O'Reilly\u0021`,
74 `greeting=H%69,&addressee=(World)`,
75 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
85 // Allowed and HTML escaped.
97 `<b> "foo%" O'Reilly &bar;`,
98 `a[href =~ "//example.com"]#foo`,
99 // Tags stripped, spaces escaped, entity not re-escaped.
100 `Hello, World &tc!`,
101 ` dir="ltr"`,
102 `c && alert("Hello, World!");`,
103 `Hello, World & O'Reilly\u0021`,
104 `greeting=H%69,&addressee=(World)`,
105 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
112 `<b> "foo%" O'Reilly &bar;`,
113 `a[href =~ "//example.com"]#foo`,
114 // Tags stripped, entity not re-escaped.
115 `Hello, World &tc!`,
116 ` dir="ltr"`,
117 `c && alert("Hello, World!");`,
118 `Hello, World & O'Reilly\u0021`,
119 `greeting=H%69,&addressee=(World)`,
120 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
125 `<textarea>{{.}}</textarea>`,
127 `<b> "foo%" O'Reilly &bar;`,
128 `a[href =~ "//example.com"]#foo`,
129 // Angle brackets escaped to prevent injection of close tags, entity not re-escaped.
130 `Hello, <b>World</b> &tc!`,
131 ` dir="ltr"`,
132 `c && alert("Hello, World!");`,
133 `Hello, World & O'Reilly\u0021`,
134 `greeting=H%69,&addressee=(World)`,
135 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
140 `<script>alert({{.}})</script>`,
142 `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
143 `"a[href =~ \"//example.com\"]#foo"`,
144 `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
147 `c && alert("Hello, World!");`,
148 // Escape sequence not over-escaped.
149 `"Hello, World & O'Reilly\u0021"`,
150 `"greeting=H%69,\u0026addressee=(World)"`,
151 `"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
156 `<button onclick="alert({{.}})">`,
158 `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
159 `"a[href =~ \"//example.com\"]#foo"`,
160 `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
161 `" dir=\"ltr\""`,
162 // Not JS escaped but HTML escaped.
163 `c && alert("Hello, World!");`,
164 // Escape sequence not over-escaped.
165 `"Hello, World & O'Reilly\u0021"`,
166 `"greeting=H%69,\u0026addressee=(World)"`,
167 `"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
172 `<script>alert("{{.}}")</script>`,
174 `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
175 `a[href =~ \u0022\/\/example.com\u0022]#foo`,
176 `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
177 ` dir=\u0022ltr\u0022`,
178 `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
179 // Escape sequence not over-escaped.
180 `Hello, World \u0026 O\u0027Reilly\u0021`,
181 `greeting=H%69,\u0026addressee=(World)`,
182 `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
187 `<script type="text/javascript">alert("{{.}}")</script>`,
189 `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
190 `a[href =~ \u0022\/\/example.com\u0022]#foo`,
191 `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
192 ` dir=\u0022ltr\u0022`,
193 `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
194 // Escape sequence not over-escaped.
195 `Hello, World \u0026 O\u0027Reilly\u0021`,
196 `greeting=H%69,\u0026addressee=(World)`,
197 `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
202 `<script type="text/javascript">alert({{.}})</script>`,
204 `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
205 `"a[href =~ \"//example.com\"]#foo"`,
206 `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
209 `c && alert("Hello, World!");`,
210 // Escape sequence not over-escaped.
211 `"Hello, World & O'Reilly\u0021"`,
212 `"greeting=H%69,\u0026addressee=(World)"`,
213 `"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
218 // Not treated as JS. The output is same as for <div>{{.}}</div>
219 `<script type="text/template">{{.}}</script>`,
221 `<b> "foo%" O'Reilly &bar;`,
222 `a[href =~ "//example.com"]#foo`,
224 `Hello, <b>World</b> &tc!`,
225 ` dir="ltr"`,
226 `c && alert("Hello, World!");`,
227 `Hello, World & O'Reilly\u0021`,
228 `greeting=H%69,&addressee=(World)`,
229 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
234 `<button onclick='alert("{{.}}")'>`,
236 `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
237 `a[href =~ \u0022\/\/example.com\u0022]#foo`,
238 `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
239 ` dir=\u0022ltr\u0022`,
240 `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
241 // Escape sequence not over-escaped.
242 `Hello, World \u0026 O\u0027Reilly\u0021`,
243 `greeting=H%69,\u0026addressee=(World)`,
244 `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
249 `<a href="?q={{.}}">`,
251 `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
252 `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
253 `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
254 `%20dir%3d%22ltr%22`,
255 `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
256 `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
257 // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
258 `greeting=H%69,&addressee=%28World%29`,
259 `greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
264 `<style>body { background: url('?img={{.}}') }</style>`,
266 `%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
267 `a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
268 `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
269 `%20dir%3d%22ltr%22`,
270 `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
271 `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
272 // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done.
273 `greeting=H%69,&addressee=%28World%29`,
274 `greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
279 `<img srcset="{{.}}">`,
283 // Commas are not esacped
285 // Leading spaces are not percent escapes.
287 // Spaces after commas are not percent escaped.
288 `#ZgotmplZ, World!%22%29;`,
290 `greeting=H%69%2c&addressee=%28World%29`,
291 // Metadata is not escaped.
292 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
297 `<img srcset={{.}}>`,
302 // Spaces are HTML escaped not %-escaped
303 ` dir=%22ltr%22`,
304 `#ZgotmplZ, World!%22%29;`,
306 `greeting=H%69%2c&addressee=%28World%29`,
307 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
308 // Commas are escaped.
313 `<img srcset="{{.}} 2x, https://golang.org/ 500.5w">`,
319 `#ZgotmplZ, World!%22%29;`,
321 `greeting=H%69%2c&addressee=%28World%29`,
322 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
327 `<img srcset="http://godoc.org/ {{.}}, https://golang.org/ 500.5w">`,
333 `#ZgotmplZ, World!%22%29;`,
335 `greeting=H%69%2c&addressee=%28World%29`,
336 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
341 `<img srcset="http://godoc.org/?q={{.}} 2x, https://golang.org/ 500.5w">`,
347 `#ZgotmplZ, World!%22%29;`,
349 `greeting=H%69%2c&addressee=%28World%29`,
350 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
355 `<img srcset="http://godoc.org/ 2x, {{.}} 500.5w">`,
361 `#ZgotmplZ, World!%22%29;`,
363 `greeting=H%69%2c&addressee=%28World%29`,
364 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
369 `<img srcset="http://godoc.org/ 2x, https://golang.org/ {{.}}">`,
375 `#ZgotmplZ, World!%22%29;`,
377 `greeting=H%69%2c&addressee=%28World%29`,
378 `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
384 for _
, test
:= range tests
{
385 tmpl
:= Must(New("x").Parse(test
.input
))
386 pre
:= strings
.Index(test
.input
, "{{.}}")
387 post
:= len(test
.input
) - (pre
+ 5)
389 for i
, x
:= range data
{
391 if err
:= tmpl
.Execute(&b
, x
); err
!= nil {
392 t
.Errorf("%q with %v: %s", test
.input
, x
, err
)
395 if want
, got
:= test
.want
[i
], b
.String()[pre
:b
.Len()-post
]; want
!= got
{
396 t
.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test
.input
, x
, want
, got
)
403 // Test that we print using the String method. Was issue 3073.
404 type myStringer
struct {
408 func (s
*myStringer
) String() string {
409 return fmt
.Sprintf("string=%d", s
.v
)
412 type errorer
struct {
416 func (s
*errorer
) Error() string {
417 return fmt
.Sprintf("error=%d", s
.v
)
420 func TestStringer(t
*testing
.T
) {
422 b
:= new(bytes
.Buffer
)
423 tmpl
:= Must(New("x").Parse("{{.}}"))
424 if err
:= tmpl
.Execute(b
, s
); err
!= nil {
427 var expect
= "string=3"
428 if b
.String() != expect
{
429 t
.Errorf("expected %q got %q", expect
, b
.String())
433 if err
:= tmpl
.Execute(b
, e
); err
!= nil {
437 if b
.String() != expect
{
438 t
.Errorf("expected %q got %q", expect
, b
.String())
442 // https://golang.org/issue/5982
443 func TestEscapingNilNonemptyInterfaces(t
*testing
.T
) {
444 tmpl
:= Must(New("x").Parse("{{.E}}"))
446 got
:= new(bytes
.Buffer
)
447 testData
:= struct{ E error
}{} // any non-empty interface here will do; error is just ready at hand
448 tmpl
.Execute(got
, testData
)
450 // A non-empty interface should print like an empty interface.
451 want
:= new(bytes
.Buffer
)
452 data
:= struct{ E any
}{}
453 tmpl
.Execute(want
, data
)
455 if !bytes
.Equal(want
.Bytes(), got
.Bytes()) {
456 t
.Errorf("expected %q got %q", string(want
.Bytes()), string(got
.Bytes()))