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 }