diff --git a/api/next/61619.txt b/api/next/61619.txt new file mode 100644 index 0000000000..c63a3140e8 --- /dev/null +++ b/api/next/61619.txt @@ -0,0 +1 @@ +pkg html/template, const ErrJSTemplate //deprecated #61619 diff --git a/src/html/template/context.go b/src/html/template/context.go index 16b5e65317..63d5c31b01 100644 --- a/src/html/template/context.go +++ b/src/html/template/context.go @@ -17,14 +17,16 @@ 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 - attr attr - element element - n parse.Node // for range break/continue - err *Error + 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 } func (c context) String() string { @@ -120,8 +122,8 @@ const ( stateJSDqStr // stateJSSqStr occurs inside a JavaScript single quoted string. stateJSSqStr - // stateJSBqStr occurs inside a JavaScript back quoted string. - stateJSBqStr + // stateJSTmplLit occurs inside a JavaScript back quoted string. + stateJSTmplLit // stateJSRegexp occurs inside a JavaScript regexp literal. stateJSRegexp // stateJSBlockCmt occurs inside a JavaScript /* block comment */. @@ -182,7 +184,7 @@ func isInScriptLiteral(s state) bool { // stateJSHTMLOpenCmt, stateJSHTMLCloseCmt) because their content is already // omitted from the output. switch s { - case stateJSDqStr, stateJSSqStr, stateJSBqStr, stateJSRegexp: + case stateJSDqStr, stateJSSqStr, stateJSTmplLit, stateJSRegexp: return true } return false diff --git a/src/html/template/error.go b/src/html/template/error.go index a763924d4a..805a788bfc 100644 --- a/src/html/template/error.go +++ b/src/html/template/error.go @@ -221,6 +221,10 @@ const ( // Discussion: // Package html/template does not support actions inside of JS template // literals. + // + // Deprecated: ErrJSTemplate is no longer returned when an action is present + // in a JS template literal. Actions inside of JS template literals are now + // escaped as expected. ErrJSTemplate ) diff --git a/src/html/template/escape.go b/src/html/template/escape.go index 01f6303a44..1eace16e25 100644 --- a/src/html/template/escape.go +++ b/src/html/template/escape.go @@ -62,22 +62,23 @@ func evalArgs(args ...any) string { // funcMap maps command names to functions that render their inputs safe. var funcMap = template.FuncMap{ - "_html_template_attrescaper": attrEscaper, - "_html_template_commentescaper": commentEscaper, - "_html_template_cssescaper": cssEscaper, - "_html_template_cssvaluefilter": cssValueFilter, - "_html_template_htmlnamefilter": htmlNameFilter, - "_html_template_htmlescaper": htmlEscaper, - "_html_template_jsregexpescaper": jsRegexpEscaper, - "_html_template_jsstrescaper": jsStrEscaper, - "_html_template_jsvalescaper": jsValEscaper, - "_html_template_nospaceescaper": htmlNospaceEscaper, - "_html_template_rcdataescaper": rcdataEscaper, - "_html_template_srcsetescaper": srcsetFilterAndEscaper, - "_html_template_urlescaper": urlEscaper, - "_html_template_urlfilter": urlFilter, - "_html_template_urlnormalizer": urlNormalizer, - "_eval_args_": evalArgs, + "_html_template_attrescaper": attrEscaper, + "_html_template_commentescaper": commentEscaper, + "_html_template_cssescaper": cssEscaper, + "_html_template_cssvaluefilter": cssValueFilter, + "_html_template_htmlnamefilter": htmlNameFilter, + "_html_template_htmlescaper": htmlEscaper, + "_html_template_jsregexpescaper": jsRegexpEscaper, + "_html_template_jsstrescaper": jsStrEscaper, + "_html_template_jstmpllitescaper": jsTmplLitEscaper, + "_html_template_jsvalescaper": jsValEscaper, + "_html_template_nospaceescaper": htmlNospaceEscaper, + "_html_template_rcdataescaper": rcdataEscaper, + "_html_template_srcsetescaper": srcsetFilterAndEscaper, + "_html_template_urlescaper": urlEscaper, + "_html_template_urlfilter": urlFilter, + "_html_template_urlnormalizer": urlNormalizer, + "_eval_args_": evalArgs, } // escaper collects type inferences about templates and changes needed to make @@ -227,16 +228,8 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { c.jsCtx = jsCtxDivOp case stateJSDqStr, stateJSSqStr: s = append(s, "_html_template_jsstrescaper") - case stateJSBqStr: - if debugAllowActionJSTmpl.Value() == "1" { - debugAllowActionJSTmpl.IncNonDefault() - s = append(s, "_html_template_jsstrescaper") - } else { - return context{ - state: stateError, - err: errorf(ErrJSTemplate, n, n.Line, "%s appears in a JS template literal", n), - } - } + case stateJSTmplLit: + s = append(s, "_html_template_jstmpllitescaper") case stateJSRegexp: s = append(s, "_html_template_jsregexpescaper") case stateCSS: @@ -395,6 +388,9 @@ var redundantFuncs = map[string]map[string]bool{ "_html_template_jsstrescaper": { "_html_template_attrescaper": true, }, + "_html_template_jstmpllitescaper": { + "_html_template_attrescaper": true, + }, "_html_template_urlescaper": { "_html_template_urlnormalizer": true, }, diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index 8a4f62e92f..9e2f4fe922 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -30,14 +30,14 @@ func (x *goodMarshaler) MarshalJSON() ([]byte, error) { func TestEscape(t *testing.T) { data := struct { - F, T bool - C, G, H string - A, E []string - B, M json.Marshaler - N int - U any // untyped nil - Z *int // typed nil - W HTML + F, T bool + C, G, H, I string + A, E []string + B, M json.Marshaler + N int + U any // untyped nil + Z *int // typed nil + W HTML }{ F: false, T: true, @@ -52,6 +52,7 @@ func TestEscape(t *testing.T) { U: nil, Z: nil, W: HTML(`¡Hello, !`), + I: "${ asd `` }", } pdata := &data @@ -718,6 +719,21 @@ func TestEscape(t *testing.T) { "
", "
", }, + { + "JS template lit special characters", + "", + "", + }, + { + "JS template lit special characters, nested lit", + "", + "", + }, + { + "JS template lit, nested JS", + "", + "", + }, } for _, test := range tests { @@ -976,6 +992,31 @@ func TestErrors(t *testing.T) { "`", "", }, + { + "", + ``, + }, + { + "", + ``, + }, + { + "", + ``, + }, + { + "", + ``, + }, + { + "", + ``, + }, + { + "", + ``, + }, + // Error cases. { "{{if .Cond}}var tmpl = `asd {{.}}`;", - `{{.}} appears in a JS template literal`, - }, + // { + // "", + // `{{.}} appears in a JS template literal`, + // }, + // { + // "", + // `{{.V}} appears in a JS template literal`, + // }, + // { + // "", + // `{{.}} appears in a JS template literal`, + // }, + // { + // "", + // `{{.}} appears in a JS template literal`, + // }, + // { + // "", + // `{{.}} appears in a JS template literal`, + // }, } for _, test := range tests { buf := new(bytes.Buffer) @@ -1349,7 +1406,7 @@ func TestEscapeText(t *testing.T) { }, { "`, context{}, }, + { + "