mirror of
https://github.com/golang/go
synced 2024-11-26 13:08:08 -07:00
html/template: ignore untyped nil arguments to default escapers
CL 95215 changed text/template so that untyped nil arguments were no longer ignored, but were instead passed to functions as expected. This had an unexpected effect on html/template, where all data is implicitly passed to functions: originally untyped nil arguments were not passed and were thus effectively ignored, but after CL 95215 they were passed and were printed, typically as an escaped version of "<nil>". This CL restores some of the behavior of html/template by ignoring untyped nil arguments passed implicitly to escaper functions. While eliminating one change to html/template relative to earlier releases, this unfortunately introduces a different one: originally values of interface type with the value nil were printed as an escaped version of "<nil>". With this CL they are ignored as though they were untyped nil values. My judgement is that this is a less common case. We'll see. This CL adds some tests of typed and untyped nil values to html/template and text/template to capture the current behavior. Updates #18716 Fixes #25875 Change-Id: I5912983ca32b31ece29e929e72d503b54d7b0cac Reviewed-on: https://go-review.googlesource.com/121815 Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Daniel Martí <mvdan@mvdan.cc> Reviewed-by: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
a67c481f76
commit
c5cb4843e1
@ -169,8 +169,17 @@ func stringify(args ...interface{}) (string, contentType) {
|
||||
return string(s), contentTypeSrcset
|
||||
}
|
||||
}
|
||||
for i, arg := range args {
|
||||
i := 0
|
||||
for _, arg := range args {
|
||||
// We skip untyped nil arguments for backward compatibility.
|
||||
// Without this they would be output as <nil>, escaped.
|
||||
// See issue 25875.
|
||||
if arg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
args[i] = indirectToStringerOrError(arg)
|
||||
i++
|
||||
}
|
||||
return fmt.Sprint(args...), contentTypePlain
|
||||
return fmt.Sprint(args[:i]...), contentTypePlain
|
||||
}
|
||||
|
@ -447,10 +447,9 @@ func TestEscapingNilNonemptyInterfaces(t *testing.T) {
|
||||
testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand
|
||||
tmpl.Execute(got, testData)
|
||||
|
||||
// Use this data instead of just hard-coding "<nil>" to avoid
|
||||
// dependencies on the html escaper and the behavior of fmt w.r.t. nil.
|
||||
// A non-empty interface should print like an empty interface.
|
||||
want := new(bytes.Buffer)
|
||||
data := struct{ E string }{E: fmt.Sprint(nil)}
|
||||
data := struct{ E interface{} }{}
|
||||
tmpl.Execute(want, data)
|
||||
|
||||
if !bytes.Equal(want.Bytes(), got.Bytes()) {
|
||||
|
@ -70,6 +70,9 @@ In this case it becomes
|
||||
where urlescaper, attrescaper, and htmlescaper are aliases for internal escaping
|
||||
functions.
|
||||
|
||||
For these internal escaping functions, if an action pipeline evaluates to
|
||||
a nil interface value, it is treated as though it were an empty string.
|
||||
|
||||
Errors
|
||||
|
||||
See the documentation of ErrorCode for details.
|
||||
|
@ -35,7 +35,8 @@ func TestEscape(t *testing.T) {
|
||||
A, E []string
|
||||
B, M json.Marshaler
|
||||
N int
|
||||
Z *int
|
||||
U interface{} // untyped nil
|
||||
Z *int // typed nil
|
||||
W HTML
|
||||
}{
|
||||
F: false,
|
||||
@ -48,6 +49,7 @@ func TestEscape(t *testing.T) {
|
||||
N: 42,
|
||||
B: &badMarshaler{},
|
||||
M: &goodMarshaler{},
|
||||
U: nil,
|
||||
Z: nil,
|
||||
W: HTML(`¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`),
|
||||
}
|
||||
@ -113,6 +115,16 @@ func TestEscape(t *testing.T) {
|
||||
"{{.T}}",
|
||||
"true",
|
||||
},
|
||||
{
|
||||
"untypedNilValue",
|
||||
"{{.U}}",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"typedNilValue",
|
||||
"{{.Z}}",
|
||||
"<nil>",
|
||||
},
|
||||
{
|
||||
"constant",
|
||||
`<a href="/search?q={{"'a<b'"}}">`,
|
||||
@ -199,10 +211,15 @@ func TestEscape(t *testing.T) {
|
||||
`<button onclick='alert( true )'>`,
|
||||
},
|
||||
{
|
||||
"jsNilValue",
|
||||
"jsNilValueTyped",
|
||||
"<button onclick='alert(typeof{{.Z}})'>",
|
||||
`<button onclick='alert(typeof null )'>`,
|
||||
},
|
||||
{
|
||||
"jsNilValueUntyped",
|
||||
"<button onclick='alert(typeof{{.U}})'>",
|
||||
`<button onclick='alert(typeof null )'>`,
|
||||
},
|
||||
{
|
||||
"jsObjValue",
|
||||
"<button onclick='alert({{.A}})'>",
|
||||
|
@ -448,6 +448,8 @@ var execTests = []execTest{
|
||||
{"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
|
||||
"<script>alert("XSS");</script>", nil, true},
|
||||
{"html", `{{html .PS}}`, "a string", tVal, true},
|
||||
{"html typed nil", `{{html .NIL}}`, "<nil>", tVal, true},
|
||||
{"html untyped nil", `{{html .Empty0}}`, "<no value>", tVal, true},
|
||||
|
||||
// JavaScript.
|
||||
{"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true},
|
||||
|
Loading…
Reference in New Issue
Block a user