| // Copyright (c) HashiCorp, Inc. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| package sanitize | |
| import ( | |
| "fmt" | |
| tfjson "github.com/hashicorp/terraform-json" | |
| ) | |
| type SanitizeStateModuleChangeMode string | |
| const ( | |
| SanitizeStateModuleChangeModeBefore SanitizeStateModuleChangeMode = "before_sensitive" | |
| SanitizeStateModuleChangeModeAfter SanitizeStateModuleChangeMode = "after_sensitive" | |
| ) | |
| // SanitizeStateModule traverses a StateModule, consulting the | |
| // supplied ResourceChange set for resources to determine whether or | |
| // not particular values should be obfuscated. | |
| // | |
| // Use mode to supply the SanitizeStateModuleChangeMode that | |
| // represents what sensitive field should be consulted to determine | |
| // whether or not the value should be obfuscated: | |
| // | |
| // * SanitizeStateModuleChangeModeBefore for before_sensitive | |
| // * SanitizeStateModuleChangeModeAfter for after_sensitive | |
| // | |
| // Sensitive values are replaced with the supplied replaceWith value. | |
| // A new state module tree is issued. | |
| func SanitizeStateModule( | |
| old *tfjson.StateModule, | |
| resourceChanges []*tfjson.ResourceChange, | |
| mode SanitizeStateModuleChangeMode, | |
| replaceWith interface{}, | |
| ) (*tfjson.StateModule, error) { | |
| result := &tfjson.StateModule{ | |
| Resources: make([]*tfjson.StateResource, len(old.Resources)), | |
| Address: old.Address, | |
| ChildModules: make([]*tfjson.StateModule, len(old.ChildModules)), | |
| } | |
| for i := range old.Resources { | |
| var err error | |
| result.Resources[i], err = sanitizeStateResource( | |
| old.Resources[i], | |
| findResourceChange(resourceChanges, old.Resources[i].Address), | |
| mode, | |
| replaceWith, | |
| ) | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| for i := range old.ChildModules { | |
| var err error | |
| result.ChildModules[i], err = SanitizeStateModule( | |
| old.ChildModules[i], | |
| resourceChanges, | |
| mode, | |
| replaceWith, | |
| ) | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| return result, nil | |
| } | |
| func sanitizeStateResource( | |
| old *tfjson.StateResource, | |
| rc *tfjson.ResourceChange, | |
| mode SanitizeStateModuleChangeMode, | |
| replaceWith interface{}, | |
| ) (*tfjson.StateResource, error) { | |
| result, err := copyStateResource(old) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if rc == nil { | |
| return result, nil | |
| } | |
| var sensitive interface{} | |
| switch mode { | |
| case SanitizeStateModuleChangeModeBefore: | |
| sensitive = rc.Change.BeforeSensitive | |
| case SanitizeStateModuleChangeModeAfter: | |
| sensitive = rc.Change.AfterSensitive | |
| default: | |
| panic(fmt.Sprintf("invalid change mode %q", mode)) | |
| } | |
| // We can re-use sanitizeChangeValue here to do the sanitization. | |
| result.AttributeValues = sanitizeChangeValue(result.AttributeValues, sensitive, replaceWith).(map[string]interface{}) | |
| return result, nil | |
| } | |
| func findResourceChange(resourceChanges []*tfjson.ResourceChange, addr string) *tfjson.ResourceChange { | |
| // Linear search here, unfortunately :P | |
| for _, rc := range resourceChanges { | |
| if rc.Address == addr { | |
| return rc | |
| } | |
| } | |
| return nil | |
| } | |
| // SanitizeStateOutputs scans the supplied map of StateOutputs and | |
| // replaces any values of outputs marked as Sensitive with the value | |
| // supplied in replaceWith. | |
| // | |
| // A new copy of StateOutputs is returned. | |
| func SanitizeStateOutputs(old map[string]*tfjson.StateOutput, replaceWith interface{}) (map[string]*tfjson.StateOutput, error) { | |
| result, err := copyStateOutputs(old) | |
| if err != nil { | |
| return nil, err | |
| } | |
| for k := range result { | |
| if result[k].Sensitive { | |
| result[k].Value = replaceWith | |
| } | |
| } | |
| return result, nil | |
| } |