diff options
| author | Roland Shoemaker <roland@golang.org> | 2024-12-09 11:53:32 -0800 |
|---|---|---|
| committer | Roland Shoemaker <roland@golang.org> | 2024-12-10 19:03:42 +0000 |
| commit | e6de1b2debe2bc7211f6f9cac4b64d7cd90f7c4e (patch) | |
| tree | 9c8e0d12ac5f919d2bac256924599aea02eb86eb /src/html | |
| parent | fce17b0c774593da0dfb39b829464d1da8ceca77 (diff) | |
| download | go-e6de1b2debe2bc7211f6f9cac4b64d7cd90f7c4e.tar.xz | |
html/template: escape script tags in JS errors case insensitively
Thanks to Juho Forsén of Mattermost for reporting this issue.
Fixes #70740
Change-Id: I1a49b199dee91cd2bb4df5b174aaa958dc040c18
Reviewed-on: https://go-review.googlesource.com/c/go/+/634696
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
Diffstat (limited to 'src/html')
| -rw-r--r-- | src/html/template/js.go | 11 | ||||
| -rw-r--r-- | src/html/template/js_test.go | 4 |
2 files changed, 9 insertions, 6 deletions
diff --git a/src/html/template/js.go b/src/html/template/js.go index d1463dee14..b3bf94801b 100644 --- a/src/html/template/js.go +++ b/src/html/template/js.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "reflect" + "regexp" "strings" "unicode/utf8" ) @@ -144,6 +145,8 @@ func indirectToJSONMarshaler(a any) any { return v.Interface() } +var scriptTagRe = regexp.MustCompile("(?i)<(/?)script") + // jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has // neither side-effects nor free variables outside (NaN, Infinity). func jsValEscaper(args ...any) string { @@ -181,9 +184,9 @@ func jsValEscaper(args ...any) string { // In particular we: // * replace "*/" comment end tokens with "* /", which does not // terminate the comment - // * replace "</script" with "\x3C/script", and "<!--" with - // "\x3C!--", which prevents confusing script block termination - // semantics + // * replace "<script" and "</script" with "\x3Cscript" and "\x3C/script" + // (case insensitively), and "<!--" with "\x3C!--", which prevents + // confusing script block termination semantics // // We also put a space before the comment so that if it is flush against // a division operator it is not turned into a line comment: @@ -192,8 +195,8 @@ func jsValEscaper(args ...any) string { // x//* error marshaling y: // second line of error message */null errStr := err.Error() + errStr = string(scriptTagRe.ReplaceAll([]byte(errStr), []byte(`\x3C${1}script`))) errStr = strings.ReplaceAll(errStr, "*/", "* /") - errStr = strings.ReplaceAll(errStr, "</script", `\x3C/script`) errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`) return fmt.Sprintf(" /* %s */null ", errStr) } diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go index 17cedcec05..015d97e6b5 100644 --- a/src/html/template/js_test.go +++ b/src/html/template/js_test.go @@ -107,7 +107,7 @@ func TestNextJsCtx(t *testing.T) { type jsonErrType struct{} func (e *jsonErrType) MarshalJSON() ([]byte, error) { - return nil, errors.New("beep */ boop </script blip <!--") + return nil, errors.New("a */ b <script c </script d <!-- e <sCrIpT f </sCrIpT") } func TestJSValEscaper(t *testing.T) { @@ -160,7 +160,7 @@ func TestJSValEscaper(t *testing.T) { {"</script", `"\u003c/script"`, false}, {"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E" {nil, " null ", false}, - {&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: beep * / boop \\x3C/script blip \\x3C!-- */null ", true}, + {&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: a * / b \\x3Cscript c \\x3C/script d \\x3C!-- e \\x3Cscript f \\x3C/script */null ", true}, } for _, test := range tests { |
