diff options
Diffstat (limited to 'libgo/go/html/template/escape.go')
-rw-r--r-- | libgo/go/html/template/escape.go | 116 |
1 files changed, 64 insertions, 52 deletions
diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go index 9ae9749db0..ee01fb12ab 100644 --- a/libgo/go/html/template/escape.go +++ b/libgo/go/html/template/escape.go @@ -13,37 +13,34 @@ import ( "text/template/parse" ) -// escapeTemplates rewrites the named templates, which must be +// escapeTemplate rewrites the named template, which must be // associated with t, 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 +// templates is properly escaped. If no error is returned, then the named templates have // been modified. Otherwise the named templates have been rendered // unusable. -func escapeTemplates(tmpl *Template, names ...string) error { +func escapeTemplate(tmpl *Template, node parse.Node, name string) error { e := newEscaper(tmpl) - for _, name := range names { - c, _ := e.escapeTree(context{}, name, 0) - var err 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)} + c, _ := e.escapeTree(context{}, node, name, 0) + var err error + if c.err != nil { + err, c.err.Name = c.err, name + } else if c.state != stateText { + err = &Error{ErrEndContext, nil, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)} + } + if err != nil { + // Prevent execution of unsafe templates. + if t := tmpl.set[name]; t != nil { + t.escapeErr = err + t.text.Tree = nil + t.Tree = nil } - if err != nil { - // Prevent execution of unsafe templates. - for _, name := range names { - if t := tmpl.set[name]; t != nil { - t.text.Tree = nil - t.Tree = nil - } - } - return err - } - tmpl.escaped = true - tmpl.Tree = tmpl.text.Tree + return err } e.commit() + if t := tmpl.set[name]; t != nil { + t.escapeErr = escapeOK + t.Tree = t.text.Tree + } return nil } @@ -164,7 +161,7 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { case urlPartUnknown: return context{ state: stateError, - err: errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n), + err: errorf(ErrAmbigContext, n, n.Line, "%s appears in an ambiguous URL context", n), } default: panic(c.urlPart.String()) @@ -207,6 +204,18 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { return c } +// allIdents returns the names of the identifiers under the Ident field of the node, +// which might be a singleton (Identifier) or a slice (Field). +func allIdents(node parse.Node) []string { + switch node := node.(type) { + case *parse.IdentifierNode: + return []string{node.Ident} + case *parse.FieldNode: + return node.Ident + } + panic("unidentified node type in allIdents") +} + // ensurePipelineContains ensures that the pipeline has commands with // the identifiers in s in order. // If the pipeline already has some of the sanitizers, do not interfere. @@ -229,27 +238,31 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { idents = p.Cmds[i+1:] } dups := 0 - for _, id := range idents { - if escFnsEq(s[dups], (id.Args[0].(*parse.IdentifierNode)).Ident) { - dups++ - if dups == len(s) { - return + for _, idNode := range idents { + for _, ident := range allIdents(idNode.Args[0]) { + if escFnsEq(s[dups], ident) { + dups++ + if dups == len(s) { + return + } } } } newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups) copy(newCmds, p.Cmds) // Merge existing identifier commands with the sanitizers needed. - for _, id := range idents { - pos := id.Args[0].Position() - i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq) - if i != -1 { - for _, name := range s[:i] { - newCmds = appendCmd(newCmds, newIdentCmd(name, pos)) + for _, idNode := range idents { + pos := idNode.Args[0].Position() + for _, ident := range allIdents(idNode.Args[0]) { + i := indexOfStr(ident, s, escFnsEq) + if i != -1 { + for _, name := range s[:i] { + newCmds = appendCmd(newCmds, newIdentCmd(name, pos)) + } + s = s[i+1:] } - s = s[i+1:] } - newCmds = appendCmd(newCmds, id) + newCmds = appendCmd(newCmds, idNode) } // Create any remaining sanitizers. for _, name := range s { @@ -318,7 +331,7 @@ func escFnsEq(a, b string) bool { func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode { return &parse.CommandNode{ NodeType: parse.NodeCommand, - Args: []parse.Node{parse.NewIdentifier(identifier).SetPos(pos)}, + Args: []parse.Node{parse.NewIdentifier(identifier).SetTree(nil).SetPos(pos)}, // TODO: SetTree. } } @@ -352,7 +365,7 @@ func nudge(c context) context { // join joins the two contexts of a branch template node. The result is an // error context if either of the input contexts are error contexts, or if the // the input contexts differ. -func join(a, b context, line int, nodeName string) context { +func join(a, b context, node parse.Node, nodeName string) context { if a.state == stateError { return a } @@ -385,14 +398,14 @@ func join(a, b context, line int, nodeName string) context { // ends in an unquoted value state even though the else branch // ends in stateBeforeValue. if c, d := nudge(a), nudge(b); !(c.eq(a) && d.eq(b)) { - if e := join(c, d, line, nodeName); e.state != stateError { + if e := join(c, d, node, nodeName); e.state != stateError { return e } } return context{ state: stateError, - err: errorf(ErrBranchEnd, line, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b), + err: errorf(ErrBranchEnd, node, 0, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b), } } @@ -404,7 +417,7 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) // We check that executing n.List once results in the same context // as executing n.List twice. c1, _ := e.escapeListConditionally(c0, n.List, nil) - c0 = join(c0, c1, n.Line, nodeName) + c0 = join(c0, c1, n, nodeName) if c0.state == stateError { // Make clear that this is a problem on loop re-entry // since developers tend to overlook that branch when @@ -415,7 +428,7 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) } } c1 := e.escapeList(c, n.ElseList) - return join(c0, c1, n.Line, nodeName) + return join(c0, c1, n, nodeName) } // escapeList escapes a list template node. @@ -467,7 +480,7 @@ func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter f // escapeTemplate escapes a {{template}} call node. func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context { - c, name := e.escapeTree(c, n.Name, n.Line) + c, name := e.escapeTree(c, n, n.Name, n.Line) if name != n.Name { e.editTemplateNode(n, name) } @@ -476,7 +489,7 @@ func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context { // escapeTree escapes the named template starting in the given context as // necessary and returns its output context. -func (e *escaper) escapeTree(c context, name string, line int) (context, string) { +func (e *escaper) escapeTree(c context, node parse.Node, name string, line int) (context, string) { // Mangle the template name with the input context to produce a reliable // identifier. dname := c.mangle(name) @@ -492,12 +505,12 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string) if e.tmpl.set[name] != nil { return context{ state: stateError, - err: errorf(ErrNoSuchTemplate, line, "%q is an incomplete or empty template", name), + err: errorf(ErrNoSuchTemplate, node, line, "%q is an incomplete or empty template", name), }, dname } return context{ state: stateError, - err: errorf(ErrNoSuchTemplate, line, "no such template %q", name), + err: errorf(ErrNoSuchTemplate, node, line, "no such template %q", name), }, dname } if dname != name { @@ -529,8 +542,7 @@ func (e *escaper) computeOutCtx(c context, t *template.Template) context { if !ok && c1.state != stateError { return context{ state: stateError, - // TODO: Find the first node with a line in t.text.Tree.Root - err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()), + err: errorf(ErrOutputContext, t.Tree.Root, 0, "cannot compute output context for template %s", t.Name()), } } return c1 @@ -664,7 +676,7 @@ func contextAfterText(c context, s []byte) (context, int) { i = len(s) } if c.delim == delimSpaceOrTagEnd { - // http://www.w3.org/TR/html5/tokenization.html#attribute-value-unquoted-state + // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state // lists the runes below as error characters. // Error out because HTML parsers may differ on whether // "<a id= onclick=f(" ends inside id's or onclick's value, @@ -674,7 +686,7 @@ func contextAfterText(c context, s []byte) (context, int) { if j := bytes.IndexAny(s[:i], "\"'<=`"); j >= 0 { return context{ state: stateError, - err: errorf(ErrBadHTML, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]), + err: errorf(ErrBadHTML, nil, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]), }, len(s) } } |