From bb1cd82d60c687573a2d31c70ce8b9660e796aa8 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 4 Jun 2025 14:21:56 -0400 Subject: [PATCH] ensure outputs carry sensitive marks forward Sensitive marks were lost from module outputs during the namedvals rewrite. Ensure output are evaluated with sensitive in accordance with their configuration. --- internal/terraform/context_plan2_test.go | 65 ++++++++++++++++++++++++ internal/terraform/evaluate.go | 5 +- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/internal/terraform/context_plan2_test.go b/internal/terraform/context_plan2_test.go index fb14518797..6df225d5a8 100644 --- a/internal/terraform/context_plan2_test.go +++ b/internal/terraform/context_plan2_test.go @@ -6847,3 +6847,68 @@ data "test_data_source" "foo" { } } + +func TestContext2Plan_sensitiveOutput(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +module "child" { + source = "./child" +} + +output "is_secret" { + // not only must the plan store the output as sensitive, it must also be + // evaluated as such + value = issensitive(module.child.secret) +} +`, + "./child/main.tf": ` +output "secret" { + sensitive = true + value = "test" +} +`, + }) + + ctx := testContext2(t, &ContextOpts{}) + + plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) + tfdiags.AssertNoErrors(t, diags) + + expectedChanges := &plans.Changes{ + Outputs: []*plans.OutputChange{ + { + Addr: mustAbsOutputValue("module.child.output.secret"), + Change: plans.Change{ + Action: plans.Create, + BeforeIdentity: cty.NullVal(cty.DynamicPseudoType), + AfterIdentity: cty.NullVal(cty.DynamicPseudoType), + Before: cty.NullVal(cty.DynamicPseudoType), + After: cty.StringVal("test"), + }, + Sensitive: true, + }, + { + Addr: mustAbsOutputValue("output.is_secret"), + Change: plans.Change{ + Action: plans.Create, + BeforeIdentity: cty.NullVal(cty.DynamicPseudoType), + AfterIdentity: cty.NullVal(cty.DynamicPseudoType), + Before: cty.NullVal(cty.DynamicPseudoType), + After: cty.True, + }, + }, + }, + } + changes, err := plan.Changes.Decode(nil) + if err != nil { + t.Fatal(err) + } + + sort.SliceStable(changes.Outputs, func(i, j int) bool { + return changes.Outputs[i].Addr.String() < changes.Outputs[j].Addr.String() + }) + + if diff := cmp.Diff(expectedChanges, changes, ctydebug.CmpOptions); diff != "" { + t.Fatalf("unexpected changes: %s", diff) + } +} diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index e3151ea654..573b937bf0 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -430,7 +430,7 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc namedVals := d.Evaluator.NamedValues moduleInstAddr := absAddr.Instance(instKey) attrs := make(map[string]cty.Value, len(outputConfigs)) - for name := range outputConfigs { + for name, cfg := range outputConfigs { outputAddr := moduleInstAddr.OutputValue(name) // Although we do typically expect the graph dependencies to @@ -446,6 +446,9 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc continue } outputVal := namedVals.GetOutputValue(outputAddr) + if cfg.Sensitive { + outputVal = outputVal.Mark(marks.Sensitive) + } attrs[name] = outputVal }