From ff32fab41a764e7b4ddbecfc997b67943a40aa25 Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Wed, 31 Mar 2021 14:28:59 -0400 Subject: [PATCH] command/jsonplan: Fix sensitive/unknown crash When rendering the JSON plan sensitivity output, if the plan contained unknown collection or structural types, Terraform would crash. We need to detect unknown values before attempting to iterate them. Unknown collection or structural values cannot have sensitive contents accidentally displayed, as those values are not known until after apply. As a result we return an empty value of the appropriate type for the sensitivity mapping. --- command/jsonplan/plan.go | 10 ++++++++++ command/jsonplan/plan_test.go | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/command/jsonplan/plan.go b/command/jsonplan/plan.go index 5a828ccce..4d8dccd95 100644 --- a/command/jsonplan/plan.go +++ b/command/jsonplan/plan.go @@ -522,6 +522,11 @@ func sensitiveAsBool(val cty.Value) cty.Value { case val.IsNull(), ty.IsPrimitiveType(), ty.Equals(cty.DynamicPseudoType): return cty.False case ty.IsListType() || ty.IsTupleType() || ty.IsSetType(): + if !val.IsKnown() { + // If the collection is unknown we can't say anything about the + // sensitivity of its contents + return cty.EmptyTupleVal + } length := val.LengthInt() if length == 0 { // If there are no elements then we can't have sensitive values @@ -540,6 +545,11 @@ func sensitiveAsBool(val cty.Value) cty.Value { // indistinguishable in JSON. return cty.TupleVal(vals) case ty.IsMapType() || ty.IsObjectType(): + if !val.IsKnown() { + // If the map/object is unknown we can't say anything about the + // sensitivity of its attributes + return cty.EmptyObjectVal + } var length int switch { case ty.IsMapType(): diff --git a/command/jsonplan/plan_test.go b/command/jsonplan/plan_test.go index f6ee837fe..9d43d1dc5 100644 --- a/command/jsonplan/plan_test.go +++ b/command/jsonplan/plan_test.go @@ -447,6 +447,22 @@ func TestSensitiveAsBool(t *testing.T) { cty.EmptyObjectVal, }), }, + { + cty.ObjectVal(map[string]cty.Value{ + "list": cty.UnknownVal(cty.List(cty.String)), + "set": cty.UnknownVal(cty.Set(cty.Bool)), + "tuple": cty.UnknownVal(cty.Tuple([]cty.Type{cty.String, cty.Number})), + "map": cty.UnknownVal(cty.Map(cty.String)), + "object": cty.UnknownVal(cty.Object(map[string]cty.Type{"a": cty.String})), + }), + cty.ObjectVal(map[string]cty.Value{ + "list": cty.EmptyTupleVal, + "set": cty.EmptyTupleVal, + "tuple": cty.EmptyTupleVal, + "map": cty.EmptyObjectVal, + "object": cty.EmptyObjectVal, + }), + }, } for _, test := range tests {