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.
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
@@ -1,25 +0,0 @@
1
- class S3BucketPolicy
2
- attr_accessor :logical_resource_id
3
-
4
- attr_reader :statements
5
-
6
- def initialize
7
- @statements = []
8
- end
9
-
10
- def add_statement(statement_hash)
11
- statements << statement_hash
12
- end
13
-
14
- def self.condition_includes?(statement, condition_hash)
15
- if statement['Condition'].nil?
16
- false
17
- else
18
- if statement['Condition'].is_a? Hash
19
- statement['Condition'] == condition_hash
20
- else
21
- statement['Condition'].include? condition_hash
22
- end
23
- end
24
- end
25
- end
@@ -1,28 +0,0 @@
1
- require_relative 's3_bucket_policy'
2
-
3
-
4
- class S3BucketPolicyParser
5
-
6
- def parse(resource_name, resource_json)
7
- properties = resource_json['Properties']
8
- bucket_policy = S3BucketPolicy.new
9
-
10
- bucket_policy.logical_resource_id = resource_name
11
-
12
- unless properties.nil?
13
- policy_document = properties['PolicyDocument']
14
- unless policy_document.nil?
15
- unless policy_document['Statement'].nil?
16
- if policy_document['Statement'].is_a? Array
17
- policy_document['Statement'].each { |statement | bucket_policy.add_statement(statement)}
18
- else
19
- bucket_policy.add_statement(policy_document['Statement'])
20
- end
21
- end
22
- end
23
- end
24
-
25
- bucket_policy
26
- end
27
- end
28
-
@@ -1,59 +0,0 @@
1
- require_relative 'cfn_model'
2
-
3
- class SecurityGroupParser
4
-
5
- # precondition: properties are actually there... other validator takes care
6
- def parse(resource_name, resource_json)
7
- properties = resource_json['Properties']
8
- security_group = SecurityGroup.new
9
-
10
- parse_ingress_rules(security_group, properties)
11
-
12
- parse_egress_rules(security_group, properties)
13
-
14
- security_group.vpc_id = properties['VpcId']
15
- security_group.group_description = properties['GroupDescription']
16
- security_group.logical_resource_id = resource_name
17
-
18
- security_group
19
- end
20
-
21
- private
22
-
23
- def parse_ingress_rules(security_group, properties)
24
- unless properties['SecurityGroupIngress'].nil?
25
- if properties['SecurityGroupIngress'].is_a? Array
26
- properties['SecurityGroupIngress'].each do |ingress_json|
27
- security_group.add_ingress_rule ingress_json
28
- end
29
- elsif properties['SecurityGroupIngress'].is_a? Hash
30
- security_group.add_ingress_rule properties['SecurityGroupIngress']
31
- end
32
- end
33
- end
34
-
35
- def parse_egress_rules(security_group, properties)
36
- unless properties['SecurityGroupEgress'].nil?
37
- if properties['SecurityGroupEgress'].is_a? Array
38
- properties['SecurityGroupEgress'].each do |egress_json|
39
- security_group.add_egress_rule egress_json
40
- end
41
- elsif properties['SecurityGroupEgress'].is_a? Hash
42
- security_group.add_egress_rule properties['SecurityGroupEgress']
43
- end
44
- end
45
- end
46
-
47
- end
48
-
49
- class SecurityGroupXgressParser
50
-
51
- def parse(resource_name, resource_json)
52
- xgress = {}
53
- xgress['logical_resource_id'] = resource_name
54
- resource_json['Properties'].each_pair do |key, value|
55
- xgress[key] = value
56
- end
57
- xgress
58
- end
59
- end
data/lib/rule.rb DELETED
@@ -1,208 +0,0 @@
1
- require 'logging'
2
- require_relative 'violation'
3
-
4
- module Rule
5
- attr_accessor :input_json
6
-
7
- # jq preamble to spit out Resources but as an array of key-value pairs
8
- # can be used in jq rule definition but... this is probably reducing replication at the cost of opaqueness
9
- def resources
10
- '.Resources|with_entries(.value.LogicalResourceId = .key)[]'
11
- end
12
-
13
- # jq to filter CloudFormation resources by Type
14
- # can be used in jq rule definition but... this is probably reducing replication at the cost of opaqueness
15
- def resources_by_type(resource)
16
- "#{resources}| select(.Type == \"#{resource}\")"
17
- end
18
-
19
- def warning(id:, jq:, message:)
20
- warning_def = @rule_registry.definition(id: id,
21
- type: Violation::WARNING,
22
- message: message)
23
-
24
- return if @stop_processing
25
-
26
- Logging.logger['log'].debug jq
27
-
28
- stdout = jq_command(@input_json, jq)
29
- result = $?.exitstatus
30
- scrape_jq_output_for_error(jq, stdout)
31
-
32
- resource_ids = parse_logical_resource_ids(stdout)
33
- new_warnings = resource_ids.size
34
- if result == 0 and new_warnings > 0
35
- add_violation(id: warning_def.id,
36
- type: Violation::WARNING,
37
- message: message,
38
- logical_resource_ids: resource_ids)
39
- end
40
- end
41
-
42
- def raw_fatal_assertion(id:, jq:, message:)
43
- failing_rule(id: id,
44
- jq_expression: jq,
45
- fail_if_found: false,
46
- fatal: true,
47
- message: message,
48
- raw: true)
49
- end
50
-
51
- def fatal_assertion(id:, jq:, message:)
52
- failing_rule(id: id,
53
- jq_expression: jq,
54
- fail_if_found: false,
55
- fatal: true,
56
- message: message)
57
- end
58
-
59
- def raw_fatal_violation(id:, jq:, message:)
60
- failing_rule(id: id,
61
- jq_expression: jq,
62
- fail_if_found: true,
63
- fatal: true,
64
- message: message,
65
- raw: true)
66
- end
67
-
68
- def fatal_violation(id:, jq:, message:)
69
- failing_rule(id: id,
70
- jq_expression: jq,
71
- fail_if_found: true,
72
- fatal: true,
73
- message: message)
74
- end
75
-
76
- def violation(id:, jq:, message:)
77
- failing_rule(id: id,
78
- jq_expression: jq,
79
- fail_if_found: true,
80
- message: message)
81
- end
82
-
83
- def assertion(id:, jq:, message:)
84
- failing_rule(id: id,
85
- jq_expression: jq,
86
- fail_if_found: false,
87
- message: message)
88
- end
89
-
90
- def self.empty?(array)
91
- array.nil? or array.size ==0
92
- end
93
-
94
- def self.count_warnings(violations)
95
- violations.inject(0) do |count, violation|
96
- if violation.type == Violation::WARNING
97
- if empty?(violation.logical_resource_ids)
98
- count += 1
99
- else
100
- count += violation.logical_resource_ids.size
101
- end
102
- end
103
- count
104
- end
105
- end
106
-
107
- def self.count_failures(violations)
108
- violations.inject(0) do |count, violation|
109
- if violation.type == Violation::FAILING_VIOLATION
110
- if empty?(violation.logical_resource_ids)
111
- count += 1
112
- else
113
- count += violation.logical_resource_ids.size
114
- end
115
- end
116
- count
117
- end
118
- end
119
-
120
- # this is to record a violation as opposed to "registering" a violation
121
- #
122
- # not super keen on this looking at it after the fact.... @violations
123
- # will have to live in the object this Rule is mixed into i.e. CfnNag
124
- def add_violation(id:,
125
- type:,
126
- message:,
127
- logical_resource_ids: nil,
128
- violating_code: nil)
129
- violation = Violation.new(id: id,
130
- type: type,
131
- message: message,
132
- logical_resource_ids: logical_resource_ids,
133
- violating_code: violating_code)
134
- @violations << violation
135
- end
136
-
137
- private
138
-
139
- def parse_logical_resource_ids(stdout)
140
- JSON.load(stdout)
141
- end
142
-
143
- def scrape_jq_output_for_error(command, stdout)
144
- fail "jq rule is likely not correct: #{command}\n\n#{stdout}" if stdout.include? 'jq: error'
145
- end
146
-
147
- # fail_if_found: this is false for an assertion, true for a violation. either way this rule ups the "failure" count
148
- #
149
- # raw: don't try to parse the output in any way. the rule is some kind of oddball so just show what matched and up
150
- # failure count by 1
151
- #
152
- # fatal: if true, any match of the rule causes immediate shutdown to avoid more complicated downstream error checking
153
- def failing_rule(id:,
154
- jq_expression:,
155
- fail_if_found:,
156
- message:,
157
- fatal: false,
158
- raw: false)
159
-
160
- fail_def = @rule_registry.definition(id: id,
161
- type: Violation::FAILING_VIOLATION,
162
- message: message)
163
-
164
- return if @stop_processing
165
-
166
- Logging.logger['log'].debug jq_expression
167
-
168
- stdout = jq_command(@input_json, jq_expression)
169
- result = $?.exitstatus
170
- scrape_jq_output_for_error(jq_expression, stdout)
171
- if (fail_if_found and result == 0) or
172
- (not fail_if_found and result != 0)
173
-
174
- if raw
175
- add_violation(id: fail_def.id,
176
- type: Violation::FAILING_VIOLATION,
177
- message: message,
178
- violating_code: stdout)
179
-
180
- if fatal
181
- @stop_processing = true
182
- end
183
- else
184
- resource_ids = parse_logical_resource_ids(stdout)
185
-
186
- if resource_ids.size > 0
187
- add_violation(id: fail_def.id,
188
- type: Violation::FAILING_VIOLATION,
189
- message: message,
190
- logical_resource_ids: resource_ids)
191
-
192
- if fatal
193
- @stop_processing = true
194
- end
195
- end
196
- end
197
- end
198
- end
199
-
200
- # the -e will return an exit code
201
- def jq_command(input_json, jq_expression)
202
- IO.popen(['jq', jq_expression, '-e'], 'r+', {:err=>[:child, :out]}) do |pipe|
203
- pipe << input_json
204
- pipe.close_write
205
- pipe.readlines.join
206
- end
207
- end
208
- end
data/lib/rule_registry.rb DELETED
@@ -1,45 +0,0 @@
1
- require_relative 'violation'
2
-
3
- class RuleRegistry
4
-
5
- attr_reader :rules
6
-
7
- def initialize
8
- @rules = []
9
- end
10
-
11
- def definition(id:,
12
- type:,
13
- message:)
14
- violation_def = Violation.new(id: id,
15
- type: type,
16
- message: message)
17
- existing_def = @rules.find { |definition| definition == violation_def }
18
-
19
- if existing_def.nil?
20
- add_rule violation_def
21
- else
22
- existing_def
23
- end
24
- end
25
-
26
- # FATAL applies to multiple rules
27
- def by_id(id)
28
- @rules.select { |rule| rule.id == id }
29
- end
30
-
31
- def warnings
32
- @rules.select { |rule| rule.type == Violation::WARNING }
33
- end
34
-
35
- def failings
36
- @rules.select { |rule| rule.type == Violation::FAILING_VIOLATION }
37
- end
38
-
39
- private
40
-
41
- def add_rule(violation_def)
42
- @rules << violation_def
43
- violation_def
44
- end
45
- end
data/lib/violation.rb DELETED
@@ -1,41 +0,0 @@
1
- require 'json'
2
-
3
- class Violation
4
- WARNING = 'WARN'
5
- FAILING_VIOLATION = 'FAIL'
6
-
7
- attr_reader :id, :type, :message, :logical_resource_ids, :violating_code
8
-
9
- def initialize(id:,
10
- type:,
11
- message:,
12
- logical_resource_ids: nil,
13
- violating_code: nil)
14
- @id = id
15
- @type = type
16
- @message = message
17
- @logical_resource_ids = logical_resource_ids
18
- @violating_code = violating_code
19
-
20
- fail if @type.nil?
21
- fail if @message.nil?
22
- end
23
-
24
- def to_s
25
- puts "#{@id} #{@type} #{@message} #{@logical_resource_ids} #{@violating_code}"
26
- end
27
-
28
- def to_h
29
- {
30
- id: @id,
31
- type: @type,
32
- message: @message,
33
- logical_resource_ids: @logical_resource_ids,
34
- violating_code: @violating_code
35
- }
36
- end
37
-
38
- def ==(other_violation)
39
- other_violation.class == self.class && other_violation.to_h == to_h
40
- end
41
- end