configs: Fix provider requirements panics

When parsing provider requirements we should check the type of the
source and version attributes rather than assuming that they are
strings. Otherwise an invalid attribute value will cause a panic.
This commit is contained in:
Alisdair McDiarmid 2020-06-24 10:09:12 -04:00
parent be510e53bc
commit 9a9f4e2696
2 changed files with 72 additions and 25 deletions

View File

@ -4,6 +4,7 @@ import (
version "github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/addrs"
"github.com/zclconf/go-cty/cty"
)
// RequiredProvider represents a declaration of a dependency on a particular
@ -55,41 +56,61 @@ func decodeRequiredProvidersBlock(block *hcl.Block) (*RequiredProviders, hcl.Dia
vc := VersionConstraint{
DeclRange: attr.Range,
}
constraintStr := expr.GetAttr("version").AsString()
constraints, err := version.NewConstraint(constraintStr)
if err != nil {
// NewConstraint doesn't return user-friendly errors, so we'll just
// ignore the provided error and produce our own generic one.
constraint := expr.GetAttr("version")
if !constraint.Type().Equals(cty.String) || constraint.IsNull() {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid version constraint",
Detail: "This string does not use correct version constraint syntax.",
Detail: "Version must be specified as a string.",
Subject: attr.Expr.Range().Ptr(),
})
} else {
vc.Required = constraints
rp.Requirement = vc
constraintStr := constraint.AsString()
constraints, err := version.NewConstraint(constraintStr)
if err != nil {
// NewConstraint doesn't return user-friendly errors, so we'll just
// ignore the provided error and produce our own generic one.
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid version constraint",
Detail: "This string does not use correct version constraint syntax.",
Subject: attr.Expr.Range().Ptr(),
})
} else {
vc.Required = constraints
rp.Requirement = vc
}
}
}
if expr.Type().HasAttribute("source") {
rp.Source = expr.GetAttr("source").AsString()
fqn, sourceDiags := addrs.ParseProviderSourceString(rp.Source)
if sourceDiags.HasErrors() {
hclDiags := sourceDiags.ToHCL()
// The diagnostics from ParseProviderSourceString don't contain
// source location information because it has no context to compute
// them from, and so we'll add those in quickly here before we
// return.
for _, diag := range hclDiags {
if diag.Subject == nil {
diag.Subject = attr.Expr.Range().Ptr()
}
}
diags = append(diags, hclDiags...)
source := expr.GetAttr("source")
if !source.Type().Equals(cty.String) || source.IsNull() {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid source",
Detail: "Source must be specified as a string.",
Subject: attr.Expr.Range().Ptr(),
})
} else {
rp.Type = fqn
rp.Source = source.AsString()
fqn, sourceDiags := addrs.ParseProviderSourceString(rp.Source)
if sourceDiags.HasErrors() {
hclDiags := sourceDiags.ToHCL()
// The diagnostics from ParseProviderSourceString don't contain
// source location information because it has no context to compute
// them from, and so we'll add those in quickly here before we
// return.
for _, diag := range hclDiags {
if diag.Subject == nil {
diag.Subject = attr.Expr.Range().Ptr()
}
}
diags = append(diags, hclDiags...)
} else {
rp.Type = fqn
}
}
}

View File

@ -306,6 +306,32 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) {
},
Error: "Invalid required_providers syntax",
},
"invalid source attribute type": {
Block: &hcl.Block{
Type: "required_providers",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcl.Attributes{
"my-test": {
Name: "my-test",
Expr: hcltest.MockExprLiteral(cty.ObjectVal(map[string]cty.Value{
"source": cty.DynamicVal,
})),
},
},
}),
DefRange: blockRange,
},
Want: &RequiredProviders{
RequiredProviders: map[string]*RequiredProvider{
"my-test": {
Name: "my-test",
DeclRange: mockRange,
},
},
DeclRange: blockRange,
},
Error: "Invalid source",
},
}
for name, test := range tests {