inspec 2.3.5 → 2.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -8
  3. data/Rakefile +1 -2
  4. data/lib/bundles/inspec-compliance/api.rb +3 -353
  5. data/lib/bundles/inspec-compliance/configuration.rb +3 -102
  6. data/lib/bundles/inspec-compliance/http.rb +3 -115
  7. data/lib/bundles/inspec-compliance/support.rb +3 -35
  8. data/lib/bundles/inspec-compliance/target.rb +3 -142
  9. data/lib/inspec/base_cli.rb +4 -1
  10. data/lib/inspec/cli.rb +1 -1
  11. data/lib/inspec/control_eval_context.rb +2 -2
  12. data/lib/inspec/version.rb +1 -1
  13. data/lib/matchers/matchers.rb +3 -3
  14. data/lib/{bundles → plugins}/inspec-compliance/README.md +0 -0
  15. data/lib/plugins/inspec-compliance/lib/inspec-compliance.rb +12 -0
  16. data/lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb +358 -0
  17. data/lib/plugins/inspec-compliance/lib/inspec-compliance/api/login.rb +192 -0
  18. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +266 -0
  19. data/lib/plugins/inspec-compliance/lib/inspec-compliance/configuration.rb +103 -0
  20. data/lib/plugins/inspec-compliance/lib/inspec-compliance/http.rb +116 -0
  21. data/lib/{bundles → plugins/inspec-compliance/lib}/inspec-compliance/images/cc-token.png +0 -0
  22. data/lib/plugins/inspec-compliance/lib/inspec-compliance/support.rb +36 -0
  23. data/lib/plugins/inspec-compliance/lib/inspec-compliance/target.rb +143 -0
  24. data/lib/plugins/inspec-compliance/test/functional/inspec_compliance_test.rb +43 -0
  25. data/lib/{bundles → plugins}/inspec-compliance/test/integration/default/cli.rb +0 -0
  26. data/lib/plugins/inspec-compliance/test/unit/api/login_test.rb +190 -0
  27. data/lib/plugins/inspec-compliance/test/unit/api_test.rb +385 -0
  28. data/lib/plugins/inspec-compliance/test/unit/target_test.rb +155 -0
  29. data/lib/resources/processes.rb +19 -3
  30. metadata +17 -10
  31. data/lib/bundles/inspec-compliance.rb +0 -16
  32. data/lib/bundles/inspec-compliance/.kitchen.yml +0 -20
  33. data/lib/bundles/inspec-compliance/api/login.rb +0 -193
  34. data/lib/bundles/inspec-compliance/bootstrap.sh +0 -41
  35. data/lib/bundles/inspec-compliance/cli.rb +0 -276
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2563861cf6fc6a68aa706c45b9eee94b2843429fd4bd49d84f9dd1dcf0ddde4e
4
- data.tar.gz: bef95a1410a887e15ce6fc22bf4a38c7cbfcde3507f2f969a99dc740a9601355
3
+ metadata.gz: d261f57b62c68d93b2eae0ac5638f4a629bb79957706cc69076c104cf48275a2
4
+ data.tar.gz: 3e56e8f8d99b7ffccd9cd3e2987728c81515170e78d0f00edd088cda80f606ab
5
5
  SHA512:
6
- metadata.gz: 5cef35305f5ad3894b168efb7c2d502dd1d8ef3923ee01fe43aa3ac7d4b5aa8e86ebfc5562d230f1f35c6756423f9f7b1d6ffa45e0b3bf19f110244411c8f380
7
- data.tar.gz: 484850ebb63fd31e554ec52f29b07d86ee543e35fabc9c36cce9cfc67d470499c14475dab199476af6a6a82c68a03ca1634ab63a08bf1b641708e9e578eda71b
6
+ metadata.gz: 6ad6c74ff0b50cc206aa2611aa0230aeaafbbb62cdeba18044437f2d482ccf3392d07b6cb8d1b2fadb5b6634a7cb2502808cd11f1de387158b6db981f2fac8ef
7
+ data.tar.gz: ee8921329a5652a91cce8a7d9720da31be2a2aae5b50af909a7f073ae1296177c8060f0ca3650eaf63b12f873fc59413e92b52c9d5221bc92cbdb3dc200018e9
@@ -1,20 +1,34 @@
1
1
  # Change Log
