github.com/opentofu/opentofu@v1.7.1/internal/configs/configschema/marks.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package configschema
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/opentofu/opentofu/internal/lang/marks"
    12  	"github.com/zclconf/go-cty/cty"
    13  )
    14  
    15  // copyAndExtendPath returns a copy of a cty.Path with some additional
    16  // `cty.PathStep`s appended to its end, to simplify creating new child paths.
    17  func copyAndExtendPath(path cty.Path, nextSteps ...cty.PathStep) cty.Path {
    18  	newPath := make(cty.Path, len(path), len(path)+len(nextSteps))
    19  	copy(newPath, path)
    20  	newPath = append(newPath, nextSteps...)
    21  	return newPath
    22  }
    23  
    24  // ValueMarks returns a set of path value marks for a given value and path,
    25  // based on the sensitive flag for each attribute within the schema. Nested
    26  // blocks are descended (if present in the given value).
    27  func (b *Block) ValueMarks(val cty.Value, path cty.Path) []cty.PathValueMarks {
    28  	var pvm []cty.PathValueMarks
    29  
    30  	// We can mark attributes as sensitive even if the value is null
    31  	for name, attrS := range b.Attributes {
    32  		if attrS.Sensitive {
    33  			// Create a copy of the path, with this step added, to add to our PathValueMarks slice
    34  			attrPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
    35  			pvm = append(pvm, cty.PathValueMarks{
    36  				Path:  attrPath,
    37  				Marks: cty.NewValueMarks(marks.Sensitive),
    38  			})
    39  		}
    40  	}
    41  
    42  	// If the value is null, no other marks are possible
    43  	if val.IsNull() {
    44  		return pvm
    45  	}
    46  
    47  	// Extract marks for nested attribute type values
    48  	for name, attrS := range b.Attributes {
    49  		// If the attribute has no nested type, or the nested type doesn't
    50  		// contain any sensitive attributes, skip inspecting it
    51  		if attrS.NestedType == nil || !attrS.NestedType.ContainsSensitive() {
    52  			continue
    53  		}
    54  
    55  		// Create a copy of the path, with this step added, to add to our PathValueMarks slice
    56  		attrPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
    57  
    58  		pvm = append(pvm, attrS.NestedType.ValueMarks(val.GetAttr(name), attrPath)...)
    59  	}
    60  
    61  	// Extract marks for nested blocks
    62  	for name, blockS := range b.BlockTypes {
    63  		// If our block doesn't contain any sensitive attributes, skip inspecting it
    64  		if !blockS.Block.ContainsSensitive() {
    65  			continue
    66  		}
    67  
    68  		blockV := val.GetAttr(name)
    69  		if blockV.IsNull() || !blockV.IsKnown() {
    70  			continue
    71  		}
    72  
    73  		// Create a copy of the path, with this step added, to add to our PathValueMarks slice
    74  		blockPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
    75  
    76  		switch blockS.Nesting {
    77  		case NestingSingle, NestingGroup:
    78  			pvm = append(pvm, blockS.Block.ValueMarks(blockV, blockPath)...)
    79  		case NestingList, NestingMap, NestingSet:
    80  			for it := blockV.ElementIterator(); it.Next(); {
    81  				idx, blockEV := it.Element()
    82  				// Create a copy of the path, with this block instance's index
    83  				// step added, to add to our PathValueMarks slice
    84  				blockInstancePath := copyAndExtendPath(blockPath, cty.IndexStep{Key: idx})
    85  				morePaths := blockS.Block.ValueMarks(blockEV, blockInstancePath)
    86  				pvm = append(pvm, morePaths...)
    87  			}
    88  		default:
    89  			panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
    90  		}
    91  	}
    92  	return pvm
    93  }
    94  
    95  // ValueMarks returns a set of path value marks for a given value and path,
    96  // based on the sensitive flag for each attribute within the nested attribute.
    97  // Attributes with nested types are descended (if present in the given value).
    98  func (o *Object) ValueMarks(val cty.Value, path cty.Path) []cty.PathValueMarks {
    99  	var pvm []cty.PathValueMarks
   100  
   101  	if val.IsNull() || !val.IsKnown() {
   102  		return pvm
   103  	}
   104  
   105  	for name, attrS := range o.Attributes {
   106  		// Skip attributes which can never produce sensitive path value marks
   107  		if !attrS.Sensitive && (attrS.NestedType == nil || !attrS.NestedType.ContainsSensitive()) {
   108  			continue
   109  		}
   110  
   111  		switch o.Nesting {
   112  		case NestingSingle, NestingGroup:
   113  			// Create a path to this attribute
   114  			attrPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
   115  
   116  			if attrS.Sensitive {
   117  				// If the entire attribute is sensitive, mark it so
   118  				pvm = append(pvm, cty.PathValueMarks{
   119  					Path:  attrPath,
   120  					Marks: cty.NewValueMarks(marks.Sensitive),
   121  				})
   122  			} else {
   123  				// The attribute has a nested type which contains sensitive
   124  				// attributes, so recurse
   125  				pvm = append(pvm, attrS.NestedType.ValueMarks(val.GetAttr(name), attrPath)...)
   126  			}
   127  		case NestingList, NestingMap, NestingSet:
   128  			// For nested attribute types which have a non-single nesting mode,
   129  			// we add path value marks for each element of the collection
   130  			for it := val.ElementIterator(); it.Next(); {
   131  				idx, attrEV := it.Element()
   132  				attrV := attrEV.GetAttr(name)
   133  
   134  				// Create a path to this element of the attribute's collection. Note
   135  				// that the path is extended in opposite order to the iteration order
   136  				// of the loops: index into the collection, then the contained
   137  				// attribute name. This is because we have one type
   138  				// representing multiple collection elements.
   139  				attrPath := copyAndExtendPath(path, cty.IndexStep{Key: idx}, cty.GetAttrStep{Name: name})
   140  
   141  				if attrS.Sensitive {
   142  					// If the entire attribute is sensitive, mark it so
   143  					pvm = append(pvm, cty.PathValueMarks{
   144  						Path:  attrPath,
   145  						Marks: cty.NewValueMarks(marks.Sensitive),
   146  					})
   147  				} else {
   148  					// The attribute has a nested type which contains sensitive
   149  					// attributes, so recurse
   150  					pvm = append(pvm, attrS.NestedType.ValueMarks(attrV, attrPath)...)
   151  				}
   152  			}
   153  		default:
   154  			panic(fmt.Sprintf("unsupported nesting mode %s", attrS.NestedType.Nesting))
   155  		}
   156  	}
   157  	return pvm
   158  }