Merge pull request #26590 from hashicorp/pselle/sensitivity-providers
Mark attributes providers mark as sensitive
This commit is contained in:
commit
cdebf2820d
|
@ -11870,7 +11870,23 @@ variable "sensitive_map" {
|
||||||
|
|
||||||
resource "test_resource" "foo" {
|
resource "test_resource" "foo" {
|
||||||
value = var.sensitive_map.x
|
value = var.sensitive_map.x
|
||||||
}`,
|
sensitive_value = "should get marked"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_resource" "bar" {
|
||||||
|
value = test_resource.foo.sensitive_value
|
||||||
|
random = test_resource.foo.id # not sensitive
|
||||||
|
|
||||||
|
nesting_single {
|
||||||
|
value = "abc"
|
||||||
|
sensitive_value = "xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_resource" "baz" {
|
||||||
|
value = test_resource.bar.nesting_single.sensitive_value
|
||||||
|
}
|
||||||
|
`,
|
||||||
})
|
})
|
||||||
|
|
||||||
p := testProvider("test")
|
p := testProvider("test")
|
||||||
|
@ -11903,10 +11919,20 @@ resource "test_resource" "foo" {
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := mustResourceInstanceAddr("test_resource.foo")
|
addr := mustResourceInstanceAddr("test_resource.foo")
|
||||||
|
|
||||||
fooChangeSrc := plan.Changes.ResourceInstance(addr)
|
fooChangeSrc := plan.Changes.ResourceInstance(addr)
|
||||||
verifySensitiveValue(fooChangeSrc.AfterValMarks)
|
verifySensitiveValue(fooChangeSrc.AfterValMarks)
|
||||||
|
|
||||||
|
// Sensitive attributes (defined by the provider) are marked
|
||||||
|
// as sensitive when referenced from another resource
|
||||||
|
// "bar" references sensitive resources in "foo"
|
||||||
|
barAddr := mustResourceInstanceAddr("test_resource.bar")
|
||||||
|
barChangeSrc := plan.Changes.ResourceInstance(barAddr)
|
||||||
|
verifySensitiveValue(barChangeSrc.AfterValMarks)
|
||||||
|
|
||||||
|
bazAddr := mustResourceInstanceAddr("test_resource.baz")
|
||||||
|
bazChangeSrc := plan.Changes.ResourceInstance(bazAddr)
|
||||||
|
verifySensitiveValue(bazChangeSrc.AfterValMarks)
|
||||||
|
|
||||||
state, diags := ctx.Apply()
|
state, diags := ctx.Apply()
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
t.Fatalf("apply errors: %s", diags.Err())
|
t.Fatalf("apply errors: %s", diags.Err())
|
||||||
|
@ -11914,6 +11940,12 @@ resource "test_resource" "foo" {
|
||||||
|
|
||||||
fooState := state.ResourceInstance(addr)
|
fooState := state.ResourceInstance(addr)
|
||||||
verifySensitiveValue(fooState.Current.AttrSensitivePaths)
|
verifySensitiveValue(fooState.Current.AttrSensitivePaths)
|
||||||
|
|
||||||
|
barState := state.ResourceInstance(barAddr)
|
||||||
|
verifySensitiveValue(barState.Current.AttrSensitivePaths)
|
||||||
|
|
||||||
|
bazState := state.ResourceInstance(bazAddr)
|
||||||
|
verifySensitiveValue(bazState.Current.AttrSensitivePaths)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContext2Apply_variableSensitivityChange(t *testing.T) {
|
func TestContext2Apply_variableSensitivityChange(t *testing.T) {
|
||||||
|
|
|
@ -425,6 +425,11 @@ func testProviderSchema(name string) *ProviderSchema {
|
||||||
Type: cty.String,
|
Type: cty.String,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
"sensitive_value": {
|
||||||
|
Type: cty.String,
|
||||||
|
Sensitive: true,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
"random": {
|
"random": {
|
||||||
Type: cty.String,
|
Type: cty.String,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
@ -440,6 +445,15 @@ func testProviderSchema(name string) *ProviderSchema {
|
||||||
},
|
},
|
||||||
Nesting: configschema.NestingSet,
|
Nesting: configschema.NestingSet,
|
||||||
},
|
},
|
||||||
|
"nesting_single": {
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"value": {Type: cty.String, Optional: true},
|
||||||
|
"sensitive_value": {Type: cty.String, Optional: true, Sensitive: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nesting: configschema.NestingSingle,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
name + "_ami_list": {
|
name + "_ami_list": {
|
||||||
|
|
|
@ -727,7 +727,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Planned resources are temporarily stored in state with empty values,
|
// Planned resources are temporarily stored in state with empty values,
|
||||||
// and need to be replaced bu the planned value here.
|
// and need to be replaced by the planned value here.
|
||||||
if is.Current.Status == states.ObjectPlanned {
|
if is.Current.Status == states.ObjectPlanned {
|
||||||
if change == nil {
|
if change == nil {
|
||||||
// If the object is in planned status then we should not get
|
// If the object is in planned status then we should not get
|
||||||
|
@ -752,6 +752,10 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If our schema contains sensitive values, mark those as sensitive
|
||||||
|
if schema.ContainsSensitive() {
|
||||||
|
val = markProviderSensitiveAttributes(schema, val)
|
||||||
|
}
|
||||||
instances[key] = val
|
instances[key] = val
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -768,7 +772,13 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
instances[key] = ios.Value
|
|
||||||
|
val := ios.Value
|
||||||
|
// If our schema contains sensitive values, mark those as sensitive
|
||||||
|
if schema.ContainsSensitive() {
|
||||||
|
val = markProviderSensitiveAttributes(schema, val)
|
||||||
|
}
|
||||||
|
instances[key] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret cty.Value
|
var ret cty.Value
|
||||||
|
@ -935,3 +945,51 @@ func moduleDisplayAddr(addr addrs.ModuleInstance) string {
|
||||||
return addr.String()
|
return addr.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// markProviderSensitiveAttributes returns an updated value
|
||||||
|
// where attributes that are Sensitive are marked
|
||||||
|
func markProviderSensitiveAttributes(schema *configschema.Block, val cty.Value) cty.Value {
|
||||||
|
return val.MarkWithPaths(getValMarks(schema, val, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getValMarks(schema *configschema.Block, val cty.Value, path cty.Path) []cty.PathValueMarks {
|
||||||
|
var pvm []cty.PathValueMarks
|
||||||
|
for name, attrS := range schema.Attributes {
|
||||||
|
if attrS.Sensitive {
|
||||||
|
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
|
||||||
|
attrPath := make(cty.Path, len(path), len(path)+1)
|
||||||
|
copy(attrPath, path)
|
||||||
|
attrPath = append(path, cty.GetAttrStep{Name: name})
|
||||||
|
pvm = append(pvm, cty.PathValueMarks{
|
||||||
|
Path: attrPath,
|
||||||
|
Marks: cty.NewValueMarks("sensitive"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, blockS := range schema.BlockTypes {
|
||||||
|
// If our block doesn't contain any sensitive attributes, skip inspecting it
|
||||||
|
if !blockS.Block.ContainsSensitive() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
|
||||||
|
blockPath := make(cty.Path, len(path), len(path)+1)
|
||||||
|
copy(blockPath, path)
|
||||||
|
blockPath = append(path, cty.GetAttrStep{Name: name})
|
||||||
|
|
||||||
|
blockV := val.GetAttr(name)
|
||||||
|
switch blockS.Nesting {
|
||||||
|
case configschema.NestingSingle, configschema.NestingGroup:
|
||||||
|
pvm = append(pvm, getValMarks(&blockS.Block, blockV, blockPath)...)
|
||||||
|
case configschema.NestingList, configschema.NestingMap, configschema.NestingSet:
|
||||||
|
for it := blockV.ElementIterator(); it.Next(); {
|
||||||
|
idx, blockEV := it.Element()
|
||||||
|
morePaths := getValMarks(&blockS.Block, blockEV, append(blockPath, cty.IndexStep{Key: idx}))
|
||||||
|
pvm = append(pvm, morePaths...)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pvm
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
"github.com/hashicorp/terraform/plans"
|
"github.com/hashicorp/terraform/plans"
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
|
@ -124,6 +125,178 @@ func TestEvaluatorGetInputVariable(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEvaluatorGetResource(t *testing.T) {
|
||||||
|
stateSync := states.BuildState(func(ss *states.SyncState) {
|
||||||
|
ss.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "test_resource",
|
||||||
|
Name: "foo",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{"id":"foo", "nesting_list": [{"sensitive_value":"abc"}], "nesting_map": {"foo":{"foo":"x"}}, "nesting_set": [{"baz":"abc"}], "nesting_single": {"boop":"abc"}, "nesting_nesting": {"nesting_list":[{"sensitive_value":"abc"}]}, "value":"hello"}`),
|
||||||
|
},
|
||||||
|
addrs.AbsProviderConfig{
|
||||||
|
Provider: addrs.NewDefaultProvider("test"),
|
||||||
|
Module: addrs.RootModule,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}).SyncWrapper()
|
||||||
|
|
||||||
|
rc := &configs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "test_resource",
|
||||||
|
Name: "foo",
|
||||||
|
Config: configs.SynthBody("", map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("foo"),
|
||||||
|
}),
|
||||||
|
Provider: addrs.Provider{
|
||||||
|
Hostname: addrs.DefaultRegistryHost,
|
||||||
|
Namespace: "hashicorp",
|
||||||
|
Type: "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluator := &Evaluator{
|
||||||
|
Meta: &ContextMeta{
|
||||||
|
Env: "foo",
|
||||||
|
},
|
||||||
|
Changes: plans.NewChanges().SyncWrapper(),
|
||||||
|
Config: &configs.Config{
|
||||||
|
Module: &configs.Module{
|
||||||
|
ManagedResources: map[string]*configs.Resource{
|
||||||
|
"test_resource.foo": rc,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
State: stateSync,
|
||||||
|
Schemas: &Schemas{
|
||||||
|
Providers: map[addrs.Provider]*ProviderSchema{
|
||||||
|
addrs.NewDefaultProvider("test"): {
|
||||||
|
Provider: &configschema.Block{},
|
||||||
|
ResourceTypes: map[string]*configschema.Block{
|
||||||
|
"test_resource": {
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"id": {
|
||||||
|
Type: cty.String,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
Type: cty.String,
|
||||||
|
Computed: true,
|
||||||
|
Sensitive: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BlockTypes: map[string]*configschema.NestedBlock{
|
||||||
|
"nesting_list": {
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"value": {Type: cty.String, Optional: true},
|
||||||
|
"sensitive_value": {Type: cty.String, Optional: true, Sensitive: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nesting: configschema.NestingList,
|
||||||
|
},
|
||||||
|
"nesting_map": {
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"foo": {Type: cty.String, Optional: true, Sensitive: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nesting: configschema.NestingMap,
|
||||||
|
},
|
||||||
|
"nesting_set": {
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"baz": {Type: cty.String, Optional: true, Sensitive: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nesting: configschema.NestingSet,
|
||||||
|
},
|
||||||
|
"nesting_single": {
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"boop": {Type: cty.String, Optional: true, Sensitive: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nesting: configschema.NestingSingle,
|
||||||
|
},
|
||||||
|
"nesting_nesting": {
|
||||||
|
Block: configschema.Block{
|
||||||
|
BlockTypes: map[string]*configschema.NestedBlock{
|
||||||
|
"nesting_list": {
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"value": {Type: cty.String, Optional: true},
|
||||||
|
"sensitive_value": {Type: cty.String, Optional: true, Sensitive: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nesting: configschema.NestingList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nesting: configschema.NestingSingle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &evaluationStateData{
|
||||||
|
Evaluator: evaluator,
|
||||||
|
}
|
||||||
|
scope := evaluator.Scope(data, nil)
|
||||||
|
|
||||||
|
want := cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("foo"),
|
||||||
|
"nesting_list": cty.ListVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive_value": cty.StringVal("abc").Mark("sensitive"),
|
||||||
|
"value": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"nesting_map": cty.MapVal(map[string]cty.Value{
|
||||||
|
"foo": cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("x").Mark("sensitive")}),
|
||||||
|
}),
|
||||||
|
"nesting_nesting": cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"nesting_list": cty.ListVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive_value": cty.StringVal("abc").Mark("sensitive"),
|
||||||
|
"value": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"nesting_set": cty.SetVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"baz": cty.StringVal("abc").Mark("sensitive"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"nesting_single": cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"boop": cty.StringVal("abc").Mark("sensitive"),
|
||||||
|
}),
|
||||||
|
"value": cty.StringVal("hello").Mark("sensitive"),
|
||||||
|
})
|
||||||
|
|
||||||
|
addr := addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "test_resource",
|
||||||
|
Name: "foo",
|
||||||
|
}
|
||||||
|
got, diags := scope.Data.GetResource(addr, tfdiags.SourceRange{})
|
||||||
|
|
||||||
|
if len(diags) != 0 {
|
||||||
|
t.Errorf("unexpected diagnostics %s", spew.Sdump(diags))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(want) {
|
||||||
|
t.Errorf("wrong result:\ngot: %#v\nwant: %#v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEvaluatorGetModule(t *testing.T) {
|
func TestEvaluatorGetModule(t *testing.T) {
|
||||||
// Create a new evaluator with an existing state
|
// Create a new evaluator with an existing state
|
||||||
stateSync := states.BuildState(func(ss *states.SyncState) {
|
stateSync := states.BuildState(func(ss *states.SyncState) {
|
||||||
|
|
Loading…
Reference in New Issue