cfn-nag 0.0.44 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cfn_nag +27 -11
  3. data/bin/cfn_nag_rules +5 -4
  4. data/bin/cfn_nag_scan +29 -0
  5. data/lib/cfn-nag.rb +3 -0
  6. data/lib/cfn-nag/cfn_nag.rb +115 -0
  7. data/lib/cfn-nag/custom_rule_loader.rb +72 -0
  8. data/lib/cfn-nag/custom_rules/CloudFormationAuthenticationRule.rb +28 -0
  9. data/lib/cfn-nag/custom_rules/CloudFrontDistributionAccessLoggingRule.rb +24 -0
  10. data/lib/cfn-nag/custom_rules/EbsVolumeHasSseRule.rb +24 -0
  11. data/lib/cfn-nag/custom_rules/ElasticLoadBalancerAccessLoggingRule.rb +24 -0
  12. data/lib/cfn-nag/custom_rules/IamManagedPolicyNotActionRule.rb +25 -0
  13. data/lib/cfn-nag/custom_rules/IamManagedPolicyNotResourceRule.rb +25 -0
  14. data/lib/cfn-nag/custom_rules/IamManagedPolicyWildcardActionRule.rb +25 -0
  15. data/lib/cfn-nag/custom_rules/IamManagedPolicyWildcardResourceRule.rb +25 -0
  16. data/lib/cfn-nag/custom_rules/IamPolicyNotActionRule.rb +25 -0
  17. data/lib/cfn-nag/custom_rules/IamPolicyNotResourceRule.rb +25 -0
  18. data/lib/cfn-nag/custom_rules/IamPolicyWildcardActionRule.rb +25 -0
  19. data/lib/cfn-nag/custom_rules/IamPolicyWildcardResourceRule.rb +25 -0
  20. data/lib/cfn-nag/custom_rules/IamRoleNotActionOnPermissionsPolicyRule.rb +28 -0
  21. data/lib/cfn-nag/custom_rules/IamRoleNotActionOnTrustPolicyRule.rb +25 -0
  22. data/lib/cfn-nag/custom_rules/IamRoleNotPrincipalOnTrustPolicyRule.rb +25 -0
  23. data/lib/cfn-nag/custom_rules/IamRoleNotResourceOnPermissionsPolicyRule.rb +28 -0
  24. data/lib/cfn-nag/custom_rules/IamRoleWildcardActionOnPermissionsPolicyRule.rb +28 -0
  25. data/lib/cfn-nag/custom_rules/IamRoleWildcardActionOnTrustPolicyRule.rb +27 -0
  26. data/lib/cfn-nag/custom_rules/IamRoleWildcardResourceOnPermissionsPolicyRule.rb +28 -0
  27. data/lib/cfn-nag/custom_rules/LambdaPermissionInvokeFunctionActionRule.rb +24 -0
  28. data/lib/cfn-nag/custom_rules/LambdaPermissionWildcardPrincipalRule.rb +24 -0
  29. data/lib/cfn-nag/custom_rules/ManagedPolicyOnUserRule.rb +24 -0
  30. data/lib/cfn-nag/custom_rules/PolicyOnUserRule.rb +24 -0
  31. data/lib/cfn-nag/custom_rules/S3BucketPolicyNotActionRule.rb +25 -0
  32. data/lib/cfn-nag/custom_rules/S3BucketPolicyNotPrincipalRule.rb +25 -0
  33. data/lib/cfn-nag/custom_rules/S3BucketPolicyWildcardActionRule.rb +30 -0
  34. data/lib/cfn-nag/custom_rules/S3BucketPolicyWildcardPrincipalRule.rb +29 -0
  35. data/lib/cfn-nag/custom_rules/S3BucketPublicReadAclRule.rb +29 -0
  36. data/lib/cfn-nag/custom_rules/S3BucketPublicReadWriteAclRule.rb +29 -0
  37. data/lib/cfn-nag/custom_rules/SecurityGroupEgressOpenToWorldRule.rb +39 -0
  38. data/lib/cfn-nag/custom_rules/SecurityGroupEgressPortRangeRule.rb +38 -0
  39. data/lib/cfn-nag/custom_rules/SecurityGroupIngressCidrNon32Rule.rb +40 -0
  40. data/lib/cfn-nag/custom_rules/SecurityGroupIngressOpenToWorldRule.rb +39 -0
  41. data/lib/cfn-nag/custom_rules/SecurityGroupIngressPortRangeRule.rb +38 -0
  42. data/lib/{custom_rules/security_group_missing_egress.rb → cfn-nag/custom_rules/SecurityGroupMissingEgressRule.rb} +6 -12
  43. data/lib/cfn-nag/custom_rules/SnsTopicPolicyNotActionRule.rb +25 -0
  44. data/lib/cfn-nag/custom_rules/SnsTopicPolicyNotPrincipalRule.rb +26 -0
  45. data/lib/cfn-nag/custom_rules/SnsTopicPolicyWildcardPrincipalRule.rb +29 -0
  46. data/lib/cfn-nag/custom_rules/SqsQueuePolicyNotActionRule.rb +25 -0
  47. data/lib/cfn-nag/custom_rules/SqsQueuePolicyNotPrincipalRule.rb +25 -0
  48. data/lib/cfn-nag/custom_rules/SqsQueuePolicyWildcardActionRule.rb +30 -0
  49. data/lib/cfn-nag/custom_rules/SqsQueuePolicyWildcardPrincipalRule.rb +29 -0
  50. data/lib/cfn-nag/custom_rules/UserHasInlinePolicyRule.rb +25 -0
  51. data/lib/cfn-nag/custom_rules/UserMissingGroupRule.rb +28 -0
  52. data/lib/cfn-nag/custom_rules/WafWebAclDefaultActionRule.rb +34 -0
  53. data/lib/cfn-nag/custom_rules/base.rb +28 -0
  54. data/lib/cfn-nag/custom_rules/unencrypted_s3_put_allowed.rb +58 -0
  55. data/lib/{profile.rb → cfn-nag/profile.rb} +0 -1
  56. data/lib/{profile_loader.rb → cfn-nag/profile_loader.rb} +2 -2
  57. data/lib/{result_view → cfn-nag/result_view}/json_results.rb +0 -0
  58. data/lib/{result_view → cfn-nag/result_view}/rules_view.rb +0 -0
  59. data/lib/{result_view → cfn-nag/result_view}/simple_stdout_results.rb +5 -12
  60. data/lib/cfn-nag/rule_definition.rb +36 -0
  61. data/lib/cfn-nag/rule_dumper.rb +23 -0
  62. data/lib/cfn-nag/rule_registry.rb +43 -0
  63. data/lib/cfn-nag/template_discovery.rb +24 -0
  64. data/lib/cfn-nag/violation.rb +58 -0
  65. metadata +79 -36
  66. data/lib/cfn_nag.rb +0 -219
  67. data/lib/custom_rule_loader.rb +0 -64
  68. data/lib/custom_rules/unencrypted_s3_put_allowed.rb +0 -58
  69. data/lib/custom_rules/user_missing_group.rb +0 -34
  70. data/lib/json_rules/basic_rules.rb +0 -49
  71. data/lib/json_rules/cfn_rules.rb +0 -4
  72. data/lib/json_rules/cidr_rules.rb +0 -77
  73. data/lib/json_rules/cloudfront_rules.rb +0 -4
  74. data/lib/json_rules/ebs_rules.rb +0 -4
  75. data/lib/json_rules/iam_policy_rules.rb +0 -153
  76. data/lib/json_rules/iam_user_rules.rb +0 -15
  77. data/lib/json_rules/lambda_rules.rb +0 -9
  78. data/lib/json_rules/loadbalancer_rules.rb +0 -9
  79. data/lib/json_rules/port_rules.rb +0 -33
  80. data/lib/json_rules/s3_bucket_rules.rb +0 -51
  81. data/lib/json_rules/sns_rules.rb +0 -29
  82. data/lib/json_rules/sqs_rules.rb +0 -25
  83. data/lib/model/action_parser.rb +0 -27
  84. data/lib/model/cfn_model.rb +0 -182
  85. data/lib/model/iam_user_parser.rb +0 -34
  86. data/lib/model/parser_registry.rb +0 -31
  87. data/lib/model/s3_bucket_policy.rb +0 -25
  88. data/lib/model/s3_bucket_policy_parser.rb +0 -28
  89. data/lib/model/security_group_parser.rb +0 -59
  90. data/lib/rule.rb +0 -208
  91. data/lib/rule_registry.rb +0 -45
  92. data/lib/violation.rb +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c77d505fee7be50a7cdc2e6b536e322752b3b9b2
