hcl2: fix crash on malformed overrides (#11881)

When an override block is either not an object, or if one of its
contents are not an object, Packer would crash trying to forcefully
cast the cty.Value to a cty.Collection.

To avoid this behaviour, we add extra checks that return hcl.Diagnostics
when they fail.
pull/11888/head
Lucas Bajolet 4 years ago committed by GitHub
parent 6794c6053a
commit 6ab16561b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,4 @@
provisioner "shell-local" {
inline = ["echo 'hi'"]
override = "hello"
}

@ -0,0 +1,6 @@
provisioner "shell-local" {
inline = ["echo 'hi'"]
override = {
test = "hello"
}
}

@ -0,0 +1,9 @@
provisioner "shell-local" {
inline = ["echo 'hi'"]
override = {
test = {
"hello" = "new value"
}
}
}

@ -104,9 +104,28 @@ func (p *Parser) decodeProvisioner(block *hcl.Block, ectx *hcl.EvalContext) (*Pr
}
if !b.Override.IsNull() {
if !b.Override.Type().IsObjectType() {
return nil, append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "provisioner's override block must be an HCL object",
Subject: block.DefRange.Ptr(),
})
}
override := make(map[string]interface{})
for buildName, overrides := range b.Override.AsValueMap() {
buildOverrides := make(map[string]interface{})
if !overrides.Type().IsObjectType() {
return nil, append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf(
"provisioner's override.'%s' block must be an HCL object",
buildName),
Subject: block.DefRange.Ptr(),
})
}
for option, value := range overrides.AsValueMap() {
buildOverrides[option] = hcl2shim.ConfigValueFromHCL2(value)
}

@ -0,0 +1,82 @@
package hcl2template
import (
"testing"
"github.com/hashicorp/hcl/v2"
)
func TestPackerConfig_ParseProvisionerBlock(t *testing.T) {
tests := []struct {
name string
inputFile string
expectError bool
expectedErrorMessage string
}{
{
"success - provisioner is valid",
"fixtures/well_formed_provisioner.pkr.hcl",
false,
"",
},
{
"failure - provisioner override is malformed",
"fixtures/malformed_override.pkr.hcl",
true,
"provisioner's override block must be an HCL object",
},
{
"failure - provisioner override.test is malformed",
"fixtures/malformed_override_innards.pkr.hcl",
true,
"provisioner's override.'test' block must be an HCL object",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := PackerConfig{parser: getBasicParser()}
f, diags := cfg.parser.ParseHCLFile(test.inputFile)
if diags.HasErrors() {
t.Errorf("failed to parse input file %s", test.inputFile)
for _, d := range diags {
t.Errorf("%s", d)
}
return
}
provBlock := f.OutermostBlockAtPos(hcl.Pos{
Line: 1,
Column: 1,
Byte: 0,
})
_, diags = cfg.parser.decodeProvisioner(provBlock, nil)
if !diags.HasErrors() {
if !test.expectError {
return
}
t.Fatalf("unexpected success")
}
if !test.expectError {
for _, d := range diags {
t.Errorf("%s", d)
}
}
gotExpectedErr := false
for _, d := range diags {
if d.Summary == test.expectedErrorMessage {
gotExpectedErr = true
}
t.Logf("got error (expected): '%s'", d.Summary)
}
if !gotExpectedErr {
t.Errorf("never got expected error: '%s'", test.expectedErrorMessage)
}
})
}
}
Loading…
Cancel
Save