inspec 2.1.43 → 2.1.54

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.
@@ -28,29 +28,23 @@ class AwsConfigDeliveryChannel < Inspec.resource(1)
28
28
  allowed_scalar_type: String,
29
29
  )
30
30
 
31
- # Make sure channel_name is given as param
32
- if validated_params[:channel_name].nil?
33
- raise ArgumentError, 'You must provide a channel_name to aws_config_delivery_channel'
34
- end
35
-
36
31
  validated_params
37
32
  end
38
33
 
39
34
  def fetch_from_api
40
35
  backend = BackendFactory.create(inspec_runner)
41
- query = { delivery_channel_names: [@channel_name] }
42
- catch_aws_errors do
43
- @resp = backend.describe_delivery_channels(query)
44
- end
45
- @exists = !@resp.empty?
46
- return unless @exists
36
+ query = @channel_name ? { delivery_channel_names: [@channel_name] } : {}
37
+ response = backend.describe_delivery_channels(query)
38
+
39
+ @exists = !response.delivery_channels.empty?
40
+ return unless exists?
47
41
 
48
- @channel = @resp.delivery_channels.first.to_h
49
- @channel_name = @channel[:name]
50
- @s3_bucket_name = @channel[:s3_bucket_name]
51
- @s3_key_prefix = @channel[:s3_key_prefix]
52
- @sns_topic_arn = @channel[:sns_topic_arn]
53
- @delivery_frequency_in_hours = @channel[:config_snapshot_delivery_properties][:delivery_frequency] unless @channel[:config_snapshot_delivery_properties].nil?
42
+ channel = response.delivery_channels.first.to_h
43
+ @channel_name = channel[:name]
44
+ @s3_bucket_name = channel[:s3_bucket_name]
45
+ @s3_key_prefix = channel[:s3_key_prefix]
46
+ @sns_topic_arn = channel[:sns_topic_arn]
47
+ @delivery_frequency_in_hours = channel.dig(:config_snapshot_delivery_properties, :delivery_frequency)
54
48
  frequencies = {
55
49
  'One_Hour' => 1,
56
50
  'TwentyFour_Hours' => 24,
@@ -59,6 +53,8 @@ class AwsConfigDeliveryChannel < Inspec.resource(1)
59
53
  'Twelve_Hours' => 12,
60
54
  }
61
55
  @delivery_frequency_in_hours = frequencies[@delivery_frequency_in_hours]
56
+ rescue Aws::ConfigService::Errors::NoSuchDeliveryChannelException
57
+ @exists = false
62
58
  end
63
59
 
64
60
  class Backend
@@ -66,10 +62,8 @@ class AwsConfigDeliveryChannel < Inspec.resource(1)
66
62
  BackendFactory.set_default_backend(self)
67
63
  self.aws_client_class = Aws::ConfigService::Client
68
64
 
69
- def describe_delivery_channels(query)
65
+ def describe_delivery_channels(query = {})
70
66
  aws_service_client.describe_delivery_channels(query)
71
- rescue Aws::ConfigService::Errors::NoSuchDeliveryChannelException
72
- return {}
73
67
  end
74
68
  end
75
69
  end
@@ -12,7 +12,7 @@ class AwsConfigurationRecorder < Inspec.resource(1)
12
12
  supports platform: 'aws'
13
13
 
14
14
  include AwsSingularResourceMixin
15
- attr_reader :role_arn, :resource_types, :recorder_name, :resp
15
+ attr_reader :role_arn, :resource_types, :recorder_name
16
16
 
17
17
  def to_s
18
18
  "Configuration_Recorder: #{@recorder_name}"
@@ -27,11 +27,11 @@ class AwsConfigurationRecorder < Inspec.resource(1)
27
27
  end
28
28
 
29
29
  def status
30
- return unless @exists
30
+ return {} unless @exists
31
31
  backend = BackendFactory.create(inspec_runner)
32
32
  catch_aws_errors do
33
- @resp = backend.describe_configuration_recorder_status(@query)
34
- @status = @resp.configuration_recorders_status.first.to_h
33
+ response = backend.describe_configuration_recorder_status(configuration_recorder_names: [@recorder_name])
34
+ @status = response.configuration_recorders_status.first.to_h
35
35
  end
36
36
  end
37
37
 
@@ -50,35 +50,30 @@ class AwsConfigurationRecorder < Inspec.resource(1)
50
50
  allowed_scalar_type: String,
51
51
  )
52
52
 
53
- # Must give it a recorder_name
54
- if validated_params[:recorder_name].nil?
55
- raise ArgumentError, 'You must provide recorder_name to aws_config_recorder'
56
- end
57
-
58
53
  validated_params
