]> Cypherpunks repositories - gostls13.git/commitdiff
text/template: check for malformed pipelines
authorDidier Spezia <didier.06@gmail.com>
Tue, 5 May 2015 06:45:49 +0000 (06:45 +0000)
committerRob Pike <r@golang.org>
Tue, 5 May 2015 23:03:54 +0000 (23:03 +0000)
Catch some malformed pipelines at parsing time.
The current code accepts pipelines such as:

{{12|.}}
{{"hello"|print|false}}
{{.|"blah blah"}}

Such pipelines generate panic in html/template at execution time.

Add an extra check to verify all the commands of the pipeline are executable
(except for the first one).

Fixes #10610

Change-Id: Id72236ba8f76a59fa284fe3d4c2cb073e50b51f1
Reviewed-on: https://go-review.googlesource.com/9626
Reviewed-by: Rob Pike <r@golang.org>
src/text/template/parse/parse.go
src/text/template/parse/parse_test.go

index 6eb303801b7becd5f116c8047a250f0ab71a67fc..88aacd1b72b55a37bf7f747bc7971d26b1c1cde4 100644 (file)
@@ -413,9 +413,8 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
        for {
                switch token := t.nextNonSpace(); token.typ {
                case itemRightDelim, itemRightParen:
-                       if len(pipe.Cmds) == 0 {
-                               t.errorf("missing value for %s", context)
-                       }
+                       // At this point, the pipeline is complete
+                       t.checkPipeline(pipe, context)
                        if token.typ == itemRightParen {
                                t.backup()
                        }
@@ -430,6 +429,21 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
        }
 }
 
+func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
+       // Reject empty pipelines
+       if len(pipe.Cmds) == 0 {
+               t.errorf("missing value for %s", context)
+       }
+       // Only the first command of a pipeline can start with a non executable operand
+       for i, c := range pipe.Cmds[1:] {
+               switch c.Args[0].Type() {
+               case NodeBool, NodeDot, NodeNil, NodeNumber, NodeString:
+                       // With A|B|C, pipeline stage 2 is B
+                       t.errorf("non executable command in pipeline stage %d", i+2)
+               }
+       }
+}
+
 func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
        defer t.popVars(len(t.vars))
        line = t.lex.lineNumber()
index 808f9a0b5ed5c5ae8725d0268e0087119c21267f..9e62bd2df6a1aa14191bfa1be2c126d25da6e6c1 100644 (file)
@@ -269,6 +269,14 @@ var parseTests = []parseTest{
        {"dot after string", `{{"hello".guys}}`, hasError, ""},
        {"dot after dot", "{{..E}}", hasError, ""},
        {"dot after nil", "{{nil.E}}", hasError, ""},
+       // Wrong pipeline
+       {"wrong pipeline dot", "{{12|.}}", hasError, ""},
+       {"wrong pipeline number", "{{.|12|printf}}", hasError, ""},
+       {"wrong pipeline string", "{{.|print|\"error\"}}", hasError, ""},
+       {"wrong pipeline char", "{{12|print|html|'e'}}", hasError, ""},
+       {"wrong pipeline boolean", "{{.|true}}", hasError, ""},
+       {"wrong pipeline nil", "{{'c'|nil}}", hasError, ""},
+       {"empty pipeline", `{{printf "%d" ( ) }}`, hasError, ""},
 }
 
 var builtins = map[string]interface{}{
@@ -422,6 +430,12 @@ var errorTests = []parseTest{
        {"wrongdot",
                "{{true.any}}",
                hasError, `unexpected . after term`},
+       {"wrongpipeline",
+               "{{12|false}}",
+               hasError, `non executable command in pipeline`},
+       {"emptypipeline",
+               `{{ ( ) }}`,
+               hasError, `missing value for parenthesized pipeline`},
 }
 
 func TestErrors(t *testing.T) {