diff --git a/pkg/iac/scanners/terraform/parser/funcs/filesystem.go b/pkg/iac/scanners/terraform/parser/funcs/filesystem.go index a53e975443f6..9d9b94f52b1a 100644 --- a/pkg/iac/scanners/terraform/parser/funcs/filesystem.go +++ b/pkg/iac/scanners/terraform/parser/funcs/filesystem.go @@ -249,11 +249,9 @@ func MakeFileSetFunc(target fs.FS, baseDir string) function.Function { path = filepath.Join(baseDir, path) } - // Join the path to the glob pattern, while ensuring the full - // pattern is canonical for the host OS. The joined path is - // automatically cleaned during this operation. - pattern = filepath.Join(path, pattern) - // Trivy uses a virtual file system + // Join the path to the glob pattern, and ensure both path and pattern + // agree on path separators, so the globbing works as expected. + pattern = filepath.ToSlash(filepath.Join(path, pattern)) path = filepath.ToSlash(path) matches, err := doublestar.Glob(target, pattern) @@ -263,7 +261,7 @@ func MakeFileSetFunc(target fs.FS, baseDir string) function.Function { var matchVals []cty.Value for _, match := range matches { - fi, err := os.Stat(match) + fi, err := fs.Stat(target, match) if err != nil { return cty.UnknownVal(cty.Set(cty.String)), fmt.Errorf("failed to stat (%s): %s", match, err) diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index a20bb2a84b58..554a8b0fb3d0 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -1631,3 +1631,71 @@ output "test_out" { assert.Equal(t, "test_value", attr.GetRawValue()) } } + +func TestExtractSetValue(t *testing.T) { + files := map[string]string{ + "main.tf": ` +resource "test" "set-value" { + value = toset(["x", "y", "x"]) +} +`, + } + + resources := parse(t, files).GetResourcesByType("test") + require.Len(t, resources, 1) + attr := resources[0].GetAttribute("value") + require.NotNil(t, attr) + assert.Equal(t, []string{"x", "y"}, attr.GetRawValue()) +} + +func TestFunc_fileset(t *testing.T) { + files := map[string]string{ + "main.tf": ` +resource "test" "fileset-func" { + value = fileset(path.module, "**/*.py") +} +`, + "a.py": ``, + "path/b.py": ``, + } + + resources := parse(t, files).GetResourcesByType("test") + require.Len(t, resources, 1) + attr := resources[0].GetAttribute("value") + require.NotNil(t, attr) + assert.Equal(t, []string{"a.py", "path/b.py"}, attr.GetRawValue()) +} + +func TestVarTypeShortcut(t *testing.T) { + files := map[string]string{ + "main.tf": ` +variable "magic_list" { + type = list + default = ["x", "y"] +} + +variable "magic_map" { + type = map + default = {a = 1, b = 2} +} + +resource "test" "values" { + l = var.magic_list + m = var.magic_map +} +`, + } + + resources := parse(t, files).GetResourcesByType("test") + require.Len(t, resources, 1) + + list_attr := resources[0].GetAttribute("l") + require.NotNil(t, list_attr) + assert.Equal(t, []string{"x", "y"}, list_attr.GetRawValue()) + + map_attr := resources[0].GetAttribute("m") + require.NotNil(t, map_attr) + assert.True(t, map_attr.Value().RawEquals(cty.MapVal(map[string]cty.Value{ + "a": cty.NumberIntVal(1), "b": cty.NumberIntVal(2), + }))) +} diff --git a/pkg/iac/terraform/attribute.go b/pkg/iac/terraform/attribute.go index 24e0ec499352..28b94bf71743 100644 --- a/pkg/iac/terraform/attribute.go +++ b/pkg/iac/terraform/attribute.go @@ -27,6 +27,14 @@ type Attribute struct { } func (a *Attribute) DecodeVarType() (cty.Type, *typeexpr.Defaults, error) { + // Special-case the shortcuts for list(any) and map(any) which aren't hcl. + switch hcl.ExprAsKeyword(a.hclAttribute.Expr) { + case "list": + return cty.List(cty.DynamicPseudoType), nil, nil + case "map": + return cty.Map(cty.DynamicPseudoType), nil, nil + } + t, def, diag := typeexpr.TypeConstraintWithDefaults(a.hclAttribute.Expr) if diag.HasErrors() { return cty.NilType, nil, diag @@ -68,7 +76,7 @@ func (a *Attribute) GetRawValue() interface{} { return float default: switch { - case typ.IsTupleType(), typ.IsListType(): + case typ.IsTupleType(), typ.IsListType(), typ.IsSetType(): values := a.Value().AsValueSlice() if len(values) == 0 { return []string{}