Skip to content

Commit 23973ba

Browse files
bendbennettbflad
andauthored
Fix Framework allows top-level schema attributes that conflict with Terraform meta-arguments (#548)
* Adding Validate() function to provider, resource and data source schema and to provider metaschema to prevent the use of reserved names for top-level attributes and blocks and invalid names for attributes and blocks at any level of nesting (#136) * Apply suggestions from code review Co-authored-by: Brian Flad <[email protected]> Co-authored-by: Brian Flad <[email protected]>
1 parent 3413b8f commit 23973ba

File tree

12 files changed

+1941
-9
lines changed

12 files changed

+1941
-9
lines changed

.changelog/548.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
```release-note:bug
2+
provider: Add `Validate` function to `Schema` to prevent usage of reserved and invalid names for attributes and blocks
3+
```
4+
5+
```release-note:bug
6+
provider: Add `Validate` function to `MetaSchema` to prevent usage of reserved and invalid names for attributes and blocks
7+
```
8+
9+
```release-note:bug
10+
resource: Add `Validate` function to `Schema` to prevent usage of reserved and invalid names for attributes and blocks
11+
```
12+
13+
```release-note:bug
14+
datasource: Add `Validate` function to `Schema` to prevent usage of reserved and invalid names for attributes and blocks
15+
```

datasource/schema/schema.go

+128-1
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package schema
22

33
import (
44
"context"
5+
"fmt"
6+
"regexp"
7+
8+
"github.com/hashicorp/terraform-plugin-go/tftypes"
59

610
"github.com/hashicorp/terraform-plugin-framework/attr"
711
"github.com/hashicorp/terraform-plugin-framework/diag"
812
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
913
"github.com/hashicorp/terraform-plugin-framework/path"
10-
"github.com/hashicorp/terraform-plugin-go/tftypes"
1114
)
1215

1316
// Schema must satify the fwschema.Schema interface.
@@ -122,6 +125,130 @@ func (s Schema) TypeAtTerraformPath(ctx context.Context, p *tftypes.AttributePat
122125
return fwschema.SchemaTypeAtTerraformPath(ctx, s, p)
123126
}
124127

128+
// Validate verifies that the schema is not using a reserved field name for a top-level attribute.
129+
func (s Schema) Validate() diag.Diagnostics {
130+
var diags diag.Diagnostics
131+
132+
// Raise error diagnostics when data source configuration uses reserved
133+
// field names for root-level attributes.
134+
reservedFieldNames := map[string]struct{}{
135+
"connection": {},
136+
"count": {},
137+
"depends_on": {},
138+
"lifecycle": {},
139+
"provider": {},
140+
"provisioner": {},
141+
}
142+
143+
attributes := s.GetAttributes()
144+
145+
for k, v := range attributes {
146+
if _, ok := reservedFieldNames[k]; ok {
147+
diags.AddAttributeError(
148+
path.Root(k),
149+
"Schema Using Reserved Field Name",
150+
fmt.Sprintf("%q is a reserved field name", k),
151+
)
152+
}
153+
154+
d := validateAttributeFieldName(path.Root(k), k, v)
155+
156+
diags.Append(d...)
157+
}
158+
159+
blocks := s.GetBlocks()
160+
161+
for k, v := range blocks {
162+
if _, ok := reservedFieldNames[k]; ok {
163+
diags.AddAttributeError(
164+
path.Root(k),
165+
"Schema Using Reserved Field Name",
166+
fmt.Sprintf("%q is a reserved field name", k),
167+
)
168+
}
169+
170+
d := validateBlockFieldName(path.Root(k), k, v)
171+
172+
diags.Append(d...)
173+
}
174+
175+
return diags
176+
}
177+
178+
// validFieldNameRegex is used to verify that name used for attributes and blocks
179+
// comply with the defined regular expression.
180+
var validFieldNameRegex = regexp.MustCompile("^[a-z0-9_]+$")
181+
182+
// validateAttributeFieldName verifies that the name used for an attribute complies with the regular
183+
// expression defined in validFieldNameRegex.
184+
func validateAttributeFieldName(path path.Path, name string, attr fwschema.Attribute) diag.Diagnostics {
185+
var diags diag.Diagnostics
186+
187+
if !validFieldNameRegex.MatchString(name) {
188+
diags.AddAttributeError(
189+
path,
190+
"Invalid Schema Field Name",
191+
fmt.Sprintf("Field name %q is invalid, the only allowed characters are a-z, 0-9 and _. This is always a problem with the provider and should be reported to the provider developer.", name),
192+
)
193+
}
194+
195+
if na, ok := attr.(fwschema.NestedAttribute); ok {
196+
nestedObject := na.GetNestedObject()
197+
198+
if nestedObject == nil {
199+
return diags
200+
}
201+
202+
attributes := nestedObject.GetAttributes()
203+
204+
for k, v := range attributes {
205+
d := validateAttributeFieldName(path.AtName(k), k, v)
206+
207+
diags.Append(d...)
208+
}
209+
}
210+
211+
return diags
212+
}
213+
214+
// validateBlockFieldName verifies that the name used for a block complies with the regular
215+
// expression defined in validFieldNameRegex.
216+
func validateBlockFieldName(path path.Path, name string, b fwschema.Block) diag.Diagnostics {
217+
var diags diag.Diagnostics
218+
219+
if !validFieldNameRegex.MatchString(name) {
220+
diags.AddAttributeError(
221+
path,
222+
"Invalid Schema Field Name",
223+
fmt.Sprintf("Field name %q is invalid, the only allowed characters are a-z, 0-9 and _. This is always a problem with the provider and should be reported to the provider developer.", name),
224+
)
225+
}
226+
227+
nestedObject := b.GetNestedObject()
228+
229+
if nestedObject == nil {
230+
return diags
231+
}
232+
233+
blocks := nestedObject.GetBlocks()
234+
235+
for k, v := range blocks {
236+
d := validateBlockFieldName(path.AtName(k), k, v)
237+
238+
diags.Append(d...)
239+
}
240+
241+
attributes := nestedObject.GetAttributes()
242+
243+
for k, v := range attributes {
244+
d := validateAttributeFieldName(path.AtName(k), k, v)
245+
246+
diags.Append(d...)
247+
}
248+
249+
return diags
250+
}
251+
125252
// schemaAttributes is a datasource to fwschema type conversion function.
126253
func schemaAttributes(attributes map[string]Attribute) map[string]fwschema.Attribute {
127254
result := make(map[string]fwschema.Attribute, len(attributes))

0 commit comments

Comments
 (0)