lang/funcs: Fix out-of-bounds ArgError in templatefile function

The templatefile function only has two arguments, so ArgErrorf can be
called with only zero or one as the argument index. If we are out of
bounds then HCL itself will panic trying to build the error message for
this call when called as an HCL function.

Unfortunately there isn't really a great layer in Terraform to test for
this class of bug systematically, because we are currently testing these
functions directly rather than going through HCL to do it. For the moment
we'll just live with that, but if we see this class of error arise again
we might consider either reworking the tests in this package to work with
HCL expression source code instead of direct calls or adding some
additional tests elsewhere that do so.
This commit is contained in:
Martin Atkins 2019-03-19 16:07:04 -07:00
parent a4df1ba6d1
commit 2b1e650df2
2 changed files with 8 additions and 2 deletions

View File

@ -92,7 +92,7 @@ func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Fun
renderTmpl := func(expr hcl.Expression, varsVal cty.Value) (cty.Value, error) { renderTmpl := func(expr hcl.Expression, varsVal cty.Value) (cty.Value, error) {
if varsTy := varsVal.Type(); !(varsTy.IsMapType() || varsTy.IsObjectType()) { if varsTy := varsVal.Type(); !(varsTy.IsMapType() || varsTy.IsObjectType()) {
return cty.DynamicVal, function.NewArgErrorf(2, "invalid vars value: must be a map") // or an object, but we don't strongly distinguish these most of the time return cty.DynamicVal, function.NewArgErrorf(1, "invalid vars value: must be a map") // or an object, but we don't strongly distinguish these most of the time
} }
ctx := &hcl.EvalContext{ ctx := &hcl.EvalContext{
@ -105,7 +105,7 @@ func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Fun
for _, traversal := range expr.Variables() { for _, traversal := range expr.Variables() {
root := traversal.RootName() root := traversal.RootName()
if _, ok := ctx.Variables[root]; !ok { if _, ok := ctx.Variables[root]; !ok {
return cty.DynamicVal, function.NewArgErrorf(2, "vars map does not contain key %q, referenced at %s", root, traversal[0].SourceRange()) return cty.DynamicVal, function.NewArgErrorf(1, "vars map does not contain key %q, referenced at %s", root, traversal[0].SourceRange())
} }
} }

View File

@ -159,6 +159,12 @@ func TestTemplateFile(t *testing.T) {
t.Run(fmt.Sprintf("TemplateFile(%#v, %#v)", test.Path, test.Vars), func(t *testing.T) { t.Run(fmt.Sprintf("TemplateFile(%#v, %#v)", test.Path, test.Vars), func(t *testing.T) {
got, err := templateFileFn.Call([]cty.Value{test.Path, test.Vars}) got, err := templateFileFn.Call([]cty.Value{test.Path, test.Vars})
if argErr, ok := err.(function.ArgError); ok {
if argErr.Index < 0 || argErr.Index > 1 {
t.Errorf("ArgError index %d is out of range for templatefile (must be 0 or 1)", argErr.Index)
}
}
if test.Err { if test.Err {
if err == nil { if err == nil {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")