inspec 4.22.1

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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +63 -0
  3. data/inspec.gemspec +36 -0
  4. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/Gemfile +11 -0
  5. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/inspec-plugin-template.gemspec +43 -0
  6. data/lib/plugins/inspec-init/templates/profiles/aws/README.md +192 -0
  7. data/lib/plugins/inspec-init/templates/profiles/aws/attributes.yml +2 -0
  8. data/lib/plugins/inspec-init/templates/profiles/aws/controls/example.rb +39 -0
  9. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +22 -0
  10. data/lib/plugins/inspec-init/templates/profiles/azure/README.md +56 -0
  11. data/lib/plugins/inspec-init/templates/profiles/azure/controls/example.rb +14 -0
  12. data/lib/plugins/inspec-init/templates/profiles/azure/inspec.yml +14 -0
  13. data/lib/plugins/inspec-init/templates/profiles/gcp/README.md +66 -0
  14. data/lib/plugins/inspec-init/templates/profiles/gcp/attributes.yml +2 -0
  15. data/lib/plugins/inspec-init/templates/profiles/gcp/controls/example.rb +27 -0
  16. data/lib/plugins/inspec-init/templates/profiles/gcp/inspec.yml +19 -0
  17. data/lib/resource_support/aws.rb +76 -0
  18. data/lib/resource_support/aws/aws_backend_base.rb +12 -0
  19. data/lib/resource_support/aws/aws_backend_factory_mixin.rb +12 -0
  20. data/lib/resource_support/aws/aws_plural_resource_mixin.rb +24 -0
  21. data/lib/resource_support/aws/aws_resource_mixin.rb +69 -0
  22. data/lib/resource_support/aws/aws_singular_resource_mixin.rb +27 -0
  23. data/lib/resources/aws/aws_billing_report.rb +107 -0
  24. data/lib/resources/aws/aws_billing_reports.rb +74 -0
  25. data/lib/resources/aws/aws_cloudtrail_trail.rb +97 -0
  26. data/lib/resources/aws/aws_cloudtrail_trails.rb +51 -0
  27. data/lib/resources/aws/aws_cloudwatch_alarm.rb +67 -0
  28. data/lib/resources/aws/aws_cloudwatch_log_metric_filter.rb +105 -0
  29. data/lib/resources/aws/aws_config_delivery_channel.rb +74 -0
  30. data/lib/resources/aws/aws_config_recorder.rb +99 -0
  31. data/lib/resources/aws/aws_ebs_volume.rb +127 -0
  32. data/lib/resources/aws/aws_ebs_volumes.rb +69 -0
  33. data/lib/resources/aws/aws_ec2_instance.rb +162 -0
  34. data/lib/resources/aws/aws_ec2_instances.rb +69 -0
  35. data/lib/resources/aws/aws_ecs_cluster.rb +88 -0
  36. data/lib/resources/aws/aws_eks_cluster.rb +105 -0
  37. data/lib/resources/aws/aws_elb.rb +85 -0
  38. data/lib/resources/aws/aws_elbs.rb +84 -0
  39. data/lib/resources/aws/aws_flow_log.rb +106 -0
  40. data/lib/resources/aws/aws_iam_access_key.rb +112 -0
  41. data/lib/resources/aws/aws_iam_access_keys.rb +153 -0
  42. data/lib/resources/aws/aws_iam_group.rb +62 -0
  43. data/lib/resources/aws/aws_iam_groups.rb +56 -0
  44. data/lib/resources/aws/aws_iam_password_policy.rb +121 -0
  45. data/lib/resources/aws/aws_iam_policies.rb +57 -0
  46. data/lib/resources/aws/aws_iam_policy.rb +311 -0
  47. data/lib/resources/aws/aws_iam_role.rb +60 -0
  48. data/lib/resources/aws/aws_iam_root_user.rb +82 -0
  49. data/lib/resources/aws/aws_iam_user.rb +145 -0
  50. data/lib/resources/aws/aws_iam_users.rb +160 -0
  51. data/lib/resources/aws/aws_kms_key.rb +100 -0
  52. data/lib/resources/aws/aws_kms_keys.rb +58 -0
  53. data/lib/resources/aws/aws_rds_instance.rb +74 -0
  54. data/lib/resources/aws/aws_route_table.rb +67 -0
  55. data/lib/resources/aws/aws_route_tables.rb +64 -0
  56. data/lib/resources/aws/aws_s3_bucket.rb +142 -0
  57. data/lib/resources/aws/aws_s3_bucket_object.rb +87 -0
  58. data/lib/resources/aws/aws_s3_buckets.rb +52 -0
  59. data/lib/resources/aws/aws_security_group.rb +314 -0
  60. data/lib/resources/aws/aws_security_groups.rb +71 -0
  61. data/lib/resources/aws/aws_sns_subscription.rb +82 -0
  62. data/lib/resources/aws/aws_sns_topic.rb +57 -0
  63. data/lib/resources/aws/aws_sns_topics.rb +60 -0
  64. data/lib/resources/aws/aws_sqs_queue.rb +66 -0
  65. data/lib/resources/aws/aws_subnet.rb +92 -0
  66. data/lib/resources/aws/aws_subnets.rb +56 -0
  67. data/lib/resources/aws/aws_vpc.rb +77 -0
  68. data/lib/resources/aws/aws_vpcs.rb +55 -0
  69. data/lib/resources/azure/azure_backend.rb +379 -0
  70. data/lib/resources/azure/azure_generic_resource.rb +55 -0
  71. data/lib/resources/azure/azure_resource_group.rb +151 -0
  72. data/lib/resources/azure/azure_virtual_machine.rb +262 -0
  73. data/lib/resources/azure/azure_virtual_machine_data_disk.rb +131 -0
  74. metadata +202 -0
