From d23f0998fb7d8fe25a0df5b884c9bfb2cc00c992 Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Fri, 8 Apr 2022 16:16:52 -0400 Subject: [PATCH] cli: Fix plan diff for sensitive nested attributes When rendering diffs for resources which use nested attribute types, we must cope with collections backing those attributes which are entirely sensitive. The most common way this will be seen is through sensitive values being present in sets, which will result in the entire set being marked sensitive. --- internal/command/format/diff.go | 9 ++ internal/command/format/diff_test.go | 147 +++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) diff --git a/internal/command/format/diff.go b/internal/command/format/diff.go index 9be2ea3ebd61..2ce3e17dd769 100644 --- a/internal/command/format/diff.go +++ b/internal/command/format/diff.go @@ -437,6 +437,7 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( nameLen, indent int, path cty.Path, action plans.Action, showJustNew bool) { p.buf.WriteString("\n") + p.writeSensitivityWarning(old, new, indent, action, false) p.buf.WriteString(strings.Repeat(" ", indent)) p.writeActionSymbol(action) @@ -445,6 +446,14 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( p.buf.WriteString(p.color.Color("[reset]")) p.buf.WriteString(strings.Repeat(" ", nameLen-len(name))) + if old.HasMark(marks.Sensitive) || new.HasMark(marks.Sensitive) { + p.buf.WriteString(" = (sensitive value)") + if p.pathForcesNewResource(path) { + p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) + } + return + } + result := &blockBodyDiffResult{} switch objS.Nesting { case configschema.NestingSingle: diff --git a/internal/command/format/diff_test.go b/internal/command/format/diff_test.go index 4cb2659b24dd..16c7a89eb9bc 100644 --- a/internal/command/format/diff_test.go +++ b/internal/command/format/diff_test.go @@ -2938,6 +2938,55 @@ func TestResourceChange_nestedList(t *testing.T) { func TestResourceChange_nestedSet(t *testing.T) { testCases := map[string]testCase{ + "creation from null - sensitive set": { + Action: plans.Create, + Mode: addrs.ManagedResourceMode, + Before: cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "ami": cty.String, + "disks": cty.Set(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + "root_block_device": cty.Set(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + })), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), + "root_block_device": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + AfterValMarks: []cty.PathValueMarks{ + { + Path: cty.Path{cty.GetAttrStep{Name: "disks"}}, + Marks: cty.NewValueMarks(marks.Sensitive), + }, + }, + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingSet), + ExpectedOutput: ` # test_instance.example will be created + + resource "test_instance" "example" { + + ami = "ami-AFTER" + + disks = (sensitive value) + + id = "i-02ae66f368e8518a9" + + + root_block_device { + + volume_type = "gp2" + } + } +`, + }, "in-place update - creation": { Action: plans.Update, Mode: addrs.ManagedResourceMode, @@ -2983,6 +3032,104 @@ func TestResourceChange_nestedSet(t *testing.T) { + volume_type = "gp2" } } +`, + }, + "in-place update - creation - sensitive set": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + "root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), + "root_block_device": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + AfterValMarks: []cty.PathValueMarks{ + { + Path: cty.Path{cty.GetAttrStep{Name: "disks"}}, + Marks: cty.NewValueMarks(marks.Sensitive), + }, + }, + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingSet), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + # Warning: this attribute value will be marked as sensitive and will not + # display in UI output after applying this change. + ~ disks = (sensitive value) + id = "i-02ae66f368e8518a9" + + + root_block_device { + + volume_type = "gp2" + } + } +`, + }, + "in-place update - marking set sensitive": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + }), + AfterValMarks: []cty.PathValueMarks{ + { + Path: cty.Path{cty.GetAttrStep{Name: "disks"}}, + Marks: cty.NewValueMarks(marks.Sensitive), + }, + }, + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingSet), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + # Warning: this attribute value will be marked as sensitive and will not + # display in UI output after applying this change. The value is unchanged. + ~ disks = (sensitive value) + id = "i-02ae66f368e8518a9" + } `, }, "in-place update - insertion": {