mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-22 02:20:07 -04:00
This new field will be consumed in order to redact sensitive values referenced in a list block's input configuration from the query logs. We simply pipe the config schema and the marked values, and include the paths in the query_start log output.
199 lines
5.9 KiB
Go
199 lines
5.9 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package json
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
|
"github.com/hashicorp/terraform/internal/lang/marks"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestNewQueryStart(t *testing.T) {
|
|
makeAddr := func(resType, resName string) addrs.AbsResourceInstance {
|
|
t.Helper()
|
|
|
|
return addrs.ResourceInstance{
|
|
Resource: addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: resType,
|
|
Name: resName,
|
|
},
|
|
Key: addrs.NoKey,
|
|
}.Absolute(addrs.RootModuleInstance)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
addr addrs.AbsResourceInstance
|
|
inputConfig cty.Value
|
|
schema *configschema.Block
|
|
want QueryStart
|
|
}{
|
|
{
|
|
name: "No sensitivity",
|
|
addr: makeAddr("test_resource", "foo"),
|
|
inputConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("bar"),
|
|
"count": cty.NumberIntVal(1),
|
|
}),
|
|
schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"foo": {Type: cty.String, Optional: true},
|
|
"count": {Type: cty.Number, Optional: true},
|
|
},
|
|
},
|
|
want: QueryStart{
|
|
Address: "test_resource.foo",
|
|
ResourceType: "test_resource",
|
|
SensitiveAttributePaths: []string{},
|
|
},
|
|
},
|
|
{
|
|
name: "Sensitivity via Value Marks (top level)",
|
|
addr: makeAddr("test_resource", "secret_val"),
|
|
inputConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"api_key": cty.StringVal("12345").Mark(marks.Sensitive),
|
|
"public": cty.StringVal("visible"),
|
|
}),
|
|
schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"api_key": {Type: cty.String, Optional: true},
|
|
"public": {Type: cty.String, Optional: true},
|
|
},
|
|
},
|
|
want: QueryStart{
|
|
Address: "test_resource.secret_val",
|
|
ResourceType: "test_resource",
|
|
SensitiveAttributePaths: []string{".api_key"},
|
|
},
|
|
},
|
|
{
|
|
name: "Sensitivity via Schema Definition",
|
|
addr: makeAddr("test_resource", "schema_secret"),
|
|
inputConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"password": cty.StringVal("hunter2"),
|
|
}),
|
|
schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
|
},
|
|
},
|
|
want: QueryStart{
|
|
Address: "test_resource.schema_secret",
|
|
ResourceType: "test_resource",
|
|
SensitiveAttributePaths: []string{".password"},
|
|
},
|
|
},
|
|
{
|
|
name: "Nested Map Value Sensitivity",
|
|
addr: makeAddr("test_resource", "nested_map"),
|
|
inputConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"tags": cty.MapVal(map[string]cty.Value{
|
|
"env": cty.StringVal("prod"),
|
|
"secret": cty.StringVal("hidden").Mark(marks.Sensitive),
|
|
}),
|
|
}),
|
|
schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"tags": {Type: cty.Map(cty.String), Optional: true},
|
|
},
|
|
},
|
|
want: QueryStart{
|
|
Address: "test_resource.nested_map",
|
|
ResourceType: "test_resource",
|
|
SensitiveAttributePaths: []string{`.tags["secret"]`},
|
|
},
|
|
},
|
|
{
|
|
name: "Nested List Value Sensitivity",
|
|
addr: makeAddr("test_resource", "nested_list"),
|
|
inputConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"list": cty.ListVal([]cty.Value{
|
|
cty.StringVal("one"),
|
|
cty.StringVal("two").Mark(marks.Sensitive),
|
|
}),
|
|
}),
|
|
schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"list": {Type: cty.List(cty.String), Optional: true},
|
|
},
|
|
},
|
|
want: QueryStart{
|
|
Address: "test_resource.nested_list",
|
|
ResourceType: "test_resource",
|
|
SensitiveAttributePaths: []string{".list[1]"},
|
|
},
|
|
},
|
|
{
|
|
name: "Complex Nested Schema Sensitivity (Nested Block)",
|
|
addr: makeAddr("test_resource", "complex_schema"),
|
|
inputConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"config_block": cty.ObjectVal(map[string]cty.Value{
|
|
"token": cty.StringVal("abc"),
|
|
}),
|
|
}),
|
|
schema: &configschema.Block{
|
|
BlockTypes: map[string]*configschema.NestedBlock{
|
|
"config_block": {
|
|
Nesting: configschema.NestingSingle,
|
|
Block: configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"token": {Type: cty.String, Sensitive: true, Optional: true},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: QueryStart{
|
|
Address: "test_resource.complex_schema",
|
|
ResourceType: "test_resource",
|
|
SensitiveAttributePaths: []string{".config_block.token"},
|
|
},
|
|
},
|
|
{
|
|
name: "Mixed: Schema Sensitive AND Value Marked",
|
|
addr: makeAddr("test_resource", "mixed"),
|
|
inputConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"double_secret": cty.StringVal("x").Mark(marks.Sensitive),
|
|
}),
|
|
schema: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"double_secret": {Type: cty.String, Sensitive: true, Optional: true},
|
|
},
|
|
},
|
|
want: QueryStart{
|
|
Address: "test_resource.mixed",
|
|
ResourceType: "test_resource",
|
|
// We expect NO duplicates if both sources flag it.
|
|
SensitiveAttributePaths: []string{".double_secret"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := NewQueryStart(tt.addr, tt.inputConfig, tt.schema)
|
|
|
|
if got.Address != tt.want.Address {
|
|
t.Errorf("Address = %q, want %q", got.Address, tt.want.Address)
|
|
}
|
|
if got.ResourceType != tt.want.ResourceType {
|
|
t.Errorf("ResourceType = %q, want %q", got.ResourceType, tt.want.ResourceType)
|
|
}
|
|
|
|
// Sort slices for deterministic comparison
|
|
sort.Strings(got.SensitiveAttributePaths)
|
|
sort.Strings(tt.want.SensitiveAttributePaths)
|
|
|
|
if !reflect.DeepEqual(got.SensitiveAttributePaths, tt.want.SensitiveAttributePaths) {
|
|
t.Errorf("SensitiveInputConfig mismatch:\nGot: %v\nWant: %v", got.SensitiveAttributePaths, tt.want.SensitiveAttributePaths)
|
|
}
|
|
})
|
|
}
|
|
}
|