From 4327cc65f2793656598474ce050cadf9a6c825c5 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin <100182843+nikpivkin@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:46:49 +0300 Subject: [PATCH] fix(cloudformation): evaluate the value for a property when comparing (#1393) fix(misconf): evaluate the value for a property when comparing --- .../cloudformation/parser/property_helpers.go | 53 ++--- .../parser/property_helpers_test.go | 195 ++++++++++++++++++ 2 files changed, 213 insertions(+), 35 deletions(-) create mode 100644 pkg/scanners/cloudformation/parser/property_helpers_test.go diff --git a/pkg/scanners/cloudformation/parser/property_helpers.go b/pkg/scanners/cloudformation/parser/property_helpers.go index 33d6ee528..19ebdcc7e 100644 --- a/pkg/scanners/cloudformation/parser/property_helpers.go +++ b/pkg/scanners/cloudformation/parser/property_helpers.go @@ -17,16 +17,20 @@ func (p *Property) IsNotNil() bool { return !p.IsUnresolved() && !p.IsNil() } -func (p *Property) IsString() bool { +func (p *Property) Is(t cftypes.CfType) bool { if p.IsNil() || p.IsUnresolved() { return false } if p.isFunction() { if prop, success := p.resolveValue(); success && prop != p { - return prop.IsString() + return prop.Is(t) } } - return p.Inner.Type == cftypes.String + return p.Inner.Type == t +} + +func (p *Property) IsString() bool { + return p.Is(cftypes.String) } func (p *Property) IsNotString() bool { @@ -34,15 +38,7 @@ func (p *Property) IsNotString() bool { } func (p *Property) IsInt() bool { - if p.IsNil() || p.IsUnresolved() { - return false - } - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.IsInt() - } - } - return p.Inner.Type == cftypes.Int + return p.Is(cftypes.Int) } func (p *Property) IsNotInt() bool { @@ -61,15 +57,7 @@ func (p *Property) IsNotMap() bool { } func (p *Property) IsList() bool { - if p.IsNil() || p.IsUnresolved() { - return false - } - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.IsList() - } - } - return p.Inner.Type == cftypes.List + return p.Is(cftypes.List) } func (p *Property) IsNotList() bool { @@ -77,15 +65,7 @@ func (p *Property) IsNotList() bool { } func (p *Property) IsBool() bool { - if p.IsNil() || p.IsUnresolved() { - return false - } - if p.isFunction() { - if prop, success := p.resolveValue(); success && prop != p { - return prop.IsBool() - } - } - return p.Inner.Type == cftypes.Bool + return p.Is(cftypes.Bool) } func (p *Property) IsUnresolved() bool { @@ -200,22 +180,25 @@ func (p *Property) EqualTo(checkValue interface{}, equalityOptions ...EqualityOp return false } - switch p.Inner.Type { - case cftypes.String: + if p.Inner.Type == cftypes.String || p.IsString() { if ignoreCase { return strings.EqualFold(p.AsString(), checkerVal) } return p.AsString() == checkerVal - case cftypes.Int: + } else if p.Inner.Type == cftypes.Int || p.IsInt() { if val, err := strconv.Atoi(checkerVal); err == nil { return p.AsInt() == val } } return false case bool: - return p.Inner.Value == checkerVal + if p.Inner.Type == cftypes.Bool || p.IsBool() { + return p.AsBool() == checkerVal + } case int: - return p.Inner.Value == checkerVal + if p.Inner.Type == cftypes.Int || p.IsInt() { + return p.AsInt() == checkerVal + } } return false diff --git a/pkg/scanners/cloudformation/parser/property_helpers_test.go b/pkg/scanners/cloudformation/parser/property_helpers_test.go new file mode 100644 index 000000000..d5e836853 --- /dev/null +++ b/pkg/scanners/cloudformation/parser/property_helpers_test.go @@ -0,0 +1,195 @@ +package parser + +import ( + "testing" + + "github.com/aquasecurity/defsec/pkg/scanners/cloudformation/cftypes" + "github.com/aquasecurity/defsec/pkg/types" + "github.com/stretchr/testify/assert" +) + +func newProp(inner PropertyInner) *Property { + return &Property{ + name: "test_prop", + ctx: &FileContext{}, + rng: types.NewRange("testfile", 1, 1, "", nil), + Inner: inner, + } +} + +func Test_EqualTo(t *testing.T) { + tests := []struct { + name string + property *Property + checkValue interface{} + opts []EqualityOptions + isEqual bool + }{ + { + name: "prop is nil", + property: nil, + checkValue: "some value", + isEqual: false, + }, + { + name: "compare strings", + property: newProp(PropertyInner{ + Type: cftypes.String, + Value: "is str", + }), + checkValue: "is str", + isEqual: true, + }, + { + name: "compare strings ignoring case", + property: newProp(PropertyInner{ + Type: cftypes.String, + Value: "is str", + }), + opts: []EqualityOptions{IgnoreCase}, + checkValue: "Is StR", + isEqual: true, + }, + { + name: "strings ate not equal", + property: newProp(PropertyInner{ + Type: cftypes.String, + Value: "some value", + }), + checkValue: "some other value", + isEqual: false, + }, + { + name: "compare prop with a int represented by a string", + property: newProp(PropertyInner{ + Type: cftypes.Int, + Value: 147, + }), + checkValue: "147", + isEqual: true, + }, + { + name: "compare ints", + property: newProp(PropertyInner{ + Type: cftypes.Int, + Value: 701, + }), + checkValue: 701, + isEqual: true, + }, + { + name: "compare bools", + property: newProp(PropertyInner{ + Type: cftypes.Bool, + Value: true, + }), + checkValue: true, + isEqual: true, + }, + { + name: "prop is string fn", + property: newProp(PropertyInner{ + Type: cftypes.Map, + Value: map[string]*Property{ + "Fn::If": { + Inner: PropertyInner{ + Type: cftypes.List, + Value: []*Property{ + { + Inner: PropertyInner{ + Type: cftypes.Bool, + Value: false, + }, + }, + { + Inner: PropertyInner{ + Type: cftypes.String, + Value: "bad", + }, + }, + { + Inner: PropertyInner{ + Type: cftypes.String, + Value: "some value", + }, + }, + }, + }, + }, + }, + }), + checkValue: "some value", + isEqual: true, + }, + { + name: "prop is int fn", + property: newProp(PropertyInner{ + Type: cftypes.Map, + Value: map[string]*Property{ + "Fn::If": { + Inner: PropertyInner{ + Type: cftypes.List, + Value: []*Property{ + { + Inner: PropertyInner{ + Type: cftypes.Bool, + Value: true, + }, + }, + { + Inner: PropertyInner{ + Type: cftypes.Int, + Value: 121, + }, + }, + { + Inner: PropertyInner{ + Type: cftypes.Int, + Value: -1, + }, + }, + }, + }, + }, + }, + }), + checkValue: 121, + isEqual: true, + }, + { + name: "prop is bool fn", + property: newProp(PropertyInner{ + Type: cftypes.Map, + Value: map[string]*Property{ + "Fn::Equals": { + Inner: PropertyInner{ + Type: cftypes.List, + Value: []*Property{ + { + Inner: PropertyInner{ + Type: cftypes.String, + Value: "foo", + }, + }, + { + Inner: PropertyInner{ + Type: cftypes.String, + Value: "foo", + }, + }, + }, + }, + }, + }, + }), + checkValue: true, + isEqual: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.isEqual, tt.property.EqualTo(tt.checkValue, tt.opts...)) + }) + } +}