4
- data.tar.gz: 33831cb14131a221d0653cdc34988e330a46d2a3
3
+ metadata.gz: 905bc16ca0d4964c17e4a532a444b174541c61a6
4
+ data.tar.gz: 5b76a420e36c46f4adeff8506cae88ff76462b83
5
5
  SHA512:
6
- metadata.gz: b460866e5500b58d732f33d6f8433b891a5fc4024c54aad491120ac460f224ae1270cf3862cbbd092bb54cdcfa4936a443dbb2e2e184d86045ed910779ad572a
7
- data.tar.gz: e2b6a603ceaeab42441dfe9e97e9954e965e5c6d4cd66a249865605d1d8d89acf1e51a52f3fc77af3078177544e0ad6616b50e2accfbf8a5a449fdfc38358370
6
+ metadata.gz: da46ac982d9a672b38b8161dbb7b123763fb88f34a0a196c6059cee52157076796c18973a385525cc9f00c50b4bf6ab8abcf992b6ddf6cd08e201c6482efce9d
7
+ data.tar.gz: da78b9bf29944292f4221ab5dd0dd4cca0c03d0398f164f03b391cf29164b29fc1fbb437851b822411e24297ca31473f6feebf7e175ff83a582d5b191badf4a2
data/bin/cfn_nag CHANGED
@@ -1,19 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'trollop'
3
- require 'cfn_nag'
3
+ require 'cfn-nag'
4
4
  require 'logging'
