From 259cbba0ed5c4c47013539601c2c8b8651a3fbe2 Mon Sep 17 00:00:00 2001 From: nikpivkin Date: Mon, 18 Nov 2024 15:02:50 +0600 Subject: [PATCH] fix(misconf): do not erase variable type for child modules Signed-off-by: nikpivkin --- .../scanners/terraform/parser/evaluator.go | 2 +- .../scanners/terraform/parser/parser_test.go | 77 +++++++++++++++++++ pkg/iac/terraform/block.go | 4 +- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/pkg/iac/scanners/terraform/parser/evaluator.go b/pkg/iac/scanners/terraform/parser/evaluator.go index 8e2e737b5d9b..6469f63c707b 100644 --- a/pkg/iac/scanners/terraform/parser/evaluator.go +++ b/pkg/iac/scanners/terraform/parser/evaluator.go @@ -471,7 +471,7 @@ func (e *evaluator) evaluateVariable(b *terraform.Block) (cty.Value, error) { var val cty.Value - if override, exists := e.inputVars[b.Label()]; exists { + if override, exists := e.inputVars[b.Label()]; exists && override.Type() != cty.NilType { val = override } else if def, exists := attributes["default"]; exists { val = def.NullableValue() diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index 9c62b958a212..926eb353af26 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -3,6 +3,7 @@ package parser import ( "bytes" "context" + "io/fs" "log/slog" "os" "path/filepath" @@ -2008,3 +2009,79 @@ variable "baz" {} assert.Contains(t, buf.String(), "Variable values was not found in the environment or variable files.") assert.Contains(t, buf.String(), "variables=\"foo\"") } + +func Test_PassingNullToChildModule_DoesNotEraseType(t *testing.T) { + tests := []struct { + name string + fsys fs.FS + }{ + { + name: "typed variable", + fsys: fstest.MapFS{ + "main.tf": &fstest.MapFile{Data: []byte(`module "test" { + source = "./modules/test" + test_var = null +}`)}, + "modules/test/main.tf": &fstest.MapFile{Data: []byte(`variable "test_var" { + type = number +} + +resource "foo" "this" { + bar = var.test_var != null ? 1 : 2 +}`)}, + }, + }, + { + name: "typed variable with default", + fsys: fstest.MapFS{ + "main.tf": &fstest.MapFile{Data: []byte(`module "test" { + source = "./modules/test" + test_var = null +}`)}, + "modules/test/main.tf": &fstest.MapFile{Data: []byte(`variable "test_var" { + type = number + default = null +} + +resource "foo" "this" { + bar = var.test_var != null ? 1 : 2 +}`)}, + }, + }, + { + name: "empty variable", + fsys: fstest.MapFS{ + "main.tf": &fstest.MapFile{Data: []byte(`module "test" { + source = "./modules/test" + test_var = null +}`)}, + "modules/test/main.tf": &fstest.MapFile{Data: []byte(`variable "test_var" {} + +resource "foo" "this" { + bar = var.test_var != null ? 1 : 2 +}`)}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parser := New( + tt.fsys, "", + OptionStopOnHCLError(true), + ) + require.NoError(t, parser.ParseFS(context.TODO(), ".")) + + _, err := parser.Load(context.TODO()) + require.NoError(t, err) + + modules, _, err := parser.EvaluateAll(context.TODO()) + require.NoError(t, err) + + res := modules.GetResourcesByType("foo")[0] + attr := res.GetAttribute("bar") + val, _ := attr.Value().AsBigFloat().Int64() + assert.Equal(t, int64(2), val) + }) + } +} diff --git a/pkg/iac/terraform/block.go b/pkg/iac/terraform/block.go index 348f938d4559..13175bb9a072 100644 --- a/pkg/iac/terraform/block.go +++ b/pkg/iac/terraform/block.go @@ -585,7 +585,7 @@ func (b *Block) Values() cty.Value { if attribute.Name() == "for_each" { continue } - values[attribute.Name()] = attribute.Value() + values[attribute.Name()] = attribute.NullableValue() } return cty.ObjectVal(postProcessValues(b, values)) } @@ -643,7 +643,7 @@ func (b *Block) expandDynamic() ([]*Block, error) { ) forEachVal.ForEachElement(func(key, val cty.Value) (stop bool) { - if val.IsNull() { + if val.IsNull() || !val.IsKnown() { return }