59
54
  end
60
55
 
61
56
  def fetch_from_api
62
57
  backend = BackendFactory.create(inspec_runner)
63
- @query = { configuration_recorder_names: [@recorder_name] }
58
+ query = @recorder_name ? { configuration_recorder_names: [@recorder_name] } : {}
59
+ response = backend.describe_configuration_recorders(query)
64
60
 
65
- catch_aws_errors do
66
- begin
67
- @resp = backend.describe_configuration_recorders(@query)
68
- rescue Aws::ConfigService::Errors::NoSuchConfigurationRecorderException
69
- @exists = false
70
- return
71
- end
72
- @exists = !@resp.empty?
73
- return unless @exists
74
-
75
- @recorder = @resp.configuration_recorders.first.to_h
76
- @recorder_name = @recorder[:name]
77
- @role_arn = @recorder[:role_arn]
78
- @recording_all_resource_types = @recorder[:recording_group][:all_supported]
79
- @recording_all_global_types = @recorder[:recording_group][:include_global_resource_types]
80
- @resource_types = @recorder[:recording_group][:resource_types]
61
+ @exists = !response.configuration_recorders.empty?
62
+ return unless exists?
63
+
64
+ if response.configuration_recorders.count > 1
65
+ raise ArgumentError, 'Internal error: unexpectedly received multiple AWS Config Recorder objects from API; expected to be singleton per-region. Please file a bug report at https://github.com/chef/inspec/issues .'
81
66
  end
67
+
68
+ recorder = response.configuration_recorders.first.to_h
69
+ @recorder_name = recorder[:name]
70
+ @role_arn = recorder[:role_arn]
71
+ @recording_all_resource_types = recorder[:recording_group][:all_supported]
72
+ @recording_all_global_types = recorder[:recording_group][:include_global_resource_types]
73
+ @resource_types = recorder[:recording_group][:resource_types]
74
+ rescue Aws::ConfigService::Errors::NoSuchConfigurationRecorderException
75
+ @exists = false
76
+ return
82
77
  end
83
78
 
84
79
  class Backend
@@ -83,7 +83,14 @@ class AwsIamPolicy < Inspec.resource(1)
83
83
 
84
84
  def statement_count
85
85
  return nil unless exists?
86
- policy['Statement'].count
86
+ # Typically it is an array of statements
87
+ if policy['Statement'].is_a? Array
88
+ policy['Statement'].count
89
+ else
90
+ # But if there is one statement, it is permissable to degenerate the array,
91
+ # and place the statement as a hash directly under the 'Statement' key
92
+ return 1
93
+ end
87
94
  end
88
95
 
89
96
  def has_statement?(raw_criteria = {})
@@ -141,6 +148,11 @@ class AwsIamPolicy < Inspec.resource(1)
141
148
  end
142
149
 
143
150
  def has_statement__normalize_statements
151
+ # Some single-statement policies place their statement
152
+ # directly in policy['Statement'], rather than in an
153
+ # Array within it. See arn:aws:iam::aws:policy/AWSCertificateManagerReadOnly
154
+ # Thus, coerce to Array.
155
+ policy['Statement'] = [policy['Statement']] if policy['Statement'].is_a? Hash
144
156
  policy['Statement'].map do |statement|
145
157
  # Coerce some values into arrays
146
158
  %w{Action Resource}.each do |field|
@@ -177,7 +189,12 @@ class AwsIamPolicy < Inspec.resource(1)
177
189
  def has_statement__array_criterion(crit_name, statement, criteria)
178
190
  return true unless criteria.key?(crit_name)
179
191
  check = criteria[crit_name]
180
- values = statement[crit_name] # This is an array due to normalize_statements
192
+ # This is an array due to normalize_statements
193
+ # If it is nil, the statement does not have an entry for that dimension;
194
+ # but since we were asked to match on it (on nothing), we
195
+ # decide to never match
196
+ values = statement[crit_name]
197
+ return false if values.nil?
181
198
 
182
199
  if check.is_a?(String)
183
200
  # If check is a string, it only has to match one of the values
@@ -11,6 +11,10 @@ class AwsIamRole < Inspec.resource(1)
11
11
  include AwsSingularResourceMixin
12
12
  attr_reader :description, :role_name
13
13
 
14
+ def to_s
15
+ "IAM Role #{role_name}"
16
+ end
17
+
14
18
  private
15
19
 
16
20
  def validate_params(raw_params)
@@ -9,12 +9,15 @@ class AwsIamUser < Inspec.resource(1)
9
9
  describe aws_iam_user(username: 'test_user') do
10
10
  it { should have_mfa_enabled }
