If successful, set will now be injection-safe. Otherwise, the returned set will
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
all names x used thus:
// EscapeSet rewrites the template set to guarantee that the output of any of
// the named templates is properly escaped.
-// Names should include the names of all templates that might be called but
-// need not include helper templates only called by top-level templates.
-// If nil is returned, then the templates have been modified. Otherwise no
-// changes were made.
+// Names should include the names of all templates that might be Executed but
+// need not include helper templates.
+// If no error is returned, then the named templates have been modified.
+// Otherwise the named templates have been rendered unusable.
func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) {
if len(names) == 0 {
// TODO: Maybe add a method to Set to enumerate template names
}
for _, name := range names {
c, _ := e.escapeTree(context{}, name, 0)
+ var err os.Error
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 {
- return nil, fmt.Errorf("%s ends in a non-text context: %v", name, c)
+ if err != nil {
+ // Prevent execution of unsafe templates.
+ for _, name := range names {
+ if t := s.Template(name); t != nil {
+ t.Tree = nil
+ }
+ }
+ return nil, err
}
}
e.commit()
}
}
}
+
+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)
+}