5
+ require 'json'
5
6
 
6
7
  opts = Trollop::options do
7
- opt :input_json_path, 'CloudFormation template to nag on or directory of templates - all *.json and *.template recursively', type: :io, required: true
8
- opt :output_format, 'Format of results: [txt, json]', type: :string, default: 'txt'
8
+ usage '[options] <cloudformation template path ...>|<cloudformation template in STDIN>'
9
+
9
10
  opt :debug, 'Enable debug output', type: :boolean, required: false, default: false
10
- opt :rule_directory, 'Extra rule directories', type: :strings, required: false, default: [], multi: true
11
+ opt :rule_directory, 'Extra rule directory', type: :io, required: false, default: nil
11
12
  opt :profile_path, 'Path to a profile file', type: :io, required: false, default: nil
12
13
  end
13
14
 
14
- Trollop::die(:output_format,
15
- 'Must be txt or json') unless %w(txt json).include?(opts[:output_format])
16
-
17
15
  CfnNag::configure_logging(opts)
18
16
 
19
17
  profile_definition = nil
@@ -21,8 +19,26 @@ unless opts[:profile_path].nil?
21
19
  profile_definition = IO.read(opts[:profile_path])
22
20
  end
23
21
 
24
- cfn_nag = CfnNag.new(profile_definition: profile_definition)
22
+ cfn_nag = CfnNag.new(profile_definition: profile_definition,
23
+ rule_directory: opts[:rule_directory])
24
+
25
+ # trollop appears to pop args off of ARGV
26
+ # ARGF concatenates which we don't want
27
+ if ARGV.size == 0
28
+ results = cfn_nag.audit(cloudformation_string: STDIN.read)
29
+
30
+ results[:violations] = results[:violations].map { |violation| violation.to_h }
31
+ puts JSON.pretty_generate(results)
32
+ exit results[:failure_count]
33
+ else
34
+ total_failure_count = 0
35
+ ARGV.each do |file_name|
36
+ results = cfn_nag.audit(cloudformation_string: IO.read(file_name))
37
+
38
+ total_failure_count += results[:failure_count]
39
+ results[:violations] = results[:violations].map { |violation| violation.to_h }
40
+ puts JSON.pretty_generate(results)
41
+ end
42
+ exit total_failure_count
43
+ end
25
44
 
26
- exit cfn_nag.audit(input_json_path: opts[:input_json_path],
27
- output_format: opts[:output_format],
28
- rule_directories: opts[:rule_directory])
data/bin/cfn_nag_rules CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'trollop'
3
- require 'cfn_nag'
3
+ require 'cfn-nag'
4
4
 
5
5
  opts = Trollop::options do
