by a period '.' and called "dot", to the value at the current location in the
structure as execution proceeds.
+The security model used by this package assumes that template authors are
+trusted. The package does not auto-escape output, so injecting code into
+a template can lead to arbitrary code execution if the template is executed
+by an untrusted source.
+
The input text for a template is UTF-8-encoded text in any format.
"Actions"--data evaluations or control structures--are delimited by
"{{" and "}}"; all text outside actions is copied to the output unchanged.
treeSet map[string]*Tree
actionLine int // line of left delim starting action
rangeDepth int
+ stackDepth int // depth of nested parenthesized expressions
}
// A mode value is a set of flags (or 0). Modes control parser behavior.
SkipFuncCheck // do not check that functions are defined
)
+// maxStackDepth is the maximum depth permitted for nested
+// parenthesized expressions.
+var maxStackDepth = 10000
+
+// init reduces maxStackDepth for WebAssembly due to its smaller stack size.
+func init() {
+ if runtime.GOARCH == "wasm" {
+ maxStackDepth = 1000
+ }
+}
+
// Copy returns a copy of the [Tree]. Any parsing state is discarded.
func (t *Tree) Copy() *Tree {
if t == nil {
t.vars = []string{"$"}
t.funcs = funcs
t.treeSet = treeSet
+ t.stackDepth = 0
lex.options = lexOptions{
emitComment: t.Mode&ParseComments != 0,
breakOK: !t.hasFunction("break"),
}
return number
case itemLeftParen:
+ if t.stackDepth >= maxStackDepth {
+ t.errorf("max expression depth exceeded")
+ }
+ t.stackDepth++
+ defer func() { t.stackDepth-- }()
return t.pipeline("parenthesized pipeline", itemRightParen)
case itemString, itemRawString:
s, err := strconv.Unquote(token.val)
{"0xef", true, true, true, false, 0xef, 0xef, 0xef, 0},
}
+func init() {
+ // Use a small stack limit for testing to avoid creating huge expressions.
+ maxStackDepth = 3
+}
+
func TestNumberParse(t *testing.T) {
for _, test := range numberTests {
// If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
{"empty pipeline", `{{printf "%d" ( ) }}`, hasError, ""},
// Missing pipeline in block
{"block definition", `{{block "foo"}}hello{{end}}`, hasError, ""},
+
+ // Expression nested depth tests.
+ {"paren nesting normal", "{{ (( 1 )) }}", noError, "{{((1))}}"},
+ {"paren nesting at limit", "{{ ((( 1 ))) }}", noError, "{{(((1)))}}"},
+ {"paren nesting exceeds limit", "{{ (((( 1 )))) }}", hasError, "template: test:1: max expression depth exceeded"},
+ {"paren nesting in pipeline", "{{ ((( 1 ))) | printf }}", noError, "{{(((1))) | printf}}"},
+ {"paren nesting in pipeline exceeds limit", "{{ (((( 1 )))) | printf }}", hasError, "template: test:1: max expression depth exceeded"},
+ {"paren nesting with other constructs", "{{ if ((( true ))) }}YES{{ end }}", noError, "{{if (((true)))}}\"YES\"{{end}}"},
+ {"paren nesting with other constructs exceeds limit", "{{ if (((( true )))) }}YES{{ end }}", hasError, "template: test:1: max expression depth exceeded"},
}
var builtins = map[string]any{