11
11
  it { should_not have_console_password }
12
+ it { should_not have_inline_user_policies }
13
+ it { should_not have_attached_user_policies }
12
14
  end
13
15
  "
14
16
  supports platform: 'aws'
15
17
 
16
18
  include AwsSingularResourceMixin
17
- attr_reader :access_keys, :has_console_password, :has_mfa_enabled, :username
19
+ attr_reader :access_keys, :attached_policy_names, :attached_policy_arns, \
20
+ :has_console_password, :has_mfa_enabled, :inline_policy_names, :username
18
21
  alias has_mfa_enabled? has_mfa_enabled
19
22
  alias has_console_password? has_console_password
20
23
 
@@ -27,6 +30,16 @@ class AwsIamUser < Inspec.resource(1)
27
30
  "IAM User #{username}"
28
31
  end
29
32
 
33
+ def has_attached_policies?
34
+ return nil unless exists?
35
+ !attached_policy_names.empty?
36
+ end
37
+
38
+ def has_inline_policies?
39
+ return nil unless exists?
40
+ !inline_policy_names.empty?
41
+ end
42
+
30
43
  private
31
44
 
32
45
  def validate_params(raw_params)
@@ -62,6 +75,10 @@ class AwsIamUser < Inspec.resource(1)
62
75
  @aws_user_struct = backend.get_user(user_name: username)
63
76
  rescue Aws::IAM::Errors::NoSuchEntity
64
77
  @exists = false
78
+ @access_keys = []
79
+ @inline_policy_names = []
80
+ @attached_policy_arns = []
81
+ @attached_policy_names = []
65
82
  return
66
83
  end
67
84
  end
@@ -84,6 +101,12 @@ class AwsIamUser < Inspec.resource(1)
84
101
  @access_keys = backend.list_access_keys(user_name: username).access_key_metadata
85
102
  # If the above call fails, we get nil here; but we promise access_keys will be an array.
86
103
  @access_keys ||= []
104
+
105
+ @inline_policy_names = backend.list_user_policies(user_name: username).policy_names
106
+
107
+ attached_policies = backend.list_attached_user_policies(user_name: username).attached_policies
108
+ @attached_policy_arns = attached_policies.map { |p| p[:policy_arn] }
109
+ @attached_policy_names = attached_policies.map { |p| p[:policy_name] }
87
110
  end
88
111
 
89
112
  class Backend
@@ -106,6 +129,14 @@ class AwsIamUser < Inspec.resource(1)
106
129
  def list_access_keys(criteria)
107
130
  aws_service_client.list_access_keys(criteria)
108
131
  end
132
+
133
+ def list_user_policies(criteria)
134
+ aws_service_client.list_user_policies(criteria)
135
+ end
136
+
137
+ def list_attached_user_policies(criteria)
138
+ aws_service_client.list_attached_user_policies(criteria)
139
+ end
109
140
  end
110
141
  end
111
142
  end
@@ -12,6 +12,12 @@ class AwsIamUsers < Inspec.resource(1)
12
12
  describe aws_iam_users.where(has_console_password?: true) do
13
13
  it { should exist }
14
14
  end
15
+ describe aws_iam_users.where(has_inline_policies?: true) do
16
+ it { should_not exist }
17
+ end
18
+ describe aws_iam_users.where(has_attached_policies?: true) do
19
+ it { should_not exist }
20
+ end
15
21
  '
16
22
  supports platform: 'aws'
17
23
 
@@ -23,10 +29,21 @@ class AwsIamUsers < Inspec.resource(1)
23
29
  .add(:exists?) { |x| !x.entries.empty? }
24
30
  .add(:has_mfa_enabled?, field: :has_mfa_enabled)
25
31
  .add(:has_console_password?, field: :has_console_password)
32
+ .add(:has_inline_policies?, field: :has_inline_policies)
33
+ .add(:has_attached_policies?, field: :has_attached_policies)
26
34
  .add(:password_ever_used?, field: :password_ever_used?)
27
35
  .add(:password_never_used?, field: :password_never_used?)
28
36
  .add(:password_last_used_days_ago, field: :password_last_used_days_ago)
29
- .add(:username, field: :user_name)
37
+ .add(:usernames, field: :user_name)
38
+ .add(:username) { |res| res.entries.map { |row| row[:user_name] } } # We should deprecate this; plural resources get plural properties
39
+ # Next three are needed to declare fields for use by the de-duped set
40
+ filter.add(:dupe_inline_policy_names, field: :inline_policy_names_source)
41
+ .add(:dupe_attached_policy_names, field: :attached_policy_names_source)
42
+ .add(:dupe_attached_policy_arns, field: :attached_policy_arns_source)
43
+ # These three are now able to access the above three in .entries
44
+ filter.add(:inline_policy_names) { |obj| obj.dupe_inline_policy_names.flatten.uniq }
45
+ .add(:attached_policy_names) { |obj| obj.dupe_attached_policy_names.flatten.uniq }
46
+ .add(:attached_policy_arns) { |obj| obj.dupe_attached_policy_arns.flatten.uniq }
30
47
  filter.connect(self, :table)
