blob: d03b8fa986ae9933c2044d7ed7c9e25bd10d97a2 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package sanitize
import (
"errors"
tfjson "github.com/hashicorp/terraform-json"
)
const DefaultSensitiveValue = "REDACTED_SENSITIVE"
var NilPlanError = errors.New("nil plan supplied")
// SanitizePlan sanitizes the entirety of a Plan, replacing sensitive
// values with the default value in DefaultSensitiveValue.
//
// See SanitizePlanWithValue for full detail on the where replacement
// takes place.
func SanitizePlan(old *tfjson.Plan) (*tfjson.Plan, error) {
return SanitizePlanWithValue(old, DefaultSensitiveValue)
}
// SanitizePlanWithValue sanitizes the entirety of a Plan to the best
// of its ability, depending on the provided metadata on sensitive
// values. These are found in:
//
// * ResourceChanges: Sanitized based on BeforeSensitive and
// AfterSensitive fields.
//
// * Variables: Based on variable config data found in the root
// module of the Config.
//
// * PlannedValues: Sanitized based on the values found in
// AfterSensitive in ResourceChanges. Outputs are sanitized
// according to the appropriate sensitivity flags provided for the
// output.
//
// * PriorState: Sanitized based on the values found in
// BeforeSensitive in ResourceChanges. Outputs are sanitized according
// to the appropriate sensitivity flags provided for the output.
//
// * OutputChanges: Sanitized based on the values found in
// BeforeSensitive and AfterSensitive. This generally means that
// any sensitive output will have OutputChange fully obfuscated as
// the BeforeSensitive and AfterSensitive in outputs are opaquely the
// same.
//
// Sensitive values are replaced with the value supplied with
// replaceWith. A copy of the Plan is returned.
func SanitizePlanWithValue(old *tfjson.Plan, replaceWith interface{}) (*tfjson.Plan, error) {
if old == nil {
return nil, NilPlanError
}
result, err := copyPlan(old)
if err != nil {
return nil, err
}
// Sanitize ResourceChanges
for i := range result.ResourceChanges {
result.ResourceChanges[i].Change, err = SanitizeChange(result.ResourceChanges[i].Change, replaceWith)
if err != nil {
return nil, err
}
}
// Sanitize Variables
result.Variables, err = SanitizePlanVariables(result.Variables, result.Config.RootModule.Variables, replaceWith)
if err != nil {
return nil, err
}
// Sanitize PlannedValues
result.PlannedValues.RootModule, err = SanitizeStateModule(
result.PlannedValues.RootModule,
result.ResourceChanges,
SanitizeStateModuleChangeModeAfter,
replaceWith)
if err != nil {
return nil, err
}
result.PlannedValues.Outputs, err = SanitizeStateOutputs(result.PlannedValues.Outputs, replaceWith)
if err != nil {
return nil, err
}
// Sanitize PriorState
if result.PriorState != nil {
result.PriorState.Values.RootModule, err = SanitizeStateModule(
result.PriorState.Values.RootModule,
result.ResourceChanges,
SanitizeStateModuleChangeModeBefore,
replaceWith)
if err != nil {
return nil, err
}
result.PriorState.Values.Outputs, err = SanitizeStateOutputs(result.PriorState.Values.Outputs, replaceWith)
if err != nil {
return nil, err
}
}
// Sanitize OutputChanges
for k := range result.OutputChanges {
result.OutputChanges[k], err = SanitizeChange(result.OutputChanges[k], replaceWith)
if err != nil {
return nil, err
}
}
return result, nil
}