Skip to content
Snippets Groups Projects
Commit e6de1b2d authored by Roland Shoemaker's avatar Roland Shoemaker
Browse files

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: default avatarDamien Neil <dneil@google.com>
parent fce17b0c
Branches
Tags
No related merge requests found
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"regexp"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
) )
...@@ -144,6 +145,8 @@ func indirectToJSONMarshaler(a any) any { ...@@ -144,6 +145,8 @@ func indirectToJSONMarshaler(a any) any {
return v.Interface() return v.Interface()
} }
var scriptTagRe = regexp.MustCompile("(?i)<(/?)script")
// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has // jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
// neither side-effects nor free variables outside (NaN, Infinity). // neither side-effects nor free variables outside (NaN, Infinity).
func jsValEscaper(args ...any) string { func jsValEscaper(args ...any) string {
...@@ -181,9 +184,9 @@ func jsValEscaper(args ...any) string { ...@@ -181,9 +184,9 @@ func jsValEscaper(args ...any) string {
// In particular we: // In particular we:
// * replace "*/" comment end tokens with "* /", which does not // * replace "*/" comment end tokens with "* /", which does not
// terminate the comment // terminate the comment
// * replace "</script" with "\x3C/script", and "<!--" with // * replace "<script" and "</script" with "\x3Cscript" and "\x3C/script"
// "\x3C!--", which prevents confusing script block termination // (case insensitively), and "<!--" with "\x3C!--", which prevents
// semantics // confusing script block termination semantics
// //
// We also put a space before the comment so that if it is flush against // 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: // a division operator it is not turned into a line comment:
...@@ -192,8 +195,8 @@ func jsValEscaper(args ...any) string { ...@@ -192,8 +195,8 @@ func jsValEscaper(args ...any) string {
// x//* error marshaling y: // x//* error marshaling y:
// second line of error message */null // second line of error message */null
errStr := err.Error() errStr := err.Error()
errStr = string(scriptTagRe.ReplaceAll([]byte(errStr), []byte(`\x3C${1}script`)))
errStr = strings.ReplaceAll(errStr, "*/", "* /") errStr = strings.ReplaceAll(errStr, "*/", "* /")
errStr = strings.ReplaceAll(errStr, "</script", `\x3C/script`)
errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`) errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`)
return fmt.Sprintf(" /* %s */null ", errStr) return fmt.Sprintf(" /* %s */null ", errStr)
} }
......
...@@ -107,7 +107,7 @@ func TestNextJsCtx(t *testing.T) { ...@@ -107,7 +107,7 @@ func TestNextJsCtx(t *testing.T) {
type jsonErrType struct{} type jsonErrType struct{}
func (e *jsonErrType) MarshalJSON() ([]byte, error) { 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) { func TestJSValEscaper(t *testing.T) {
...@@ -160,7 +160,7 @@ func TestJSValEscaper(t *testing.T) { ...@@ -160,7 +160,7 @@ func TestJSValEscaper(t *testing.T) {
{"</script", `"\u003c/script"`, false}, {"</script", `"\u003c/script"`, false},
{"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E" {"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E"
{nil, " null ", false}, {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 { for _, test := range tests {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment