diff --git a/aws_policy_equivalence.go b/aws_policy_equivalence.go index 7de5bf8..af7049a 100644 --- a/aws_policy_equivalence.go +++ b/aws_policy_equivalence.go @@ -13,6 +13,7 @@ import ( "reflect" "regexp" "sort" + "strconv" "strings" "github.com/aws/aws-sdk-go/aws/arn" @@ -381,26 +382,31 @@ func newStringSet(members interface{}) stringSet { return stringSet{} } - if single, ok := members.(string); ok { - return stringSet{single} - } - - if multiple, ok := members.([]interface{}); ok { - if len(multiple) == 0 { - return stringSet{} - } - actions := make([]string, len(multiple)) - for i, action := range multiple { - if _, ok := action.(string); !ok { + switch v := members.(type) { + case string: + return stringSet{v} + case bool: + return stringSet{strconv.FormatBool(v)} + case float64: + return stringSet{strconv.FormatFloat(v, 'f', -1, 64)} + case []interface{}: + var actions []string + for _, action := range v { + switch action := action.(type) { + case string: + actions = append(actions, action) + case bool: + actions = append(actions, strconv.FormatBool(action)) + case float64: + actions = append(actions, strconv.FormatFloat(action, 'f', -1, 64)) + default: return nil } - - actions[i] = action.(string) } return stringSet(actions) + default: + return nil } - - return nil } func newPrincipalStringSet(members interface{}) principalStringSet { diff --git a/aws_policy_equivalence_test.go b/aws_policy_equivalence_test.go index ad2799b..db05489 100644 --- a/aws_policy_equivalence_test.go +++ b/aws_policy_equivalence_test.go @@ -261,6 +261,24 @@ func TestPolicyEquivalence(t *testing.T) { policy2: policyTest34c, equivalent: false, }, + { + name: "Boolean condition with and without quotes on single value", + policy1: policyTest35a, + policy2: policyTest35b, + equivalent: true, + }, + { + name: "Numeric condition with and without quotes on single value", + policy1: policyTest36a, + policy2: policyTest36b, + equivalent: true, + }, + { + name: "Numeric condition with and without quotes on array of values", + policy1: policyTest37a, + policy2: policyTest37b, + equivalent: true, + }, } for _, tc := range cases { @@ -1255,7 +1273,7 @@ const policyTest32 = `{ "Action": [ "s3:PutObject" ], - "Resource": 42 + "Resource": {} } ] }` @@ -1269,7 +1287,7 @@ const policyTest33 = `{ "Action": [ "s3:PutObject" ], - "Resource": [42] + "Resource": [[42]] } ] }` @@ -1340,6 +1358,132 @@ const policyTest34c = `{ ] }` +const policyTest35a = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "statement1", + "Effect": "Allow", + "Action": [ + "s3:PutObject" + ], + "Resource": [ + "arn:aws:s3:::examplebucket/*" + ], + "Condition": { + "Bool": { + "aws:MultiFactorAuthPresent": true + } + } + } + ] + }` + +const policyTest35b = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "statement1", + "Effect": "Allow", + "Action": [ + "s3:PutObject" + ], + "Resource": [ + "arn:aws:s3:::examplebucket/*" + ], + "Condition": { + "Bool": { + "aws:MultiFactorAuthPresent": "true" + } + } + } + ] + }` + +const policyTest36a = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "statement1", + "Effect": "Allow", + "Action": [ + "s3:PutObject" + ], + "Resource": [ + "arn:aws:s3:::examplebucket/*" + ], + "Condition": { + "NumericLessThanEquals": { + "aws:MultiFactorAuthAge": 100 + } + } + } + ] + }` + +const policyTest36b = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "statement1", + "Effect": "Allow", + "Action": [ + "s3:PutObject" + ], + "Resource": [ + "arn:aws:s3:::examplebucket/*" + ], + "Condition": { + "NumericLessThanEquals": { + "aws:MultiFactorAuthAge": "100" + } + } + } + ] + }` + +const policyTest37a = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "statement1", + "Effect": "Allow", + "Action": [ + "s3:PutObject" + ], + "Resource": [ + "arn:aws:s3:::examplebucket/*" + ], + "Condition": { + "NumericEquals": { + "aws:MultiFactorAuthAge": [100.01, 200.2] + } + } + } + ] + }` + +const policyTest37b = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "statement1", + "Effect": "Allow", + "Action": [ + "s3:PutObject" + ], + "Resource": [ + "arn:aws:s3:::examplebucket/*" + ], + "Condition": { + "NumericEquals": { + "aws:MultiFactorAuthAge": ["100.01", "200.2"] + } + } + } + ] + }` + func TestStringValueSlicesEqualIgnoreOrder(t *testing.T) { equal := []interface{}{ []interface{}{