github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/configs/configschema/marks.go (about)

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