2
2
  <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
3
- <!-- latest_release 2.3.5 -->
4
- ## [v2.3.5](https://github.com/inspec/inspec/tree/v2.3.5) (2018-09-28)
3
+ <!-- latest_release 2.3.10 -->
4
+ ## [v2.3.10](https://github.com/inspec/inspec/tree/v2.3.10) (2018-10-04)
5
5
 
6
- #### Bug Fixes
7
- - Update plugin gem install code [#3453](https://github.com/inspec/inspec/pull/3453) ([jquick](https://github.com/jquick))
6
+ #### Enhancements
7
+ - Move compliance to v2 plugin [#3423](https://github.com/inspec/inspec/pull/3423) ([jquick](https://github.com/jquick))
8
8
  <!-- latest_release -->
9
9
 
10
- <!-- release_rollup since=2.3.4 -->
11
- ### Changes since 2.3.4 release
10
+ <!-- release_rollup since=2.3.5 -->
11
+ ### Changes since 2.3.5 release
12
12
 
13
13
  #### Bug Fixes
14
- - Update plugin gem install code [#3453](https://github.com/inspec/inspec/pull/3453) ([jquick](https://github.com/jquick)) <!-- 2.3.5 -->
14
+ - Fix distinct_exit cli desc to reflect reality [#3463](https://github.com/inspec/inspec/pull/3463) ([teknofire](https://github.com/teknofire)) <!-- 2.3.8 -->
15
+
16
+ #### Merged Pull Requests
17
+ - Fix `attribute` with empty hash regression [#3454](https://github.com/inspec/inspec/pull/3454) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 2.3.7 -->
18
+
19
+ #### Enhancements
20
+ - Move compliance to v2 plugin [#3423](https://github.com/inspec/inspec/pull/3423) ([jquick](https://github.com/jquick)) <!-- 2.3.10 -->
21
+ - Support finding larger processes on Busybox [#3446](https://github.com/inspec/inspec/pull/3446) ([RoboticCheese](https://github.com/RoboticCheese)) <!-- 2.3.9 -->
22
+ - Modify `cmp` matcher output to use `.inspect` [#3450](https://github.com/inspec/inspec/pull/3450) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 2.3.6 -->
15
23
  <!-- release_rollup -->
16
24
 
17
25
  <!-- latest_stable_release -->
26
+ ## [v2.3.5](https://github.com/inspec/inspec/tree/v2.3.5) (2018-10-01)
27
+
28
+ #### Bug Fixes
29
+ - Update plugin gem install code [#3453](https://github.com/inspec/inspec/pull/3453) ([jquick](https://github.com/jquick))
30
+ <!-- latest_stable_release -->
31
+
18
32
  ## [v2.3.4](https://github.com/inspec/inspec/tree/v2.3.4) (2018-09-28)
19
33
 
20
34
  #### New Features
@@ -37,7 +51,6 @@
37
51
  - RFC inspec style guide [#3356](https://github.com/inspec/inspec/pull/3356) ([arlimus](https://github.com/arlimus))
38
52
  - Pin postgresql to a lower cookbook version [#3449](https://github.com/inspec/inspec/pull/3449) ([jquick](https://github.com/jquick))
39
53
  - Plugins: Example CLI Plugin, a Resource Lister [#3421](https://github.com/inspec/inspec/pull/3421) ([clintoncwolfe](https://github.com/clintoncwolfe))
40
- <!-- latest_stable_release -->
41
54
 
42
55
  ## [v2.2.112](https://github.com/inspec/inspec/tree/v2.2.112) (2018-09-19)
43
56
 
data/Rakefile CHANGED
@@ -58,7 +58,7 @@ Rake::TestTask.new do |t|
58
58
  'test/unit/**/*_test.rb',
59
59
  'lib/plugins/inspec-*/test/unit/**/*_test.rb',
60
60
  ])
61
- t.warning = true
61
+ t.warning = false
62
62
  t.verbose = true
63
63
  t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
64
64
  end
@@ -91,7 +91,6 @@ namespace :test do
91
91
  'test/functional/inspec_exec_json_test.rb',
92
92
  'test/functional/inspec_detect_test.rb',
93
93
  'test/functional/inspec_vendor_test.rb',
94
- 'test/functional/inspec_compliance_test.rb',
95
94
  'test/functional/inspec_check_test.rb',
96
95
  'test/functional/filter_table_test.rb',
97
96
  ]
@@ -1,354 +1,4 @@
1
- # encoding: utf-8
2
- # author: Christoph Hartmann
3
- # author: Dominik Richter
1
+ # This file has been moved to the v2.0 plugins. This redirect allows for legacy use.
2
+ # TODO: Remove in inspec 4.0
4
3
 
5
- require 'net/http'
6
- require 'uri'
7
- require 'json'
8
-
9
- require_relative 'api/login'
10
-
11
- module Compliance
12
- class ServerConfigurationMissing < StandardError; end
13
-
14
- # API Implementation does not hold any state by itself,
15
- # everything will be stored in local Configuration store
16
- class API
17
- extend Compliance::API::Login
18
-
19
- # return all compliance profiles available for the user
20
- # the user is either specified in the options hash or by default
21
- # the username of the account is used that is logged in
22
- def self.profiles(config, profile_filter = nil) # rubocop:disable PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
23
- owner = config['owner'] || config['user']
24
-
25
- # Chef Compliance
26
- if is_compliance_server?(config)
27
- url = "#{config['server']}/user/compliance"
28
- # Chef Automate2
29
- elsif is_automate2_server?(config)
30
- url = "#{config['server']}/compliance/profiles/search"
31
- # Chef Automate
32
- elsif is_automate_server?(config)
33
- url = "#{config['server']}/profiles/#{owner}"
34
- else
35
- raise ServerConfigurationMissing
36
- end
37
-
38
- headers = get_headers(config)
39
- if profile_filter
40
- _owner, id, ver = profile_split(profile_filter)
41
- else
42
- id, ver = nil
43
- end
44
-
45
- if is_automate2_server?(config)
46
- body = { owner: owner, name: id }.to_json
47
- response = Compliance::HTTP.post_with_headers(url, headers, body, config['insecure'])
48
- else
49
- response = Compliance::HTTP.get(url, headers, config['insecure'])
50
- end
51
- data = response.body
52
- response_code = response.code
53
- case response_code
54
- when '200'
55
- msg = 'success'
56
- profiles = JSON.parse(data)
57
- # iterate over profiles
58
- if is_compliance_server?(config)
59
- mapped_profiles = []
60
- profiles.values.each { |org|
61
- mapped_profiles += org.values
62
- }
63
- # Chef Automate pre 0.8.0
64
- elsif is_automate_server_pre_080?(config)
65
- mapped_profiles = profiles.values.flatten
66
- elsif is_automate2_server?(config)
67
- mapped_profiles = []
68
- profiles['profiles'].each { |p|
69
- mapped_profiles << p
70
- }
71
- else
72
- mapped_profiles = profiles.map { |e|
73
- e['owner_id'] = owner
74
- e
75
- }
76
- end
77
- # filter by name and version if they were specified in profile_filter
78
- mapped_profiles.select! do |p|
79
- (!ver || p['version'] == ver) && (!id || p['name'] == id)
80
- end
81
- return msg, mapped_profiles
82
- when '401'
83
- msg = '401 Unauthorized. Please check your token.'
84
- return msg, []
85
- else
86
- msg = "An unexpected error occurred (HTTP #{response_code}): #{response.message}"
87
- return msg, []
88
- end
89
- end
90
-
91
- # return the server api version
92
- # NB this method does not use Compliance::Configuration to allow for using
93
- # it before we know the version (e.g. oidc or not)
94
- def self.version(config)
95
- url = config['server']
96
- insecure = config['insecure']
97
-
98
- raise ServerConfigurationMissing if url.nil?
99
-
100
- headers = get_headers(config)
101
- response = Compliance::HTTP.get(url+'/version', headers, insecure)
102
- return {} if response.code == '404'
103
-
104
- data = response.body
105
- return {} if data.nil? || data.empty?
106
-
107
- parsed = JSON.parse(data)
108
- return {} unless parsed.key?('version') && !parsed['version'].empty?
109
-
110
- parsed
111
- end
112
-
113
- # verifies that a profile exists
114
- def self.exist?(config, profile)
115
- _msg, profiles = Compliance::API.profiles(config, profile)
116
- !profiles.empty?
117
- end
118
-
119
- def self.upload(config, owner, profile_name, archive_path)
120
- # Chef Compliance
121
- if is_compliance_server?(config)
122
- url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar"
123
- # Chef Automate pre 0.8.0
124
- elsif is_automate_server_pre_080?(config)
125
- url = "#{config['server']}/#{owner}"
126
- elsif is_automate2_server?(config)
127
- url = "#{config['server']}/compliance/profiles?owner=#{owner}"
128
- # Chef Automate
129
- else
130
- url = "#{config['server']}/profiles/#{owner}"
131
- end
132
-
133
- headers = get_headers(config)
134
- if is_automate2_server?(config)
135
- res = Compliance::HTTP.post_multipart_file(url, headers, archive_path, config['insecure'])
136
- else
137
- res = Compliance::HTTP.post_file(url, headers, archive_path, config['insecure'])
138
- end
139
-
140
- [res.is_a?(Net::HTTPSuccess), res.body]
141
- end
142
-
143
- # Use username and refresh_token to get an API access token
144
- def self.get_token_via_refresh_token(url, refresh_token, insecure)
145
- uri = URI.parse("#{url}/login")
146
- req = Net::HTTP::Post.new(uri.path)
147
- req.body = { token: refresh_token }.to_json
148
- access_token = nil
149
- response = Compliance::HTTP.send_request(uri, req, insecure)
150
- data = response.body
151
- if response.code == '200'
152
- begin
153
- tokendata = JSON.parse(data)
154
- access_token = tokendata['access_token']
155
- msg = 'Successfully fetched API access token'
156
- success = true
157
- rescue JSON::ParserError => e
158
- success = false
159
- msg = e.message
160
- end
161
- else
162
- success = false
163
- msg = "Failed to authenticate to #{url} \n\
164
- Response code: #{response.code}\n Body: #{response.body}"
165
- end
166
-
167
- [success, msg, access_token]
168
- end
169
-
170
- # Use username and password to get an API access token
171
- def self.get_token_via_password(url, username, password, insecure)
172
- uri = URI.parse("#{url}/login")
173
- req = Net::HTTP::Post.new(uri.path)
174
- req.body = { userid: username, password: password }.to_json
175
- access_token = nil
176
- response = Compliance::HTTP.send_request(uri, req, insecure)
177
- data = response.body
178
- if response.code == '200'
179
- access_token = data
180
- msg = 'Successfully fetched an API access token valid for 12 hours'
181
- success = true
182
- else
183
- success = false
184
- msg = "Failed to authenticate to #{url} \n\
185
- Response code: #{response.code}\n Body: #{response.body}"
186
- end
187
-
188
- [success, msg, access_token]
189
- end
190
-
191
- def self.get_headers(config)
192
- token = get_token(config)
193
- if is_automate_server?(config) || is_automate2_server?(config)
194
- headers = { 'chef-delivery-enterprise' => config['automate']['ent'] }
195
- if config['automate']['token_type'] == 'dctoken'
196
- headers['x-data-collector-token'] = token
197
- else
198
- headers['chef-delivery-user'] = config['user']
199
- headers['chef-delivery-token'] = token
200
- end
201
- else
202
- headers = { 'Authorization' => "Bearer #{token}" }
203
- end
204
- headers
205
- end
206
-
207
- def self.get_token(config)
208
- return config['token'] unless config['refresh_token']
209
- _success, _msg, token = get_token_via_refresh_token(config['server'], config['refresh_token'], config['insecure'])
210
- token
211
- end
212
-
213
- def self.target_url(config, profile)
214
- owner, id, ver = profile_split(profile)
215
-
216
- return "#{config['server']}/compliance/profiles/tar" if is_automate2_server?(config)
217
- return "#{config['server']}/owners/#{owner}/compliance/#{id}/tar" unless is_automate_server?(config)
218
-
219
- if ver.nil?
220
- "#{config['server']}/profiles/#{owner}/#{id}/tar"
221
- else
222
- "#{config['server']}/profiles/#{owner}/#{id}/version/#{ver}/tar"
223
- end
224
- end
225
-
226
- def self.profile_split(profile)
227
- owner, id = profile.split('/')
228
- id, version = id.split('#')
229
- [owner, id, version]
230
- end
231
-
232
- # returns a parsed url for `admin/profile` or `compliance://admin/profile`
233
- def self.sanitize_profile_name(profile)
234
- if URI(profile).scheme == 'compliance'
235
- uri = URI(profile)
236
- else
237
- uri = URI("compliance://#{profile}")
238
- end
239
- uri.to_s.sub(%r{^compliance:\/\/}, '')
240
- end
241
-
242
- def self.is_compliance_server?(config)
243
- config['server_type'] == 'compliance'
244
- end
245
-
246
- def self.is_automate_server_pre_080?(config)
247
- # Automate versions before 0.8.x do not have a valid version in the config
248
- return false unless config['server_type'] == 'automate'
249
- server_version_from_config(config).nil?
250
- end
251
-
252
- def self.is_automate_server_080_and_later?(config)
253
- # Automate versions 0.8.x and later will have a "version" key in the config
254
- # that is properly parsed out via server_version_from_config below
255
- return false unless config['server_type'] == 'automate'
256
- !server_version_from_config(config).nil?
257
- end
258
-
259
- def self.is_automate2_server?(config)
260
- config['server_type'] == 'automate2'
261
- end
262
-
263
- def self.is_automate_server?(config)
264
- config['server_type'] == 'automate'
265
- end
266
-
267
- def self.server_version_from_config(config)
268
- # Automate versions 0.8.x and later will have a "version" key in the config
269
- # that looks like: "version":{"api":"compliance","version":"0.8.24"}
270
- return nil unless config.key?('version')
271
- return nil unless config['version'].is_a?(Hash)
272
- config['version']['version']
273
- end
274
-
275
- def self.determine_server_type(url, insecure)
276
- if target_is_automate2_server?(url, insecure)
277
- :automate2
278
- elsif target_is_automate_server?(url, insecure)
279
- :automate
280
- elsif target_is_compliance_server?(url, insecure)
281
- :compliance
282
- else
283
- Inspec::Log.debug('Could not determine server type using known endpoints')
284
- nil
285
- end
286
- end
287
-
288
- def self.target_is_automate2_server?(url, insecure)
289
- automate_endpoint = '/dex/auth'
290
- response = Compliance::HTTP.get(url + automate_endpoint, nil, insecure)
291
- if response.code == '400'
292
- Inspec::Log.debug(
293
- "Received 400 from #{url}#{automate_endpoint} - " \
294
- 'assuming target is a Chef Automate2 instance',
295
- )
296
- true
297
- else
298
- false
299
- end
300
- end
301
-
302
- def self.target_is_automate_server?(url, insecure)
303
- automate_endpoint = '/compliance/version'
304
- response = Compliance::HTTP.get(url + automate_endpoint, nil, insecure)
305
- case response.code
306
- when '401'
307
- Inspec::Log.debug(
308
- "Received 401 from #{url}#{automate_endpoint} - " \
309
- 'assuming target is a Chef Automate instance',
310
- )
311
- true
312
- when '200'
313
- # Chef Automate currently returns 401 for `/compliance/version` but some
314
- # versions of OpsWorks Chef Automate return 200 and a Chef Manage page
315
- # when unauthenticated requests are received.
316
- if response.body.include?('Are You Looking For the Chef Server?')
317
- Inspec::Log.debug(
318
- "Received 200 from #{url}#{automate_endpoint} - " \
319
- 'assuming target is an OpsWorks Chef Automate instance',
320
- )
321
- true
322
- else
323
- Inspec::Log.debug(
324
- "Received 200 from #{url}#{automate_endpoint} " \
325
- 'but did not receive the Chef Manage page - ' \
326
- 'assuming target is not a Chef Automate instance',
327
- )
328
- false
329
- end
330
- else
331
- Inspec::Log.debug(
332
- "Received unexpected status code #{response.code} " \
333
- "from #{url}#{automate_endpoint} - " \
334
- 'assuming target is not a Chef Automate instance',
335
- )
336
- false
337
- end
338
- end
339
-
340
- def self.target_is_compliance_server?(url, insecure)
341
- # All versions of Chef Compliance return 200 for `/api/version`
342
- compliance_endpoint = '/api/version'
343
-
344
- response = Compliance::HTTP.get(url + compliance_endpoint, nil, insecure)
345
- return false unless response.code == '200'
346
-
347
- Inspec::Log.debug(
348
- "Received 200 from #{url}#{compliance_endpoint} - " \
349
- 'assuming target is a Compliance server',
350
- )
351
- true
352
- end
353
- end
354
- end
4
+ require 'plugins/inspec-compliance/lib/inspec-compliance/api'
@@ -1,103 +1,4 @@
1
- # encoding: utf-8
2
- # author: Christoph Hartmann
3
- # author: Dominik Richter
1
+ # This file has been moved to the v2.0 plugins. This redirect allows for legacy use.
2
+ # TODO: Remove in inspec 4.0
4
3
 
5
- module Compliance
6
- # stores configuration on local filesystem
7
- class Configuration
8
- def initialize
9
- @config_path = File.join(Dir.home, '.inspec', 'compliance')
10
- # ensure the directory is available
11
- unless File.directory?(@config_path)
12
- FileUtils.mkdir_p(@config_path)
13
- end
14
- # set config file path
15
- @config_file = File.join(@config_path, '/config.json')
16
- @config = {}
17
-
18
- # load the data
19
- get
20
- end
21
-
22
- # direct access to config
23
- def [](key)
24
- @config[key]
25
- end
26
-
27
- def []=(key, value)
28
- @config[key] = value
29
- end
30
-
31
- def key?(key)
32
- @config.key?(key)
33
- end
34
-
35
- def clean
36
- @config = {}
37
- end
38
-
39
- # return the json data
40
- def get
41
- if File.exist?(@config_file)
42
- file = File.read(@config_file)
43
- @config = JSON.parse(file)
44
- end
45
- @config
46
- end
47
-
48
- # stores a hash to json
49
- def store
50
- File.open(@config_file, 'w') do |f|
51
- f.chmod(0600)
52
- f.write(@config.to_json)
53
- end
54
- end
55
-
56
- # deletes data
57
- def destroy
58
- if File.exist?(@config_file)
59
- File.delete(@config_file)
60
- else
61
- true
62
- end
63
- end
64
-
65
- # return if the (stored) api version does not support a certain feature
66
- def supported?(feature)
67
- sup = version_with_support(feature)
68
-
69
- # we do not know the version, therefore we do not know if its possible to use the feature
70
- return if self['version'].nil? || self['version']['version'].nil?
71
-
72
- if sup.is_a?(Array)
73
- Gem::Version.new(self['version']['version']) >= sup[0] &&
74
- Gem::Version.new(self['version']['version']) < sup[1]
75
- else
76
- Gem::Version.new(self['version']['version']) >= sup
77
- end
78
- end
79
-
80
- # exit 1 if the version of compliance that we're working with doesn't support odic
81
- def legacy_check!(feature)
82
- return if supported?(feature)
83
-
84
- puts "This feature (#{feature}) is not available for legacy installations."
85
- puts 'Please upgrade to a recent version of Chef Compliance.'
86
- exit 1
87
- end
88
-
89
- private
90
-
91
- # for a feature, returns either:
92
- # - a version v0: v supports v0 iff v0 <= v
93
- # - an array [v0, v1] of two versions: v supports [v0, v1] iff v0 <= v < v1
94
- def version_with_support(feature)
95
- case feature.to_sym
96
- when :oidc
97
- Gem::Version.new('0.16.19')
98
- else
99
- Gem::Version.new('0.0.0')
100
- end
101
- end
102
- end
103
- end
4
+ require 'plugins/inspec-compliance/lib/inspec-compliance/configuration'