mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 07:10:41 -08:00
fix(misconf): populate context correctly for module instances (#8656)
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
@@ -137,6 +137,8 @@ func (e *evaluator) EvaluateAll(ctx context.Context) (terraform.Modules, map[str
|
|||||||
|
|
||||||
// expand out resources and modules via count, for-each and dynamic
|
// expand out resources and modules via count, for-each and dynamic
|
||||||
// (not a typo, we do this twice so every order is processed)
|
// (not a typo, we do this twice so every order is processed)
|
||||||
|
// TODO: using a module in for_each or count does not work,
|
||||||
|
// because the child module is evaluated later
|
||||||
e.blocks = e.expandBlocks(e.blocks)
|
e.blocks = e.expandBlocks(e.blocks)
|
||||||
e.blocks = e.expandBlocks(e.blocks)
|
e.blocks = e.expandBlocks(e.blocks)
|
||||||
|
|
||||||
@@ -239,10 +241,17 @@ func (e *evaluator) evaluateSubmodule(ctx context.Context, sm *submodule) bool {
|
|||||||
sm.modules, sm.fsMap = sm.eval.EvaluateAll(ctx)
|
sm.modules, sm.fsMap = sm.eval.EvaluateAll(ctx)
|
||||||
outputs := sm.eval.exportOutputs()
|
outputs := sm.eval.exportOutputs()
|
||||||
|
|
||||||
|
valueMap := e.ctx.Get("module").AsValueMap()
|
||||||
|
if valueMap == nil {
|
||||||
|
valueMap = make(map[string]cty.Value)
|
||||||
|
}
|
||||||
|
|
||||||
// lastState needs to be captured after applying outputs – so that they
|
// lastState needs to be captured after applying outputs – so that they
|
||||||
// don't get treated as changes – but before running post-submodule
|
// don't get treated as changes – but before running post-submodule
|
||||||
// evaluation, so that changes from that can trigger re-evaluations of
|
// evaluation, so that changes from that can trigger re-evaluations of
|
||||||
// the submodule if/when they feed back into inputs.
|
// the submodule if/when they feed back into inputs.
|
||||||
|
ref := sm.definition.Definition.Reference()
|
||||||
|
e.ctx.Set(blockInstanceValues(sm.definition.Definition, valueMap, outputs), "module", ref.NameLabel())
|
||||||
e.ctx.Set(outputs, "module", sm.definition.Name)
|
e.ctx.Set(outputs, "module", sm.definition.Name)
|
||||||
sm.lastState = sm.definition.inputVars()
|
sm.lastState = sm.definition.inputVars()
|
||||||
e.evaluateSteps()
|
e.evaluateSteps()
|
||||||
@@ -564,7 +573,7 @@ func (e *evaluator) getValuesByBlockType(blockType string) cty.Value {
|
|||||||
if valueMap == nil {
|
if valueMap == nil {
|
||||||
valueMap = make(map[string]cty.Value)
|
valueMap = make(map[string]cty.Value)
|
||||||
}
|
}
|
||||||
valueMap[ref.NameLabel()] = blockInstanceValues(b, valueMap)
|
valueMap[ref.NameLabel()] = blockInstanceValues(b, valueMap, b.Values())
|
||||||
|
|
||||||
// Update the map of all blocks with the same type.
|
// Update the map of all blocks with the same type.
|
||||||
values[ref.TypeLabel()] = cty.ObjectVal(valueMap)
|
values[ref.TypeLabel()] = cty.ObjectVal(valueMap)
|
||||||
@@ -588,7 +597,7 @@ func (e *evaluator) getResources() map[string]cty.Value {
|
|||||||
typeValues = make(map[string]cty.Value)
|
typeValues = make(map[string]cty.Value)
|
||||||
values[ref.TypeLabel()] = typeValues
|
values[ref.TypeLabel()] = typeValues
|
||||||
}
|
}
|
||||||
typeValues[ref.NameLabel()] = blockInstanceValues(b, typeValues)
|
typeValues[ref.NameLabel()] = blockInstanceValues(b, typeValues, b.Values())
|
||||||
}
|
}
|
||||||
|
|
||||||
return lo.MapValues(values, func(v map[string]cty.Value, _ string) cty.Value {
|
return lo.MapValues(values, func(v map[string]cty.Value, _ string) cty.Value {
|
||||||
@@ -600,14 +609,14 @@ func (e *evaluator) getResources() map[string]cty.Value {
|
|||||||
// If the count argument is used, a tuple is returned where the index corresponds to the argument index.
|
// If the count argument is used, a tuple is returned where the index corresponds to the argument index.
|
||||||
// If the for_each argument is used, an object is returned where the key corresponds to the argument key.
|
// If the for_each argument is used, an object is returned where the key corresponds to the argument key.
|
||||||
// In other cases, the values of the block itself are returned.
|
// In other cases, the values of the block itself are returned.
|
||||||
func blockInstanceValues(b *terraform.Block, typeValues map[string]cty.Value) cty.Value {
|
func blockInstanceValues(b *terraform.Block, typeValues map[string]cty.Value, values cty.Value) cty.Value {
|
||||||
ref := b.Reference()
|
ref := b.Reference()
|
||||||
key := ref.RawKey()
|
key := ref.RawKey()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case key.Type().Equals(cty.Number) && b.GetAttribute("count") != nil:
|
case key.Type().Equals(cty.Number) && b.GetAttribute("count") != nil:
|
||||||
idx, _ := key.AsBigFloat().Int64()
|
idx, _ := key.AsBigFloat().Int64()
|
||||||
return insertTupleElement(typeValues[ref.NameLabel()], int(idx), b.Values())
|
return insertTupleElement(typeValues[ref.NameLabel()], int(idx), values)
|
||||||
case isForEachKey(key) && b.GetAttribute("for_each") != nil:
|
case isForEachKey(key) && b.GetAttribute("for_each") != nil:
|
||||||
keyStr := ref.Key()
|
keyStr := ref.Key()
|
||||||
|
|
||||||
@@ -621,11 +630,10 @@ func blockInstanceValues(b *terraform.Block, typeValues map[string]cty.Value) ct
|
|||||||
instances = make(map[string]cty.Value)
|
instances = make(map[string]cty.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
instances[keyStr] = b.Values()
|
instances[keyStr] = values
|
||||||
return cty.ObjectVal(instances)
|
return cty.ObjectVal(instances)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return b.Values()
|
return values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1708,10 +1708,12 @@ func TestPopulateContextWithBlockInstances(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
blockType string
|
||||||
files map[string]string
|
files map[string]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "data blocks with count",
|
name: "data blocks with count",
|
||||||
|
blockType: "data",
|
||||||
files: map[string]string{
|
files: map[string]string{
|
||||||
"main.tf": `data "d" "foo" {
|
"main.tf": `data "d" "foo" {
|
||||||
count = 1
|
count = 1
|
||||||
@@ -1731,6 +1733,7 @@ data "c" "foo" {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "resource blocks with count",
|
name: "resource blocks with count",
|
||||||
|
blockType: "resource",
|
||||||
files: map[string]string{
|
files: map[string]string{
|
||||||
"main.tf": `resource "d" "foo" {
|
"main.tf": `resource "d" "foo" {
|
||||||
count = 1
|
count = 1
|
||||||
@@ -1745,11 +1748,37 @@ resource "b" "foo" {
|
|||||||
resource "c" "foo" {
|
resource "c" "foo" {
|
||||||
count = 1
|
count = 1
|
||||||
value = b.foo[0].value
|
value = b.foo[0].value
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "module block with count",
|
||||||
|
blockType: "data",
|
||||||
|
files: map[string]string{
|
||||||
|
"main.tf": `module "a" {
|
||||||
|
source = "./modules/a"
|
||||||
|
count = 2
|
||||||
|
inp = "Index ${count.index}"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "b" "foo" {
|
||||||
|
count = 1
|
||||||
|
value = module.a[0].value
|
||||||
|
}
|
||||||
|
|
||||||
|
data "c" "foo" {
|
||||||
|
count = 1
|
||||||
|
value = data.b.foo[0].value
|
||||||
|
}`,
|
||||||
|
"modules/a/main.tf": `variable "inp" {}
|
||||||
|
output "value" {
|
||||||
|
value = var.inp
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "data blocks with for_each",
|
name: "data blocks with for_each",
|
||||||
|
blockType: "data",
|
||||||
files: map[string]string{
|
files: map[string]string{
|
||||||
"main.tf": `data "d" "foo" {
|
"main.tf": `data "d" "foo" {
|
||||||
for_each = toset([0])
|
for_each = toset([0])
|
||||||
@@ -1769,6 +1798,7 @@ data "c" "foo" {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "resource blocks with for_each",
|
name: "resource blocks with for_each",
|
||||||
|
blockType: "resource",
|
||||||
files: map[string]string{
|
files: map[string]string{
|
||||||
"main.tf": `resource "d" "foo" {
|
"main.tf": `resource "d" "foo" {
|
||||||
for_each = toset([0])
|
for_each = toset([0])
|
||||||
@@ -1783,6 +1813,25 @@ resource "b" "foo" {
|
|||||||
resource "c" "foo" {
|
resource "c" "foo" {
|
||||||
for_each = b.foo
|
for_each = b.foo
|
||||||
value = each.value.value
|
value = each.value.value
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "module block with for_each",
|
||||||
|
blockType: "data",
|
||||||
|
files: map[string]string{
|
||||||
|
"main.tf": `module "a" {
|
||||||
|
for_each = toset([0])
|
||||||
|
source = "./modules/a"
|
||||||
|
inp = "Index ${each.key}"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "b" "foo" {
|
||||||
|
value = module.a["0"].value
|
||||||
|
}`,
|
||||||
|
"modules/a/main.tf": `variable "inp" {}
|
||||||
|
output "value" {
|
||||||
|
value = var.inp
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1791,8 +1840,8 @@ resource "c" "foo" {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
modules := parse(t, tt.files)
|
modules := parse(t, tt.files)
|
||||||
require.Len(t, modules, 1)
|
require.GreaterOrEqual(t, len(modules), 1)
|
||||||
for _, b := range modules.GetBlocks() {
|
for _, b := range modules.GetBlocks().OfType(tt.blockType) {
|
||||||
attr := b.GetAttribute("value")
|
attr := b.GetAttribute("value")
|
||||||
assert.Equal(t, "Index 0", attr.Value().AsString())
|
assert.Equal(t, "Index 0", attr.Value().AsString())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user