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 }