]> Cypherpunks repositories - gostls13.git/commitdiff
html/template: escape script tags in JS errors case insensitively
authorRoland Shoemaker <roland@golang.org>
Mon, 9 Dec 2024 19:53:32 +0000 (11:53 -0800)
committerRoland Shoemaker <roland@golang.org>
Tue, 10 Dec 2024 19:03:42 +0000 (19:03 +0000)
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>
src/html/template/js.go
src/html/template/js_test.go

index d1463dee149101418f7c1ccc85a61f7113e10073..b3bf94801b238a034b59050dba0260aa7f360a57 100644 (file)
@@ -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)
        }
index 17cedcec05f7fdf238f2259f4d227058780694d2..015d97e6b501197f83b5dbf0297bc519fad27391 100644 (file)
@@ -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 {