summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/go
diff options
context:
space:
mode:
authorShubham Kulkarni <skulkarni@mvista.com>2023-05-02 21:40:12 +0530
committerSteve Sakoman <steve@sakoman.com>2023-05-16 06:18:21 -1000
commit79dcce4413fc4c785c7ed562dd2e7ca91fe9d68c (patch)
treea3c11a641268b23c2324cdfab7701fc1ec8af1e1 /meta/recipes-devtools/go
parenta631bfc3a38f7d00b2c666661a89a758a0af9831 (diff)
downloadpoky-79dcce4413fc4c785c7ed562dd2e7ca91fe9d68c.tar.gz
go: Security fix for CVE-2023-24538
html/template: disallow actions in JS template literals Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b (From OE-Core rev: c8a597b76505dab7649f4c9b18e1e14b0e3d57af) Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
Diffstat (limited to 'meta/recipes-devtools/go')
-rw-r--r--meta/recipes-devtools/go/go-1.14.inc3
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch125
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch196
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-24538-3.patch208
4 files changed, 532 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc
index 3b99b8fe7e..f734fe1ac8 100644
--- a/meta/recipes-devtools/go/go-1.14.inc
+++ b/meta/recipes-devtools/go/go-1.14.inc
@@ -58,6 +58,9 @@ SRC_URI += "\
58 file://CVE-2020-29510.patch \ 58 file://CVE-2020-29510.patch \
59 file://CVE-2023-24537.patch \ 59 file://CVE-2023-24537.patch \
60 file://CVE-2023-24534.patch \ 60 file://CVE-2023-24534.patch \
61 file://CVE-2023-24538-1.patch \
62 file://CVE-2023-24538-2.patch \
63 file://CVE-2023-24538-3.patch \
61" 64"
62 65
63SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" 66SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch"
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
new file mode 100644
index 0000000000..eda26e5ff6
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
@@ -0,0 +1,125 @@
1From 8acd01094d9ee17f6e763a61e49a8a808b3a9ddb Mon Sep 17 00:00:00 2001
2From: Brad Fitzpatrick <bradfitz@golang.org>
3Date: Mon, 2 Aug 2021 14:55:51 -0700
4Subject: [PATCH 1/3] net/netip: add new IP address package
5
6Co-authored-by: Alex Willmer <alex@moreati.org.uk> (GitHub @moreati)
7Co-authored-by: Alexander Yastrebov <yastrebov.alex@gmail.com>
8Co-authored-by: David Anderson <dave@natulte.net> (Tailscale CLA)
9Co-authored-by: David Crawshaw <crawshaw@tailscale.com> (Tailscale CLA)
10Co-authored-by: Dmytro Shynkevych <dmytro@tailscale.com> (Tailscale CLA)
11Co-authored-by: Elias Naur <mail@eliasnaur.com>
12Co-authored-by: Joe Tsai <joetsai@digital-static.net> (Tailscale CLA)
13Co-authored-by: Jonathan Yu <jawnsy@cpan.org> (GitHub @jawnsy)
14Co-authored-by: Josh Bleecher Snyder <josharian@gmail.com> (Tailscale CLA)
15Co-authored-by: Maisem Ali <maisem@tailscale.com> (Tailscale CLA)
16Co-authored-by: Manuel Mendez (Go AUTHORS mmendez534@...)
17Co-authored-by: Matt Layher <mdlayher@gmail.com>
18Co-authored-by: Noah Treuhaft <noah.treuhaft@gmail.com> (GitHub @nwt)
19Co-authored-by: Stefan Majer <stefan.majer@gmail.com>
20Co-authored-by: Terin Stock <terinjokes@gmail.com> (Cloudflare CLA)
21Co-authored-by: Tobias Klauser <tklauser@distanz.ch>
22
23Fixes #46518
24
25Change-Id: I0041f9e1115d61fa6e95fcf32b01d9faee708712
26Reviewed-on: https://go-review.googlesource.com/c/go/+/339309
27Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
28TryBot-Result: Go Bot <gobot@golang.org>
29Reviewed-by: Russ Cox <rsc@golang.org>
30Trust: Brad Fitzpatrick <bradfitz@golang.org>
31
32Dependency Patch #1
33
34Upstream-Status: Backport [https://github.com/golang/go/commit/a59e33224e42d60a97fa720a45e1b74eb6aaa3d0]
35CVE: CVE-2023-24538
36Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
37---
38 src/internal/godebug/godebug.go | 34 ++++++++++++++++++++++++++++++++++
39 src/internal/godebug/godebug_test.go | 34 ++++++++++++++++++++++++++++++++++
40 2 files changed, 68 insertions(+)
41 create mode 100644 src/internal/godebug/godebug.go
42 create mode 100644 src/internal/godebug/godebug_test.go
43
44diff --git a/src/internal/godebug/godebug.go b/src/internal/godebug/godebug.go
45new file mode 100644
46index 0000000..ac434e5
47--- /dev/null
48+++ b/src/internal/godebug/godebug.go
49@@ -0,0 +1,34 @@
50+// Copyright 2021 The Go Authors. All rights reserved.
51+// Use of this source code is governed by a BSD-style
52+// license that can be found in the LICENSE file.
53+
54+// Package godebug parses the GODEBUG environment variable.
55+package godebug
56+
57+import "os"
58+
59+// Get returns the value for the provided GODEBUG key.
60+func Get(key string) string {
61+ return get(os.Getenv("GODEBUG"), key)
62+}
63+
64+// get returns the value part of key=value in s (a GODEBUG value).
65+func get(s, key string) string {
66+ for i := 0; i < len(s)-len(key)-1; i++ {
67+ if i > 0 && s[i-1] != ',' {
68+ continue
69+ }
70+ afterKey := s[i+len(key):]
71+ if afterKey[0] != '=' || s[i:i+len(key)] != key {
72+ continue
73+ }
74+ val := afterKey[1:]
75+ for i, b := range val {
76+ if b == ',' {
77+ return val[:i]
78+ }
79+ }
80+ return val
81+ }
82+ return ""
83+}
84diff --git a/src/internal/godebug/godebug_test.go b/src/internal/godebug/godebug_test.go
85new file mode 100644
86index 0000000..41b9117
87--- /dev/null
88+++ b/src/internal/godebug/godebug_test.go
89@@ -0,0 +1,34 @@
90+// Copyright 2021 The Go Authors. All rights reserved.
91+// Use of this source code is governed by a BSD-style
92+// license that can be found in the LICENSE file.
93+
94+package godebug
95+
96+import "testing"
97+
98+func TestGet(t *testing.T) {
99+ tests := []struct {
100+ godebug string
101+ key string
102+ want string
103+ }{
104+ {"", "", ""},
105+ {"", "foo", ""},
106+ {"foo=bar", "foo", "bar"},
107+ {"foo=bar,after=x", "foo", "bar"},
108+ {"before=x,foo=bar,after=x", "foo", "bar"},
109+ {"before=x,foo=bar", "foo", "bar"},
110+ {",,,foo=bar,,,", "foo", "bar"},
111+ {"foodecoy=wrong,foo=bar", "foo", "bar"},
112+ {"foo=", "foo", ""},
113+ {"foo", "foo", ""},
114+ {",foo", "foo", ""},
115+ {"foo=bar,baz", "loooooooong", ""},
116+ }
117+ for _, tt := range tests {
118+ got := get(tt.godebug, tt.key)
119+ if got != tt.want {
120+ t.Errorf("get(%q, %q) = %q; want %q", tt.godebug, tt.key, got, tt.want)
121+ }
122+ }
123+}
124--
1252.7.4
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch
new file mode 100644
index 0000000000..5036f2890b
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch
@@ -0,0 +1,196 @@
1From 6fc21505614f36178df0dad7034b6b8e3f7588d5 Mon Sep 17 00:00:00 2001
2From: empijei <robclap8@gmail.com>
3Date: Fri, 27 Mar 2020 19:27:55 +0100
4Subject: [PATCH 2/3] html/template,text/template: switch to Unicode escapes
5 for JSON compatibility
6MIME-Version: 1.0
7Content-Type: text/plain; charset=UTF-8
8Content-Transfer-Encoding: 8bit
9
10The existing implementation is not compatible with JSON
11escape as it uses hex escaping.
12Unicode escape, instead, is valid for both JSON and JS.
13This fix avoids creating a separate escaping context for
14scripts of type "application/ld+json" and it is more
15future-proof in case more JSON+JS contexts get added
16to the platform (e.g. import maps).
17
18Fixes #33671
19Fixes #37634
20
21Change-Id: Id6f6524b4abc52e81d9d744d46bbe5bf2e081543
22Reviewed-on: https://go-review.googlesource.com/c/go/+/226097
23Reviewed-by: Carl Johnson <me@carlmjohnson.net>
24Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
25Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
26TryBot-Result: Gobot Gobot <gobot@golang.org>
27
28Dependency Patch #2
29
30Upstream-Status: Backport from https://github.com/golang/go/commit/d4d298040d072ddacea0e0d6b55fb148fff18070
31CVE: CVE-2023-24538
32Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
33---
34 src/html/template/js.go | 70 +++++++++++++++++++++++++++-------------------
35 src/text/template/funcs.go | 8 +++---
36 2 files changed, 46 insertions(+), 32 deletions(-)
37
38diff --git a/src/html/template/js.go b/src/html/template/js.go
39index 0e91458..ea9c183 100644
40--- a/src/html/template/js.go
41+++ b/src/html/template/js.go
42@@ -163,7 +163,6 @@ func jsValEscaper(args ...interface{}) string {
43 }
44 // TODO: detect cycles before calling Marshal which loops infinitely on
45 // cyclic data. This may be an unacceptable DoS risk.
46-
47 b, err := json.Marshal(a)
48 if err != nil {
49 // Put a space before comment so that if it is flush against
50@@ -178,8 +177,8 @@ func jsValEscaper(args ...interface{}) string {
51 // TODO: maybe post-process output to prevent it from containing
52 // "<!--", "-->", "<![CDATA[", "]]>", or "</script"
53 // in case custom marshalers produce output containing those.
54-
55- // TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
56+ // Note: Do not use \x escaping to save bytes because it is not JSON compatible and this escaper
57+ // supports ld+json content-type.
58 if len(b) == 0 {
59 // In, `x=y/{{.}}*z` a json.Marshaler that produces "" should
60 // not cause the output `x=y/*z`.
61@@ -260,6 +259,8 @@ func replace(s string, replacementTable []string) string {
62 r, w = utf8.DecodeRuneInString(s[i:])
63 var repl string
64 switch {
65+ case int(r) < len(lowUnicodeReplacementTable):
66+ repl = lowUnicodeReplacementTable[r]
67 case int(r) < len(replacementTable) && replacementTable[r] != "":
68 repl = replacementTable[r]
69 case r == '\u2028':
70@@ -283,67 +284,80 @@ func replace(s string, replacementTable []string) string {
71 return b.String()
72 }
73
74+var lowUnicodeReplacementTable = []string{
75+ 0: `\u0000`, 1: `\u0001`, 2: `\u0002`, 3: `\u0003`, 4: `\u0004`, 5: `\u0005`, 6: `\u0006`,
76+ '\a': `\u0007`,
77+ '\b': `\u0008`,
78+ '\t': `\t`,
79+ '\n': `\n`,
80+ '\v': `\u000b`, // "\v" == "v" on IE 6.
81+ '\f': `\f`,
82+ '\r': `\r`,
83+ 0xe: `\u000e`, 0xf: `\u000f`, 0x10: `\u0010`, 0x11: `\u0011`, 0x12: `\u0012`, 0x13: `\u0013`,
84+ 0x14: `\u0014`, 0x15: `\u0015`, 0x16: `\u0016`, 0x17: `\u0017`, 0x18: `\u0018`, 0x19: `\u0019`,
85+ 0x1a: `\u001a`, 0x1b: `\u001b`, 0x1c: `\u001c`, 0x1d: `\u001d`, 0x1e: `\u001e`, 0x1f: `\u001f`,
86+}
87+
88 var jsStrReplacementTable = []string{
89- 0: `\0`,
90+ 0: `\u0000`,
91 '\t': `\t`,
92 '\n': `\n`,
93- '\v': `\x0b`, // "\v" == "v" on IE 6.
94+ '\v': `\u000b`, // "\v" == "v" on IE 6.
95 '\f': `\f`,
96 '\r': `\r`,
97 // Encode HTML specials as hex so the output can be embedded
98 // in HTML attributes without further encoding.
99- '"': `\x22`,
100- '&': `\x26`,
101- '\'': `\x27`,
102- '+': `\x2b`,
103+ '"': `\u0022`,
104+ '&': `\u0026`,
105+ '\'': `\u0027`,
106+ '+': `\u002b`,
107 '/': `\/`,
108- '<': `\x3c`,
109- '>': `\x3e`,
110+ '<': `\u003c`,
111+ '>': `\u003e`,
112 '\\': `\\`,
113 }
114
115 // jsStrNormReplacementTable is like jsStrReplacementTable but does not
116 // overencode existing escapes since this table has no entry for `\`.
117 var jsStrNormReplacementTable = []string{
118- 0: `\0`,
119+ 0: `\u0000`,
120 '\t': `\t`,
121 '\n': `\n`,
122- '\v': `\x0b`, // "\v" == "v" on IE 6.
123+ '\v': `\u000b`, // "\v" == "v" on IE 6.
124 '\f': `\f`,
125 '\r': `\r`,
126 // Encode HTML specials as hex so the output can be embedded
127 // in HTML attributes without further encoding.
128- '"': `\x22`,
129- '&': `\x26`,
130- '\'': `\x27`,
131- '+': `\x2b`,
132+ '"': `\u0022`,
133+ '&': `\u0026`,
134+ '\'': `\u0027`,
135+ '+': `\u002b`,
136 '/': `\/`,
137- '<': `\x3c`,
138- '>': `\x3e`,
139+ '<': `\u003c`,
140+ '>': `\u003e`,
141 }
142-
143 var jsRegexpReplacementTable = []string{
144- 0: `\0`,
145+ 0: `\u0000`,
146 '\t': `\t`,
147 '\n': `\n`,
148- '\v': `\x0b`, // "\v" == "v" on IE 6.
149+ '\v': `\u000b`, // "\v" == "v" on IE 6.
150 '\f': `\f`,
151 '\r': `\r`,
152 // Encode HTML specials as hex so the output can be embedded
153 // in HTML attributes without further encoding.
154- '"': `\x22`,
155+ '"': `\u0022`,
156 '$': `\$`,
157- '&': `\x26`,
158- '\'': `\x27`,
159+ '&': `\u0026`,
160+ '\'': `\u0027`,
161 '(': `\(`,
162 ')': `\)`,
163 '*': `\*`,
164- '+': `\x2b`,
165+ '+': `\u002b`,
166 '-': `\-`,
167 '.': `\.`,
168 '/': `\/`,
169- '<': `\x3c`,
170- '>': `\x3e`,
171+ '<': `\u003c`,
172+ '>': `\u003e`,
173 '?': `\?`,
174 '[': `\[`,
175 '\\': `\\`,
176diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
177index 46125bc..f3de9fb 100644
178--- a/src/text/template/funcs.go
179+++ b/src/text/template/funcs.go
180@@ -640,10 +640,10 @@ var (
181 jsBackslash = []byte(`\\`)
182 jsApos = []byte(`\'`)
183 jsQuot = []byte(`\"`)
184- jsLt = []byte(`\x3C`)
185- jsGt = []byte(`\x3E`)
186- jsAmp = []byte(`\x26`)
187- jsEq = []byte(`\x3D`)
188+ jsLt = []byte(`\u003C`)
189+ jsGt = []byte(`\u003E`)
190+ jsAmp = []byte(`\u0026`)
191+ jsEq = []byte(`\u003D`)
192 )
193
194 // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
195--
1962.7.4
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-3.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-3.patch
new file mode 100644
index 0000000000..d5bb33e091
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-3.patch
@@ -0,0 +1,208 @@
1From 16f4882984569f179d73967c9eee679bb9b098c5 Mon Sep 17 00:00:00 2001
2From: Roland Shoemaker <bracewell@google.com>
3Date: Mon, 20 Mar 2023 11:01:13 -0700
4Subject: [PATCH 3/3] html/template: disallow actions in JS template literals
5
6ECMAScript 6 introduced template literals[0][1] which are delimited with
7backticks. These need to be escaped in a similar fashion to the
8delimiters for other string literals. Additionally template literals can
9contain special syntax for string interpolation.
10
11There is no clear way to allow safe insertion of actions within JS
12template literals, as handling (JS) string interpolation inside of these
13literals is rather complex. As such we've chosen to simply disallow
14template actions within these template literals.
15
16A new error code is added for this parsing failure case, errJsTmplLit,
17but it is unexported as it is not backwards compatible with other minor
18release versions to introduce an API change in a minor release. We will
19export this code in the next major release.
20
21The previous behavior (with the cavet that backticks are now escaped
22properly) can be re-enabled with GODEBUG=jstmpllitinterp=1.
23
24This change subsumes CL471455.
25
26Thanks to Sohom Datta, Manipal Institute of Technology, for reporting
27this issue.
28
29Fixes CVE-2023-24538
30For #59234
31Fixes #59271
32
33[0] https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-template-literals
34[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
35
36Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802457
37Reviewed-by: Damien Neil <dneil@google.com>
38Run-TryBot: Damien Neil <dneil@google.com>
39Reviewed-by: Julie Qiu <julieqiu@google.com>
40Reviewed-by: Roland Shoemaker <bracewell@google.com>
41Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802612
42Run-TryBot: Roland Shoemaker <bracewell@google.com>
43Change-Id: Ic7f10595615f2b2740d9c85ad7ef40dc0e78c04c
44Reviewed-on: https://go-review.googlesource.com/c/go/+/481987
45Auto-Submit: Michael Knyszek <mknyszek@google.com>
46TryBot-Result: Gopher Robot <gobot@golang.org>
47Run-TryBot: Michael Knyszek <mknyszek@google.com>
48Reviewed-by: Matthew Dempsky <mdempsky@google.com>
49
50Upstream-Status: Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b
51CVE: CVE-2023-24538
52Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
53---
54 src/html/template/context.go | 2 ++
55 src/html/template/error.go | 13 +++++++++++++
56 src/html/template/escape.go | 11 +++++++++++
57 src/html/template/js.go | 2 ++
58 src/html/template/jsctx_string.go | 9 +++++++++
59 src/html/template/transition.go | 7 ++++++-
60 6 files changed, 43 insertions(+), 1 deletion(-)
61
62diff --git a/src/html/template/context.go b/src/html/template/context.go
63index f7d4849..0b65313 100644
64--- a/src/html/template/context.go
65+++ b/src/html/template/context.go
66@@ -116,6 +116,8 @@ const (
67 stateJSDqStr
68 // stateJSSqStr occurs inside a JavaScript single quoted string.
69 stateJSSqStr
70+ // stateJSBqStr occurs inside a JavaScript back quoted string.
71+ stateJSBqStr
72 // stateJSRegexp occurs inside a JavaScript regexp literal.
73 stateJSRegexp
74 // stateJSBlockCmt occurs inside a JavaScript /* block comment */.
75diff --git a/src/html/template/error.go b/src/html/template/error.go
76index 0e52706..fd26b64 100644
77--- a/src/html/template/error.go
78+++ b/src/html/template/error.go
79@@ -211,6 +211,19 @@ const (
80 // pipeline occurs in an unquoted attribute value context, "html" is
81 // disallowed. Avoid using "html" and "urlquery" entirely in new templates.
82 ErrPredefinedEscaper
83+
84+ // errJSTmplLit: "... appears in a JS template literal"
85+ // Example:
86+ // <script>var tmpl = `{{.Interp}`</script>
87+ // Discussion:
88+ // Package html/template does not support actions inside of JS template
89+ // literals.
90+ //
91+ // TODO(rolandshoemaker): we cannot add this as an exported error in a minor
92+ // release, since it is backwards incompatible with the other minor
93+ // releases. As such we need to leave it unexported, and then we'll add it
94+ // in the next major release.
95+ errJSTmplLit
96 )
97
98 func (e *Error) Error() string {
99diff --git a/src/html/template/escape.go b/src/html/template/escape.go
100index f12dafa..29ca5b3 100644
101--- a/src/html/template/escape.go
102+++ b/src/html/template/escape.go
103@@ -8,6 +8,7 @@ import (
104 "bytes"
105 "fmt"
106 "html"
107+ "internal/godebug"
108 "io"
109 "text/template"
110 "text/template/parse"
111@@ -203,6 +204,16 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
112 c.jsCtx = jsCtxDivOp
113 case stateJSDqStr, stateJSSqStr:
114 s = append(s, "_html_template_jsstrescaper")
115+ case stateJSBqStr:
116+ debugAllowActionJSTmpl := godebug.Get("jstmpllitinterp")
117+ if debugAllowActionJSTmpl == "1" {
118+ s = append(s, "_html_template_jsstrescaper")
119+ } else {
120+ return context{
121+ state: stateError,
122+ err: errorf(errJSTmplLit, n, n.Line, "%s appears in a JS template literal", n),
123+ }
124+ }
125 case stateJSRegexp:
126 s = append(s, "_html_template_jsregexpescaper")
127 case stateCSS:
128diff --git a/src/html/template/js.go b/src/html/template/js.go
129index ea9c183..b888eaf 100644
130--- a/src/html/template/js.go
131+++ b/src/html/template/js.go
132@@ -308,6 +308,7 @@ var jsStrReplacementTable = []string{
133 // Encode HTML specials as hex so the output can be embedded
134 // in HTML attributes without further encoding.
135 '"': `\u0022`,
136+ '`': `\u0060`,
137 '&': `\u0026`,
138 '\'': `\u0027`,
139 '+': `\u002b`,
140@@ -331,6 +332,7 @@ var jsStrNormReplacementTable = []string{
141 '"': `\u0022`,
142 '&': `\u0026`,
143 '\'': `\u0027`,
144+ '`': `\u0060`,
145 '+': `\u002b`,
146 '/': `\/`,
147 '<': `\u003c`,
148diff --git a/src/html/template/jsctx_string.go b/src/html/template/jsctx_string.go
149index dd1d87e..2394893 100644
150--- a/src/html/template/jsctx_string.go
151+++ b/src/html/template/jsctx_string.go
152@@ -4,6 +4,15 @@ package template
153
154 import "strconv"
155
156+func _() {
157+ // An "invalid array index" compiler error signifies that the constant values have changed.
158+ // Re-run the stringer command to generate them again.
159+ var x [1]struct{}
160+ _ = x[jsCtxRegexp-0]
161+ _ = x[jsCtxDivOp-1]
162+ _ = x[jsCtxUnknown-2]
163+}
164+
165 const _jsCtx_name = "jsCtxRegexpjsCtxDivOpjsCtxUnknown"
166
167 var _jsCtx_index = [...]uint8{0, 11, 21, 33}
168diff --git a/src/html/template/transition.go b/src/html/template/transition.go
169index 06df679..92eb351 100644
170--- a/src/html/template/transition.go
171+++ b/src/html/template/transition.go
172@@ -27,6 +27,7 @@ var transitionFunc = [...]func(context, []byte) (context, int){
173 stateJS: tJS,
174 stateJSDqStr: tJSDelimited,
175 stateJSSqStr: tJSDelimited,
176+ stateJSBqStr: tJSDelimited,
177 stateJSRegexp: tJSDelimited,
178 stateJSBlockCmt: tBlockCmt,
179 stateJSLineCmt: tLineCmt,
180@@ -262,7 +263,7 @@ func tURL(c context, s []byte) (context, int) {
181
182 // tJS is the context transition function for the JS state.
183 func tJS(c context, s []byte) (context, int) {
184- i := bytes.IndexAny(s, `"'/`)
185+ i := bytes.IndexAny(s, "\"`'/")
186 if i == -1 {
187 // Entire input is non string, comment, regexp tokens.
188 c.jsCtx = nextJSCtx(s, c.jsCtx)
189@@ -274,6 +275,8 @@ func tJS(c context, s []byte) (context, int) {
190 c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp
191 case '\'':
192 c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp
193+ case '`':
194+ c.state, c.jsCtx = stateJSBqStr, jsCtxRegexp
195 case '/':
196 switch {
197 case i+1 < len(s) && s[i+1] == '/':
198@@ -303,6 +306,8 @@ func tJSDelimited(c context, s []byte) (context, int) {
199 switch c.state {
200 case stateJSSqStr:
201 specials = `\'`
202+ case stateJSBqStr:
203+ specials = "`\\"
204 case stateJSRegexp:
205 specials = `\/[]`
206 }
207--
2082.7.4