@@ -58,9 +58,12 @@ SRC_URI += "\
file://CVE-2020-29510.patch \
file://CVE-2023-24537.patch \
file://CVE-2023-24534.patch \
- file://CVE-2023-24538-1.patch \
- file://CVE-2023-24538-2.patch \
- file://CVE-2023-24538-3.patch \
+ file://CVE-2023-24538_1.patch \
+ file://CVE-2023-24538_2.patch \
+ file://CVE-2023-24538_3.patch \
+ file://CVE-2023-24538_4.patch \
+ file://CVE-2023-24538_5.patch \
+ file://CVE-2023-24538_6.patch \
file://CVE-2023-24539.patch \
file://CVE-2023-24540.patch \
file://CVE-2023-29405-1.patch \
deleted file mode 100644
@@ -1,196 +0,0 @@
-From 6fc21505614f36178df0dad7034b6b8e3f7588d5 Mon Sep 17 00:00:00 2001
-From: empijei <robclap8@gmail.com>
-Date: Fri, 27 Mar 2020 19:27:55 +0100
-Subject: [PATCH 2/3] html/template,text/template: switch to Unicode escapes
- for JSON compatibility
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The existing implementation is not compatible with JSON
-escape as it uses hex escaping.
-Unicode escape, instead, is valid for both JSON and JS.
-This fix avoids creating a separate escaping context for
-scripts of type "application/ld+json" and it is more
-future-proof in case more JSON+JS contexts get added
-to the platform (e.g. import maps).
-
-Fixes #33671
-Fixes #37634
-
-Change-Id: Id6f6524b4abc52e81d9d744d46bbe5bf2e081543
-Reviewed-on: https://go-review.googlesource.com/c/go/+/226097
-Reviewed-by: Carl Johnson <me@carlmjohnson.net>
-Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
-Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
-TryBot-Result: Gobot Gobot <gobot@golang.org>
-
-Dependency Patch #2
-
-Upstream-Status: Backport from https://github.com/golang/go/commit/d4d298040d072ddacea0e0d6b55fb148fff18070
-CVE: CVE-2023-24538
-Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
----
- src/html/template/js.go | 70 +++++++++++++++++++++++++++-------------------
- src/text/template/funcs.go | 8 +++---
- 2 files changed, 46 insertions(+), 32 deletions(-)
-
-diff --git a/src/html/template/js.go b/src/html/template/js.go
-index 0e91458..ea9c183 100644
---- a/src/html/template/js.go
-+++ b/src/html/template/js.go
-@@ -163,7 +163,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
-@@ -178,8 +177,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`.
-@@ -260,6 +259,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':
-@@ -283,67 +284,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`,
- '?': `\?`,
- '[': `\[`,
- '\\': `\\`,
-diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
-index 46125bc..f3de9fb 100644
---- a/src/text/template/funcs.go
-+++ b/src/text/template/funcs.go
-@@ -640,10 +640,10 @@ var (
- jsBackslash = []byte(`\\`)
- jsApos = []byte(`\'`)
- jsQuot = []byte(`\"`)
-- jsLt = []byte(`\x3C`)
-- jsGt = []byte(`\x3E`)
-- jsAmp = []byte(`\x26`)
-- jsEq = []byte(`\x3D`)
-+ jsLt = []byte(`\u003C`)
-+ jsGt = []byte(`\u003E`)
-+ jsAmp = []byte(`\u0026`)
-+ jsEq = []byte(`\u003D`)
- )
-
- // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
---
-2.7.4
deleted file mode 100644
@@ -1,208 +0,0 @@
-From 16f4882984569f179d73967c9eee679bb9b098c5 Mon Sep 17 00:00:00 2001
-From: Roland Shoemaker <bracewell@google.com>
-Date: Mon, 20 Mar 2023 11:01:13 -0700
-Subject: [PATCH 3/3] html/template: disallow actions in JS template literals
-
-ECMAScript 6 introduced template literals[0][1] which are delimited with
-backticks. These need to be escaped in a similar fashion to the
-delimiters for other string literals. Additionally template literals can
-contain special syntax for string interpolation.
-
-There is no clear way to allow safe insertion of actions within JS
-template literals, as handling (JS) string interpolation inside of these
-literals is rather complex. As such we've chosen to simply disallow
-template actions within these template literals.
-
-A new error code is added for this parsing failure case, errJsTmplLit,
-but it is unexported as it is not backwards compatible with other minor
-release versions to introduce an API change in a minor release. We will
-export this code in the next major release.
-
-The previous behavior (with the cavet that backticks are now escaped
-properly) can be re-enabled with GODEBUG=jstmpllitinterp=1.
-
-This change subsumes CL471455.
-
-Thanks to Sohom Datta, Manipal Institute of Technology, for reporting
-this issue.
-
-Fixes CVE-2023-24538
-For #59234
-Fixes #59271
-
-[0] https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-template-literals
-[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
-
-Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802457
-Reviewed-by: Damien Neil <dneil@google.com>
-Run-TryBot: Damien Neil <dneil@google.com>
-Reviewed-by: Julie Qiu <julieqiu@google.com>
-Reviewed-by: Roland Shoemaker <bracewell@google.com>
-Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802612
-Run-TryBot: Roland Shoemaker <bracewell@google.com>
-Change-Id: Ic7f10595615f2b2740d9c85ad7ef40dc0e78c04c
-Reviewed-on: https://go-review.googlesource.com/c/go/+/481987
-Auto-Submit: Michael Knyszek <mknyszek@google.com>
-TryBot-Result: Gopher Robot <gobot@golang.org>
-Run-TryBot: Michael Knyszek <mknyszek@google.com>
-Reviewed-by: Matthew Dempsky <mdempsky@google.com>
-
-Upstream-Status: Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b
-CVE: CVE-2023-24538
-Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
----
- src/html/template/context.go | 2 ++
- src/html/template/error.go | 13 +++++++++++++
- src/html/template/escape.go | 11 +++++++++++
- src/html/template/js.go | 2 ++
- src/html/template/jsctx_string.go | 9 +++++++++
- src/html/template/transition.go | 7 ++++++-
- 6 files changed, 43 insertions(+), 1 deletion(-)
-
-diff --git a/src/html/template/context.go b/src/html/template/context.go
-index f7d4849..0b65313 100644
---- a/src/html/template/context.go
-+++ b/src/html/template/context.go
-@@ -116,6 +116,8 @@ const (
- stateJSDqStr
- // stateJSSqStr occurs inside a JavaScript single quoted string.
- stateJSSqStr
-+ // stateJSBqStr occurs inside a JavaScript back quoted string.
-+ stateJSBqStr
- // stateJSRegexp occurs inside a JavaScript regexp literal.
- stateJSRegexp
- // stateJSBlockCmt occurs inside a JavaScript /* block comment */.
-diff --git a/src/html/template/error.go b/src/html/template/error.go
-index 0e52706..fd26b64 100644
---- a/src/html/template/error.go
-+++ b/src/html/template/error.go
-@@ -211,6 +211,19 @@ const (
- // pipeline occurs in an unquoted attribute value context, "html" is
- // disallowed. Avoid using "html" and "urlquery" entirely in new templates.
- ErrPredefinedEscaper
-+
-+ // errJSTmplLit: "... appears in a JS template literal"
-+ // Example:
-+ // <script>var tmpl = `{{.Interp}`</script>
-+ // Discussion:
-+ // Package html/template does not support actions inside of JS template
-+ // literals.
-+ //
-+ // TODO(rolandshoemaker): we cannot add this as an exported error in a minor
-+ // release, since it is backwards incompatible with the other minor
-+ // releases. As such we need to leave it unexported, and then we'll add it
-+ // in the next major release.
-+ errJSTmplLit
- )
-
- func (e *Error) Error() string {
-diff --git a/src/html/template/escape.go b/src/html/template/escape.go
-index f12dafa..29ca5b3 100644
---- a/src/html/template/escape.go
-+++ b/src/html/template/escape.go
-@@ -8,6 +8,7 @@ import (
- "bytes"
- "fmt"
- "html"
-+ "internal/godebug"
- "io"
- "text/template"
- "text/template/parse"
-@@ -203,6 +204,16 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
- c.jsCtx = jsCtxDivOp
- case stateJSDqStr, stateJSSqStr:
- s = append(s, "_html_template_jsstrescaper")
-+ case stateJSBqStr:
-+ debugAllowActionJSTmpl := godebug.Get("jstmpllitinterp")
-+ if debugAllowActionJSTmpl == "1" {
-+ s = append(s, "_html_template_jsstrescaper")
-+ } else {
-+ return context{
-+ state: stateError,
-+ err: errorf(errJSTmplLit, n, n.Line, "%s appears in a JS template literal", n),
-+ }
-+ }
- case stateJSRegexp:
- s = append(s, "_html_template_jsregexpescaper")
- case stateCSS:
-diff --git a/src/html/template/js.go b/src/html/template/js.go
-index ea9c183..b888eaf 100644
---- a/src/html/template/js.go
-+++ b/src/html/template/js.go
-@@ -308,6 +308,7 @@ var jsStrReplacementTable = []string{
- // Encode HTML specials as hex so the output can be embedded
- // in HTML attributes without further encoding.
- '"': `\u0022`,
-+ '`': `\u0060`,
- '&': `\u0026`,
- '\'': `\u0027`,
- '+': `\u002b`,
-@@ -331,6 +332,7 @@ var jsStrNormReplacementTable = []string{
- '"': `\u0022`,
- '&': `\u0026`,
- '\'': `\u0027`,
-+ '`': `\u0060`,
- '+': `\u002b`,
- '/': `\/`,
- '<': `\u003c`,
-diff --git a/src/html/template/jsctx_string.go b/src/html/template/jsctx_string.go
-index dd1d87e..2394893 100644
---- a/src/html/template/jsctx_string.go
-+++ b/src/html/template/jsctx_string.go
-@@ -4,6 +4,15 @@ package template
-
- import "strconv"
-
-+func _() {
-+ // An "invalid array index" compiler error signifies that the constant values have changed.
-+ // Re-run the stringer command to generate them again.
-+ var x [1]struct{}
-+ _ = x[jsCtxRegexp-0]
-+ _ = x[jsCtxDivOp-1]
-+ _ = x[jsCtxUnknown-2]
-+}
-+
- const _jsCtx_name = "jsCtxRegexpjsCtxDivOpjsCtxUnknown"
-
- var _jsCtx_index = [...]uint8{0, 11, 21, 33}
-diff --git a/src/html/template/transition.go b/src/html/template/transition.go
-index 06df679..92eb351 100644
---- a/src/html/template/transition.go
-+++ b/src/html/template/transition.go
-@@ -27,6 +27,7 @@ var transitionFunc = [...]func(context, []byte) (context, int){
- stateJS: tJS,
- stateJSDqStr: tJSDelimited,
- stateJSSqStr: tJSDelimited,
-+ stateJSBqStr: tJSDelimited,
- stateJSRegexp: tJSDelimited,
- stateJSBlockCmt: tBlockCmt,
- stateJSLineCmt: tLineCmt,
-@@ -262,7 +263,7 @@ func tURL(c context, s []byte) (context, int) {
-
- // tJS is the context transition function for the JS state.
- func tJS(c context, s []byte) (context, int) {
-- i := bytes.IndexAny(s, `"'/`)
-+ i := bytes.IndexAny(s, "\"`'/")
- if i == -1 {
- // Entire input is non string, comment, regexp tokens.
- c.jsCtx = nextJSCtx(s, c.jsCtx)
-@@ -274,6 +275,8 @@ func tJS(c context, s []byte) (context, int) {
- c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp
- case '\'':
- c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp
-+ case '`':
-+ c.state, c.jsCtx = stateJSBqStr, jsCtxRegexp
- case '/':
- switch {
- case i+1 < len(s) && s[i+1] == '/':
-@@ -303,6 +306,8 @@ func tJSDelimited(c context, s []byte) (context, int) {
- switch c.state {
- case stateJSSqStr:
- specials = `\'`
-+ case stateJSBqStr:
-+ specials = "`\\"
- case stateJSRegexp:
- specials = `\/[]`
- }
---
-2.7.4
similarity index 93%
rename from meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
rename to meta/recipes-devtools/go/go-1.14/CVE-2023-24538_1.patch
@@ -1,7 +1,7 @@
-From 8acd01094d9ee17f6e763a61e49a8a808b3a9ddb Mon Sep 17 00:00:00 2001
+From 828f69b9cf7c535a6e9591db546289c7b266b7f9 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick <bradfitz@golang.org>
Date: Mon, 2 Aug 2021 14:55:51 -0700
-Subject: [PATCH 1/3] net/netip: add new IP address package
+Subject: [PATCH 1/6] net/netip: add new IP address package
Co-authored-by: Alex Willmer <alex@moreati.org.uk> (GitHub @moreati)
Co-authored-by: Alexander Yastrebov <yastrebov.alex@gmail.com>
@@ -31,12 +31,12 @@ Trust: Brad Fitzpatrick <bradfitz@golang.org>
Dependency Patch #1
-Upstream-Status: Backport [https://github.com/golang/go/commit/a59e33224e42d60a97fa720a45e1b74eb6aaa3d0]
+Upstream-Status: Backport from https://github.com/golang/go/commit/a59e33224e42d60a97fa720a45e1b74eb6aaa3d0
CVE: CVE-2023-24538
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
---
- src/internal/godebug/godebug.go | 34 ++++++++++++++++++++++++++++++++++
- src/internal/godebug/godebug_test.go | 34 ++++++++++++++++++++++++++++++++++
+ src/internal/godebug/godebug.go | 34 ++++++++++++++++++++++++++++
+ src/internal/godebug/godebug_test.go | 34 ++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
create mode 100644 src/internal/godebug/godebug.go
create mode 100644 src/internal/godebug/godebug_test.go
@@ -121,5 +121,6 @@ index 0000000..41b9117
+ }
+ }
+}
---
-2.7.4
+--
+2.42.0
+
new file mode 100644
@@ -0,0 +1,636 @@
+From 778315e61f81f1e3d443d435f52304222c74269d Mon Sep 17 00:00:00 2001
+From: empijei <robclap8@gmail.com>
+Date: Fri, 27 Mar 2020 19:27:55 +0100
+Subject: [PATCH 2/6] html/template,text/template: switch to Unicode escapes
+ for JSON compatibility
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The existing implementation is not compatible with JSON
+escape as it uses hex escaping.
+Unicode escape, instead, is valid for both JSON and JS.
+This fix avoids creating a separate escaping context for
+scripts of type "application/ld+json" and it is more
+future-proof in case more JSON+JS contexts get added
+to the platform (e.g. import maps).
+
+Fixes #33671
+Fixes #37634
+
+Change-Id: Id6f6524b4abc52e81d9d744d46bbe5bf2e081543
+Reviewed-on: https://go-review.googlesource.com/c/go/+/226097
+Reviewed-by: Carl Johnson <me@carlmjohnson.net>
+Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
+Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
+TryBot-Result: Gobot Gobot <gobot@golang.org>
+
+Dependency Patch #2
+
+Upstream-Status: Backport from https://github.com/golang/go/commit/d4d298040d072ddacea0e0d6b55fb148fff18070
+CVE: CVE-2023-24538
+Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
+---
+ src/html/template/content_test.go | 70 +++++++++++++++---------------
+ src/html/template/escape_test.go | 6 +--
+ src/html/template/example_test.go | 6 +--
+ src/html/template/js.go | 70 ++++++++++++++++++------------
+ src/html/template/js_test.go | 68 ++++++++++++++---------------
+ src/html/template/template_test.go | 39 +++++++++++++++++
+ src/text/template/exec_test.go | 6 +--
+ src/text/template/funcs.go | 8 ++--
+ 8 files changed, 163 insertions(+), 110 deletions(-)
+
+diff --git a/src/html/template/content_test.go b/src/html/template/content_test.go
+index 72d56f5..bd86527 100644
+--- a/src/html/template/content_test.go
++++ b/src/html/template/content_test.go
+@@ -18,7 +18,7 @@ func TestTypedContent(t *testing.T) {
+ HTML(`Hello, <b>World</b> &tc!`),
+ HTMLAttr(` dir="ltr"`),
+ JS(`c && alert("Hello, World!");`),
+- JSStr(`Hello, World & O'Reilly\x21`),
++ JSStr(`Hello, World & O'Reilly\u0021`),
+ URL(`greeting=H%69,&addressee=(World)`),
+ Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`),
+ URL(`,foo/,`),
+@@ -70,7 +70,7 @@ func TestTypedContent(t *testing.T) {
+ `Hello, <b>World</b> &tc!`,
+ ` dir="ltr"`,
+ `c && alert("Hello, World!");`,
+- `Hello, World & O'Reilly\x21`,
++ `Hello, World & O'Reilly\u0021`,
+ `greeting=H%69,&addressee=(World)`,
+ `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
+ `,foo/,`,
+@@ -100,7 +100,7 @@ func TestTypedContent(t *testing.T) {
+ `Hello, World &tc!`,
+ ` dir="ltr"`,
+ `c && alert("Hello, World!");`,
+- `Hello, World & O'Reilly\x21`,
++ `Hello, World & O'Reilly\u0021`,
+ `greeting=H%69,&addressee=(World)`,
+ `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
+ `,foo/,`,
+@@ -115,7 +115,7 @@ func TestTypedContent(t *testing.T) {
+ `Hello, World &tc!`,
+ ` dir="ltr"`,
+ `c && alert("Hello, World!");`,
+- `Hello, World & O'Reilly\x21`,
++ `Hello, World & O'Reilly\u0021`,
+ `greeting=H%69,&addressee=(World)`,
+ `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
+ `,foo/,`,
+@@ -130,7 +130,7 @@ func TestTypedContent(t *testing.T) {
+ `Hello, <b>World</b> &tc!`,
+ ` dir="ltr"`,
+ `c && alert("Hello, World!");`,
+- `Hello, World & O'Reilly\x21`,
++ `Hello, World & O'Reilly\u0021`,
+ `greeting=H%69,&addressee=(World)`,
+ `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
+ `,foo/,`,
+@@ -146,7 +146,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/,"`,
+@@ -162,7 +162,7 @@ func TestTypedContent(t *testing.T) {
+ // Not JS escaped but HTML 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/,"`,
+@@ -171,30 +171,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\/,`,
+ },
+ },
+@@ -208,7 +208,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/,"`,
+@@ -224,7 +224,7 @@ func TestTypedContent(t *testing.T) {
+ `Hello, <b>World</b> &tc!`,
+ ` dir="ltr"`,
+ `c && alert("Hello, World!");`,
+- `Hello, World & O'Reilly\x21`,
++ `Hello, World & O'Reilly\u0021`,
+ `greeting=H%69,&addressee=(World)`,
+ `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
+ `,foo/,`,
+@@ -233,15 +233,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\/,`,
+ },
+ },
+@@ -253,7 +253,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,&addressee=%28World%29`,
+ `greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
+@@ -268,7 +268,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/src/html/template/escape_test.go b/src/html/template/escape_test.go
+index e72a9ba..c709660 100644
+--- a/src/html/template/escape_test.go
++++ b/src/html/template/escape_test.go
+@@ -238,7 +238,7 @@ func TestEscape(t *testing.T) {
+ {
+ "jsStr",
+ "<button onclick='alert("{{.H}}")'>",
+- `<button onclick='alert("\x3cHello\x3e")'>`,
++ `<button onclick='alert("\u003cHello\u003e")'>`,
+ },
+ {
+ "badMarshaler",
+@@ -259,7 +259,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",
+@@ -825,7 +825,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 <100></button>`,
++ `<button onclick="title='11 of \u003c100\u003e'; ...">11 of <100></button>`,
+ },
+ // A non-recursive template that ends in a different context.
+ // helper starts in jsCtxRegexp and ends in jsCtxDivOp.
+diff --git a/src/html/template/example_test.go b/src/html/template/example_test.go
+index 9d965f1..6cf936f 100644
+--- a/src/html/template/example_test.go
++++ b/src/html/template/example_test.go
+@@ -116,9 +116,9 @@ func Example_escape() {
+ // "Fran & Freddie's Diner" <tasty@example.com>
+ // "Fran & Freddie's Diner" <tasty@example.com>
+ // "Fran & Freddie's Diner"32<tasty@example.com>
+- // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E
+- // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E
+- // \"Fran \x26 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/src/html/template/js.go b/src/html/template/js.go
+index 0e91458..ea9c183 100644
+--- a/src/html/template/js.go
++++ b/src/html/template/js.go
+@@ -163,7 +163,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
+@@ -178,8 +177,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`.
+@@ -260,6 +259,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':
+@@ -283,67 +284,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`,
+ '?': `\?`,
+ '[': `\[`,
+ '\\': `\\`,
+diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go
+index 075adaa..d7ee47b 100644
+--- a/src/html/template/js_test.go
++++ b/src/html/template/js_test.go
+@@ -137,7 +137,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{}{}, "[]"},
+@@ -173,7 +173,7 @@ func TestJSStrEscaper(t *testing.T) {
+ }{
+ {"", ``},
+ {"foo", `foo`},
+- {"\u0000", `\0`},
++ {"\u0000", `\u0000`},
+ {"\t", `\t`},
+ {"\n", `\n`},
+ {"\r", `\r`},
+@@ -183,14 +183,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.
+- {`&`, `\x26amp;`},
++ {`&`, `\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
+@@ -201,11 +201,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"},
+@@ -228,7 +228,7 @@ func TestJSRegexpEscaper(t *testing.T) {
+ }{
+ {"", `(?:)`},
+ {"foo", `foo`},
+- {"\u0000", `\0`},
++ {"\u0000", `\u0000`},
+ {"\t", `\t`},
+ {"\n", `\n`},
+ {"\r", `\r`},
+@@ -238,19 +238,19 @@ func TestJSRegexpEscaper(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.
+- {`&`, `\x26amp;`},
++ {`&`, `\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`},
+ // Escaping text spans.
+- {"<!--", `\x3c!\-\-`},
+- {"-->", `\-\-\x3e`},
++ {"<!--", `\u003c!\-\-`},
++ {"-->", `\-\-\u003e`},
+ {"*", `\*`},
+- {"+", `\x2b`},
++ {"+", `\u002b`},
+ {"?", `\?`},
+ {"[](){}", `\[\]\(\)\{\}`},
+ {"$foo|x.y", `\$foo\|x\.y`},
+@@ -284,27 +284,27 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
+ {
+ "jsStrEscaper",
+ jsStrEscaper,
+- "\\0\x01\x02\x03\x04\x05\x06\x07" +
+- "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
+- "\x10\x11\x12\x13\x14\x15\x16\x17" +
+- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
+- ` !\x22#$%\x26\x27()*\x2b,-.\/` +
+- `0123456789:;\x3c=\x3e?` +
++ `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007` +
++ `\u0008\t\n\u000b\f\r\u000e\u000f` +
++ `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017` +
++ `\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` +
++ ` !\u0022#$%\u0026\u0027()*\u002b,-.\/` +
++ `0123456789:;\u003c=\u003e?` +
+ `@ABCDEFGHIJKLMNO` +
+ `PQRSTUVWXYZ[\\]^_` +
+ "`abcdefghijklmno" +
+- "pqrstuvwxyz{|}~\x7f" +
++ "pqrstuvwxyz{|}~\u007f" +
+ "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
+ },
+ {
+ "jsRegexpEscaper",
+ jsRegexpEscaper,
+- "\\0\x01\x02\x03\x04\x05\x06\x07" +
+- "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
+- "\x10\x11\x12\x13\x14\x15\x16\x17" +
+- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
+- ` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` +
+- `0123456789:;\x3c=\x3e\?` +
++ `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007` +
++ `\u0008\t\n\u000b\f\r\u000e\u000f` +
++ `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017` +
++ `\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` +
++ ` !\u0022#\$%\u0026\u0027\(\)\*\u002b,\-\.\/` +
++ `0123456789:;\u003c=\u003e\?` +
+ `@ABCDEFGHIJKLMNO` +
+ `PQRSTUVWXYZ\[\\\]\^_` +
+ "`abcdefghijklmno" +
+diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go
+index 13e6ba4..86bd4db 100644
+--- a/src/html/template/template_test.go
++++ b/src/html/template/template_test.go
+@@ -6,6 +6,7 @@ package template_test
+
+ import (
+ "bytes"
++ "encoding/json"
+ . "html/template"
+ "strings"
+ "testing"
+@@ -121,6 +122,44 @@ func TestNumbers(t *testing.T) {
+ c.mustExecute(c.root, nil, "12.34 7.5")
+ }
+
++func TestStringsInScriptsWithJsonContentTypeAreCorrectlyEscaped(t *testing.T) {
++ // See #33671 and #37634 for more context on this.
++ tests := []struct{ name, in string }{
++ {"empty", ""},
++ {"invalid", string(rune(-1))},
++ {"null", "\u0000"},
++ {"unit separator", "\u001F"},
++ {"tab", "\t"},
++ {"gt and lt", "<>"},
++ {"quotes", `'"`},
++ {"ASCII letters", "ASCII letters"},
++ {"Unicode", "ʕ⊙ϖ⊙ʔ"},
++ {"Pizza", "