6
- opt :rule_directory, 'Extra rule directories', type: :strings, required: false, default: [], multi: true
6
+ opt :rule_directory, 'Extra rule directories', type: :io, required: false, default: nil
7
7
  opt :profile_path, 'Path to a profile file', type: :io, required: false, default: nil
8
8
  end
9
9
 
@@ -12,6 +12,7 @@ unless opts[:profile_path].nil?
12
12
  profile_definition = IO.read(opts[:profile_path])
13
13
  end
14
14
 
15
- cfn_nag = CfnNag.new(profile_definition: profile_definition)
15
+ rule_dumper = CfnNagRuleDumper.new(profile_definition: profile_definition,
16
+ rule_directory: opts[:rule_directory])
16
17
 
17
- cfn_nag.dump_rules(rule_directories: opts[:rule_directory])
18
+ rule_dumper.dump_rules
data/bin/cfn_nag_scan ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ require 'trollop'
3
+ require 'cfn-nag'
4
+ require 'logging'
5
+ require 'json'
6
+
7
+ opts = Trollop::options do
8
+ opt :input_path, 'CloudFormation template to nag on or directory of templates - all *.json, *.yaml, *.yml and *.template recursively', type: :io, required: true
9
+ opt :output_format, 'Format of results: [txt, json]', type: :string, default: 'txt'
10
+ opt :debug, 'Enable debug output', type: :boolean, required: false, default: false
11
+ opt :rule_directory, 'Extra rule directory', type: :io, required: false, default: nil
12
+ opt :profile_path, 'Path to a profile file', type: :io, required: false, default: nil
13
+ end
14
+
15
+ Trollop::die(:output_format,
16
+ 'Must be txt or json') unless %w(txt json).include?(opts[:output_format])
17
+
18
+ CfnNag::configure_logging(opts)
19
+
20
+ profile_definition = nil
21
+ unless opts[:profile_path].nil?
22
+ profile_definition = IO.read(opts[:profile_path])
23
+ end
24
+
25
+ cfn_nag = CfnNag.new(profile_definition: profile_definition,
26
+ rule_directory: opts[:rule_directory])
27
+
28
+ exit cfn_nag.audit_aggregate_across_files_and_render_results(input_path: opts[:input_path],
29
+ output_format: opts[:output_format])
data/lib/cfn-nag.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'cfn-nag/cfn_nag'
2
+ require 'cfn-nag/violation'
3
+ require 'cfn-nag/rule_dumper'
@@ -0,0 +1,115 @@
1
+ require_relative 'custom_rule_loader'
2
+ require_relative 'rule_registry'
3
+ require_relative 'profile_loader'
4
+ require_relative 'template_discovery'
5
+ require_relative 'result_view/simple_stdout_results'
6
+ require_relative 'result_view/json_results'
7
+ require 'cfn-model'
8
+ require 'logging'
9
+
10
+ class CfnNag
11
+ def initialize(profile_definition: nil,
12
+ rule_directory: nil)
13
+ @rule_directory = rule_directory
14
+ @custom_rule_loader = CustomRuleLoader.new(rule_directory: rule_directory)
15
+ @profile_definition = profile_definition
16
+ end
17
+
18
+ ##
19
+ # Given a file or directory path, emit aggregate results to stdout
20
+ #
21
+ # Return an aggregate failure count (for exit code usage)
22
+ #
23
+ def audit_aggregate_across_files_and_render_results(input_path:,
24
+ output_format:'txt')
25
+ aggregate_results = audit_aggregate_across_files input_path: input_path
26
+
27
+ render_results(aggregate_results: aggregate_results,
28
+ output_format: output_format)
29
+
30
+ aggregate_results.inject(0) do |total_failure_count, results|
31
+ total_failure_count + results[:file_results][:failure_count]
32
+ end
33
+ end
34
+
35
+ ##
36
+ # Given a file or directory path, return aggregate results
37
+ #
38
+ def audit_aggregate_across_files(input_path:)
39
+ templates = TemplateDiscovery.new.discover_templates(input_path)
40
+
41
+ aggregate_results = []
42
+ templates.each do |template|
43
+ aggregate_results << {
44
+ filename: template,
45
+ file_results: audit(cloudformation_string: IO.read(template))
46
+ }
47
+ end
48
+ aggregate_results
49
+ end
50
+
51
+ ##
52
+ # Given cloudformation json/yml, run all the rules against it
53
+ #
54
+ # Return a hash with failure count
55
+ #
56
+ def audit(cloudformation_string:)
57
+ stop_processing = false
58
+ violations = []
59
+
60
+ begin
61
+ cfn_model = CfnParser.new.parse cloudformation_string
62
+ rescue ParserError => parser_error
63
+ violations << Violation.new(id: 'FATAL',
64
+ type: Violation::FAILING_VIOLATION,
65
+ message: parser_error.to_s)
66
+ stop_processing = true
67
+ end
68
+
69
+ violations += @custom_rule_loader.execute_custom_rules(cfn_model) unless stop_processing == true
70
+
71
+ violations = filter_violations_by_profile violations unless stop_processing == true
72
+
73
+ {
74
+ failure_count: Violation.count_failures(violations),
75
+ violations: violations
76
+ }
77
+ end
78
+
79
+ def self.configure_logging(opts)
80
+ logger = Logging.logger['log']
81
+ if opts[:debug]
82
+ logger.level = :debug
83
+ else
84
+ logger.level = :info
85
+ end
86
+
87
+ logger.add_appenders Logging.appenders.stdout
88
+ end
89
+
90
+ private
91
+
92
+ def filter_violations_by_profile(violations)
93
+ profile = nil
94
+ unless @profile_definition.nil?
95
+ profile = ProfileLoader.new(@custom_rule_loader.rule_definitions).load(profile_definition: @profile_definition)
96
+ end
97
+
98
+ violations.reject do |violation|
99
+ not profile.nil? and not profile.execute_rule?(violation.id)
100
+ end
101
+ end
102
+
103
+ def render_results(aggregate_results:,
104
+ output_format:)
105
+ results_renderer(output_format).new.render(aggregate_results)
106
+ end
107
+
108
+ def results_renderer(output_format)
109
+ registry = {
110
+ 'txt' => SimpleStdoutResults,
111
+ 'json' => JsonResults
112
+ }
113
+ registry[output_format]
114
+ end
115
+ end
@@ -0,0 +1,72 @@
1
+ require 'cfn-model'
2
+ require 'logging'
3
+ require_relative 'rule_registry'
4
+
5
+ ##
6
+ # This object can discover the internal and custom user-provided rules and
7
+ # apply these rules to a CfnModel object
8
+ #
9
+ class CustomRuleLoader
10
+ def initialize(rule_directory: nil)
11
+ @rule_directory = rule_directory
12
+ validate_extra_rule_directory rule_directory
13
+ end
14
+
15
+ def rule_definitions
16
+ rule_registry = RuleRegistry.new
17
+
18
+ discover_rule_classes(@rule_directory).each do |rule_class|
19
+ rule = rule_class.new
20
+ rule_registry.definition(id: rule.rule_id,
21
+ type: rule.rule_type,
22
+ message: rule.rule_text)
23
+ end
24
+ rule_registry
25
+ end
26
+
27
+ def execute_custom_rules(cfn_model)
28
+ Logging.logger['log'].debug "cfn_model: #{cfn_model}"
29
+
30
+ violations = []
31
+
32
+ discover_rule_classes(@rule_directory).each do |rule_class|
33
+ audit_result = rule_class.new.audit(cfn_model)
34
+ violations << audit_result unless audit_result.nil?
35
+ end
36
+ violations
37
+ end
38
+
39
+ private
40
+
41
+ def validate_extra_rule_directory(rule_directory)
42
+ unless rule_directory.nil?
43
+ fail "Not a real directory #{rule_directory}" unless File.directory? rule_directory
44
+ end
45
+ end
46
+
47
+ def discover_rule_filenames(rule_directory)
48
+ rule_filenames = []
49
+ unless rule_directory.nil?
50
+ rule_filenames += Dir[File.join(rule_directory, '*Rule.rb')].sort
51
+ end
52
+ rule_filenames += Dir[File.join(__dir__, 'custom_rules', '*Rule.rb')].sort
53
+ Logging.logger['log'].debug "rule_filenames: #{rule_filenames}"
54
+ rule_filenames
55
+ end
56
+
57
+ def discover_rule_classes(rule_directory)
58
+ rule_classes = []
59
+
60
+ rule_filenames = discover_rule_filenames(rule_directory)
61
+
62
+ rule_filenames.each do |rule_filename|
63
+ require(rule_filename)
64
+ rule_classname = File.basename(rule_filename, '.rb')
65
+
66
+ rule_classes << Object.const_get(rule_classname)
67
+ end
68
+ Logging.logger['log'].debug "rule_classes: #{rule_classes}"
69
+
70
+ rule_classes
71
+ end
72
+ end
@@ -0,0 +1,28 @@
1
+ require 'cfn-nag/violation'
2
+ require_relative 'base'
3
+
4
+ class CloudFormationAuthenticationRule < BaseRule
5
+ def rule_text
6
+ 'Specifying credentials in the template itself is probably not the safest thing'
7
+ end
8
+
9
+ def rule_type
10
+ Violation::WARNING
11
+ end
12
+
13
+ def rule_id
14
+ 'W1'
15
+ end
16
+
17
+ def audit_impl(cfn_model)
18
+ logical_resource_ids = []
19
+ cfn_model.raw_model['Resources'].each do |resource_name, resource|
20
+ unless resource['Metadata'].nil?
21
+ if !resource['Metadata']['AWS::CloudFormation::Authentication'].nil?
22
+ logical_resource_ids << resource_name
23
+ end
24
+ end
25
+ end
26
+ logical_resource_ids
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require 'cfn-nag/violation'
2
+ require_relative 'base'
3
+
4
+ class CloudFrontDistributionAccessLoggingRule < BaseRule
5
+ def rule_text
6
+ 'CloudFront Distribution should enable access logging'
7
+ end
8
+
9
+ def rule_type
10
+ Violation::WARNING
11
+ end
12
+
13
+ def rule_id
14
+ 'W10'
15
+ end
16
+
17
+ def audit_impl(cfn_model)
18
+ violating_distributions = cfn_model.resources_by_type('AWS::CloudFront::Distribution').select do |distribution|
19
+ distribution.distributionConfig['Logging'].nil?
20
+ end
21
+
22
+ violating_distributions.map { |distribution| distribution.logical_resource_id }
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'cfn-nag/violation'
2
+ require_relative 'base'
3
+
4
+ class EbsVolumeHasSseRule < BaseRule
5
+ def rule_text
6
+ 'EBS volume should have server-side encryption enabled'
7
+ end
8
+
9
+ def rule_type
10
+ Violation::FAILING_VIOLATION
11
+ end
12
+
13
+ def rule_id
14
+ 'F1'
15
+ end
16
+
17
+ def audit_impl(cfn_model)
18
+ violating_volumes = cfn_model.resources_by_type('AWS::EC2::Volume').select do |volume|
19
+ volume.encrypted.nil? || volume.encrypted == false
20
+ end
21
+
22
+ violating_volumes.map { |violating_user| violating_user.logical_resource_id }
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'cfn-nag/violation'
2
+ require_relative 'base'
3
+
4
+ class ElasticLoadBalancerAccessLoggingRule < BaseRule
5
+ def rule_text
6
+ 'Elastic Load Balancer should have access logging enabled'
7
+ end
8
+
9
+ def rule_type
10
+ Violation::WARNING
11
+ end
12
+
13
+ def rule_id
14
+ 'W26'
15
+ end
16
+
17
+ def audit_impl(cfn_model)
18
+ violating_elbs = cfn_model.resources_by_type('AWS::ElasticLoadBalancing::LoadBalancer').select do |elb|
19
+ elb.accessLoggingPolicy.nil? || elb.accessLoggingPolicy['Enabled'] != true
20
+ end
21
+
22
+ violating_elbs.map { |violating_user| violating_user.logical_resource_id }
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ require 'cfn-nag/violation'
2
+ require_relative 'base'
3
+
4
+ class IamManagedPolicyNotActionRule < BaseRule
5
+
6
+ def rule_text
7
+ 'IAM managed policy should not allow Allow+NotAction'
8
+ end
9
+
10
+ def rule_type
11
+ Violation::WARNING
12
+ end
13
+
14
+ def rule_id
15
+ 'W17'
16
+ end
17
+
18
+ def audit_impl(cfn_model)
19
+ violating_policies = cfn_model.resources_by_type('AWS::IAM::ManagedPolicy').select do |policy|
20
+ !policy.policyDocument.allows_not_action.empty?
21
+ end
22
+
23
+ violating_policies.map { |policy| policy.logical_resource_id }
24
+ end
25
+ end