terraform/internal/command/views/json/query.go
Sebastian Rivera 305dba5dcc feat: denote sensitive attribute paths in query start msg
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.
2026-02-27 11:01:23 +01:00

120 lines
3.9 KiB
Go

// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package json
import (
"encoding/json"
"sort"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/genconfig"
"github.com/hashicorp/terraform/internal/lang/format"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
)
type QueryStart struct {
Address string `json:"address"`
ResourceType string `json:"resource_type"`
InputConfig map[string]json.RawMessage `json:"input_config,omitempty"`
SensitiveAttributePaths []string `json:"sensitive_attribute_paths,omitempty"`
}
type QueryResult struct {
Address string `json:"address"`
DisplayName string `json:"display_name"`
Identity map[string]json.RawMessage `json:"identity"`
IdentityVersion int64 `json:"identity_version"`
ResourceType string `json:"resource_type"`
ResourceObject map[string]json.RawMessage `json:"resource_object,omitempty"`
Config string `json:"config,omitempty"`
ImportConfig string `json:"import_config,omitempty"`
}
type QueryComplete struct {
Address string `json:"address"`
ResourceType string `json:"resource_type"`
Total int `json:"total"`
}
func NewQueryStart(addr addrs.AbsResourceInstance, inputConfig cty.Value, configSchema *configschema.Block) QueryStart {
unmarkVal, valPaths := inputConfig.UnmarkDeepWithPaths()
// We only want a unique set of attribute paths. A path
// can either be defined as sensitive by the provider or
// marked by configuration.
uniquePaths := make(map[string]struct{})
// Search for any marked sensitive paths, irrespective
// of the schema
for _, pvm := range valPaths {
if _, ok := pvm.Marks[marks.Sensitive]; ok {
uniquePaths[format.CtyPath(pvm.Path)] = struct{}{}
}
}
// Now let's loop through the resource schema defined by
// the provider
schemaPaths := configSchema.SensitivePaths(inputConfig, nil)
for _, path := range schemaPaths {
uniquePaths[format.CtyPath(path)] = struct{}{}
}
sensitiveAttributePaths := make([]string, 0, len(uniquePaths))
for p := range uniquePaths {
sensitiveAttributePaths = append(sensitiveAttributePaths, p)
}
sort.Strings(sensitiveAttributePaths)
return QueryStart{
Address: addr.String(),
ResourceType: addr.Resource.Resource.Type,
InputConfig: marshalValues(unmarkVal),
SensitiveAttributePaths: sensitiveAttributePaths,
}
}
func NewQueryResult(listAddr addrs.AbsResourceInstance, value cty.Value, identityVersion int64, generated *genconfig.ResourceImport) QueryResult {
result := QueryResult{
Address: listAddr.String(),
DisplayName: value.GetAttr("display_name").AsString(),
Identity: marshalValues(value.GetAttr("identity")),
IdentityVersion: identityVersion,
ResourceType: listAddr.Resource.Resource.Type,
ResourceObject: marshalValues(value.GetAttr("state")),
}
if generated != nil {
result.Config = generated.Resource.String()
result.ImportConfig = string(generated.ImportBody)
}
return result
}
func NewQueryComplete(addr addrs.AbsResourceInstance, total int) QueryComplete {
return QueryComplete{
Address: addr.String(),
ResourceType: addr.Resource.Resource.Type,
Total: total,
}
}
func marshalValues(value cty.Value) map[string]json.RawMessage {
if value == cty.NilVal || value.IsNull() {
return nil
}
ret := make(map[string]json.RawMessage)
it := value.ElementIterator()
for it.Next() {
k, v := it.Element()
vJSON, _ := ctyjson.Marshal(v, v.Type())
ret[k.AsString()] = json.RawMessage(vJSON)
}
return ret
}