diff --git a/internal/terraform/context_plan2_test.go b/internal/terraform/context_plan2_test.go index e6315c313..3964fac98 100644 --- a/internal/terraform/context_plan2_test.go +++ b/internal/terraform/context_plan2_test.go @@ -2859,3 +2859,65 @@ func TestContext2Plan_preconditionErrors(t *testing.T) { }) } } + +func TestContext2Plan_preconditionSensitiveValues(t *testing.T) { + p := testProvider("test") + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +terraform { + experiments = [preconditions_postconditions] +} + +variable "boop" { + sensitive = true + type = string +} + +output "a" { + sensitive = true + value = var.boop + + precondition { + condition = length(var.boop) <= 4 + error_message = "Boop is too long, ${length(var.boop)} > 4" + } +} +`, + }) + + _, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ + Mode: plans.NormalMode, + SetVariables: InputValues{ + "boop": &InputValue{ + Value: cty.StringVal("bleep"), + SourceType: ValueFromCLIArg, + }, + }, + }) + if !diags.HasErrors() { + t.Fatal("succeeded; want errors") + } + if got, want := len(diags), 2; got != want { + t.Errorf("wrong number of diags, got %d, want %d", got, want) + } + for _, diag := range diags { + desc := diag.Description() + if desc.Summary == "Module output value precondition failed" { + if got, want := desc.Detail, "The error message included a sensitive value, so it will not be displayed."; !strings.Contains(got, want) { + t.Errorf("unexpected detail\ngot: %s\nwant to contain %q", got, want) + } + } else if desc.Summary == "Error message refers to sensitive values" { + if got, want := desc.Detail, "The error expression used to explain this condition refers to sensitive values."; !strings.Contains(got, want) { + t.Errorf("unexpected detail\ngot: %s\nwant to contain %q", got, want) + } + } else { + t.Errorf("unexpected summary\ngot: %s", desc.Summary) + } + } +} diff --git a/internal/terraform/eval_conditions.go b/internal/terraform/eval_conditions.go index 4bf311428..80858eee8 100644 --- a/internal/terraform/eval_conditions.go +++ b/internal/terraform/eval_conditions.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" + "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/tfdiags" ) @@ -110,6 +111,10 @@ func evalCheckRules(typ checkType, rules []*configs.CheckRule, ctx EvalContext, continue } + // The condition result may be marked if the expression refers to a + // sensitive value. + result, _ = result.Unmark() + if result.True() { continue } @@ -128,7 +133,23 @@ func evalCheckRules(typ checkType, rules []*configs.CheckRule, ctx EvalContext, EvalContext: hclCtx, }) } else { - errorMessage = strings.TrimSpace(errorValue.AsString()) + if marks.Has(errorValue, marks.Sensitive) { + diags = diags.Append(&hcl.Diagnostic{ + Severity: severity, + + Summary: "Error message refers to sensitive values", + Detail: `The error expression used to explain this condition refers to sensitive values. Terraform will not display the resulting message. + +You can correct this by removing references to sensitive values, or by carefully using the nonsensitive() function if the expression will not reveal the sensitive data.`, + + Subject: rule.ErrorMessage.Range().Ptr(), + Expression: rule.ErrorMessage, + EvalContext: hclCtx, + }) + errorMessage = "The error message included a sensitive value, so it will not be displayed." + } else { + errorMessage = strings.TrimSpace(errorValue.AsString()) + } } } if errorMessage == "" {