diff options
Diffstat (limited to 'src/html/template')
| -rw-r--r-- | src/html/template/context.go | 23 | ||||
| -rw-r--r-- | src/html/template/escape_test.go | 14 | ||||
| -rw-r--r-- | src/html/template/transition.go | 30 |
3 files changed, 37 insertions, 30 deletions
diff --git a/src/html/template/context.go b/src/html/template/context.go index 63d5c31b01..b78f0f7325 100644 --- a/src/html/template/context.go +++ b/src/html/template/context.go @@ -17,16 +17,19 @@ import ( // https://www.w3.org/TR/html5/syntax.html#the-end // where the context element is null. type context struct { - state state - delim delim - urlPart urlPart - jsCtx jsCtx - jsTmplExprDepth int - jsBraceDepth int - attr attr - element element - n parse.Node // for range break/continue - err *Error + state state + delim delim + urlPart urlPart + jsCtx jsCtx + // jsBraceDepth contains the current depth, for each JS template literal + // string interpolation expression, of braces we've seen. This is used to + // determine if the next } will close a JS template literal string + // interpolation expression or not. + jsBraceDepth []int + attr attr + element element + n parse.Node // for range break/continue + err *Error } func (c context) String() string { diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index 91fbfb9a3c..497ead8716 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -1797,13 +1797,25 @@ func TestEscapeText(t *testing.T) { context{state: stateJS, element: elementScript, jsCtx: jsCtxDivOp}, }, { - "<script>`${ { `` }`", + "<script>`${ { `` }", context{state: stateJS, element: elementScript}, }, { "<script>`${ { }`", context{state: stateJSTmplLit, element: elementScript}, }, + { + "<script>var foo = `${ foo({ a: { c: `${", + context{state: stateJS, element: elementScript}, + }, + { + "<script>var foo = `${ foo({ a: { c: `${ {{.}} }` }, b: ", + context{state: stateJS, element: elementScript}, + }, + { + "<script>`${ `}", + context{state: stateJSTmplLit, element: elementScript}, + }, } for _, test := range tests { diff --git a/src/html/template/transition.go b/src/html/template/transition.go index 4aa2920986..d5a05f66da 100644 --- a/src/html/template/transition.go +++ b/src/html/template/transition.go @@ -323,29 +323,23 @@ func tJS(c context, s []byte) (context, int) { case '{': // We only care about tracking brace depth if we are inside of a // template literal. - if c.jsTmplExprDepth == 0 { + if len(c.jsBraceDepth) == 0 { return c, i + 1 } - c.jsBraceDepth++ + c.jsBraceDepth[len(c.jsBraceDepth)-1]++ case '}': - if c.jsTmplExprDepth == 0 { + if len(c.jsBraceDepth) == 0 { return c, i + 1 } - for j := 0; j <= i; j++ { - switch s[j] { - case '\\': - j++ - case '{': - c.jsBraceDepth++ - case '}': - c.jsBraceDepth-- - } - } - if c.jsBraceDepth >= 0 { + // There are no cases where a brace can be escaped in the JS context + // that are not syntax errors, it seems. Because of this we can just + // 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 { return c, i + 1 } - c.jsTmplExprDepth-- - c.jsBraceDepth = 0 + c.jsBraceDepth = c.jsBraceDepth[:len(c.jsBraceDepth)-1] c.state = stateJSTmplLit default: panic("unreachable") @@ -354,7 +348,6 @@ func tJS(c context, s []byte) (context, int) { } func tJSTmpl(c context, s []byte) (context, int) { - c.jsBraceDepth = 0 var k int for { i := k + bytes.IndexAny(s[k:], "`\\$") @@ -372,8 +365,7 @@ func tJSTmpl(c context, s []byte) (context, int) { } case '$': if len(s) >= i+2 && s[i+1] == '{' { - c.jsTmplExprDepth++ - c.jsBraceDepth = 0 + c.jsBraceDepth = append(c.jsBraceDepth, 0) c.state = stateJS return c, i + 2 } |
