HTML(`Hello, <b>World</b> &tc!`),
HTMLAttr(` dir="ltr"`),
JS(`c && alert("Hello, World!");`),
- JSStr(`Hello, World & O'Reilly\x21`),
+ JSStr(`Hello, World & O'Reilly\u0021`),
URL(`greeting=H%69,&addressee=(World)`),
Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`),
URL(`,foo/,`),
`Hello, <b>World</b> &tc!`,
` dir="ltr"`,
`c && alert("Hello, World!");`,
- `Hello, World & O'Reilly\x21`,
+ `Hello, World & O'Reilly\u0021`,
`greeting=H%69,&addressee=(World)`,
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
`Hello, World &tc!`,
` dir="ltr"`,
`c && alert("Hello, World!");`,
- `Hello, World & O'Reilly\x21`,
+ `Hello, World & O'Reilly\u0021`,
`greeting=H%69,&addressee=(World)`,
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
`Hello, World &tc!`,
` dir="ltr"`,
`c && alert("Hello, World!");`,
- `Hello, World & O'Reilly\x21`,
+ `Hello, World & O'Reilly\u0021`,
`greeting=H%69,&addressee=(World)`,
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
`Hello, <b>World</b> &tc!`,
` dir="ltr"`,
`c && alert("Hello, World!");`,
- `Hello, World & O'Reilly\x21`,
+ `Hello, World & O'Reilly\u0021`,
`greeting=H%69,&addressee=(World)`,
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
// 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/,"`,
// Not JS escaped but HTML escaped.
`c && alert("Hello, World!");`,
// Escape sequence not over-escaped.
- `"Hello, World & O'Reilly\x21"`,
+ `"Hello, World & O'Reilly\u0021"`,
`"greeting=H%69,\u0026addressee=(World)"`,
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
`",foo/,"`,
{
`<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\/,`,
},
},
// 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/,"`,
`Hello, <b>World</b> &tc!`,
` dir="ltr"`,
`c && alert("Hello, World!");`,
- `Hello, World & O'Reilly\x21`,
+ `Hello, World & O'Reilly\u0021`,
`greeting=H%69,&addressee=(World)`,
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
`,foo/,`,
{
`<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\/,`,
},
},
`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
`%20dir%3d%22ltr%22`,
`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
- `Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
+ `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
`greeting=H%69,&addressee=%28World%29`,
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
`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`,
{
"jsStr",
"<button onclick='alert("{{.H}}")'>",
- `<button onclick='alert("\x3cHello\x3e")'>`,
+ `<button onclick='alert("\u003cHello\u003e")'>`,
},
{
"badMarshaler",
{
"jsRe",
`<button onclick='alert(/{{"foo+bar"}}/.test(""))'>`,
- `<button onclick='alert(/foo\x2bbar/.test(""))'>`,
+ `<button onclick='alert(/foo\u002bbar/.test(""))'>`,
},
{
"jsReBlank",
"main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`,
"helper": `{{11}} of {{"<100>"}}`,
},
- `<button onclick="title='11 of \x3c100\x3e'; ...">11 of <100></button>`,
+ `<button onclick="title='11 of \u003c100\u003e'; ...">11 of <100></button>`,
},
// A non-recursive template that ends in a different context.
// helper starts in jsCtxRegexp and ends in jsCtxDivOp.
// "Fran & Freddie's Diner" <tasty@example.com>
// "Fran & Freddie's Diner" <tasty@example.com>
// "Fran & Freddie's Diner"32<tasty@example.com>
- // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E
- // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E
- // \"Fran \x26 Freddie\'s Diner\"32\x3Ctasty@example.com\x3E
+ // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
+ // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
+ // \"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E
// %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E
}
}
// 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
// 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`.
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':
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`,
'?': `\?`,
'[': `\[`,
'\\': `\\`,
{"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{}{}, "[]"},
}{
{"", ``},
{"foo", `foo`},
- {"\u0000", `\0`},
+ {"\u0000", `\u0000`},
{"\t", `\t`},
{"\n", `\n`},
{"\r", `\r`},
{"\\n", `\\n`},
{"foo\r\nbar", `foo\r\nbar`},
// Preserve attribute boundaries.
- {`"`, `\x22`},
- {`'`, `\x27`},
+ {`"`, `\u0022`},
+ {`'`, `\u0027`},
// Allow embedding in HTML without further escaping.
- {`&`, `\x26amp;`},
+ {`&`, `\u0026amp;`},
// Prevent breaking out of text node and element boundaries.
- {"</script>", `\x3c\/script\x3e`},
- {"<![CDATA[", `\x3c![CDATA[`},
- {"]]>", `]]\x3e`},
+ {"</script>", `\u003c\/script\u003e`},
+ {"<![CDATA[", `\u003c![CDATA[`},
+ {"]]>", `]]\u003e`},
// https://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span
// "The text in style, script, title, and textarea elements
// must not have an escaping text span start that is not
// 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"},
}{
{"", `(?:)`},
{"foo", `foo`},
- {"\u0000", `\0`},
+ {"\u0000", `\u0000`},
{"\t", `\t`},
{"\n", `\n`},
{"\r", `\r`},
{"\\n", `\\n`},
{"foo\r\nbar", `foo\r\nbar`},
// Preserve attribute boundaries.
- {`"`, `\x22`},
- {`'`, `\x27`},
+ {`"`, `\u0022`},
+ {`'`, `\u0027`},
// Allow embedding in HTML without further escaping.
- {`&`, `\x26amp;`},
+ {`&`, `\u0026amp;`},
// Prevent breaking out of text node and element boundaries.
- {"</script>", `\x3c\/script\x3e`},
- {"<![CDATA[", `\x3c!\[CDATA\[`},
- {"]]>", `\]\]\x3e`},
+ {"</script>", `\u003c\/script\u003e`},
+ {"<![CDATA[", `\u003c!\[CDATA\[`},
+ {"]]>", `\]\]\u003e`},
// Escaping text spans.
- {"<!--", `\x3c!\-\-`},
- {"-->", `\-\-\x3e`},
+ {"<!--", `\u003c!\-\-`},
+ {"-->", `\-\-\u003e`},
{"*", `\*`},
- {"+", `\x2b`},
+ {"+", `\u002b`},
{"?", `\?`},
{"[](){}", `\[\]\(\)\{\}`},
{"$foo|x.y", `\$foo\|x\.y`},
{
"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" +
import (
"bytes"
+ "encoding/json"
. "html/template"
"strings"
"testing"
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", "🍕"},
+ }
+ const (
+ prefix = `<script type="application/ld+json">`
+ suffix = `</script>`
+ templ = prefix + `"{{.}}"` + suffix
+ )
+ tpl := Must(New("JS string is JSON string").Parse(templ))
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var buf bytes.Buffer
+ if err := tpl.Execute(&buf, tt.in); err != nil {
+ t.Fatalf("Cannot render template: %v", err)
+ }
+ trimmed := bytes.TrimSuffix(bytes.TrimPrefix(buf.Bytes(), []byte(prefix)), []byte(suffix))
+ var got string
+ if err := json.Unmarshal(trimmed, &got); err != nil {
+ t.Fatalf("Cannot parse JS string %q as JSON: %v", trimmed[1:len(trimmed)-1], err)
+ }
+ if got != tt.in {
+ t.Errorf("Serialization changed the string value: got %q want %q", got, tt.in)
+ }
+ })
+ }
+}
+
type testCase struct {
t *testing.T
root *Template
{`Go "jump" \`, `Go \"jump\" \\`},
{`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
{"unprintable \uFDFF", `unprintable \uFDFF`},
- {`<html>`, `\x3Chtml\x3E`},
- {`no = in attributes`, `no \x3D in attributes`},
- {`' does not become HTML entity`, `\x26#x27; does not become HTML entity`},
+ {`<html>`, `\u003Chtml\u003E`},
+ {`no = in attributes`, `no \u003D in attributes`},
+ {`' does not become HTML entity`, `\u0026#x27; does not become HTML entity`},
}
for _, tc := range testCases {
s := JSEscapeString(tc.in)
jsBackslash = []byte(`\\`)
jsApos = []byte(`\'`)
jsQuot = []byte(`\"`)
- jsLt = []byte(`\x3C`)
- jsGt = []byte(`\x3E`)
- jsAmp = []byte(`\x26`)
- jsEq = []byte(`\x3D`)
+ jsLt = []byte(`\u003C`)
+ jsGt = []byte(`\u003E`)
+ jsAmp = []byte(`\u0026`)
+ jsEq = []byte(`\u003D`)
)
// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.