Commit 3eb41fbe authored by Mike Samuel's avatar Mike Samuel

exp/template/html: render templates unusable when escaping fails

This moots a caveat in the proposed package documentation by
rendering useless any template that could not be escaped.

From https://golang.org/cl/4969078/
> If EscapeSet returns an error, do not Execute the set; it is not
> safe against injection.
r: [but isn't the returned set nil? i guess you don't overwrite the
r: original if there's a problem, but i think you're in your rights to
r: do so]

R=r
CC=golang-dev
https://golang.org/cl/5020043
parent 5c303259
...@@ -19,7 +19,6 @@ that will be passed to Execute. ...@@ -19,7 +19,6 @@ that will be passed to Execute.
If successful, set will now be injection-safe. Otherwise, the returned set will If successful, set will now be injection-safe. Otherwise, the returned set will
be nil and an error, described below, will explain the problem. be nil and an error, described below, will explain the problem.
If an error is returned, do not use the original set; it is insecure.
The template names do not need to include helper templates but should include The template names do not need to include helper templates but should include
all names x used thus: all names x used thus:
......
...@@ -28,10 +28,10 @@ func Escape(t *template.Template) (*template.Template, os.Error) { ...@@ -28,10 +28,10 @@ func Escape(t *template.Template) (*template.Template, os.Error) {
// EscapeSet rewrites the template set to guarantee that the output of any of // EscapeSet rewrites the template set to guarantee that the output of any of
// the named templates is properly escaped. // the named templates is properly escaped.
// Names should include the names of all templates that might be called but // Names should include the names of all templates that might be Executed but
// need not include helper templates only called by top-level templates. // need not include helper templates.
// If nil is returned, then the templates have been modified. Otherwise no // If no error is returned, then the named templates have been modified.
// changes were made. // Otherwise the named templates have been rendered unusable.
func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) { func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) {
if len(names) == 0 { if len(names) == 0 {
// TODO: Maybe add a method to Set to enumerate template names // TODO: Maybe add a method to Set to enumerate template names
...@@ -48,11 +48,20 @@ func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) { ...@@ -48,11 +48,20 @@ func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) {
} }
for _, name := range names { for _, name := range names {
c, _ := e.escapeTree(context{}, name, 0) c, _ := e.escapeTree(context{}, name, 0)
var err os.Error
if c.errStr != "" { if c.errStr != "" {
return nil, fmt.Errorf("%s:%d: %s", name, c.errLine, c.errStr) err = fmt.Errorf("%s:%d: %s", name, c.errLine, c.errStr)
} else if c.state != stateText {
err = fmt.Errorf("%s ends in a non-text context: %v", name, c)
} }
if c.state != stateText { if err != nil {
return nil, fmt.Errorf("%s ends in a non-text context: %v", name, c) // Prevent execution of unsafe templates.
for _, name := range names {
if t := s.Template(name); t != nil {
t.Tree = nil
}
}
return nil, err
} }
} }
e.commit() e.commit()
......
...@@ -1148,3 +1148,32 @@ func TestEnsurePipelineContains(t *testing.T) { ...@@ -1148,3 +1148,32 @@ func TestEnsurePipelineContains(t *testing.T) {
} }
} }
} }
func expectExecuteFailure(t *testing.T, b *bytes.Buffer) {
if x := recover(); x != nil {
if b.Len() != 0 {
t.Errorf("output on buffer: %q", b.String())
}
} else {
t.Errorf("unescaped template executed")
}
}
func TestEscapeErrorsNotIgnorable(t *testing.T) {
var b bytes.Buffer
tmpl := template.Must(template.New("dangerous").Parse("<a"))
Escape(tmpl)
defer expectExecuteFailure(t, &b)
tmpl.Execute(&b, nil)
}
func TestEscapeSetErrorsNotIgnorable(t *testing.T) {
s, err := (&template.Set{}).Parse(`{{define "t"}}<a{{end}}`)
if err != nil {
t.Error("failed to parse set: %q", err)
}
EscapeSet(s, "t")
var b bytes.Buffer
defer expectExecuteFailure(t, &b)
s.Execute(&b, "t", nil)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment