mirror of
https://github.com/gohugoio/hugo.git
synced 2025-09-02 22:52:51 +02:00
Create lightweight forks of text/template and html/template
This commit also removes support for Ace and Amber templates. Updates #6594
This commit is contained in:
461
tpl/internal/go_templates/htmltemplate/content_test.go
Normal file
461
tpl/internal/go_templates/htmltemplate/content_test.go
Normal file
@@ -0,0 +1,461 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13,!windows
|
||||
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
htmltemplate "html/template"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTypedContent(t *testing.T) {
|
||||
data := []interface{}{
|
||||
`<b> "foo%" O'Reilly &bar;`,
|
||||
htmltemplate.CSS(`a[href =~ "//example.com"]#foo`),
|
||||
htmltemplate.HTML(`Hello, <b>World</b> &tc!`),
|
||||
htmltemplate.HTMLAttr(` dir="ltr"`),
|
||||
htmltemplate.JS(`c && alert("Hello, World!");`),
|
||||
htmltemplate.JSStr(`Hello, World & O'Reilly\x21`),
|
||||
htmltemplate.URL(`greeting=H%69,&addressee=(World)`),
|
||||
htmltemplate.Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`),
|
||||
htmltemplate.URL(`,foo/,`),
|
||||
}
|
||||
|
||||
// For each content sensitive escaper, see how it does on
|
||||
// each of the typed strings above.
|
||||
tests := []struct {
|
||||
// A template containing a single {{.}}.
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
`<style>{{.}} { color: blue }</style>`,
|
||||
[]string{
|
||||
`ZgotmplZ`,
|
||||
// Allowed but not escaped.
|
||||
`a[href =~ "//example.com"]#foo`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div style="{{.}}">`,
|
||||
[]string{
|
||||
`ZgotmplZ`,
|
||||
// Allowed and HTML escaped.
|
||||
`a[href =~ "//example.com"]#foo`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`{{.}}`,
|
||||
[]string{
|
||||
`<b> "foo%" O'Reilly &bar;`,
|
||||
`a[href =~ "//example.com"]#foo`,
|
||||
// Not escaped.
|
||||
`Hello, <b>World</b> &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<a{{.}}>`,
|
||||
[]string{
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
// Allowed and HTML escaped.
|
||||
` dir="ltr"`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
`ZgotmplZ`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<a title={{.}}>`,
|
||||
[]string{
|
||||
`<b> "foo%" O'Reilly &bar;`,
|
||||
`a[href =~ "//example.com"]#foo`,
|
||||
// Tags stripped, spaces escaped, entity not re-escaped.
|
||||
`Hello, World &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<a title='{{.}}'>`,
|
||||
[]string{
|
||||
`<b> "foo%" O'Reilly &bar;`,
|
||||
`a[href =~ "//example.com"]#foo`,
|
||||
// Tags stripped, entity not re-escaped.
|
||||
`Hello, World &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<textarea>{{.}}</textarea>`,
|
||||
[]string{
|
||||
`<b> "foo%" O'Reilly &bar;`,
|
||||
`a[href =~ "//example.com"]#foo`,
|
||||
// Angle brackets escaped to prevent injection of close tags, entity not re-escaped.
|
||||
`Hello, <b>World</b> &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<script>alert({{.}})</script>`,
|
||||
[]string{
|
||||
`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
|
||||
`"a[href =~ \"//example.com\"]#foo"`,
|
||||
`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
|
||||
`" dir=\"ltr\""`,
|
||||
// Not escaped.
|
||||
`c && alert("Hello, World!");`,
|
||||
// Escape sequence not over-escaped.
|
||||
`"Hello, World & O'Reilly\x21"`,
|
||||
`"greeting=H%69,\u0026addressee=(World)"`,
|
||||
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
|
||||
`",foo/,"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<button onclick="alert({{.}})">`,
|
||||
[]string{
|
||||
`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
|
||||
`"a[href =~ \"//example.com\"]#foo"`,
|
||||
`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
|
||||
`" dir=\"ltr\""`,
|
||||
// Not JS escaped but HTML escaped.
|
||||
`c && alert("Hello, World!");`,
|
||||
// Escape sequence not over-escaped.
|
||||
`"Hello, World & O'Reilly\x21"`,
|
||||
`"greeting=H%69,\u0026addressee=(World)"`,
|
||||
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
|
||||
`",foo/,"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<script>alert("{{.}}")</script>`,
|
||||
[]string{
|
||||
`\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
|
||||
`a[href =~ \x22\/\/example.com\x22]#foo`,
|
||||
`Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
|
||||
` dir=\x22ltr\x22`,
|
||||
`c \x26\x26 alert(\x22Hello, World!\x22);`,
|
||||
// Escape sequence not over-escaped.
|
||||
`Hello, World \x26 O\x27Reilly\x21`,
|
||||
`greeting=H%69,\x26addressee=(World)`,
|
||||
`greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
`,foo\/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<script type="text/javascript">alert("{{.}}")</script>`,
|
||||
[]string{
|
||||
`\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
|
||||
`a[href =~ \x22\/\/example.com\x22]#foo`,
|
||||
`Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
|
||||
` dir=\x22ltr\x22`,
|
||||
`c \x26\x26 alert(\x22Hello, World!\x22);`,
|
||||
// Escape sequence not over-escaped.
|
||||
`Hello, World \x26 O\x27Reilly\x21`,
|
||||
`greeting=H%69,\x26addressee=(World)`,
|
||||
`greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
`,foo\/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<script type="text/javascript">alert({{.}})</script>`,
|
||||
[]string{
|
||||
`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
|
||||
`"a[href =~ \"//example.com\"]#foo"`,
|
||||
`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
|
||||
`" dir=\"ltr\""`,
|
||||
// Not escaped.
|
||||
`c && alert("Hello, World!");`,
|
||||
// Escape sequence not over-escaped.
|
||||
`"Hello, World & O'Reilly\x21"`,
|
||||
`"greeting=H%69,\u0026addressee=(World)"`,
|
||||
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
|
||||
`",foo/,"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Not treated as JS. The output is same as for <div>{{.}}</div>
|
||||
`<script type="text/template">{{.}}</script>`,
|
||||
[]string{
|
||||
`<b> "foo%" O'Reilly &bar;`,
|
||||
`a[href =~ "//example.com"]#foo`,
|
||||
// Not escaped.
|
||||
`Hello, <b>World</b> &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
`Hello, World & O'Reilly\x21`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<button onclick='alert("{{.}}")'>`,
|
||||
[]string{
|
||||
`\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
|
||||
`a[href =~ \x22\/\/example.com\x22]#foo`,
|
||||
`Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
|
||||
` dir=\x22ltr\x22`,
|
||||
`c \x26\x26 alert(\x22Hello, World!\x22);`,
|
||||
// Escape sequence not over-escaped.
|
||||
`Hello, World \x26 O\x27Reilly\x21`,
|
||||
`greeting=H%69,\x26addressee=(World)`,
|
||||
`greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
`,foo\/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<a href="?q={{.}}">`,
|
||||
[]string{
|
||||
`%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
|
||||
`a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
|
||||
`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
|
||||
`%20dir%3d%22ltr%22`,
|
||||
`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
|
||||
`Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
|
||||
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
|
||||
`greeting=H%69,&addressee=%28World%29`,
|
||||
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<style>body { background: url('?img={{.}}') }</style>`,
|
||||
[]string{
|
||||
`%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
|
||||
`a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
|
||||
`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
|
||||
`%20dir%3d%22ltr%22`,
|
||||
`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
|
||||
`Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
|
||||
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done.
|
||||
`greeting=H%69,&addressee=%28World%29`,
|
||||
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
|
||||
`,foo/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<img srcset="{{.}}">`,
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
// Commas are not esacped
|
||||
`Hello,#ZgotmplZ`,
|
||||
// Leading spaces are not percent escapes.
|
||||
` dir=%22ltr%22`,
|
||||
// Spaces after commas are not percent escaped.
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
// Metadata is not escaped.
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<img srcset={{.}}>`,
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
// Spaces are HTML escaped not %-escaped
|
||||
` dir=%22ltr%22`,
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
// Commas are escaped.
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<img srcset="{{.}} 2x, https://golang.org/ 500.5w">`,
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
` dir=%22ltr%22`,
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<img srcset="http://godoc.org/ {{.}}, https://golang.org/ 500.5w">`,
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
` dir=%22ltr%22`,
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<img srcset="http://godoc.org/?q={{.}} 2x, https://golang.org/ 500.5w">`,
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
` dir=%22ltr%22`,
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<img srcset="http://godoc.org/ 2x, {{.}} 500.5w">`,
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
` dir=%22ltr%22`,
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<img srcset="http://godoc.org/ 2x, https://golang.org/ {{.}}">`,
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
` dir=%22ltr%22`,
|
||||
`#ZgotmplZ, World!%22%29;`,
|
||||
`Hello,#ZgotmplZ`,
|
||||
`greeting=H%69%2c&addressee=%28World%29`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`%2cfoo/%2c`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tmpl := Must(New("x").Parse(test.input))
|
||||
pre := strings.Index(test.input, "{{.}}")
|
||||
post := len(test.input) - (pre + 5)
|
||||
var b bytes.Buffer
|
||||
for i, x := range data {
|
||||
b.Reset()
|
||||
if err := tmpl.Execute(&b, x); err != nil {
|
||||
t.Errorf("%q with %v: %s", test.input, x, err)
|
||||
continue
|
||||
}
|
||||
if want, got := test.want[i], b.String()[pre:b.Len()-post]; want != got {
|
||||
t.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test.input, x, want, got)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we print using the String method. Was issue 3073.
|
||||
type stringer struct {
|
||||
v int
|
||||
}
|
||||
|
||||
func (s *stringer) String() string {
|
||||
return fmt.Sprintf("string=%d", s.v)
|
||||
}
|
||||
|
||||
type errorer struct {
|
||||
v int
|
||||
}
|
||||
|
||||
func (s *errorer) Error() string {
|
||||
return fmt.Sprintf("error=%d", s.v)
|
||||
}
|
||||
|
||||
func TestStringer(t *testing.T) {
|
||||
s := &stringer{3}
|
||||
b := new(bytes.Buffer)
|
||||
tmpl := Must(New("x").Parse("{{.}}"))
|
||||
if err := tmpl.Execute(b, s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var expect = "string=3"
|
||||
if b.String() != expect {
|
||||
t.Errorf("expected %q got %q", expect, b.String())
|
||||
}
|
||||
e := &errorer{7}
|
||||
b.Reset()
|
||||
if err := tmpl.Execute(b, e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expect = "error=7"
|
||||
if b.String() != expect {
|
||||
t.Errorf("expected %q got %q", expect, b.String())
|
||||
}
|
||||
}
|
||||
|
||||
// https://golang.org/issue/5982
|
||||
func TestEscapingNilNonemptyInterfaces(t *testing.T) {
|
||||
tmpl := Must(New("x").Parse("{{.E}}"))
|
||||
|
||||
got := new(bytes.Buffer)
|
||||
testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand
|
||||
tmpl.Execute(got, testData)
|
||||
|
||||
// A non-empty interface should print like an empty interface.
|
||||
want := new(bytes.Buffer)
|
||||
data := struct{ E interface{} }{}
|
||||
tmpl.Execute(want, data)
|
||||
|
||||
if !bytes.Equal(want.Bytes(), got.Bytes()) {
|
||||
t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes()))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user