@@ -0,0 +1,314 @@
1
+ require "set"
2
+ require "ipaddr"
3
+
4
+ require "resource_support/aws/aws_singular_resource_mixin"
5
+ require "resource_support/aws/aws_backend_base"
6
+ require "aws-sdk-ec2"
7
+
8
+ class AwsSecurityGroup < Inspec.resource(1)
9
+ name "aws_security_group"
10
+ desc "Verifies settings for an individual AWS Security Group."
11
+ example <<~EXAMPLE
12
+ describe aws_security_group('sg-12345678') do
13
+ it { should exist }
14
+ end
15
+ EXAMPLE
16
+ supports platform: "aws"
17
+
18
+ include AwsSingularResourceMixin
19
+ attr_reader :description, :group_id, :group_name, :vpc_id, :inbound_rules, :outbound_rules, :inbound_rules_count, :outbound_rules_count
20
+
21
+ def to_s
22
+ "EC2 Security Group #{@group_id}"
23
+ end
24
+
25
+ def allow_in?(criteria = {})
26
+ allow(inbound_rules, criteria.dup)
27
+ end
28
+ RSpec::Matchers.alias_matcher :allow_in, :be_allow_in
29
+
30
+ def allow_out?(criteria = {})
31
+ allow(outbound_rules, criteria.dup)
32
+ end
33
+ RSpec::Matchers.alias_matcher :allow_out, :be_allow_out
34
+
35
+ def allow_in_only?(criteria = {})
36
+ allow_only(inbound_rules, criteria.dup)
37
+ end
38
+ RSpec::Matchers.alias_matcher :allow_in_only, :be_allow_in_only
39
+
40
+ def allow_out_only?(criteria = {})
41
+ allow_only(outbound_rules, criteria.dup)
42
+ end
43
+ RSpec::Matchers.alias_matcher :allow_out_only, :be_allow_out_only
44
+
45
+ private
46
+
47
+ def allow_only(rules, criteria)
48
+ rules = allow__focus_on_position(rules, criteria)
49
+ # allow_{in_out}_only require either a single-rule group, or you
50
+ # to select a rule using position.
51
+ return false unless rules.count == 1 || criteria.key?(:position)
52
+
53
+ if criteria.key?(:security_group)
54
+ if criteria.key?(:position)
55
+ pos = criteria[:position] - 1
56
+ else
57
+ pos = 0
58
+ end
59
+ return false unless rules[pos].key?(:user_id_group_pairs) && rules[pos][:user_id_group_pairs].count == 1
60
+ end
61
+ criteria[:exact] = true
62
+ allow(rules, criteria)
63
+ end
64
+
65
+ def allow(rules, criteria)
66
+ criteria = allow__check_criteria(criteria)
67
+ rules = allow__focus_on_position(rules, criteria)
68
+
69
+ rules.any? do |rule|
70
+ matched = true
71
+ matched &&= allow__match_port(rule, criteria)
72
+ matched &&= allow__match_protocol(rule, criteria)
73
+ matched &&= allow__match_ipv4_range(rule, criteria)
74
+ matched &&= allow__match_ipv6_range(rule, criteria)
75
+ matched &&= allow__match_security_group(rule, criteria)
76
+ matched
77
+ end
78
+ end
79
+
80
+ def allow__check_criteria(raw_criteria)
81
+ allowed_criteria = [
82
+ :from_port,
83
+ :ipv4_range,
84
+ :ipv6_range,
85
+ :security_group,
86
+ :port,
87
+ :position,
88
+ :protocol,
89
+ :to_port,
90
+ :exact, # Internal
91
+ ]
92
+ recognized_criteria = {}
93
+ allowed_criteria.each do |expected_criterion|
94
+ if raw_criteria.key?(expected_criterion)
95
+ recognized_criteria[expected_criterion] = raw_criteria.delete(expected_criterion)
96
+ end
97
+ end
98
+
99
+ # Any leftovers are unwelcome
100
+ unless raw_criteria.empty?
101
+ raise ArgumentError, "Unrecognized security group rule 'allow' criteria '#{raw_criteria.keys.join(",")}'. Expected criteria: #{allowed_criteria.join(", ")}"
102
+ end
103
+
104
+ recognized_criteria
105
+ end
106
+
107
+ def allow__focus_on_position(rules, criteria)
108
+ return rules unless criteria.key?(:position)
109
+
110
+ idx = criteria.delete(:position)
111
+
112
+ # Normalize to a zero-based numeric index
113
+ case # rubocop: disable Style/EmptyCaseCondition
114
+ when idx.is_a?(Symbol) && idx == :first
115
+ idx = 0
116
+ when idx.is_a?(Symbol) && idx == :last
117
+ idx = rules.count - 1
118
+ when idx.is_a?(String)
119
+ idx = idx.to_i - 1 # We document this as 1-based, so adjust to be zero-based.
120
+ when idx.is_a?(Numeric)
121
+ idx -= 1 # We document this as 1-based, so adjust to be zero-based.
122
+ else
123
+ raise ArgumentError, "aws_security_group 'allow' 'position' criteria must be an integer or the symbols :first or :last"
124
+ end
125
+
126
+ unless idx < rules.count
127
+ raise ArgumentError, "aws_security_group 'allow' 'position' criteria #{idx + 1} is out of range - there are only #{rules.count} rules for security group #{group_id}."
128
+ end
129
+
130
+ [rules[idx]]
131
+ end
132
+
133
+ def allow__match_port(rule, criteria) # rubocop: disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
134
+ if criteria[:exact] || criteria[:from_port] || criteria[:to_port]
135
+ # Exact match mode
136
+ # :port is shorthand for a single-valued port range.
137
+ criteria[:to_port] = criteria[:from_port] = criteria[:port] if criteria[:port]
138
+ to = criteria[:to_port]
139
+ from = criteria[:from_port]
140
+ # It's a match if neither criteria was specified
141
+ return true if to.nil? && from.nil?
142
+
143
+ # Normalize to integers
144
+ to = to.to_i unless to.nil?
145
+ from = from.to_i unless from.nil?
146
+ # It's a match if either was specified and the other was not
147
+ return true if rule[:to_port] == to && from.nil?
148
+ return true if rule[:from_port] == from && to.nil?
149
+
150
+ # Finally, both must match.
151
+ rule[:to_port] == to && rule[:from_port] == from
152
+ elsif !criteria[:port]
153
+ # port not specified, match anything
154
+ true
155
+ else
156
+ # Range membership mode
157
+ rule_from = rule[:from_port] || 0
158
+ rule_to = rule[:to_port] || 65535
159
+ (rule_from..rule_to).cover?(criteria[:port].to_i)
160
+ end
161
+ end
162
+
163
+ def allow__match_protocol(rule, criteria)
164
+ return true unless criteria.key?(:protocol)
165
+
166
+ prot = criteria[:protocol]
167
+ # We provide a "fluency alias" for -1 (any).
168
+ prot = "-1" if prot == "any"
169
+
170
+ rule[:ip_protocol] == prot
171
+ end
172
+
173
+ def match_ipv4_or_6_range(rule, criteria)
174
+ if criteria.key?(:ipv4_range)
175
+ query = criteria[:ipv4_range]
176
+ query = [query] unless query.is_a?(Array)
177
+ ranges = rule[:ip_ranges].map { |rng| rng[:cidr_ip] }
178
+ else # IPv6
179
+ query = criteria[:ipv6_range]
180
+ query = [query] unless query.is_a?(Array)
181
+ ranges = rule[:ipv_6_ranges].map { |rng| rng[:cidr_ipv_6] }
182
+ end
183
+
184
+ if criteria[:exact]
185
+ Set.new(query) == Set.new(ranges)
186
+ else
187
+ # CIDR subset mode
188
+ # "Each of the provided IP ranges must be a member of one of the rule's listed IP ranges"
189
+ query.all? do |candidate|
190
+ candidate = IPAddr.new(candidate)
191
+ ranges.any? do |range|
192
+ range = IPAddr.new(range)
193
+ range.include?(candidate)
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ def allow__match_ipv4_range(rule, criteria)
200
+ return true unless criteria.key?(:ipv4_range)
201
+
202
+ match_ipv4_or_6_range(rule, criteria)
203
+ end
204
+
205
+ def allow__match_ipv6_range(rule, criteria)
206
+ return true unless criteria.key?(:ipv6_range)
207
+
208
+ match_ipv4_or_6_range(rule, criteria)
209
+ end
210
+
211
+ def allow__match_security_group(rule, criteria)
212
+ return true unless criteria.key?(:security_group)
213
+
214
+ query = criteria[:security_group]
215
+ return false unless rule[:user_id_group_pairs]
216
+
217
+ rule[:user_id_group_pairs].any? { |group| query == group[:group_id] }
218
+ end
219
+
220
+ def validate_params(raw_params)
221
+ recognized_params = check_resource_param_names(
222
+ raw_params: raw_params,
223
+ allowed_params: %i{id group_id group_name vpc_id},
224
+ allowed_scalar_name: :group_id,
225
+ allowed_scalar_type: String
226
+ )
227
+
228
+ # id is an alias for group_id
229
+ recognized_params[:group_id] = recognized_params.delete(:id) if recognized_params.key?(:id)
230
+
231
+ if recognized_params.key?(:group_id) && recognized_params[:group_id] !~ /^sg\-[0-9a-f]{8}/
232
+ raise ArgumentError, 'aws_security_group security group ID must be in the format "sg-" followed by 8 hexadecimal characters.'
233
+ end
234
+
235
+ if recognized_params.key?(:vpc_id) && recognized_params[:vpc_id] !~ /^vpc\-[0-9a-f]{8}/
236
+ raise ArgumentError, 'aws_security_group VPC ID must be in the format "vpc-" followed by 8 hexadecimal characters.'
237
+ end
238
+
239
+ validated_params = recognized_params
240
+
241
+ if validated_params.empty?
242
+ raise ArgumentError, "You must provide parameters to aws_security_group, such as group_name, group_id, or vpc_id.g_group."
243
+ end
244
+
245
+ validated_params
246
+ end
247
+
248
+ def count_sg_rules(ip_permissions)
249
+ rule_count = 0
250
+ ip_permissions.each do |ip_permission|
251
+ %i{ip_ranges ipv_6_ranges user_id_group_pairs}.each do |key|
252
+ if ip_permission.key? key
253
+ rule_count += ip_permission[key].length
254
+ end
255
+ end
256
+ end
257
+ rule_count
258
+ end
259
+
260
+ def fetch_from_api # rubocop: disable Metrics/AbcSize
261
+ backend = BackendFactory.create(inspec_runner)
262
+
263
+ # Transform into filter format expected by AWS
264
+ filters = []
265
+ %i{
266
+ description
267
+ group_id
268
+ group_name
269
+ vpc_id
270
+ }.each do |criterion_name|
271
+ instance_var = "@#{criterion_name}".to_sym
272
+ next unless instance_variable_defined?(instance_var)
273
+
274
+ val = instance_variable_get(instance_var)
275
+ next if val.nil?
276
+
277
+ filters.push(
278
+ {
279
+ name: criterion_name.to_s.tr("_", "-"),
280
+ values: [val],
281
+ }
282
+ )
283
+ end
284
+ dsg_response = backend.describe_security_groups(filters: filters)
285
+
286
+ if dsg_response.security_groups.empty?
287
+ @exists = false
288
+ @inbound_rules = []
289
+ @outbound_rules = []
290
+ return
291
+ end
292
+
293
+ @exists = true
294
+ @description = dsg_response.security_groups[0].description
295
+ @group_id = dsg_response.security_groups[0].group_id
296
+ @group_name = dsg_response.security_groups[0].group_name
297
+ @vpc_id = dsg_response.security_groups[0].vpc_id
298
+ @inbound_rules = dsg_response.security_groups[0].ip_permissions.map(&:to_h)
299
+ @inbound_rules_count = count_sg_rules(dsg_response.security_groups[0].ip_permissions.map(&:to_h))
300
+ @outbound_rules = dsg_response.security_groups[0].ip_permissions_egress.map(&:to_h)
301
+ @outbound_rules_count = count_sg_rules(dsg_response.security_groups[0].ip_permissions_egress.map(&:to_h))
302
+ end
303
+
304
+ class Backend
305
+ class AwsClientApi < AwsBackendBase
306
+ BackendFactory.set_default_backend self
307
+ self.aws_client_class = Aws::EC2::Client
308
+
309
+ def describe_security_groups(query)
310
+ aws_service_client.describe_security_groups(query)
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,71 @@
1
+ require "resource_support/aws/aws_plural_resource_mixin"
2
+ require "resource_support/aws/aws_backend_base"
3
+ require "aws-sdk-ec2"
4
+
5
+ class AwsSecurityGroups < Inspec.resource(1)
6
+ name "aws_security_groups"
7
+ desc "Verifies settings for AWS Security Groups in bulk"
8
+ example <<~EXAMPLE
9
+ # Verify that you have security groups defined
10
+ describe aws_security_groups do
11
+ it { should exist }
12
+ end
13
+
14
+ # Verify you have more than the default security group
15
+ describe aws_security_groups do
16
+ its('entries.count') { should be > 1 }
17
+ end
18
+ EXAMPLE
19
+ supports platform: "aws"
20
+
21
+ include AwsPluralResourceMixin
22
+
23
+ # Underlying FilterTable implementation.
24
+ filter = FilterTable.create
25
+ filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
26
+ filter.register_column(:group_ids, field: :group_id)
27
+ filter.install_filter_methods_on_resource(self, :table)
28
+
29
+ def to_s
30
+ "EC2 Security Groups"
31
+ end
32
+
33
+ private
34
+
35
+ def validate_params(raw_criteria)
36
+ unless raw_criteria.is_a? Hash
37
+ raise "Unrecognized criteria for fetching Security Groups. " \
38
+ "Use 'criteria: value' format."
39
+ end
40
+
41
+ # No criteria yet
42
+ unless raw_criteria.empty?
43
+ raise ArgumentError, "aws_ec2_security_groups does not currently accept resource parameters."
44
+ end
45
+
46
+ raw_criteria
47
+ end
48
+
49
+ def fetch_from_api
50
+ @table = []
51
+ backend = BackendFactory.create(inspec_runner)
52
+ backend.describe_security_groups({}).security_groups.each do |sg_info|
53
+ @table.push({
54
+ group_id: sg_info.group_id,
55
+ group_name: sg_info.group_name,
56
+ vpc_id: sg_info.vpc_id,
57
+ })
58
+ end
59
+ end
60
+
61
+ class Backend
62
+ class AwsClientApi < AwsBackendBase
63
+ BackendFactory.set_default_backend self
64
+ self.aws_client_class = Aws::EC2::Client
65
+
66
+ def describe_security_groups(query)
67
+ aws_service_client.describe_security_groups(query)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,82 @@
1
+ require "resource_support/aws/aws_singular_resource_mixin"
2
+ require "resource_support/aws/aws_backend_base"
3
+ require "aws-sdk-sns"
4
+
5
+ class AwsSnsSubscription < Inspec.resource(1)
6
+ name "aws_sns_subscription"
7
+ desc "Verifies settings for an SNS Subscription"
8
+ example <<~EXAMPLE
9
+ describe aws_sns_subscription('arn:aws:sns:us-east-1::test-topic-01:b214aff5-a2c7-438f-a753-8494493f2ff6') do
10
+ it { should_not have_raw_message_delivery }
11
+ it { should be_confirmation_authenticated }
12
+ its('owner') { should cmp '12345678' }
13
+ its('topic_arn') { should cmp 'arn:aws:sns:us-east-1::test-topic-01' }
14
+ its('endpoint') { should cmp 'arn:aws:sqs:us-east-1::test-queue-01' }
15
+ its('protocol') { should cmp 'sqs' }
16
+ end
17
+ EXAMPLE
18
+
19
+ supports platform: "aws"
20
+
21
+ include AwsSingularResourceMixin
22
+ attr_reader :arn, :owner, :raw_message_delivery, :topic_arn, :endpoint, :protocol,
23
+ :confirmation_was_authenticated, :aws_response
24
+
25
+ alias confirmation_authenticated? confirmation_was_authenticated
26
+ alias raw_message_delivery? raw_message_delivery
27
+
28
+ def has_raw_message_delivery?
29
+ raw_message_delivery
30
+ end
31
+
32
+ def to_s
33
+ "SNS Subscription #{@arn}"
34
+ end
35
+
36
+ private
37
+
38
+ def validate_params(raw_params)
39
+ validated_params = check_resource_param_names(
40
+ raw_params: raw_params,
41
+ allowed_params: [:subscription_arn],
42
+ allowed_scalar_name: :subscription_arn,
43
+ allowed_scalar_type: String
44
+ )
45
+
46
+ if validated_params.empty?
47
+ raise ArgumentError, "You must provide a subscription_arn to aws_sns_subscription."
48
+ end
49
+
50
+ validated_params
51
+ end
52
+
53
+ def fetch_from_api
54
+ backend = BackendFactory.create(inspec_runner)
55
+ catch_aws_errors do
56
+ begin
57
+ aws_response = backend.get_subscription_attributes(subscription_arn: @subscription_arn).attributes
58
+ @exists = true
59
+ @owner = aws_response["Owner"]
60
+ @raw_message_delivery = aws_response["RawMessageDelivery"].eql?("true")
61
+ @topic_arn = aws_response["TopicArn"]
62
+ @endpoint = aws_response["Endpoint"]
63
+ @protocol = aws_response["Protocol"]
64
+ @confirmation_was_authenticated = aws_response["ConfirmationWasAuthenticated"].eql?("true")
65
+ rescue Aws::SNS::Errors::NotFound
66
+ @exists = false
67
+ return
68
+ end
69
+ end
70
+ end
71
+
72
+ class Backend
73
+ class AwsClientApi < AwsBackendBase
74
+ BackendFactory.set_default_backend self
75
+ self.aws_client_class = Aws::SNS::Client
76
+
77
+ def get_subscription_attributes(criteria)
78
+ aws_service_client.get_subscription_attributes(criteria)
79
+ end
80
+ end
81
+ end
82
+ end