src/html/template/escape.go | 5 ++--- src/html/template/escape_test.go | 15 +++++++++++++++ src/html/template/html.go | 3 +++ diff --git a/src/html/template/escape.go b/src/html/template/escape.go index 3d4cc19b5da85c22859b1182d053b1a19adba2ef..bcba8db4aa00bca3850b27e5ee77be5c6e7a113a 100644 --- a/src/html/template/escape.go +++ b/src/html/template/escape.go @@ -380,9 +380,8 @@ // redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x) // for all x. var redundantFuncs = map[string]map[string]bool{ "_html_template_commentescaper": { - "_html_template_attrescaper": true, - "_html_template_nospaceescaper": true, - "_html_template_htmlescaper": true, + "_html_template_attrescaper": true, + "_html_template_htmlescaper": true, }, "_html_template_cssescaper": { "_html_template_attrescaper": true, diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index 972b00b921d35be8eeec5a462b6d7657826a0ea6..a1a6c1cd1600416a94524f730691799d20016496 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -678,6 +678,21 @@ "srcset buffer growth", ``, ``, }, + { + "unquoted empty attribute value (plaintext)", + "

", + "

", + }, + { + "unquoted empty attribute value (url)", + "

", + "

", + }, + { + "quoted empty attribute value", + "

", + "

", + }, } for _, test := range tests { diff --git a/src/html/template/html.go b/src/html/template/html.go index 46e9d931511cfa3403ead650d4d3451d1812fb73..6fb9237bdac005116f8f09e52c3da3ec018612da 100644 --- a/src/html/template/html.go +++ b/src/html/template/html.go @@ -14,6 +14,9 @@ // htmlNospaceEscaper escapes for inclusion in unquoted attribute values. func htmlNospaceEscaper(args ...any) string { s, t := stringify(args...) + if s == "" { + return filterFailsafe + } if t == contentTypeHTML { return htmlReplacer(stripTags(s), htmlNospaceNormReplacementTable, false) }