Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions macro_import_depth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package pongo2_test

import (
"strings"
"testing"
"testing/fstest"

"github.com/flosch/pongo2/v7"
)

func TestMacroDepthLimitCoversDefinitionsAndImports(t *testing.T) {
t.Parallel()
tests := map[string]fstest.MapFS{
"definition": {
"main.tpl": {Data: []byte(
`{% macro rec(n) %}{% if n > 0 %}{{ rec(n-1) }}{% endif %}{% endmacro %}{{ rec(1100) }}`)},
},
"import": {
"main.tpl": {Data: []byte(`{% import "macros.tpl" rec %}{{ rec(1100) }}`)},
"macros.tpl": {Data: []byte(
`{% macro rec(n) export %}{% if n > 0 %}{{ rec(n-1) }}{% endif %}{% endmacro %}`)},
},
}
for name, files := range tests {
t.Run(name, func(t *testing.T) {
set := pongo2.NewSet(name, pongo2.NewFSLoader(files))
tpl, err := set.FromFile("main.tpl")
if err != nil {
t.Fatalf("FromFile: %v", err)
}
_, err = tpl.Execute(nil)
if err == nil || !strings.Contains(err.Error(),
"maximum recursive macro call depth reached (max is 1000)") {
t.Fatalf("Execute error = %v, want macro depth limit", err)
}
})
}
}
4 changes: 1 addition & 3 deletions tags_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ type tagImportNode struct {
func (node *tagImportNode) Execute(ctx *ExecutionContext, writer TemplateWriter) error {
for name, macro := range node.macros {
func(name string, macro *tagMacroNode) {
ctx.Private[name] = func(args ...*Value) (*Value, error) {
return macro.call(ctx, args...)
}
ctx.Private[name] = macro.callable(ctx)
}(name, macro)
}
return nil
Expand Down
11 changes: 8 additions & 3 deletions tags_macro.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,14 @@ type tagMacroNode struct {
// Execute registers the macro as a callable function in the private context.
// The macro can then be called like {{ macro_name(args) }}.
func (node *tagMacroNode) Execute(ctx *ExecutionContext, writer TemplateWriter) error {
ctx.Private[node.name] = func(args ...*Value) (*Value, error) {
ctx.Private[node.name] = node.callable(ctx)
return nil
}

// callable applies the same recursion bound to a macro registered at its
// definition site and to one registered by an import.
func (node *tagMacroNode) callable(ctx *ExecutionContext) func(args ...*Value) (*Value, error) {
return func(args ...*Value) (*Value, error) {
ctx.macroDepth++
defer func() {
ctx.macroDepth--
Expand All @@ -81,8 +88,6 @@ func (node *tagMacroNode) Execute(ctx *ExecutionContext, writer TemplateWriter)

return node.call(ctx, args...)
}

return nil
}

// call executes the macro body with the provided arguments and returns the
Expand Down