Skip to content

Commit e6de1b2

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 <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent fce17b0 commit e6de1b2

File tree

2 files changed

+9
-6
lines changed

2 files changed

+9
-6
lines changed

src/html/template/js.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/json"
1010
"fmt"
1111
"reflect"
12+
"regexp"
1213
"strings"
1314
"unicode/utf8"
1415
)
@@ -144,6 +145,8 @@ func indirectToJSONMarshaler(a any) any {
144145
return v.Interface()
145146
}
146147

148+
var scriptTagRe = regexp.MustCompile("(?i)<(/?)script")
149+
147150
// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
148151
// neither side-effects nor free variables outside (NaN, Infinity).
149152
func jsValEscaper(args ...any) string {
@@ -181,9 +184,9 @@ func jsValEscaper(args ...any) string {
181184
// In particular we:
182185
// * replace "*/" comment end tokens with "* /", which does not
183186
// terminate the comment
184-
// * replace "</script" with "\x3C/script", and "<!--" with
185-
// "\x3C!--", which prevents confusing script block termination
186-
// semantics
187+
// * replace "<script" and "</script" with "\x3Cscript" and "\x3C/script"
188+
// (case insensitively), and "<!--" with "\x3C!--", which prevents
189+
// confusing script block termination semantics
187190
//
188191
// We also put a space before the comment so that if it is flush against
189192
// a division operator it is not turned into a line comment:
@@ -192,8 +195,8 @@ func jsValEscaper(args ...any) string {
192195
// x//* error marshaling y:
193196
// second line of error message */null
194197
errStr := err.Error()
198+
errStr = string(scriptTagRe.ReplaceAll([]byte(errStr), []byte(`\x3C${1}script`)))
195199
errStr = strings.ReplaceAll(errStr, "*/", "* /")
196-
errStr = strings.ReplaceAll(errStr, "</script", `\x3C/script`)
197200
errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`)
198201
return fmt.Sprintf(" /* %s */null ", errStr)
199202
}

src/html/template/js_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func TestNextJsCtx(t *testing.T) {
107107
type jsonErrType struct{}
108108

109109
func (e *jsonErrType) MarshalJSON() ([]byte, error) {
110-
return nil, errors.New("beep */ boop </script blip <!--")
110+
return nil, errors.New("a */ b <script c </script d <!-- e <sCrIpT f </sCrIpT")
111111
}
112112

113113
func TestJSValEscaper(t *testing.T) {
@@ -160,7 +160,7 @@ func TestJSValEscaper(t *testing.T) {
160160
{"</script", `"\u003c/script"`, false},
161161
{"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E"
162162
{nil, " null ", false},
163-
{&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: beep * / boop \\x3C/script blip \\x3C!-- */null ", true},
163+
{&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: a * / b \\x3Cscript c \\x3C/script d \\x3C!-- e \\x3Cscript f \\x3C/script */null ", true},
164164
}
165165

166166
for _, test := range tests {

0 commit comments

Comments
 (0)