summaryrefslogtreecommitdiffstats
path: root/tpl
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2020-05-23 15:32:27 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2020-05-23 22:00:34 +0200
commit6c3c6686f5d3c7155e2d455b07ac8ab70f42cb88 (patch)
tree666c09383480c74ba69ea7a0442a67029b83a095 /tpl
parentc34bf48560c91c8a2fa106867af7b08a569609b5 (diff)
Fix Go template script escaping
Fixes #6695
Diffstat (limited to 'tpl')
-rw-r--r--tpl/internal/go_templates/cfg/cfg.go64
-rw-r--r--tpl/internal/go_templates/fmtsort/sort.go14
-rw-r--r--tpl/internal/go_templates/fmtsort/sort_test.go2
-rw-r--r--tpl/internal/go_templates/htmltemplate/content_test.go70
-rw-r--r--tpl/internal/go_templates/htmltemplate/doc.go45
-rw-r--r--tpl/internal/go_templates/htmltemplate/escape_test.go6
-rw-r--r--tpl/internal/go_templates/htmltemplate/example_test.go6
-rw-r--r--tpl/internal/go_templates/htmltemplate/js.go72
-rw-r--r--tpl/internal/go_templates/htmltemplate/js_test.go68
-rw-r--r--tpl/internal/go_templates/htmltemplate/template_test.go39
-rw-r--r--tpl/internal/go_templates/testenv/testenv.go272
-rw-r--r--tpl/internal/go_templates/testenv/testenv_cgo.go11
-rw-r--r--tpl/internal/go_templates/testenv/testenv_notwin.go20
-rw-r--r--tpl/internal/go_templates/testenv/testenv_windows.go48
-rw-r--r--tpl/internal/go_templates/texttemplate/doc.go18
-rw-r--r--tpl/internal/go_templates/texttemplate/exec.go27
-rw-r--r--tpl/internal/go_templates/texttemplate/exec_test.go93
-rw-r--r--tpl/internal/go_templates/texttemplate/funcs.go219
-rw-r--r--tpl/internal/go_templates/texttemplate/hugo_template.go2
-rw-r--r--tpl/internal/go_templates/texttemplate/multi_test.go2
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/lex.go1
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/node.go170
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/parse.go9
-rw-r--r--tpl/internal/go_templates/texttemplate/parse/parse_test.go52
-rw-r--r--tpl/internal/go_templates/texttemplate/template.go23
25 files changed, 1060 insertions, 293 deletions
diff --git a/tpl/internal/go_templates/cfg/cfg.go b/tpl/internal/go_templates/cfg/cfg.go
new file mode 100644
index 000000000..bdbe9df3e
--- /dev/null
+++ b/tpl/internal/go_templates/cfg/cfg.go
@@ -0,0 +1,64 @@
+// Copyright 2019 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.
+
+// Package cfg holds configuration shared by the Go command and internal/testenv.
+// Definitions that don't need to be exposed outside of cmd/go should be in
+// cmd/go/internal/cfg instead of this package.
+package cfg
+
+// KnownEnv is a list of environment variables that affect the operation
+// of the Go command.
+const KnownEnv = `
+ AR
+ CC
+ CGO_CFLAGS
+ CGO_CFLAGS_ALLOW
+ CGO_CFLAGS_DISALLOW
+ CGO_CPPFLAGS
+ CGO_CPPFLAGS_ALLOW
+ CGO_CPPFLAGS_DISALLOW
+ CGO_CXXFLAGS
+ CGO_CXXFLAGS_ALLOW
+ CGO_CXXFLAGS_DISALLOW
+ CGO_ENABLED
+ CGO_FFLAGS
+ CGO_FFLAGS_ALLOW
+ CGO_FFLAGS_DISALLOW
+ CGO_LDFLAGS
+ CGO_LDFLAGS_ALLOW
+ CGO_LDFLAGS_DISALLOW
+ CXX
+ FC
+ GCCGO
+ GO111MODULE
+ GO386
+ GOARCH
+ GOARM
+ GOBIN
+ GOCACHE
+ GOENV
+ GOEXE
+ GOFLAGS
+ GOGCCFLAGS
+ GOHOSTARCH
+ GOHOSTOS
+ GOINSECURE
+ GOMIPS
+ GOMIPS64
+ GOMODCACHE
+ GONOPROXY
+ GONOSUMDB
+ GOOS
+ GOPATH
+ GOPPC64
+ GOPRIVATE
+ GOPROXY
+ GOROOT
+ GOSUMDB
+ GOTMPDIR
+ GOTOOLDIR
+ GOWASM
+ GO_EXTLINK_ENABLED
+ PKG_CONFIG
+`
diff --git a/tpl/internal/go_templates/fmtsort/sort.go b/tpl/internal/go_templates/fmtsort/sort.go
index 70a305a3a..b01229bd0 100644
--- a/tpl/internal/go_templates/fmtsort/sort.go
+++ b/tpl/internal/go_templates/fmtsort/sort.go
@@ -53,12 +53,16 @@ func Sort(mapValue reflect.Value) *SortedMap {
if mapValue.Type().Kind() != reflect.Map {
return nil
}
- key := make([]reflect.Value, mapValue.Len())
- value := make([]reflect.Value, len(key))
+ // Note: this code is arranged to not panic even in the presence
+ // of a concurrent map update. The runtime is responsible for
+ // yelling loudly if that happens. See issue 33275.
+ n := mapValue.Len()
+ key := make([]reflect.Value, 0, n)
+ value := make([]reflect.Value, 0, n)
iter := mapValue.MapRange()
- for i := 0; iter.Next(); i++ {
- key[i] = iter.Key()
- value[i] = iter.Value()
+ for iter.Next() {
+ key = append(key, iter.Key())
+ value = append(value, iter.Value())
}
sorted := &SortedMap{
Key: key,
diff --git a/tpl/internal/go_templates/fmtsort/sort_test.go b/tpl/internal/go_templates/fmtsort/sort_test.go
index 601ec9d25..364c5bf6d 100644
--- a/tpl/internal/go_templates/fmtsort/sort_test.go
+++ b/tpl/internal/go_templates/fmtsort/sort_test.go
@@ -119,7 +119,7 @@ var sortTests = []sortTest{
"PTR0:0 PTR1:1 PTR2:2",
},
{
- map[toy]string{toy{7, 2}: "72", toy{7, 1}: "71", toy{3, 4}: "34"},
+ map[toy]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"},
"{3 4}:34 {7 1}:71 {7 2}:72",
},
{
diff --git a/tpl/internal/go_templates/htmltemplate/content_test.go b/tpl/internal/go_templates/htmltemplate/content_test.go
index 2a1abfbfb..b5de701d3 100644
--- a/tpl/internal/go_templates/htmltemplate/content_test.go
+++ b/tpl/internal/go_templates/htmltemplate/content_test.go
@@ -21,7 +21,7 @@ func TestTypedContent(t *testing.T) {
htmltemplate.HTML(`Hello, <b>World</b> &amp;tc!`),
htmltemplate.HTMLAttr(` dir="ltr"`),
htmltemplate.JS(`c && alert("Hello, World!");`),
- htmltemplate.JSStr(`Hello, World & O'Reilly\x21`),
+ htmltemplate.JSStr(`Hello, World & O'Reilly\u0021`),
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/,`),
@@ -73,7 +73,7 @@ func TestTypedContent(t *testing.T) {
`Hello, <b>World</b> &amp;tc!`,
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- `Hello, World &amp; O&#39;Reilly\x21`,
+ `Hello, World &amp; O&#39;Reilly\u0021`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
@@ -103,7 +103,7 @@ func TestTypedContent(t *testing.T) {
`Hello,&#32;World&#32;&amp;tc!`,
`&#32;dir&#61;&#34;ltr&#34;`,
`c&#32;&amp;&amp;&#32;alert(&#34;Hello,&#32;World!&#34;);`,
- `Hello,&#32;World&#32;&amp;&#32;O&#39;Reilly\x21`,
+ `Hello,&#32;World&#32;&amp;&#32;O&#39;Reilly\u0021`,
`greeting&#61;H%69,&amp;addressee&#61;(World)`,
`greeting&#61;H%69,&amp;addressee&#61;(World)&#32;2x,&#32;https://golang.org/favicon.ico&#32;500.5w`,
`,foo/,`,
@@ -118,7 +118,7 @@ func TestTypedContent(t *testing.T) {
`Hello, World &amp;tc!`,
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- `Hello, World &amp; O&#39;Reilly\x21`,
+ `Hello, World &amp; O&#39;Reilly\u0021`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
@@ -133,7 +133,7 @@ func TestTypedContent(t *testing.T) {
`Hello, &lt;b&gt;World&lt;/b&gt; &amp;tc!`,
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- `Hello, World &amp; O&#39;Reilly\x21`,
+ `Hello, World &amp; O&#39;Reilly\u0021`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
@@ -149,7 +149,7 @@ func TestTypedContent(t *testing.T) {
// Not escaped.
`c && alert("Hello, World!");`,
// Escape sequence not over-escaped.
- `"Hello, World & O'Reilly\x21"`,
+ `"Hello, World & O'Reilly\u0021"`,
`"greeting=H%69,\u0026addressee=(World)"`,
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
`",foo/,"`,
@@ -165,7 +165,7 @@ func TestTypedContent(t *testing.T) {
// Not JS escaped but HTML escaped.
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
// Escape sequence not over-escaped.
- `&#34;Hello, World &amp; O&#39;Reilly\x21&#34;`,
+ `&#34;Hello, World &amp; O&#39;Reilly\u0021&#34;`,
`&#34;greeting=H%69,\u0026addressee=(World)&#34;`,
`&#34;greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w&#34;`,
`&#34;,foo/,&#34;`,
@@ -174,30 +174,30 @@ func TestTypedContent(t *testing.T) {
{
`<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);`,
+ `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
+ `a[href =~ \u0022\/\/example.com\u0022]#foo`,
+ `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
+ ` dir=\u0022ltr\u0022`,
+ `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
// 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`,
+ `Hello, World \u0026 O\u0027Reilly\u0021`,
+ `greeting=H%69,\u0026addressee=(World)`,
+ `greeting=H%69,\u0026addressee=(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);`,
+ `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
+ `a[href =~ \u0022\/\/example.com\u0022]#foo`,
+ `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
+ ` dir=\u0022ltr\u0022`,
+ `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
// 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`,
+ `Hello, World \u0026 O\u0027Reilly\u0021`,
+ `greeting=H%69,\u0026addressee=(World)`,
+ `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
`,foo\/,`,
},
},
@@ -211,7 +211,7 @@ func TestTypedContent(t *testing.T) {
// Not escaped.
`c && alert("Hello, World!");`,
// Escape sequence not over-escaped.
- `"Hello, World & O'Reilly\x21"`,
+ `"Hello, World & O'Reilly\u0021"`,
`"greeting=H%69,\u0026addressee=(World)"`,
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
`",foo/,"`,
@@ -227,7 +227,7 @@ func TestTypedContent(t *testing.T) {
`Hello, <b>World</b> &amp;tc!`,
` dir=&#34;ltr&#34;`,
`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
- `Hello, World &amp; O&#39;Reilly\x21`,
+ `Hello, World &amp; O&#39;Reilly\u0021`,
`greeting=H%69,&amp;addressee=(World)`,
`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
@@ -236,15 +236,15 @@ func TestTypedContent(t *testing.T) {
{
`<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);`,
+ `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
+ `a[href =~ \u0022\/\/example.com\u0022]#foo`,
+ `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
+ ` dir=\u0022ltr\u0022`,
+ `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
// 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`,
+ `Hello, World \u0026 O\u0027Reilly\u0021`,
+ `greeting=H%69,\u0026addressee=(World)`,
+ `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
`,foo\/,`,
},
},
@@ -256,7 +256,7 @@ func TestTypedContent(t *testing.T) {
`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`,
+ `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
`greeting=H%69,&amp;addressee=%28World%29`,
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
@@ -271,7 +271,7 @@ func TestTypedContent(t *testing.T) {
`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`,
+ `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
// 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`,
diff --git a/tpl/internal/go_templates/htmltemplate/doc.go b/tpl/internal/go_templates/htmltemplate/doc.go
index 0a3004a9b..b6a1504f8 100644
--- a/tpl/internal/go_templates/htmltemplate/doc.go
+++ b/tpl/internal/go_templates/htmltemplate/doc.go
@@ -73,6 +73,51 @@ functions.
For these internal escaping functions, if an action pipeline evaluates to
a nil interface value, it is treated as though it were an empty string.
+Namespaced and data- attributes
+
+Attributes with a namespace are treated as if they had no namespace.
+Given the excerpt
+
+ <a my:href="{{.}}"></a>
+
+At parse time the attribute will be treated as if it were just "href".
+So at parse time the template becomes:
+
+ <a my:href="{{. | urlescaper | attrescaper}}"></a>
+
+Similarly to attributes with namespaces, attributes with a "data-" prefix are
+treated as if they had no "data-" prefix. So given
+
+ <a data-href="{{.}}"></a>
+
+At parse time this becomes
+
+ <a data-href="{{. | urlescaper | attrescaper}}"></a>
+
+If an attribute has both a namespace and a "data-" prefix, only the namespace
+will be removed when determining the context. For example
+
+ <a my:data-href="{{.}}"></a>
+
+This is handled as if "my:data-href" was just "data-href" and not "href" as
+it would be if the "data-" prefix were to be ignored too. Thus at parse
+time this becomes just
+
+ <a my:data-href="{{. | attrescaper}}"></a>
+
+As a special case, attributes with the namespace "xmlns" are always treated
+as containing URLs. Given the excerpts
+
+ <a xmlns:title="{{.}}"></a>
+ <a xmlns:href="{{.}}"></a>
+ <a xmlns:onclick="{{.}}"></a>
+
+At parse time they become:
+
+ <a xmlns:title="{{. | urlescaper | attrescaper}}"></a>
+ <a xmlns:href="{{. | urlescaper | attrescaper}}"></a>
+ <a xmlns:onclick="{{. | urlescaper | attrescaper}}"></a>
+
Errors
See the documentation of ErrorCode for details.
diff --git a/tpl/internal/go_templates/htmltemplate/escape_test.go b/tpl/internal/go_templates/htmltemplate/escape_test.go
index 9e9db7800..075db4e13 100644
--- a/tpl/internal/go_templates/htmltemplate/escape_test.go
+++ b/tpl/internal/go_templates/htmltemplate/escape_test.go
@@ -242,7 +242,7 @@ func TestEscape(t *testing.T) {
{
"jsStr",
"<button onclick='alert(&quot;{{.H}}&quot;)'>",
- `<button onclick='alert(&quot;\x3cHello\x3e&quot;)'>`,
+ `<button onclick='alert(&quot;\u003cHello\u003e&quot;)'>`,
},
{
"badMarshaler",
@@ -263,7 +263,7 @@ func TestEscape(t *testing.T) {
{
"jsRe",
`<button onclick='alert(/{{"foo+bar"}}/.test(""))'>`,
- `<button onclick='alert(/foo\x2bbar/.test(""))'>`,
+ `<button onclick='alert(/foo\u002bbar/.test(""))'>`,
},
{
"jsReBlank",
@@ -829,7 +829,7 @@ func TestEscapeSet(t *testing.T) {
"main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`,
"helper": `{{11}} of {{"<100>"}}`,
},
- `<button onclick="title='11 of \x3c100\x3e'; ...">11 of &lt;100&gt;</button>`,
+ `<button onclick="title='11 of \u003c100\u003e'; ...">11 of &lt;100&gt;</button>`,
},
// A non-recursive template that ends in a different context.
// helper starts in jsCtxRegexp and ends in jsCtxDivOp.
diff --git a/tpl/internal/go_templates/htmltemplate/example_test.go b/tpl/internal/go_templates/htmltemplate/example_test.go
index a3e7910ee..a93b8d2fb 100644
--- a/tpl/internal/go_templates/htmltemplate/example_test.go
+++ b/tpl/internal/go_templates/htmltemplate/example_test.go
@@ -119,9 +119,9 @@ func Example_escape() {
// &#34;Fran &amp; Freddie&#39;s Diner&#34; &lt;tasty@example.com&gt;
// &#34;Fran &amp; Freddie&#39;s Diner&#34; &lt;tasty@example.com&gt;
// &#34;Fran &amp; Freddie&#39;s Diner&#34;32&lt;tasty@example.com&gt;
- // \"Fran & Freddie\'s Diner\" \x3Ctasty@example.com\x3E
- // \"Fran & Freddie\'s Diner\" \x3Ctasty@example.com\x3E
- // \"Fran & Freddie\'s Diner\"32\x3Ctasty@example.com\x3E
+ // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
+ // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
+ // \"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E
// %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E
}
diff --git a/tpl/internal/go_templates/htmltemplate/js.go b/tpl/internal/go_templates/htmltemplate/js.go
index 57622d152..cfd413461 100644
--- a/tpl/internal/go_templates/htmltemplate/js.go
+++ b/tpl/internal/go_templates/htmltemplate/js.go
@@ -164,7 +164,6 @@ func jsValEscaper(args ...interface{}) string {
}
// TODO: detect cycles before calling Marshal which loops infinitely on
// cyclic data. This may be an unacceptable DoS risk.
-
b, err := json.Marshal(a)
if err != nil {
// Put a space before comment so that if it is flush against
@@ -179,8 +178,8 @@ func jsValEscaper(args ...interface{}) string {
// TODO: maybe post-process output to prevent it from containing
// "<!--", "-->", "<![CDATA[", "]]>", or "</script"
// in case custom marshalers produce output containing those.
-
- // TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
+ // Note: Do not use \x escaping to save bytes because it is not JSON compatible and this escaper
+ // supports ld+json content-type.
if len(b) == 0 {
// In, `x=y/{{.}}*z` a json.Marshaler that produces "" should
// not cause the output `x=y/*z`.
@@ -261,6 +260,8 @@ func replace(s string, replacementTable []string) string {
r, w = utf8.DecodeRuneInString(s[i:])
var repl string
switch {
+ case int(r) < len(lowUnicodeReplacementTable):
+ repl = lowUnicodeReplacementTable[r]
case int(r) < len(replacementTable) && replacementTable[r] != "":
repl = replacementTable[r]
case r == '\u2028':
@@ -284,67 +285,80 @@ func replace(s string, replacementTable []string) string {
return b.String()
}
+var lowUnicodeReplacementTable = []string{
+ 0: `\u0000`, 1: `\u0001`, 2: `\u0002`, 3: `\u0003`, 4: `\u0004`, 5: `\u0005`, 6: `\u0006`,
+ '\a': `\u0007`,
+ '\b': `\u0008`,
+ '\t': `\t`,
+ '\n': `\n`,
+ '\v': `\u000b`, // "\v" == "v" on IE 6.
+ '\f': `\f`,
+ '\r': `\r`,
+ 0xe: `\u000e`, 0xf: `\u000f`, 0x10: `\u0010`, 0x11: `\u0011`, 0x12: `\u0012`, 0x13: `\u0013`,
+ 0x14: `\u0014`, 0x15: `\u0015`, 0x16: `\u0016`, 0x17: `\u0017`, 0x18: `\u0018`, 0x19: `\u0019`,
+ 0x1a: `\u001a`, 0x1b: `\u001b`, 0x1c: `\u001c`, 0x1d: `\u001d`, 0x1e: `\u001e`, 0x1f: `\u001f`,
+}
+
var jsStrReplacementTable = []string{
- 0: `\0`,
+ 0: `\u0000`,
'\t': `\t`,
'\n': `\n`,
- '\v': `\x0b`, // "\v" == "v" on IE 6.
+ '\v': `\u000b`, // "\v" == "v" on IE 6.
'\f': `\f`,
'\r': `\r`,
// Encode HTML specials as hex so the output can be embedded
// in HTML attributes without further encoding.
- '"': `\x22`,
- '&': `\x26`,
- '\'': `\x27`,
- '+': `\x2b`,
+ '"': `\u0022`,
+ '&': `\u0026`,
+ '\'': `\u0027`,
+ '+': `\u002b`,
'/': `\/`,
- '<': `\x3c`,
- '>': `\x3e`,
+ '<': `\u003c`,
+ '>': `\u003e`,
'\\': `\\`,
}
// jsStrNormReplacementTable is like jsStrReplacementTable but does not
// overencode existing escapes since this table has no entry for `\`.
var jsStrNormReplacementTable = []string{
- 0: `\0`,
+ 0: `\u0000`,
'\t': `\t`,
'\n': `\n`,
- '\v': `\x0b`, // "\v" == "v" on IE 6.
+ '\v': `\u000b`, // "\v" == "v" on IE 6.
'\f': `\f`,
'\r': `\r`,
// Encode HTML specials as hex so the output can be embedded
// in HTML attributes without further encoding.
- '"': `\x22`,
- '&': `\x26`,
- '\'': `\x27`,
- '+': `\x2b`,
+ '"': `\u0022`,
+ '&': `\u0026`,
+ '\'': `\u0027`,
+ '+': `\u002b`,
'/': `\/`,
- '<': `\x3c`,
- '>': `\x3e`,
+ '<': `\u003c`,
+ '>': `\u003e`,
}
-
var jsRegexpReplacementTable = []string{
- 0: `\0`,
+ 0: `\u0000`,
'\t': `\t`,
'\n': `\n`,
- '\v': `\x0b`, // "\v" == "v" on IE 6.
+ '\v': `\u000b`, // "\v" == "v" on IE 6.
'\f': `\f`,
'\r': `\r`,
// Encode HTML specials as hex so the output can be embedded
// in HTML attributes without further encoding.
- '"': `\x22`,
+ '"': `\u0022`,
'$': `\$`,
- '&': `\x26`,
- '\'': `\x27`,
+ '&': `\u0026`,
+ '\'': `\u0027`,
'(': `\(`,
')': `\)`,
'*': `\*`,
- '+': `\x2b`,
+ '+': `\u002b`,
'-': `\-`,
'.': `\.`,
'/': `\/`,
- '<': `\x3c`,
- '>': `\x3e`,
+ '<': `\u003c`,
+ '>': `\u003e`,
'?': `\?`,
'[': `\[`,
'\\': `\\`,
@@ -384,11 +398,11 @@ func isJSType(mimeType string) bool {
// https://tools.ietf.org/html/rfc7231#section-3.1.1
// https://tools.ietf.org/html/rfc4329#section-3
// https://www.ietf.org/rfc/rfc4627.txt
- mimeType = strings.ToLower(mimeType)
// discard parameters
if i := strings.Index(mimeType, ";"); i >= 0 {
mimeType = mimeType[:i]
}
+ mimeType = strings.ToLower(mimeType)
mimeType = strings.TrimSpace(mimeType)
switch mimeType {
case
diff --git a/tpl/internal/go_templates/htmltemplate/js_test.go b/tpl/internal/go_templates/htmltemplate/js_test.go
index 0a6f332a6..e15087f0f 100644
--- a/tpl/internal/go_templates/htmltemplate/js_test.go
+++ b/tpl/internal/go_templates/htmltemplate/js_test.go
@@ -139,7 +139,7 @@ func TestJSValEscaper(t *testing.T) {
{"foo", `"foo"`},
// Newlines.
{"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
- // "\v" == "v" on IE 6 so use "\x0b" instead.
+ // "\v" == "v" on IE 6 so use "\u000b" instead.
{"\t\x0b", `"\t\u000b"`},
{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
{[]interface{}{}, "[]"},
@@ -175,7 +175,7 @@ func TestJSStrEscaper(t *testing.T) {
}{
{"", ``},
{"foo", `foo`},
- {"\u0000", `\0`},
+ {"\u0000", `\u0000`},
{"\t", `\t`},
{"\n", `\n`},
{"\r", `\r`},
@@ -185,14 +185,14 @@ func TestJSStrEscaper(t *testing.T) {
{"\\n", `\\n`},
{"foo\r\nbar", `foo\r\nbar`},
// Preserve attribute boundaries.
- {`"`, `\x22`},
- {`'`, `\x27`},
+ {`"`, `\u0022`},
+ {`'`, `\u0027`},
// Allow embedding in HTML without further escaping.
- {`&amp;`, `\x26amp;`},
+ {`&amp;`, `\u0026amp;`},
// Prevent breaking out of text node and element boundaries.
- {"</script>", `\x3c\/script\x3e`},
- {"<![CDATA[", `\x3c![CDATA[`},
- {"]]>", `]]\x3e`},
+ {"</script>", `\u003c\/script\u003e`},
+ {"<![CDATA[", `\u003c![CDATA[`},
+ {"]]>", `]]\u003e`},
// https://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span
// "The text in style, script, title, and textarea elements
// must not have an escaping text span start that is not
@@ -203,11 +203,11 @@ func TestJSStrEscaper(t *testing.T) {
// allow regular text content to be interpreted as script
// allowing script execution via a combination of a JS string
// injection followed by an HTML text injection.
- {"<!--", `\x3c!--`},
- {"-->", `--\x3e`},
+ {"<!--", `\u003c!--`},
+ {"-->", `--\u003e`},
// From https://code.google.com/p/doctype/wiki/ArticleUtf7
{"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
- `\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`,
+ `\u002bADw-script\u002bAD4-alert(1)\u002bADw-\/script\u002bAD4-`,
},
// Invalid UTF-8 sequence
{"foo\xA0bar", "foo\xA0bar"},
@@ -230,7 +230,7 @@ func TestJSRegexpEscaper(t *testing.T) {
}{
{"", `(?:)`},
{"foo", `foo`},