Skip to content

Commit

Permalink
fix(cloudformation): fix panic when use pseudo-parameters NoValue or …
Browse files Browse the repository at this point in the history
…NotificationARNs (#1395)

* fix(cloudformation): fix panic when use pseudo-parameters NoValue or NotificationARNs

* fix typo
  • Loading branch information
nikpivkin authored Jul 20, 2023
1 parent 193ef12 commit 9046b67
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 11 deletions.
2 changes: 1 addition & 1 deletion pkg/scanners/cloudformation/parser/fn_ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func ResolveReference(property *Property) (resolved *Property, success bool) {
refValue := refProp.AsString()

if pseudo, ok := pseudoParameters[refValue]; ok {
return property.deriveResolved(cftypes.String, pseudo.(string)), true
return property.deriveResolved(pseudo.t, pseudo.val), true
}

if property.ctx == nil {
Expand Down
41 changes: 41 additions & 0 deletions pkg/scanners/cloudformation/parser/fn_select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,44 @@ Resources:

require.Equal(t, "redis", engineProp.AsString())
}

func Test_SelectPseudoListParam(t *testing.T) {
src := `---
Resources:
myASGrpOne:
Type: AWS::AutoScaling::AutoScalingGroup
Version: "2009-05-15"
Properties:
AvailabilityZones:
- "us-east-1a"
LaunchConfigurationName:
Ref: MyLaunchConfiguration
MinSize: "0"
MaxSize: "0"
NotificationConfigurations:
- TopicARN:
Fn::Select:
- "1"
- Ref: AWS::NotificationARNs
NotificationTypes:
- autoscaling:EC2_INSTANCE_LAUNCH
- autoscaling:EC2_INSTANCE_LAUNCH_ERROR
`

ctx := createTestFileContext(t, src)
require.NotNil(t, ctx)

resource := ctx.GetResourceByLogicalID("myASGrpOne")
require.NotNil(t, resource)

notification := resource.GetProperty("NotificationConfigurations")
require.True(t, notification.IsNotNil())
require.True(t, notification.IsList())
first := notification.AsList()[0]
require.True(t, first.IsMap())
topic, ok := first.AsMap()["TopicARN"]
require.True(t, ok)
require.Equal(t, "notification::arn::2", topic.AsString())

}
4 changes: 2 additions & 2 deletions pkg/scanners/cloudformation/parser/fn_sub.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func resolveMapSub(refValue *Property, original *Property) (*Property, bool) {
func resolveStringSub(refValue *Property, original *Property) *Property {
workingString := refValue.AsString()

for k, v := range pseudoParameters {
workingString = strings.ReplaceAll(workingString, fmt.Sprintf("${%s}", k), fmt.Sprintf("%v", v))
for k, param := range pseudoParameters {
workingString = strings.ReplaceAll(workingString, fmt.Sprintf("${%s}", k), fmt.Sprintf("%v", param.getRawValue()))
}

return original.deriveResolved(cftypes.String, workingString)
Expand Down
51 changes: 43 additions & 8 deletions pkg/scanners/cloudformation/parser/pseudo_parameters.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,46 @@
package parser

var pseudoParameters = map[string]interface{}{
"AWS::AccountId": "123456789012",
"AWS::NotificationARNs": []string{"notification::arn::1", "notification::arn::2"},
"AWS::NoValue": nil,
"AWS::Partition": "aws",
"AWS::Region": "eu-west-1",
"AWS::StackId": "arn:aws:cloudformation:eu-west-1:stack/ID",
"AWS::StackName": "cfsec-test-stack",
import "github.com/aquasecurity/defsec/pkg/scanners/cloudformation/cftypes"

type pseudoParameter struct {
t cftypes.CfType
val interface{}
raw interface{}
}

var pseudoParameters = map[string]pseudoParameter{
"AWS::AccountId": {t: cftypes.String, val: "123456789012"},
"AWS::NotificationARNs": {
t: cftypes.List,
val: []*Property{
{
Inner: PropertyInner{
Type: cftypes.String,
Value: "notification::arn::1",
},
},
{
Inner: PropertyInner{
Type: cftypes.String,
Value: "notification::arn::2",
},
},
},
raw: []string{"notification::arn::1", "notification::arn::2"},
},
"AWS::NoValue": {t: cftypes.String, val: ""},
"AWS::Partition": {t: cftypes.String, val: "aws"},
"AWS::Region": {t: cftypes.String, val: "eu-west-1"},
"AWS::StackId": {t: cftypes.String, val: "arn:aws:cloudformation:eu-west-1:stack/ID"},
"AWS::StackName": {t: cftypes.String, val: "cfsec-test-stack"},
"AWS::URLSuffix": {t: cftypes.String, val: "amazonaws.com"},
}

func (p pseudoParameter) getRawValue() interface{} {
switch p.t {
case cftypes.List:
return p.raw
default:
return p.val
}
}
36 changes: 36 additions & 0 deletions pkg/scanners/cloudformation/parser/pseudo_parameters_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package parser

import (
"testing"

"github.com/stretchr/testify/assert"
)

func Test_Raw(t *testing.T) {
tests := []struct {
name string
key string
expected interface{}
}{
{
name: "parameter with a string type value",
key: "AWS::AccountId",
expected: "123456789012",
},
{
name: "a parameter with a list type value",
key: "AWS::NotificationARNs",
expected: []string{"notification::arn::1", "notification::arn::2"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if val, ok := pseudoParameters[tt.key]; ok {
assert.Equal(t, tt.expected, val.getRawValue())
} else {
t.Fatal("unexpected parameter key")
}
})
}
}

0 comments on commit 9046b67

Please sign in to comment.