From 99fb2b0dc5031a530ae26a80851474ce987618af Mon Sep 17 00:00:00 2001 From: Ryan Catherman Date: Thu, 8 Aug 2024 08:41:48 -0400 Subject: [PATCH] Avoid rendering cycle in partials We need to avoid self referential and non-self referential use cases or things will overflow. This avoids the condition by ensuring that when a partial renders itself, it removes itself from the available lookups downstream. If lookup failures are enabled, then the operation will fail. Otherwise, it will be a silent error. --- mustache.go | 11 ++++++++++- mustache_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/mustache.go b/mustache.go index 542b4e1..5f0cecc 100644 --- a/mustache.go +++ b/mustache.go @@ -209,7 +209,16 @@ type partialNode struct { func (p *partialNode) render(t *Template, w *writer, c ...interface{}) error { w.tag() if template, ok := t.partials[p.name]; ok { - template.partials = t.partials + + // We can avoid cycles by removing this node's template from the lookup + // before we render + template.partials = make(map[string]*Template) + for k, v := range t.partials { + if k != p.name { + template.partials[k] = v + } + } + err := template.render(w, c...) if err != nil { if !t.silentMiss { diff --git a/mustache_test.go b/mustache_test.go index a61cb53..c4bb5e9 100644 --- a/mustache_test.go +++ b/mustache_test.go @@ -228,3 +228,34 @@ func TestSectionTestValue(t *testing.T) { } } + +func TestPartialsCannotCycle(t *testing.T) { + innerTemplate := New(Name("inner")) + err := innerTemplate.Parse(strings.NewReader(`I am the inner.{{>outer}}`)) + if err != nil { + t.Error(err) + } + + outerTemplate := New(Name("outer")) + err = outerTemplate.Parse(strings.NewReader(`I am the outer.{{>inner}}`)) + if err != nil { + t.Error(err) + } + + mainTemplate := New(Partial(outerTemplate), Partial(innerTemplate)) + err = mainTemplate.Parse(strings.NewReader(`{{>outer}}`)) + if err != nil { + t.Error(err) + } + + var output bytes.Buffer + err = mainTemplate.Render(&output) + if err != nil { + t.Error(err) + } + + expected := `I am the outer.I am the inner.` + if output.String() != expected { + t.Errorf("expected %q got %q", expected, output.String()) + } +}