Skip to content

Commit

Permalink
hcl2: fix crash on malformed overrides
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lbajolet-hashicorp committed Jul 15, 2022
1 parent 9f28ced commit a8e06e1
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 0 deletions.
4 changes: 4 additions & 0 deletions hcl2template/fixtures/malformed_override.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
provisioner "shell-local" {
inline = ["echo 'hi'"]
override = "hello"
}
6 changes: 6 additions & 0 deletions hcl2template/fixtures/malformed_override_innards.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
provisioner "shell-local" {
inline = ["echo 'hi'"]
override = {
test = "hello"
}
}
9 changes: 9 additions & 0 deletions hcl2template/fixtures/well_formed_provisioner.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
provisioner "shell-local" {
inline = ["echo 'hi'"]
override = {
test = {
"hello" = "new value"
}
}
}

19 changes: 19 additions & 0 deletions hcl2template/types.build.provisioners.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
62 changes: 62 additions & 0 deletions hcl2template/types.build.provisioners_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package hcl2template

import (
"testing"

"github.com/hashicorp/hcl/v2"
)

func TestPackerConfig_ParseProvisionerBlock(t *testing.T) {
tests := []struct {
name string
inputFile string
expectError bool
}{
{
"success - provisioner is valid",
"fixtures/well_formed_provisioner.pkr.hcl",
false,
},
{
"failure - provisioner override is malformed",
"fixtures/malformed_override.pkr.hcl",
true,
},
{
"failure - provisioner override.test is malformed",
"fixtures/malformed_override_innards.pkr.hcl",
true,
},
}

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() == test.expectError {
return
}

if !diags.HasErrors() {
t.Fatalf("unexpected successful parsing of provisioner")
}

for _, d := range diags {
t.Errorf("unexpected error: %s", d)
}
})
}
}

0 comments on commit a8e06e1

Please sign in to comment.