src/html/template/escape_test.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/html/template/transition.go | 4 ++++ diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index 5ff173420207dd703908420b9abb67a3d54cd0ad..3b26f7815fee377bb6d768629f7a76f45a8b5736 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -985,7 +985,6 @@ if got := b.String(); test.want != got { t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got) } } - } func TestErrors(t *testing.T) { @@ -1259,7 +1258,6 @@ } // Check that we get the same error if we call Execute again. if err := tmpl.Execute(buf, nil); err == nil || err.Error() != got { t.Errorf("input=%q: unexpected error on second call %q", test.input, err) - } } } @@ -1864,7 +1862,7 @@ context{state: stateJS, element: elementScript, jsCtx: jsCtxDivOp}, }, { "`, + input: "a.b", + want: ``, + }, + { + name: "regexp after close brace", + tmpl: ``, + input: "a.b", + want: ``, + }, + { + name: "regexp pathological attacker input", + tmpl: ``, + input: `./;alert(1);var q=/.`, + want: ``, + }, + { + name: "regexp after open brace in template literal", + tmpl: "", + input: "a.b", + want: "", + }, + { + name: "regexp after close brace in template literal", + tmpl: "", + input: "a.b", + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpl := Must(New("test").Parse(tt.tmpl)) + var buf bytes.Buffer + if err := tmpl.Execute(&buf, tt.input); err != nil { + t.Fatalf("Execute: %v", err) + } + if got := buf.String(); got != tt.want { + t.Errorf("got: %s\nwant: %s", got, tt.want) + } + }) + } +} diff --git a/src/html/template/transition.go b/src/html/template/transition.go index 05b6abd03d1772926a45a387bdca6116026d0ec4..d9d4f63beba80737fc4e5e1edc1a9f42cc122263 100644 --- a/src/html/template/transition.go +++ b/src/html/template/transition.go @@ -336,11 +336,14 @@ case '{': // We only care about tracking brace depth if we are inside of a // template literal. if len(c.jsBraceDepth) == 0 { + c.jsCtx = nextJSCtx(s[i:i+1], c.jsCtx) return c, i + 1 } c.jsBraceDepth[len(c.jsBraceDepth)-1]++ + c.jsCtx = nextJSCtx(s[i:i+1], c.jsCtx) case '}': if len(c.jsBraceDepth) == 0 { + c.jsCtx = nextJSCtx(s[i:i+1], c.jsCtx) return c, i + 1 } // There are no cases where a brace can be escaped in the JS context @@ -349,6 +352,7 @@ // count "\}" as "}" and move on, the script is already broken as // fully fledged parsers will just fail anyway. c.jsBraceDepth[len(c.jsBraceDepth)-1]-- if c.jsBraceDepth[len(c.jsBraceDepth)-1] >= 0 { + c.jsCtx = nextJSCtx(s[i:i+1], c.jsCtx) return c, i + 1 } c.jsBraceDepth = c.jsBraceDepth[:len(c.jsBraceDepth)-1]