diff --git a/template/template.go b/template/template.go index 60fa6464d..bb6e3970f 100644 --- a/template/template.go +++ b/template/template.go @@ -34,6 +34,19 @@ var ( ErrMissingReaderFunction = errors.New("template: missing a reader function") ) +var ( + sprigFuncMap template.FuncMap +) + +func init() { + sprigFuncMap = make(template.FuncMap, len(sprig.HermeticTxtFuncMap())) + // Add the Sprig functions to the funcmap + for k, v := range sprig.HermeticTxtFuncMap() { + target := "sprig_" + k + sprigFuncMap[target] = v + } +} + // Template is the internal representation of an individual template to process. // The template retains the relationship between its contents and is // responsible for it's own execution. @@ -436,10 +449,9 @@ func funcMap(i *funcMapInput) template.FuncMap { "spew_sprintf": spewSprintf, } - // Add the Sprig functions to the funcmap - for k, v := range sprig.TxtFuncMap() { - target := "sprig_" + k - r[target] = v + // Add the pre-computed Sprig functions to the funcmap + for k, v := range sprigFuncMap { + r[k] = v } // Add external functions diff --git a/template/template_test.go b/template/template_test.go index a9def4d7a..f6e19c092 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -2745,3 +2745,54 @@ func TestTemplate_ExtFuncMap(t *testing.T) { }) } } + +// TestTemplate_HermeticSprigFunctions tests that hermetic Sprig functions +// are available, but non-hermetic ones are not. +func TestTemplate_HermeticSprigFunctions(t *testing.T) { + cases := []struct { + name string + contents string + expected string + wantErr bool + }{ + { + "hermetic function", + `{{ sprig_upper "hello" }}`, + "HELLO", + false, + }, + { + "generic function not available", + `{{ sprig_repeat "hello" 3 }}`, + "", + true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) { + tpl, err := NewTemplate(&NewTemplateInput{ + Contents: tc.contents, + }) + if err != nil { + t.Fatal(err) + } + + result, err := tpl.Execute(nil) + if tc.wantErr { + if err == nil { + t.Fatalf("expected error for %s, but got nil", tc.name) + } + return + } + + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(result.Output, []byte(tc.expected)) { + t.Errorf("\nexpected: %q\nactual: %q", tc.expected, result.Output) + } + }) + } +}