// 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 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 // and use those instead. return nil, &Error{ErrNoNames, "", 0, "must specify names of top level templates"} } e := newEscaper(s) for _, name := range names { c, _ := e.escapeTree(context{}, name, 0) var err os.Error if c.err != nil { err, c.err.Name = c.err, name } else if c.state != stateText { err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", 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() return s, nil }
func TestEscapeSet(t *testing.T) { type dataItem struct { Children []*dataItem X string } data := dataItem{ Children: []*dataItem{ &dataItem{X: "foo"}, &dataItem{X: "<bar>"}, &dataItem{ Children: []*dataItem{ &dataItem{X: "baz"}, }, }, }, } tests := []struct { inputs map[string]string want string }{ // The trivial set. { map[string]string{ "main": ``, }, ``, }, // A template called in the start context. { map[string]string{ "main": `Hello, {{template "helper"}}!`, // Not a valid top level HTML template. // "<b" is not a full tag. "helper": `{{"<World>"}}`, }, `Hello, <World>!`, }, // A template called in a context other than the start. { map[string]string{ "main": `<a onclick='a = {{template "helper"}};'>`, // Not a valid top level HTML template. // "<b" is not a full tag. "helper": `{{"<a>"}}<b`, }, `<a onclick='a = "\u003ca\u003e"<b;'>`, }, // A recursive template that ends in its start context. { map[string]string{ "main": `{{range .Children}}{{template "main" .}}{{else}}{{.X}} {{end}}`, }, `foo <bar> baz `, }, // A recursive helper template that ends in its start context. { map[string]string{ "main": `{{template "helper" .}}`, "helper": `{{if .Children}}<ul>{{range .Children}}<li>{{template "main" .}}</li>{{end}}</ul>{{else}}{{.X}}{{end}}`, }, `<ul><li>foo</li><li><bar></li><li><ul><li>baz</li></ul></li></ul>`, }, // Co-recursive templates that end in its start context. { map[string]string{ "main": `<blockquote>{{range .Children}}{{template "helper" .}}{{end}}</blockquote>`, "helper": `{{if .Children}}{{template "main" .}}{{else}}{{.X}}<br>{{end}}`, }, `<blockquote>foo<br><bar><br><blockquote>baz<br></blockquote></blockquote>`, }, // A template that is called in two different contexts. { map[string]string{ "main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`, "helper": `{{11}} of {{"<100>"}}`, }, `<button onclick="title='11 of \x3c100\x3e'; ...">11 of <100></button>`, }, // A non-recursive template that ends in a different context. // helper starts in jsCtxRegexp and ends in jsCtxDivOp. { map[string]string{ "main": `<script>var x={{template "helper"}}/{{"42"}};</script>`, "helper": "{{126}}", }, `<script>var x= 126 /"42";</script>`, }, // A recursive template that ends in a similar context. { map[string]string{ "main": `<script>var x=[{{template "countdown" 4}}];</script>`, "countdown": `{{.}}{{if .}},{{template "countdown" . | pred}}{{end}}`, }, `<script>var x=[ 4 , 3 , 2 , 1 , 0 ];</script>`, }, // A recursive template that ends in a different context. /* { map[string]string{ "main": `<a href="/foo{{template "helper" .}}">`, "helper": `{{if .Children}}{{range .Children}}{{template "helper" .}}{{end}}{{else}}?x={{.X}}{{end}}`, }, `<a href="/foo?x=foo?x=%3cbar%3e?x=baz">`, }, */ } // pred is a template function that returns the predecessor of a // natural number for testing recursive templates. fns := template.FuncMap{"pred": func(a ...interface{}) (interface{}, error) { if len(a) == 1 { if i, _ := a[0].(int); i > 0 { return i - 1, nil } } return nil, fmt.Errorf("undefined pred(%v)", a) }} for _, test := range tests { var s template.Set for name, src := range test.inputs { t := template.New(name) t.Funcs(fns) s.Add(template.Must(t.Parse(src))) } s.Funcs(fns) if _, err := EscapeSet(&s, "main"); err != nil { t.Errorf("%s for input:\n%v", err, test.inputs) continue } var b bytes.Buffer if err := s.Execute(&b, "main", data); err != nil { t.Errorf("%q executing %v", err.Error(), s.Template("main")) continue } if got := b.String(); test.want != got { t.Errorf("want\n\t%q\ngot\n\t%q", test.want, got) } } }