diff --git a/plans/objchange/compatible.go b/plans/objchange/compatible.go index 6d92fca4d..efb4023d2 100644 --- a/plans/objchange/compatible.go +++ b/plans/objchange/compatible.go @@ -357,6 +357,10 @@ func couldHaveUnknownBlockPlaceholder(v cty.Value, blockS *configschema.NestedBl return false // treated as if the list were empty, so we would see zero iterations below } + // Unmark before we call ElementIterator in case this iterable is marked sensitive. + // This can arise in the case where a member of a Set is sensitive, and thus the + // whole Set is marked sensitive + v, _ := v.Unmark() // For all other nesting modes, our value should be something iterable. for it := v.ElementIterator(); it.Next(); { _, ev := it.Element() diff --git a/plans/objchange/compatible_test.go b/plans/objchange/compatible_test.go index 9a6924441..ac3793674 100644 --- a/plans/objchange/compatible_test.go +++ b/plans/objchange/compatible_test.go @@ -194,6 +194,49 @@ func TestAssertObjectCompatible(t *testing.T) { `.name: inconsistent values for sensitive attribute`, }, }, + { + // This tests the codepath that leads to couldHaveUnknownBlockPlaceholder, + // where a set may be sensitive and need to be unmarked before it + // is iterated upon + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "configuration": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "sensitive_fields": { + Nesting: configschema.NestingSet, + Block: schemaWithFoo, + }, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "configuration": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "sensitive_fields": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("secret"), + }), + }).Mark("sensitive"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "configuration": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "sensitive_fields": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("secret"), + }), + }).Mark("sensitive"), + }), + }), + }), + nil, + }, { &configschema.Block{ Attributes: map[string]*configschema.Attribute{