31
48
 
32
49
  def validate_params(raw_params)
@@ -49,12 +66,14 @@ class AwsIamUsers < Inspec.resource(1)
49
66
  table
50
67
  end
51
68
 
52
- def fetch_from_api
69
+ def fetch_from_api # rubocop: disable Metrics/AbcSize
53
70
  backend = BackendFactory.create(inspec_runner)
54
71
  @table = fetch_from_api_paginated(backend)
55
72
 
56
73
  # TODO: lazy columns - https://github.com/chef/inspec-aws/issues/100
57
74
  @table.each do |user|
75
+ # Some of these throw exceptions to indicate empty results;
76
+ # others return empty arrays
58
77
  begin
59
78
  _login_profile = backend.get_login_profile(user_name: user[:user_name])
60
79
  user[:has_console_password] = true
@@ -70,6 +89,17 @@ class AwsIamUsers < Inspec.resource(1)
70
89
  user[:has_mfa_enabled] = false
71
90
  end
72
91
  user[:has_mfa_enabled?] = user[:has_mfa_enabled]
92
+
93
+ user[:inline_policy_names_source] = backend.list_user_policies(user_name: user[:user_name]).policy_names
94
+ user[:has_inline_policies] = !user[:inline_policy_names_source].empty?
95
+ user[:has_inline_policies?] = user[:has_inline_policies]
96
+
97
+ attached_policies = backend.list_attached_user_policies(user_name: user[:user_name]).attached_policies
98
+ user[:has_attached_policies] = !attached_policies.empty?
99
+ user[:has_attached_policies?] = user[:has_attached_policies]
100
+ user[:attached_policy_names_source] = attached_policies.map { |p| p[:policy_name] }
101
+ user[:attached_policy_arns_source] = attached_policies.map { |p| p[:policy_arn] }
102
+
73
103
  password_last_used = user[:password_last_used]
74
104
  user[:password_ever_used?] = !password_last_used.nil?
75
105
  user[:password_never_used?] = password_last_used.nil?
@@ -103,6 +133,14 @@ class AwsIamUsers < Inspec.resource(1)
103
133
  def list_mfa_devices(query)
104
134
  aws_service_client.list_mfa_devices(query)
105
135
  end
136
+
137
+ def list_user_policies(query)
138
+ aws_service_client.list_user_policies(query)
139
+ end
140
+
141
+ def list_attached_user_policies(query)
142
+ aws_service_client.list_attached_user_policies(query)
143
+ end
106
144
  end
107
145
  end
108
146
  end
@@ -162,7 +162,7 @@ module Inspec::Resources
162
162
  elsif %w{aix}.include?(platform)
163
163
  SrcMstr.new(inspec)
164
164
  elsif %w{amazon}.include?(platform)
165
- if os[:release].start_with?('20\d\d')
165
+ if os[:release] =~ /^20\d\d/
166
166
  Upstart.new(inspec, service_ctl)
167
167
  else
168
168
  Systemd.new(inspec, service_ctl)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.43
4
+ version: 2.1.54
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-12 00:00:00.000000000 Z
11
+ date: 2018-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train
@@ -312,7 +312,7 @@ files:
312
312
  - docs/resources/aws_cloudtrail_trails.md.erb
313
313
  - docs/resources/aws_cloudwatch_alarm.md.erb
314
314
  - docs/resources/aws_cloudwatch_log_metric_filter.md.erb
315
- - docs/resources/aws_config_delivery_channel.md
315
+ - docs/resources/aws_config_delivery_channel.md.erb
316
316
  - docs/resources/aws_config_recorder.md.erb
317
317
  - docs/resources/aws_ec2_instance.md.erb
318
318
  - docs/resources/aws_iam_access_key.md.erb
@@ -475,6 +475,7 @@ files:
475
475
  - examples/kitchen-puppet/README.md
476
476
  - examples/kitchen-puppet/manifests/site.pp
477
477
  - examples/kitchen-puppet/metadata.json
478
+ - examples/kitchen-puppet/modules/.gitkeep
478
479
  - examples/kitchen-puppet/test/integration/default/web_spec.rb
479
480
  - examples/meta-profile/README.md
480
481
  - examples/meta-profile/controls/example.rb