diff mbox series

[dunfell] go: Security fix for CVE-2023-24538

Message ID 1682315031-18840-1-git-send-email-skulkarni@mvista.com
State New, archived
Headers show
Series [dunfell] go: Security fix for CVE-2023-24538 | expand

Commit Message

Shubham Kulkarni April 24, 2023, 5:43 a.m. UTC
From: Shubham Kulkarni <skulkarni@mvista.com>

html/template: disallow actions in JS template literals

Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b

Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
---
 meta/recipes-devtools/go/go-1.14.inc               |   2 +
 .../go/go-1.14/CVE-2023-24538-1.patch              | 633 +++++++++++++++++++++
 .../go/go-1.14/CVE-2023-24538-2.patch              | 371 ++++++++++++
 3 files changed, 1006 insertions(+)
 create mode 100644 meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
 create mode 100644 meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch

Comments

Steve Sakoman April 24, 2023, 5:44 p.m. UTC | #1
Unfortunately the dunfell version of this patch does not apply
(kirkstone seems fine):

Applying: go: Security fix for CVE-2023-24538
error: corrupt patch at line 594
error: could not build fake ancestor
Patch failed at 0001 go: Security fix for CVE-2023-24538
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

Steve

On Sun, Apr 23, 2023 at 7:43 PM Shubham Kulkarni <skulkarni@mvista.com> wrote:
>
> From: Shubham Kulkarni <skulkarni@mvista.com>
>
> html/template: disallow actions in JS template literals
>
> Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b
>
> Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
> ---
>  meta/recipes-devtools/go/go-1.14.inc               |   2 +
>  .../go/go-1.14/CVE-2023-24538-1.patch              | 633 +++++++++++++++++++++
>  .../go/go-1.14/CVE-2023-24538-2.patch              | 371 ++++++++++++
>  3 files changed, 1006 insertions(+)
>  create mode 100644 meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
>  create mode 100644 meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch
>
> diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc
> index 56f4f12..01f9f1e 100644
> --- a/meta/recipes-devtools/go/go-1.14.inc
> +++ b/meta/recipes-devtools/go/go-1.14.inc
> @@ -57,6 +57,8 @@ SRC_URI += "\
>      file://CVE-2022-41722-2.patch \
>      file://CVE-2020-29510.patch \
>      file://CVE-2023-24537.patch \
> +    file://CVE-2023-24538-1.patch \
> +    file://CVE-2023-24538-2.patch \
>  "
>
>  SRC_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 0000000..4b0d200
> --- /dev/null
> +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
> @@ -0,0 +1,633 @@
> +From 64779a6dfb11631133ff3cfba7ae7e449c191a88 Mon Sep 17 00:00:00 2001
> +From: empijei <robclap8@gmail.com>
> +Date: Fri, 27 Mar 2020 19:27:55 +0100
> +Subject: [PATCH 1/2] 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>
> +
> +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> &amp;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> &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/,`,
> +@@ -100,7 +100,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/,`,
> +@@ -115,7 +115,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/,`,
> +@@ -130,7 +130,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/,`,
> +@@ -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 &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;`,
> +@@ -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> &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/,`,
> +@@ -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,&amp;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(&quot;{{.H}}&quot;)'>",
> +-                      `<button onclick='alert(&quot;\x3cHello\x3e&quot;)'>`,
> ++                      `<button onclick='alert(&quot;\u003cHello\u003e&quot;)'>`,
> +               },
> +               {
> +                       "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 &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/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() {
> +       // &#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 \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.
> +-              {`&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
> +@@ -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.
> +-              {`&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`},
> +               // 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", "
Steve Sakoman April 25, 2023, 2:32 p.m. UTC | #2
On Mon, Apr 24, 2023 at 7:45 AM Steve Sakoman via
lists.openembedded.org <steve=sakoman.com@lists.openembedded.org>
wrote:
>
> Unfortunately the dunfell version of this patch does not apply
> (kirkstone seems fine):

Ah, I spoke too soon!

The kirkstone version of the patch applies just fine but fails to
build both locally and on the autobuilder.

An example log of the failure from the autobuilder:

https://errors.yoctoproject.org/Errors/Details/700566/

Steve

> Applying: go: Security fix for CVE-2023-24538
> error: corrupt patch at line 594
> error: could not build fake ancestor
> Patch failed at 0001 go: Security fix for CVE-2023-24538
> hint: Use 'git am --show-current-patch=diff' to see the failed patch
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".
>
> Steve
>
> On Sun, Apr 23, 2023 at 7:43 PM Shubham Kulkarni <skulkarni@mvista.com> wrote:
> >
> > From: Shubham Kulkarni <skulkarni@mvista.com>
> >
> > html/template: disallow actions in JS template literals
> >
> > Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b
> >
> > Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
> > ---
> >  meta/recipes-devtools/go/go-1.14.inc               |   2 +
> >  .../go/go-1.14/CVE-2023-24538-1.patch              | 633 +++++++++++++++++++++
> >  .../go/go-1.14/CVE-2023-24538-2.patch              | 371 ++++++++++++
> >  3 files changed, 1006 insertions(+)
> >  create mode 100644 meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
> >  create mode 100644 meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch
> >
> > diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc
> > index 56f4f12..01f9f1e 100644
> > --- a/meta/recipes-devtools/go/go-1.14.inc
> > +++ b/meta/recipes-devtools/go/go-1.14.inc
> > @@ -57,6 +57,8 @@ SRC_URI += "\
> >      file://CVE-2022-41722-2.patch \
> >      file://CVE-2020-29510.patch \
> >      file://CVE-2023-24537.patch \
> > +    file://CVE-2023-24538-1.patch \
> > +    file://CVE-2023-24538-2.patch \
> >  "
> >
> >  SRC_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 0000000..4b0d200
> > --- /dev/null
> > +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
> > @@ -0,0 +1,633 @@
> > +From 64779a6dfb11631133ff3cfba7ae7e449c191a88 Mon Sep 17 00:00:00 2001
> > +From: empijei <robclap8@gmail.com>
> > +Date: Fri, 27 Mar 2020 19:27:55 +0100
> > +Subject: [PATCH 1/2] 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>
> > +
> > +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> &amp;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> &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/,`,
> > +@@ -100,7 +100,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/,`,
> > +@@ -115,7 +115,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/,`,
> > +@@ -130,7 +130,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/,`,
> > +@@ -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 &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;`,
> > +@@ -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> &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/,`,
> > +@@ -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,&amp;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(&quot;{{.H}}&quot;)'>",
> > +-                      `<button onclick='alert(&quot;\x3cHello\x3e&quot;)'>`,
> > ++                      `<button onclick='alert(&quot;\u003cHello\u003e&quot;)'>`,
> > +               },
> > +               {
> > +                       "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 &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/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() {
> > +       // &#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 \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.
> > +-              {`&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
> > +@@ -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.
> > +-              {`&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`},
> > +               // 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", "
Shubham Kulkarni April 26, 2023, 5:04 a.m. UTC | #3
Hi Steve,

Apologies for the inconvenience caused. I will look into the issue and will
send v2 for both the branches

Thanks,
Shubham

On Tue, Apr 25, 2023 at 8:02 PM Steve Sakoman <steve@sakoman.com> wrote:

> On Mon, Apr 24, 2023 at 7:45 AM Steve Sakoman via
> lists.openembedded.org <steve=sakoman.com@lists.openembedded.org>
> wrote:
> >
> > Unfortunately the dunfell version of this patch does not apply
> > (kirkstone seems fine):
>
> Ah, I spoke too soon!
>
> The kirkstone version of the patch applies just fine but fails to
> build both locally and on the autobuilder.
>
> An example log of the failure from the autobuilder:
>
> https://errors.yoctoproject.org/Errors/Details/700566/
>
> Steve
>
> > Applying: go: Security fix for CVE-2023-24538
> > error: corrupt patch at line 594
> > error: could not build fake ancestor
> > Patch failed at 0001 go: Security fix for CVE-2023-24538
> > hint: Use 'git am --show-current-patch=diff' to see the failed patch
> > When you have resolved this problem, run "git am --continue".
> > If you prefer to skip this patch, run "git am --skip" instead.
> > To restore the original branch and stop patching, run "git am --abort".
> >
> > Steve
> >
> > On Sun, Apr 23, 2023 at 7:43 PM Shubham Kulkarni <skulkarni@mvista.com>
> wrote:
> > >
> > > From: Shubham Kulkarni <skulkarni@mvista.com>
> > >
> > > html/template: disallow actions in JS template literals
> > >
> > > Backport from
> https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b
> > >
> > > Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
> > > ---
> > >  meta/recipes-devtools/go/go-1.14.inc               |   2 +
> > >  .../go/go-1.14/CVE-2023-24538-1.patch              | 633
> +++++++++++++++++++++
> > >  .../go/go-1.14/CVE-2023-24538-2.patch              | 371 ++++++++++++
> > >  3 files changed, 1006 insertions(+)
> > >  create mode 100644
> meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
> > >  create mode 100644
> meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch
> > >
> > > diff --git a/meta/recipes-devtools/go/go-1.14.inc
> b/meta/recipes-devtools/go/go-1.14.inc
> > > index 56f4f12..01f9f1e 100644
> > > --- a/meta/recipes-devtools/go/go-1.14.inc
> > > +++ b/meta/recipes-devtools/go/go-1.14.inc
> > > @@ -57,6 +57,8 @@ SRC_URI += "\
> > >      file://CVE-2022-41722-2.patch \
> > >      file://CVE-2020-29510.patch \
> > >      file://CVE-2023-24537.patch \
> > > +    file://CVE-2023-24538-1.patch \
> > > +    file://CVE-2023-24538-2.patch \
> > >  "
> > >
> > >  SRC_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 0000000..4b0d200
> > > --- /dev/null
> > > +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
> > > @@ -0,0 +1,633 @@
> > > +From 64779a6dfb11631133ff3cfba7ae7e449c191a88 Mon Sep 17 00:00:00 2001
> > > +From: empijei <robclap8@gmail.com>
> > > +Date: Fri, 27 Mar 2020 19:27:55 +0100
> > > +Subject: [PATCH 1/2] 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>
> > > +
> > > +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> &amp;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> &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/,`,
> > > +@@ -100,7 +100,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`
> <https://golang.org/favicon.ico&#32;500.5w>,
> > > +                               `,foo/,`,
> > > +@@ -115,7 +115,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/,`,
> > > +@@ -130,7 +130,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/,`,
> > > +@@ -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 &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;`,
> > > +@@ -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> &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/,`,
> > > +@@ -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,&amp;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(&quot;{{.H}}&quot;)'>",
> > > +-                      `<button
> onclick='alert(&quot;\x3cHello\x3e&quot;)'>`,
> > > ++                      `<button
> onclick='alert(&quot;\u003cHello\u003e&quot;)'>`,
> > > +               },
> > > +               {
> > > +                       "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 &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/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() {
> > > +       // &#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 \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.
> > > +-              {`&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
> > > +@@ -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.
> > > +-              {`&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`},
> > > +               // 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", "
diff mbox series

Patch

diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc
index 56f4f12..01f9f1e 100644
--- a/meta/recipes-devtools/go/go-1.14.inc
+++ b/meta/recipes-devtools/go/go-1.14.inc
@@ -57,6 +57,8 @@  SRC_URI += "\
     file://CVE-2022-41722-2.patch \
     file://CVE-2020-29510.patch \
     file://CVE-2023-24537.patch \
+    file://CVE-2023-24538-1.patch \
+    file://CVE-2023-24538-2.patch \
 "
 
 SRC_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 0000000..4b0d200
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch
@@ -0,0 +1,633 @@ 
+From 64779a6dfb11631133ff3cfba7ae7e449c191a88 Mon Sep 17 00:00:00 2001
+From: empijei <robclap8@gmail.com>
+Date: Fri, 27 Mar 2020 19:27:55 +0100
+Subject: [PATCH 1/2] 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>
+
+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> &amp;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> &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/,`,
+@@ -100,7 +100,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/,`,
+@@ -115,7 +115,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/,`,
+@@ -130,7 +130,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/,`,
+@@ -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 &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;`,
+@@ -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> &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/,`,
+@@ -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,&amp;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(&quot;{{.H}}&quot;)'>",
+-			`<button onclick='alert(&quot;\x3cHello\x3e&quot;)'>`,
++			`<button onclick='alert(&quot;\u003cHello\u003e&quot;)'>`,
+		},
+		{
+			"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 &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/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() {
+	// &#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 \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.
+-		{`&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
+@@ -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.
+-		{`&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`},
+		// 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", "