cfn-nag 0.0.44 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/cfn_nag +27 -11
- data/bin/cfn_nag_rules +5 -4
- data/bin/cfn_nag_scan +29 -0
- data/lib/cfn-nag.rb +3 -0
- data/lib/cfn-nag/cfn_nag.rb +115 -0
- data/lib/cfn-nag/custom_rule_loader.rb +72 -0
- data/lib/cfn-nag/custom_rules/CloudFormationAuthenticationRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/CloudFrontDistributionAccessLoggingRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/EbsVolumeHasSseRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/ElasticLoadBalancerAccessLoggingRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyNotResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyWildcardActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamManagedPolicyWildcardResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyNotResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyWildcardActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamPolicyWildcardResourceRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotActionOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotActionOnTrustPolicyRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotPrincipalOnTrustPolicyRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/IamRoleNotResourceOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/IamRoleWildcardActionOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/IamRoleWildcardActionOnTrustPolicyRule.rb +27 -0
- data/lib/cfn-nag/custom_rules/IamRoleWildcardResourceOnPermissionsPolicyRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/LambdaPermissionInvokeFunctionActionRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/LambdaPermissionWildcardPrincipalRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/ManagedPolicyOnUserRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/PolicyOnUserRule.rb +24 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyNotPrincipalRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyWildcardActionRule.rb +30 -0
- data/lib/cfn-nag/custom_rules/S3BucketPolicyWildcardPrincipalRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/S3BucketPublicReadAclRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/S3BucketPublicReadWriteAclRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupEgressOpenToWorldRule.rb +39 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupEgressPortRangeRule.rb +38 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupIngressCidrNon32Rule.rb +40 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupIngressOpenToWorldRule.rb +39 -0
- data/lib/cfn-nag/custom_rules/SecurityGroupIngressPortRangeRule.rb +38 -0
- data/lib/{custom_rules/security_group_missing_egress.rb → cfn-nag/custom_rules/SecurityGroupMissingEgressRule.rb} +6 -12
- data/lib/cfn-nag/custom_rules/SnsTopicPolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/SnsTopicPolicyNotPrincipalRule.rb +26 -0
- data/lib/cfn-nag/custom_rules/SnsTopicPolicyWildcardPrincipalRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyNotActionRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyNotPrincipalRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyWildcardActionRule.rb +30 -0
- data/lib/cfn-nag/custom_rules/SqsQueuePolicyWildcardPrincipalRule.rb +29 -0
- data/lib/cfn-nag/custom_rules/UserHasInlinePolicyRule.rb +25 -0
- data/lib/cfn-nag/custom_rules/UserMissingGroupRule.rb +28 -0
- data/lib/cfn-nag/custom_rules/WafWebAclDefaultActionRule.rb +34 -0
- data/lib/cfn-nag/custom_rules/base.rb +28 -0
- data/lib/cfn-nag/custom_rules/unencrypted_s3_put_allowed.rb +58 -0
- data/lib/{profile.rb → cfn-nag/profile.rb} +0 -1
- data/lib/{profile_loader.rb → cfn-nag/profile_loader.rb} +2 -2
- data/lib/{result_view → cfn-nag/result_view}/json_results.rb +0 -0
- data/lib/{result_view → cfn-nag/result_view}/rules_view.rb +0 -0
- data/lib/{result_view → cfn-nag/result_view}/simple_stdout_results.rb +5 -12
- data/lib/cfn-nag/rule_definition.rb +36 -0
- data/lib/cfn-nag/rule_dumper.rb +23 -0
- data/lib/cfn-nag/rule_registry.rb +43 -0
- data/lib/cfn-nag/template_discovery.rb +24 -0
- data/lib/cfn-nag/violation.rb +58 -0
- metadata +79 -36
- data/lib/cfn_nag.rb +0 -219
- data/lib/custom_rule_loader.rb +0 -64
- data/lib/custom_rules/unencrypted_s3_put_allowed.rb +0 -58
- data/lib/custom_rules/user_missing_group.rb +0 -34
- data/lib/json_rules/basic_rules.rb +0 -49
- data/lib/json_rules/cfn_rules.rb +0 -4
- data/lib/json_rules/cidr_rules.rb +0 -77
- data/lib/json_rules/cloudfront_rules.rb +0 -4
- data/lib/json_rules/ebs_rules.rb +0 -4
- data/lib/json_rules/iam_policy_rules.rb +0 -153
- data/lib/json_rules/iam_user_rules.rb +0 -15
- data/lib/json_rules/lambda_rules.rb +0 -9
- data/lib/json_rules/loadbalancer_rules.rb +0 -9
- data/lib/json_rules/port_rules.rb +0 -33
- data/lib/json_rules/s3_bucket_rules.rb +0 -51
- data/lib/json_rules/sns_rules.rb +0 -29
- data/lib/json_rules/sqs_rules.rb +0 -25
- data/lib/model/action_parser.rb +0 -27
- data/lib/model/cfn_model.rb +0 -182
- data/lib/model/iam_user_parser.rb +0 -34
- data/lib/model/parser_registry.rb +0 -31
- data/lib/model/s3_bucket_policy.rb +0 -25
- data/lib/model/s3_bucket_policy_parser.rb +0 -28
- data/lib/model/security_group_parser.rb +0 -59
- data/lib/rule.rb +0 -208
- data/lib/rule_registry.rb +0 -45
- data/lib/violation.rb +0 -41
data/lib/custom_rule_loader.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require_relative 'model/parser_registry'
|
2
|
-
require_relative 'model/cfn_model'
|
3
|
-
require_relative 'custom_rules/security_group_missing_egress'
|
4
|
-
require_relative 'custom_rules/unencrypted_s3_put_allowed'
|
5
|
-
require_relative 'custom_rules/user_missing_group'
|
6
|
-
|
7
|
-
class CustomRuleLoader
|
8
|
-
|
9
|
-
attr_reader :custom_rule_registry
|
10
|
-
|
11
|
-
@custom_rule_directory = '/var/lib/cfn_nag_plugins'
|
12
|
-
|
13
|
-
def self.custom_rule_directory=(directory)
|
14
|
-
@custom_rule_directory = directory
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.custom_rule_directory
|
18
|
-
@custom_rule_directory
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize(rule_registry)
|
22
|
-
@custom_rule_registry = [
|
23
|
-
SecurityGroupMissingEgressRule,
|
24
|
-
UserMissingGroupRule,
|
25
|
-
UnencryptedS3PutObjectAllowedRule
|
26
|
-
]
|
27
|
-
@violations = []
|
28
|
-
@rule_registry = rule_registry
|
29
|
-
discover_rules
|
30
|
-
end
|
31
|
-
|
32
|
-
def custom_rules(input_json)
|
33
|
-
@violations = []
|
34
|
-
|
35
|
-
@custom_rule_registry.each do |rule_class|
|
36
|
-
rule = rule_class.new
|
37
|
-
@rule_registry.definition(id: rule.rule_id,
|
38
|
-
type: rule.rule_type,
|
39
|
-
message: rule.rule_text)
|
40
|
-
|
41
|
-
if rule.respond_to? 'custom_parsers'
|
42
|
-
rule.custom_parsers.each do |custom_parser|
|
43
|
-
ParserRegistry.instance.add_parser custom_parser[0], custom_parser[1]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
cfn_model = CfnModel.new.parse(input_json)
|
48
|
-
audit_result = rule_class.new.audit(cfn_model)
|
49
|
-
@violations << audit_result unless audit_result.nil?
|
50
|
-
end
|
51
|
-
@violations
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def discover_rules(rule_directory: CustomRuleLoader.custom_rule_directory)
|
57
|
-
rules = Dir[File.join(rule_directory, '*Rule.rb')].sort
|
58
|
-
|
59
|
-
rules.each do |rule|
|
60
|
-
require(rule)
|
61
|
-
@custom_rule_registry << Object.const_get(File.basename(rule, '.rb'))
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
require_relative '../violation'
|
2
|
-
require 'model/action_parser'
|
3
|
-
|
4
|
-
class UnencryptedS3PutObjectAllowedRule
|
5
|
-
|
6
|
-
def rule_text
|
7
|
-
'It appears that the S3 Bucket Policy allows s3:PutObject without server-side encryption'
|
8
|
-
end
|
9
|
-
|
10
|
-
def rule_type
|
11
|
-
Violation::WARNING
|
12
|
-
end
|
13
|
-
|
14
|
-
def rule_id
|
15
|
-
'W1000'
|
16
|
-
end
|
17
|
-
|
18
|
-
def audit(cfn_model)
|
19
|
-
logical_resource_ids = []
|
20
|
-
cfn_model.bucket_policies.each do |bucket_policy|
|
21
|
-
found_statement = bucket_policy.statements.find do |statement|
|
22
|
-
blocks_put_object_without_encryption(statement)
|
23
|
-
end
|
24
|
-
if found_statement.nil?
|
25
|
-
logical_resource_ids << bucket_policy.logical_resource_id
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
if logical_resource_ids.size > 0
|
30
|
-
Violation.new(id: rule_id,
|
31
|
-
type: rule_type,
|
32
|
-
message: rule_text,
|
33
|
-
logical_resource_ids: logical_resource_ids)
|
34
|
-
else
|
35
|
-
nil
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def blocks_put_object_without_encryption(statement)
|
42
|
-
encryption_condition = {
|
43
|
-
'StringNotEquals' => {
|
44
|
-
's3:x-amz-server-side-encryption' => 'AES256'
|
45
|
-
}
|
46
|
-
}
|
47
|
-
|
48
|
-
# this isn't quite complete. parsing the Resource field can be tricky
|
49
|
-
# looking for a trailing wildcard will likely be right most of the time
|
50
|
-
# but there are a lot of string manipulations to confuse things so...
|
51
|
-
# just warn when we can't find at least the Deny+encryption - they may have an
|
52
|
-
# incomplete Deny (for encryption) and that will slip through
|
53
|
-
statement['Effect'] == 'Deny' and
|
54
|
-
ActionParser.new.include?(actual_action: statement['Action'], action_to_look_for: 's3:PutObject') and
|
55
|
-
S3BucketPolicy::condition_includes?(statement, encryption_condition) and
|
56
|
-
statement['Principal'] == '*'
|
57
|
-
end
|
58
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require_relative '../violation'
|
2
|
-
|
3
|
-
class UserMissingGroupRule
|
4
|
-
|
5
|
-
def rule_text
|
6
|
-
'User is not assigned to a group'
|
7
|
-
end
|
8
|
-
|
9
|
-
def rule_type
|
10
|
-
Violation::FAILING_VIOLATION
|
11
|
-
end
|
12
|
-
|
13
|
-
def rule_id
|
14
|
-
'F2000'
|
15
|
-
end
|
16
|
-
|
17
|
-
def audit(cfn_model)
|
18
|
-
logical_resource_ids = []
|
19
|
-
cfn_model.iam_users.each do |iam_user|
|
20
|
-
if iam_user.groups.size == 0
|
21
|
-
logical_resource_ids << iam_user.logical_resource_id
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
if logical_resource_ids.size > 0
|
26
|
-
Violation.new(id: rule_id,
|
27
|
-
type: rule_type,
|
28
|
-
message: rule_text,
|
29
|
-
logical_resource_ids: logical_resource_ids)
|
30
|
-
else
|
31
|
-
nil
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
raw_fatal_assertion id: 'FATAL',
|
2
|
-
jq: '.Resources|length > 0',
|
3
|
-
message: 'A CloudFormation template must have at least 1 resource'
|
4
|
-
|
5
|
-
|
6
|
-
%w(
|
7
|
-
AWS::IAM::Role
|
8
|
-
AWS::IAM::Policy
|
9
|
-
AWS::IAM::ManagedPolicy
|
10
|
-
AWS::S3::BucketPolicy
|
11
|
-
AWS::SQS::QueuePolicy
|
12
|
-
AWS::SNS::TopicPolicy
|
13
|
-
AWS::IAM::UserToGroupAddition
|
14
|
-
AWS::EC2::SecurityGroup
|
15
|
-
AWS::EC2::SecurityGroupIngress
|
16
|
-
AWS::EC2::SecurityGroupEgress
|
17
|
-
).each do |resource_must_have_properties|
|
18
|
-
fatal_violation id: 'FATAL',
|
19
|
-
jq: "[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == \"#{resource_must_have_properties}\" and .Properties == null)]|map(.LogicalResourceId)",
|
20
|
-
message: "#{resource_must_have_properties} must have Properties"
|
21
|
-
end
|
22
|
-
|
23
|
-
missing_reference_jq = <<END
|
24
|
-
(
|
25
|
-
(
|
26
|
-
([..|.Ref?]|map(select(. != null)) + [..|."Fn::GetAtt"?[0]]|map(select(. != null)))
|
27
|
-
)
|
28
|
-
-
|
29
|
-
(
|
30
|
-
["AWS::AccountId","AWS::StackName","AWS::Region","AWS::StackId","AWS::NoValue","AWS::NotificationARNs"] +
|
31
|
-
([.Resources|keys]|flatten) +
|
32
|
-
(if .Parameters? then ([.Parameters|keys]|flatten) else [] end)
|
33
|
-
)
|
34
|
-
)|if length==0 then false else . end
|
35
|
-
END
|
36
|
-
|
37
|
-
raw_fatal_violation id: 'FATAL',
|
38
|
-
jq: missing_reference_jq,
|
39
|
-
message: 'All Ref and Fn::GetAtt must reference existing logical resource ids'
|
40
|
-
|
41
|
-
|
42
|
-
%w(
|
43
|
-
AWS::EC2::SecurityGroupIngress
|
44
|
-
AWS::EC2::SecurityGroupEgress
|
45
|
-
).each do |xgress|
|
46
|
-
fatal_violation id: 'FATAL',
|
47
|
-
jq: "[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == \"#{xgress}\" and .Properties.GroupName != null)]|map(.LogicalResourceId)",
|
48
|
-
message: "#{xgress} must not have GroupName - EC2 classic is a no-go!"
|
49
|
-
end
|
data/lib/json_rules/cfn_rules.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
##### inline ingress
|
2
|
-
warning id: 'W2',
|
3
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroup" and (.Properties.SecurityGroupIngress|type == "object"))|select(.Properties.SecurityGroupIngress.CidrIp? == "0.0.0.0/0")]|map(.LogicalResourceId)',
|
4
|
-
message: 'Security Groups found with cidr open to world on ingress. This should never be true on instance. Permissible on ELB'
|
5
|
-
|
6
|
-
warning id: 'W3',
|
7
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroup" and (.Properties.SecurityGroupIngress|type == "array"))|first(select(.Properties.SecurityGroupIngress[].CidrIp? == "0.0.0.0/0"))]|map(.LogicalResourceId)',
|
8
|
-
message: 'Security Groups found with cidr open to world on ingress array. This should never be true on instance. Permissible on ELB'
|
9
|
-
|
10
|
-
##### external ingress
|
11
|
-
warning id: 'W4',
|
12
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroupIngress")|select(.Properties.CidrIp? == "0.0.0.0/0")]|map(.LogicalResourceId)',
|
13
|
-
message: 'Security Group Standalone Ingress found with cidr open to world. This should never be true on instance. Permissible on ELB'
|
14
|
-
|
15
|
-
|
16
|
-
###### inline egress
|
17
|
-
warning id: 'W5',
|
18
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroup" and (.Properties.SecurityGroupEgress|type == "object"))|select(.Properties.SecurityGroupEgress.CidrIp? == "0.0.0.0/0")]|map(.LogicalResourceId)',
|
19
|
-
message: 'Security Groups found with cidr open to world on egress'
|
20
|
-
|
21
|
-
|
22
|
-
warning id: 'W6',
|
23
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroup" and (.Properties.SecurityGroupEgress|type == "array"))|first(select(.Properties.SecurityGroupEgress[].CidrIp? == "0.0.0.0/0"))]|map(.LogicalResourceId)',
|
24
|
-
message: 'Security Groups found with cidr open to world on egress array'
|
25
|
-
|
26
|
-
|
27
|
-
##### external egress
|
28
|
-
warning id: 'W7',
|
29
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::SecurityGroupEgress")|select(.Properties.CidrIp? == "0.0.0.0/0")]|map(.LogicalResourceId)',
|
30
|
-
message: 'Security Group Standalone Egress found with cidr open to world.'
|
31
|
-
|
32
|
-
non32_cidr_standalone_ingress = <<END
|
33
|
-
[.Resources|
|
34
|
-
with_entries(.value.LogicalResourceId = .key)[] |
|
35
|
-
select(.Type == "AWS::EC2::SecurityGroupIngress") |
|
36
|
-
if(.Properties.CidrIp|type == "string")
|
37
|
-
then select(.Properties.CidrIp|endswith("/32")|not)
|
38
|
-
else (
|
39
|
-
if(.Properties.CidrIp|type == "array")
|
40
|
-
then (select(.Properties.CidrIp[]|if (type == "string") then (endswith("/32")|not) else false end))
|
41
|
-
else empty
|
42
|
-
end
|
43
|
-
)
|
44
|
-
end ]|map(.LogicalResourceId)
|
45
|
-
END
|
46
|
-
|
47
|
-
# BEWARE with escapes \d -> \\\d because of how the escapes get munged from ruby through to shell
|
48
|
-
warning id: 'W8',
|
49
|
-
jq: non32_cidr_standalone_ingress,
|
50
|
-
message: 'Security Group Standalone Ingress cidr found that is not /32'
|
51
|
-
|
52
|
-
non_32_cidr_jq_expression = <<END
|
53
|
-
[.Resources |
|
54
|
-
with_entries(.value.LogicalResourceId = .key)[] |
|
55
|
-
select(.Type == "AWS::EC2::SecurityGroup") |
|
56
|
-
if (.Properties.SecurityGroupIngress|type == "object")
|
57
|
-
then (
|
58
|
-
if (.Properties.SecurityGroupIngress.CidrIp|type == "string")
|
59
|
-
then (select(.Properties.SecurityGroupIngress.CidrIp|endswith("/32")|not))
|
60
|
-
else empty
|
61
|
-
end
|
62
|
-
)
|
63
|
-
else (
|
64
|
-
if (.Properties.SecurityGroupIngress|type == "array")
|
65
|
-
then (
|
66
|
-
select(.Properties.SecurityGroupIngress[]|(if (.CidrIp|type == "string") then (select(.CidrIp|endswith("/32")|not)) else empty end))
|
67
|
-
)
|
68
|
-
else empty
|
69
|
-
end
|
70
|
-
)
|
71
|
-
end
|
72
|
-
]|map(.LogicalResourceId)
|
73
|
-
END
|
74
|
-
|
75
|
-
warning id: 'W9',
|
76
|
-
jq: non_32_cidr_jq_expression,
|
77
|
-
message: 'Security Groups found with cidr that is not /32'
|
@@ -1,4 +0,0 @@
|
|
1
|
-
warning id: 'W10',
|
2
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::CloudFront::Distribution")|'\
|
3
|
-
'select(.Properties.DistributionConfig.Logging == null)]|map(.LogicalResourceId) ',
|
4
|
-
message: 'CloudFront Distribution should enable access logging'
|
data/lib/json_rules/ebs_rules.rb
DELETED
@@ -1,4 +0,0 @@
|
|
1
|
-
violation id: 'F1',
|
2
|
-
jq: '[.Resources|with_entries(.value.LogicalResourceId = .key)[] | select(.Type == "AWS::EC2::Volume")|'\
|
3
|
-
'select(.Properties.Encrypted == null or .Properties.Encrypted == false)]|map(.LogicalResourceId) ',
|
4
|
-
message: 'EBS volume should have server-side encryption enabled'
|
@@ -1,153 +0,0 @@
|
|
1
|
-
wildcard_action_filter = <<END
|
2
|
-
def wildcard_action:
|
3
|
-
if .Statement|type == "object"
|
4
|
-
then select(.Statement.Effect == "Allow" and (if .Statement.Action|type=="string" then (.Statement.Action == "*") else (.Statement.Action|indices("*")|length > 0) end))
|
5
|
-
else select(.Statement[]|.Effect == "Allow" and (if .Action|type=="string" then (.Action == "*") else (.Action|indices("*")|length > 0) end))
|
6
|
-
end;
|
7
|
-
END
|
8
|
-
|
9
|
-
violation id: 'F2',
|
10
|
-
jq: wildcard_action_filter +
|
11
|
-
"[#{resources_by_type('AWS::IAM::Role')}|select(.Properties.AssumeRolePolicyDocument|wildcard_action)]|map(.LogicalResourceId) ",
|
12
|
-
message: 'IAM role should not allow * action on its trust policy'
|
13
|
-
|
14
|
-
violation id: 'F3',
|
15
|
-
jq: wildcard_action_filter +
|
16
|
-
"[#{resources_by_type('AWS::IAM::Role')}|select(.Properties.Policies !=null)|select(.Properties.Policies[].PolicyDocument|wildcard_action)]|map(.LogicalResourceId)",
|
17
|
-
message: 'IAM role should not allow * action on its permissions policy'
|
18
|
-
|
19
|
-
|
20
|
-
violation id: 'F4',
|
21
|
-
jq: wildcard_action_filter +
|
22
|
-
"[#{resources_by_type('AWS::IAM::Policy')}|select(.Properties.PolicyDocument|wildcard_action)]|map(.LogicalResourceId) ",
|
23
|
-
message: 'IAM policy should not allow * action'
|
24
|
-
|
25
|
-
|
26
|
-
violation id: 'F5',
|
27
|
-
jq: wildcard_action_filter +
|
28
|
-
"[#{resources_by_type('AWS::IAM::ManagedPolicy')}|select(.Properties.PolicyDocument|wildcard_action)]|map(.LogicalResourceId) ",
|
29
|
-
message: 'IAM managed policy should not allow * action'
|
30
|
-
|
31
|
-
wildcard_resource_filter = <<END
|
32
|
-
def wildcard_resource:
|
33
|
-
if .Statement|type == "object"
|
34
|
-
then select(.Statement.Effect == "Allow" and (if .Statement.Resource|type=="string" then (.Statement.Resource == "*") else (.Statement.Resource|indices("*")|length > 0) end))
|
35
|
-
else select(.Statement[]|.Effect == "Allow" and (if .Resource|type=="string" then (.Resource == "*") else (.Statement.Resource|indices("*")|length > 0) end))
|
36
|
-
end;
|
37
|
-
END
|
38
|
-
|
39
|
-
warning id: 'W11',
|
40
|
-
jq: wildcard_resource_filter +
|
41
|
-
"[#{resources_by_type('AWS::IAM::Role')}|select(.Properties.Policies !=null)|select(.Properties.Policies[].PolicyDocument|wildcard_resource)]|map(.LogicalResourceId)",
|
42
|
-
message: 'IAM role should not allow * resource on its permissions policy'
|
43
|
-
|
44
|
-
|
45
|
-
warning id: 'W12',
|
46
|
-
jq: wildcard_resource_filter +
|
47
|
-
"[#{resources_by_type('AWS::IAM::Policy')}|select(.Properties.PolicyDocument|wildcard_resource)]|map(.LogicalResourceId)",
|
48
|
-
message: 'IAM policy should not allow * resource'
|
49
|
-
|
50
|
-
warning id: 'W13',
|
51
|
-
jq: wildcard_resource_filter +
|
52
|
-
"[#{resources_by_type('AWS::IAM::ManagedPolicy')}|select(.Properties.PolicyDocument|wildcard_resource)]|map(.LogicalResourceId)",
|
53
|
-
message: 'IAM managed policy should not allow * resource'
|
54
|
-
|
55
|
-
allow_not_action_filter = <<END
|
56
|
-
def allow_not_action:
|
57
|
-
if .Statement|type == "object"
|
58
|
-
then select(.Statement.Effect == "Allow" and .Statement.NotAction != null)
|
59
|
-
else select(.Statement[]|(.Effect == "Allow" and .NotAction != null))
|
60
|
-
end;
|
61
|
-
END
|
62
|
-
|
63
|
-
warning id: 'W14',
|
64
|
-
jq: allow_not_action_filter +
|
65
|
-
"[#{resources_by_type('AWS::IAM::Role')}|select(.Properties.AssumeRolePolicyDocument|allow_not_action)]|map(.LogicalResourceId)",
|
66
|
-
message: 'IAM role should not allow Allow+NotAction on trust permissinos'
|
67
|
-
|
68
|
-
|
69
|
-
warning id: 'W15',
|
70
|
-
jq: allow_not_action_filter +
|
71
|
-
"[#{resources_by_type('AWS::IAM::Role')}|select(.Properties.Policies !=null)|select(.Properties.Policies[].PolicyDocument|allow_not_action)]|map(.LogicalResourceId)",
|
72
|
-
message: 'IAM role should not allow Allow+NotAction'
|
73
|
-
|
74
|
-
|
75
|
-
warning id: 'W16',
|
76
|
-
jq: allow_not_action_filter +
|
77
|
-
"[#{resources_by_type('AWS::IAM::Policy')}|select(.Properties.PolicyDocument|allow_not_action)]|map(.LogicalResourceId)",
|
78
|
-
message: 'IAM policy should not allow Allow+NotAction'
|
79
|
-
|
80
|
-
|
81
|
-
warning id: 'W17',
|
82
|
-
jq: allow_not_action_filter +
|
83
|
-
"[#{resources_by_type('AWS::IAM::ManagedPolicy')}|select(.Properties.PolicyDocument|allow_not_action)]|map(.LogicalResourceId)",
|
84
|
-
message: 'IAM managed policy should not allow Allow+NotAction'
|
85
|
-
|
86
|
-
warning id: 'W18',
|
87
|
-
jq: allow_not_action_filter +
|
88
|
-
"[#{resources_by_type('AWS::SQS::QueuePolicy')}|select(.Properties.PolicyDocument|allow_not_action)]|map(.LogicalResourceId)",
|
89
|
-
message: 'SQS Queue policy should not allow Allow+NotAction'
|
90
|
-
|
91
|
-
warning id: 'W19',
|
92
|
-
jq: allow_not_action_filter +
|
93
|
-
"[#{resources_by_type('AWS::SNS::TopicPolicy')}|select(.Properties.PolicyDocument|allow_not_action)]|map(.LogicalResourceId)",
|
94
|
-
message: 'SNS Topic policy should not allow Allow+NotAction'
|
95
|
-
|
96
|
-
warning id: 'W20',
|
97
|
-
jq: allow_not_action_filter +
|
98
|
-
"[#{resources_by_type('AWS::S3::BucketPolicy')}|select(.Properties.PolicyDocument|allow_not_action)]|map(.LogicalResourceId)",
|
99
|
-
message: 'S3 Bucket policy should not allow Allow+NotAction'
|
100
|
-
|
101
|
-
allow_not_resource_filter = <<END
|
102
|
-
def allow_not_resource:
|
103
|
-
if .Statement|type == "object"
|
104
|
-
then select(.Statement.Effect == "Allow" and .Statement.NotResource != null)
|
105
|
-
else select(.Statement[]|(.Effect == "Allow" and .NotResource != null))
|
106
|
-
end;
|
107
|
-
END
|
108
|
-
|
109
|
-
warning id: 'W21',
|
110
|
-
jq: allow_not_resource_filter +
|
111
|
-
"[#{resources_by_type('AWS::IAM::Role')}|select(.Properties.Policies !=null)|select(.Properties.Policies[].PolicyDocument|allow_not_resource)]|map(.LogicalResourceId)",
|
112
|
-
message: 'IAM role should not allow Allow+NotResource'
|
113
|
-
|
114
|
-
|
115
|
-
warning id: 'W22',
|
116
|
-
jq: allow_not_resource_filter +
|
117
|
-
"[#{resources_by_type('AWS::IAM::Policy')}|select(.Properties.PolicyDocument|allow_not_resource)]|map(.LogicalResourceId)",
|
118
|
-
message: 'IAM policy should not allow Allow+NotResource'
|
119
|
-
|
120
|
-
|
121
|
-
warning id: 'W23',
|
122
|
-
jq: allow_not_resource_filter +
|
123
|
-
"[#{resources_by_type('AWS::IAM::ManagedPolicy')}|select(.Properties.PolicyDocument|allow_not_resource)]|map(.LogicalResourceId)",
|
124
|
-
message: 'IAM managed policy should not allow Allow+NotResource'
|
125
|
-
|
126
|
-
|
127
|
-
allow_not_principal_filter = <<END
|
128
|
-
def allow_not_principal:
|
129
|
-
if .Statement|type == "object"
|
130
|
-
then select(.Statement.Effect == "Allow" and .Statement.NotPrincipal != null)
|
131
|
-
else select(.Statement[]|(.Effect == "Allow" and .NotPrincipal != null))
|
132
|
-
end;
|
133
|
-
END
|
134
|
-
|
135
|
-
violation id: 'F6',
|
136
|
-
jq: allow_not_principal_filter +
|
137
|
-
"[#{resources_by_type('AWS::IAM::Role')}|select(.Properties.AssumeRolePolicyDocument|allow_not_principal)]|map(.LogicalResourceId)",
|
138
|
-
message: 'IAM role should not allow Allow+NotPrincipal in its trust policy'
|
139
|
-
|
140
|
-
violation id: 'F7',
|
141
|
-
jq: allow_not_principal_filter +
|
142
|
-
"[#{resources_by_type('AWS::SQS::QueuePolicy')}|select(.Properties.PolicyDocument|allow_not_principal)]|map(.LogicalResourceId)",
|
143
|
-
message: 'SQS Queue policy should not allow Allow+NotPrincipal'
|
144
|
-
|
145
|
-
violation id: 'F8',
|
146
|
-
jq: allow_not_principal_filter +
|
147
|
-
"[#{resources_by_type('AWS::SNS::TopicPolicy')}|select(.Properties.PolicyDocument|allow_not_principal)]|map(.LogicalResourceId)",
|
148
|
-
message: 'SNS Topic policy should not allow Allow+NotPrincipal'
|
149
|
-
|
150
|
-
violation id: 'F9',
|
151
|
-
jq: allow_not_principal_filter +
|
152
|
-
"[#{resources_by_type('AWS::S3::BucketPolicy')}|select(.Properties.PolicyDocument|allow_not_principal)]|map(.LogicalResourceId)",
|
153
|
-
message: 'S3 Bucket policy should not allow Allow+NotPrincipal'
|