inspec 0.34.1 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +323 -6
- data/Gemfile +10 -0
- data/README.md +1 -1
- data/Rakefile +4 -55
- data/docs/.gitignore +2 -0
- data/docs/README.md +40 -0
- data/docs/cli.md +342 -0
- data/docs/dsl_inspec.md +245 -0
- data/docs/dsl_resource.md +93 -0
- data/docs/inspec_and_friends.md +105 -0
- data/docs/matchers.md +136 -0
- data/docs/plugin_kitchen_inspec.html.md +55 -0
- data/docs/profiles.md +285 -0
- data/docs/resources/apache_conf.md.erb +75 -0
- data/docs/resources/apt.md.erb +84 -0
- data/docs/resources/audit_policy.md.erb +61 -0
- data/docs/resources/auditd_conf.md.erb +79 -0
- data/docs/resources/auditd_rules.md.erb +132 -0
- data/docs/resources/bash.md.erb +84 -0
- data/docs/resources/bond.md.erb +97 -0
- data/docs/resources/bridge.md.erb +67 -0
- data/docs/resources/bsd_service.md.erb +76 -0
- data/docs/resources/command.md.erb +151 -0
- data/docs/resources/csv.md.erb +62 -0
- data/docs/resources/directory.md.erb +43 -0
- data/docs/resources/etc_group.md.erb +116 -0
- data/docs/resources/etc_passwd.md.erb +155 -0
- data/docs/resources/etc_shadow.md.erb +149 -0
- data/docs/resources/file.md.erb +460 -0
- data/docs/resources/gem.md.erb +73 -0
- data/docs/resources/group.md.erb +74 -0
- data/docs/resources/grub_conf.md.erb +115 -0
- data/docs/resources/host.md.erb +85 -0
- data/docs/resources/iis_site.md.erb +142 -0
- data/docs/resources/inetd_conf.md.erb +99 -0
- data/docs/resources/ini.md.erb +69 -0
- data/docs/resources/interface.md.erb +66 -0
- data/docs/resources/iptables.md.erb +70 -0
- data/docs/resources/json.md.erb +76 -0
- data/docs/resources/kernel_module.md.erb +60 -0
- data/docs/resources/kernel_parameter.md.erb +72 -0
- data/docs/resources/launchd_service.md.erb +76 -0
- data/docs/resources/limits_conf.md.erb +80 -0
- data/docs/resources/login_def.md.erb +77 -0
- data/docs/resources/mount.md.erb +83 -0
- data/docs/resources/mysql_conf.md.erb +102 -0
- data/docs/resources/mysql_session.md.erb +63 -0
- data/docs/resources/npm.md.erb +75 -0
- data/docs/resources/ntp_conf.md.erb +76 -0
- data/docs/resources/oneget.md.erb +67 -0
- data/docs/resources/os.md.erb +154 -0
- data/docs/resources/os_env.md.erb +98 -0
- data/docs/resources/package.md.erb +115 -0
- data/docs/resources/parse_config.md.erb +122 -0
- data/docs/resources/parse_config_file.md.erb +143 -0
- data/docs/resources/pip.md.erb +74 -0
- data/docs/resources/port.md.erb +150 -0
- data/docs/resources/postgres_conf.md.erb +90 -0
- data/docs/resources/postgres_session.md.erb +75 -0
- data/docs/resources/powershell.md.erb +116 -0
- data/docs/resources/process.md.erb +73 -0
- data/docs/resources/registry_key.md.erb +149 -0
- data/docs/resources/runit_service.md.erb +76 -0
- data/docs/resources/security_policy.md.erb +61 -0
- data/docs/resources/service.md.erb +135 -0
- data/docs/resources/ssh_config.md.erb +94 -0
- data/docs/resources/sshd_config.md.erb +97 -0
- data/docs/resources/ssl.md.erb +133 -0
- data/docs/resources/sys_info.md.erb +55 -0
- data/docs/resources/systemd_service.md.erb +76 -0
- data/docs/resources/sysv_service.md.erb +76 -0
- data/docs/resources/upstart_service.md.erb +76 -0
- data/docs/resources/user.md.erb +154 -0
- data/docs/resources/users.md.erb +140 -0
- data/docs/resources/vbscript.md.erb +69 -0
- data/docs/resources/windows_feature.md.erb +61 -0
- data/docs/resources/wmi.md.erb +95 -0
- data/docs/resources/xinetd_conf.md.erb +170 -0
- data/docs/resources/yaml.md.erb +69 -0
- data/docs/resources/yum.md.erb +103 -0
- data/docs/ruby_usage.md +154 -0
- data/docs/shared/matcher_be.md.erb +1 -0
- data/docs/shared/matcher_cmp.md.erb +45 -0
- data/docs/shared/matcher_eq.md.erb +3 -0
- data/docs/shared/matcher_include.md.erb +1 -0
- data/docs/shared/matcher_match.md.erb +1 -0
- data/docs/shell.md +150 -0
- data/examples/meta-profile/inspec.yml +2 -4
- data/inspec.gemspec +1 -1
- data/lib/bundles/inspec-compliance/api.rb +28 -18
- data/lib/bundles/inspec-compliance/cli.rb +19 -27
- data/lib/bundles/inspec-compliance/target.rb +15 -2
- data/lib/fetchers/git.rb +4 -0
- data/lib/fetchers/local.rb +16 -1
- data/lib/fetchers/mock.rb +4 -0
- data/lib/fetchers/url.rb +46 -20
- data/lib/inspec/base_cli.rb +14 -0
- data/lib/inspec/cached_fetcher.rb +67 -0
- data/lib/inspec/cli.rb +22 -8
- data/lib/inspec/control_eval_context.rb +12 -2
- data/lib/inspec/dependencies/{vendor_index.rb → cache.rb} +5 -4
- data/lib/inspec/dependencies/dependency_set.rb +8 -14
- data/lib/inspec/dependencies/lockfile.rb +5 -13
- data/lib/inspec/dependencies/requirement.rb +31 -41
- data/lib/inspec/dependencies/resolver.rb +3 -3
- data/lib/inspec/dsl.rb +16 -4
- data/lib/inspec/errors.rb +1 -0
- data/lib/inspec/fetcher.rb +25 -1
- data/lib/inspec/objects/each_loop.rb +5 -2
- data/lib/inspec/objects/test.rb +8 -2
- data/lib/inspec/plugins/fetcher.rb +13 -12
- data/lib/inspec/plugins/resource.rb +3 -0
- data/lib/inspec/profile.rb +62 -26
- data/lib/inspec/profile_context.rb +44 -8
- data/lib/inspec/resource.rb +18 -3
- data/lib/inspec/rspec_json_formatter.rb +91 -40
- data/lib/inspec/rule.rb +45 -2
- data/lib/inspec/runner.rb +51 -39
- data/lib/inspec/shell.rb +5 -16
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/apache_conf.rb +1 -1
- data/lib/resources/gem.rb +1 -0
- data/lib/resources/groups.rb +190 -0
- data/lib/resources/oneget.rb +1 -0
- data/lib/resources/os.rb +1 -1
- data/lib/resources/package.rb +3 -1
- data/lib/resources/parse_config.rb +18 -1
- data/lib/resources/pip.rb +1 -1
- data/lib/resources/security_policy.rb +54 -0
- data/lib/resources/ssl.rb +9 -11
- data/lib/resources/sys_info.rb +1 -1
- data/lib/resources/users.rb +81 -64
- metadata +91 -17
- data/docs/cli.rst +0 -442
- data/docs/dsl_inspec.rst +0 -259
- data/docs/dsl_resource.rst +0 -90
- data/docs/inspec_and_friends.rst +0 -85
- data/docs/matchers.rst +0 -137
- data/docs/profiles.rst +0 -169
- data/docs/readme.rst +0 -105
- data/docs/resources.rst +0 -4836
- data/docs/ruby_usage.rst +0 -145
- data/docs/shell.rst +0 -130
- data/docs/template.rst +0 -51
- data/lib/resources/group.rb +0 -137
|
@@ -36,11 +36,8 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
|
|
|
36
36
|
# Called after stop has been called and the run is complete.
|
|
37
37
|
def dump_summary(summary)
|
|
38
38
|
@output_hash[:version] = Inspec::VERSION
|
|
39
|
-
@output_hash[:
|
|
39
|
+
@output_hash[:statistics] = {
|
|
40
40
|
duration: summary.duration,
|
|
41
|
-
example_count: summary.example_count,
|
|
42
|
-
failure_count: summary.failure_count,
|
|
43
|
-
skip_count: summary.pending_count,
|
|
44
41
|
}
|
|
45
42
|
end
|
|
46
43
|
|
|
@@ -66,10 +63,18 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
|
|
|
66
63
|
private
|
|
67
64
|
|
|
68
65
|
def format_example(example)
|
|
66
|
+
if example.metadata[:description_args].length == 0
|
|
67
|
+
code_description = example.metadata[:full_description]
|
|
68
|
+
else
|
|
69
|
+
# For skipped profiles, rspec returns in full_description the skip_message as well. We don't want
|
|
70
|
+
# to mix the two, so we pick the full_description from the example.metadata[:example_group] hash.
|
|
71
|
+
code_description = example.metadata[:example_group][:description]
|
|
72
|
+
end
|
|
73
|
+
|
|
69
74
|
res = {
|
|
70
75
|
id: example.metadata[:id],
|
|
71
76
|
status: example.execution_result.status.to_s,
|
|
72
|
-
code_desc:
|
|
77
|
+
code_desc: code_description,
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
unless (pid = example.metadata[:profile_id]).nil?
|
|
@@ -86,7 +91,7 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
|
|
|
86
91
|
end
|
|
87
92
|
end
|
|
88
93
|
|
|
89
|
-
class InspecRspecJson < InspecRspecMiniJson
|
|
94
|
+
class InspecRspecJson < InspecRspecMiniJson # rubocop:disable Metrics/ClassLength
|
|
90
95
|
RSpec::Core::Formatters.register self, :start, :stop, :dump_summary
|
|
91
96
|
attr_writer :backend
|
|
92
97
|
|
|
@@ -108,7 +113,7 @@ class InspecRspecJson < InspecRspecMiniJson
|
|
|
108
113
|
def start(_notification)
|
|
109
114
|
# Note that the default profile may have no name - therefore
|
|
110
115
|
# the hash may have a valid nil => entry.
|
|
111
|
-
@profiles_info
|
|
116
|
+
@profiles_info = @profiles.map(&:info!).map(&:dup)
|
|
112
117
|
end
|
|
113
118
|
|
|
114
119
|
def dump_one_example(example, control)
|
|
@@ -133,20 +138,59 @@ class InspecRspecJson < InspecRspecMiniJson
|
|
|
133
138
|
@output_hash[:other_checks] = missing
|
|
134
139
|
end
|
|
135
140
|
|
|
136
|
-
def
|
|
137
|
-
|
|
141
|
+
def controls_summary
|
|
142
|
+
failed = 0
|
|
143
|
+
skipped = 0
|
|
144
|
+
passed = 0
|
|
145
|
+
critical = 0
|
|
146
|
+
major = 0
|
|
147
|
+
minor = 0
|
|
148
|
+
|
|
149
|
+
@control_tests.each do |control|
|
|
150
|
+
next if control[:id].start_with? '(generated from '
|
|
151
|
+
next unless control[:results]
|
|
152
|
+
if control[:results].any? { |r| r[:status] == 'failed' }
|
|
153
|
+
failed += 1
|
|
154
|
+
if control[:impact] >= 0.7
|
|
155
|
+
critical += 1
|
|
156
|
+
elsif control[:impact] >= 0.4
|
|
157
|
+
major += 1
|
|
158
|
+
else
|
|
159
|
+
minor += 1
|
|
160
|
+
end
|
|
161
|
+
elsif control[:results].any? { |r| r[:status] == 'skipped' }
|
|
162
|
+
skipped += 1
|
|
163
|
+
else
|
|
164
|
+
passed += 1
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
total = failed + passed + skipped
|
|
169
|
+
|
|
170
|
+
{ 'total' => total,
|
|
171
|
+
'failed' => {
|
|
172
|
+
'total' => failed,
|
|
173
|
+
'critical' => critical,
|
|
174
|
+
'major' => major,
|
|
175
|
+
'minor' => minor,
|
|
176
|
+
},
|
|
177
|
+
'skipped' => skipped,
|
|
178
|
+
'passed' => passed }
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def tests_summary
|
|
138
182
|
total = 0
|
|
139
183
|
failed = 0
|
|
140
184
|
skipped = 0
|
|
141
185
|
passed = 0
|
|
142
186
|
|
|
143
|
-
@
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if
|
|
187
|
+
all_tests = @anonymous_tests + @control_tests
|
|
188
|
+
all_tests.each do |control|
|
|
189
|
+
next unless control[:results]
|
|
190
|
+
control[:results].each do |result|
|
|
191
|
+
if result[:status] == 'failed'
|
|
148
192
|
failed += 1
|
|
149
|
-
elsif
|
|
193
|
+
elsif result[:status] == 'skipped'
|
|
150
194
|
skipped += 1
|
|
151
195
|
else
|
|
152
196
|
passed += 1
|
|
@@ -154,16 +198,11 @@ class InspecRspecJson < InspecRspecMiniJson
|
|
|
154
198
|
end
|
|
155
199
|
end
|
|
156
200
|
|
|
157
|
-
|
|
201
|
+
{ 'total' => total, 'failed' => failed, 'skipped' => skipped, 'passed' => passed }
|
|
158
202
|
end
|
|
159
203
|
|
|
160
204
|
private
|
|
161
205
|
|
|
162
|
-
def profile_info(profile)
|
|
163
|
-
info = profile.info.dup
|
|
164
|
-
[info[:name], info]
|
|
165
|
-
end
|
|
166
|
-
|
|
167
206
|
#
|
|
168
207
|
# TODO(ssd+vj): We should probably solve this by either ensuring the example has
|
|
169
208
|
# the profile_id of the top level profile when it is included as a dependency, or
|
|
@@ -171,7 +210,7 @@ class InspecRspecJson < InspecRspecMiniJson
|
|
|
171
210
|
# this heuristic matching.
|
|
172
211
|
#
|
|
173
212
|
def example2profile(example, profiles)
|
|
174
|
-
profiles.
|
|
213
|
+
profiles.find { |p| profile_contains_example?(p, example) }
|
|
175
214
|
end
|
|
176
215
|
|
|
177
216
|
def profile_contains_example?(profile, example)
|
|
@@ -180,7 +219,7 @@ class InspecRspecJson < InspecRspecMiniJson
|
|
|
180
219
|
# Case 2: The profile contains a control that matches the id of the example
|
|
181
220
|
if profile[:name] == example[:profile_id]
|
|
182
221
|
true
|
|
183
|
-
elsif profile[:controls] && profile[:controls].
|
|
222
|
+
elsif profile[:controls] && profile[:controls].any? { |x| x[:id] == example[:id] }
|
|
184
223
|
true
|
|
185
224
|
else
|
|
186
225
|
false
|
|
@@ -188,8 +227,9 @@ class InspecRspecJson < InspecRspecMiniJson
|
|
|
188
227
|
end
|
|
189
228
|
|
|
190
229
|
def example2control(example, profiles)
|
|
191
|
-
|
|
192
|
-
|
|
230
|
+
profile = example2profile(example, profiles)
|
|
231
|
+
return nil unless profile && profile[:controls]
|
|
232
|
+
profile[:controls].find { |x| x[:id] == example[:id] }
|
|
193
233
|
end
|
|
194
234
|
|
|
195
235
|
def format_example(example)
|
|
@@ -246,16 +286,17 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
246
286
|
@current_profile = nil
|
|
247
287
|
@missing_controls = []
|
|
248
288
|
@anonymous_tests = []
|
|
289
|
+
@control_tests = []
|
|
249
290
|
super(*args)
|
|
250
291
|
end
|
|
251
292
|
|
|
252
|
-
def close(_notification)
|
|
293
|
+
def close(_notification) # rubocop:disable Metrics/AbcSize
|
|
253
294
|
flush_current_control
|
|
254
295
|
output.puts('') unless @current_control.nil?
|
|
255
296
|
print_tests
|
|
256
297
|
output.puts('')
|
|
257
298
|
|
|
258
|
-
@profiles_info.each do |
|
|
299
|
+
@profiles_info.each do |profile|
|
|
259
300
|
next if profile[:already_printed]
|
|
260
301
|
@current_profile = profile
|
|
261
302
|
next unless print_current_profile
|
|
@@ -266,13 +307,20 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
266
307
|
output.puts('')
|
|
267
308
|
end
|
|
268
309
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
COLORS['
|
|
274
|
-
COLORS['
|
|
275
|
-
|
|
310
|
+
controls_res = controls_summary
|
|
311
|
+
tests_res = tests_summary
|
|
312
|
+
|
|
313
|
+
s = format('Profile Summary: %s%d successful%s, %s%d failures%s, %s%d skipped%s',
|
|
314
|
+
COLORS['passed'], controls_res['passed'], COLORS['reset'],
|
|
315
|
+
COLORS['failed'], controls_res['failed']['total'], COLORS['reset'],
|
|
316
|
+
COLORS['skipped'], controls_res['skipped'], COLORS['reset'])
|
|
317
|
+
output.puts(s) if controls_res['total'] > 0
|
|
318
|
+
|
|
319
|
+
s = format('Test Summary: %s%d successful%s, %s%d failures%s, %s%d skipped%s',
|
|
320
|
+
COLORS['passed'], tests_res['passed'], COLORS['reset'],
|
|
321
|
+
COLORS['failed'], tests_res['failed'], COLORS['reset'],
|
|
322
|
+
COLORS['skipped'], tests_res['skipped'], COLORS['reset'])
|
|
323
|
+
output.puts(s) if !@anonymous_tests.empty? || @current_control.nil?
|
|
276
324
|
end
|
|
277
325
|
|
|
278
326
|
private
|
|
@@ -334,8 +382,6 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
334
382
|
if res.length == 1
|
|
335
383
|
# Single test - be nice and just print the exception message if the test
|
|
336
384
|
# failed. No need to say "1 failed".
|
|
337
|
-
fails.clear
|
|
338
|
-
skips.clear
|
|
339
385
|
res[0][:message].to_s
|
|
340
386
|
else
|
|
341
387
|
[
|
|
@@ -371,7 +417,11 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
371
417
|
test_color = @colors[test_status]
|
|
372
418
|
indicator = @indicators[x[:status]]
|
|
373
419
|
indicator = @indicators['empty'] if indicator.nil?
|
|
374
|
-
|
|
420
|
+
if x[:message]
|
|
421
|
+
msg = x[:code_desc] + "\n" + x[:message]
|
|
422
|
+
else
|
|
423
|
+
msg = x[:skip_message] || x[:code_desc]
|
|
424
|
+
end
|
|
375
425
|
print_line(
|
|
376
426
|
color: test_color,
|
|
377
427
|
indicator: @indicators['small'] + indicator,
|
|
@@ -381,7 +431,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
381
431
|
end
|
|
382
432
|
end
|
|
383
433
|
|
|
384
|
-
def print_tests
|
|
434
|
+
def print_tests # rubocop:disable Metrics/AbcSize
|
|
385
435
|
@anonymous_tests.each do |control|
|
|
386
436
|
control_result = control[:results]
|
|
387
437
|
title = control_result[0][:code_desc].split[0..1].join(' ')
|
|
@@ -394,7 +444,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
394
444
|
test_result = test[:message]
|
|
395
445
|
else
|
|
396
446
|
# determine title
|
|
397
|
-
test_result = test[:code_desc].split[2..-1].join(' ')
|
|
447
|
+
test_result = test[:skip_message] || test[:code_desc].split[2..-1].join(' ')
|
|
398
448
|
# show error message
|
|
399
449
|
test_result += "\n" + test[:message] unless test[:message].nil?
|
|
400
450
|
end
|
|
@@ -414,7 +464,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
414
464
|
return if @current_control.nil?
|
|
415
465
|
|
|
416
466
|
prev_profile = @current_profile
|
|
417
|
-
@current_profile = @profiles_info[@current_control[:profile_id]
|
|
467
|
+
@current_profile = @profiles_info.find { |i| i[:id] == @current_control[:profile_id] }
|
|
418
468
|
print_current_profile if prev_profile != @current_profile
|
|
419
469
|
|
|
420
470
|
fails, skips, passes, summary_indicator = current_control_infos
|
|
@@ -425,6 +475,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
425
475
|
if control_id.start_with? '(generated from '
|
|
426
476
|
@anonymous_tests.push(@current_control)
|
|
427
477
|
else
|
|
478
|
+
@control_tests.push(@current_control)
|
|
428
479
|
print_line(
|
|
429
480
|
color: @colors[summary_indicator] || '',
|
|
430
481
|
indicator: @indicators[summary_indicator] || @indicators['unknown'],
|
data/lib/inspec/rule.rb
CHANGED
|
@@ -12,6 +12,24 @@ module Inspec
|
|
|
12
12
|
class Rule # rubocop:disable Metrics/ClassLength
|
|
13
13
|
include ::RSpec::Matchers
|
|
14
14
|
|
|
15
|
+
#
|
|
16
|
+
# Include any resources from the given resource DSL. The passed
|
|
17
|
+
# resource_dsl will also be included in any Inspec::Expect objects
|
|
18
|
+
# we make.
|
|
19
|
+
#
|
|
20
|
+
# @params resource_dsl [Module]
|
|
21
|
+
# @returns [TrueClass]
|
|
22
|
+
#
|
|
23
|
+
def self.with_resource_dsl(resource_dsl)
|
|
24
|
+
include resource_dsl
|
|
25
|
+
@resource_dsl = resource_dsl
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.resource_dsl # rubocop:disable Style/TrivialAccessors
|
|
30
|
+
@resource_dsl
|
|
31
|
+
end
|
|
32
|
+
|
|
15
33
|
def initialize(id, profile_id, _opts, &block)
|
|
16
34
|
@impact = nil
|
|
17
35
|
@title = nil
|
|
@@ -106,12 +124,12 @@ module Inspec
|
|
|
106
124
|
include dsl
|
|
107
125
|
end.new(method(:__add_check))
|
|
108
126
|
else
|
|
109
|
-
__add_check('describe', values, block)
|
|
127
|
+
__add_check('describe', values, with_dsl(block))
|
|
110
128
|
end
|
|
111
129
|
end
|
|
112
130
|
|
|
113
131
|
def expect(value, &block)
|
|
114
|
-
target = Inspec::Expect.new(value, &block)
|
|
132
|
+
target = Inspec::Expect.new(value, &with_dsl(block))
|
|
115
133
|
__add_check('expect', [value], target)
|
|
116
134
|
target
|
|
117
135
|
end
|
|
@@ -188,6 +206,31 @@ module Inspec
|
|
|
188
206
|
@__checks.push([describe_or_expect, values, block])
|
|
189
207
|
end
|
|
190
208
|
|
|
209
|
+
#
|
|
210
|
+
# Takes a block and returns a block that will run the given block
|
|
211
|
+
# with access to the resource_dsl of the current class. This is to
|
|
212
|
+
# ensure that inside the constructed Rspec::ExampleGroup users
|
|
213
|
+
# have access to DSL methods. Previous this was done in
|
|
214
|
+
# Inspec::Runner before sending the example groups to rspec. It
|
|
215
|
+
# was moved here to ensure that code inside `its` blocks hae the
|
|
216
|
+
# same visibility into resources as code outside its blocks.
|
|
217
|
+
#
|
|
218
|
+
# @param [Proc] block
|
|
219
|
+
# @return [Proc]
|
|
220
|
+
#
|
|
221
|
+
def with_dsl(block)
|
|
222
|
+
return nil if block.nil?
|
|
223
|
+
if self.class.resource_dsl
|
|
224
|
+
dsl = self.class.resource_dsl
|
|
225
|
+
proc do |*args|
|
|
226
|
+
include dsl
|
|
227
|
+
instance_exec(*args, &block)
|
|
228
|
+
end
|
|
229
|
+
else
|
|
230
|
+
block
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
191
234
|
# Idio(ma)tic unindent
|
|
192
235
|
# TODO: replace this
|
|
193
236
|
#
|
data/lib/inspec/runner.rb
CHANGED
|
@@ -11,6 +11,7 @@ require 'inspec/profile_context'
|
|
|
11
11
|
require 'inspec/profile'
|
|
12
12
|
require 'inspec/metadata'
|
|
13
13
|
require 'inspec/secrets'
|
|
14
|
+
require 'inspec/dependencies/cache'
|
|
14
15
|
# spec requirements
|
|
15
16
|
|
|
16
17
|
module Inspec
|
|
@@ -32,7 +33,6 @@ module Inspec
|
|
|
32
33
|
extend Forwardable
|
|
33
34
|
|
|
34
35
|
def_delegator :@test_collector, :report
|
|
35
|
-
def_delegator :@test_collector, :reset
|
|
36
36
|
|
|
37
37
|
attr_reader :backend, :rules, :attributes
|
|
38
38
|
def initialize(conf = {})
|
|
@@ -42,7 +42,8 @@ module Inspec
|
|
|
42
42
|
@target_profiles = []
|
|
43
43
|
@controls = @conf[:controls] || []
|
|
44
44
|
@ignore_supports = @conf[:ignore_supports]
|
|
45
|
-
|
|
45
|
+
@create_lockfile = @conf[:create_lockfile]
|
|
46
|
+
@cache = Inspec::Cache.new(@conf[:cache])
|
|
46
47
|
@test_collector = @conf.delete(:test_collector) || begin
|
|
47
48
|
require 'inspec/runner_rspec'
|
|
48
49
|
RunnerRspec.new(@conf)
|
|
@@ -64,13 +65,20 @@ module Inspec
|
|
|
64
65
|
@test_collector.backend = @backend
|
|
65
66
|
end
|
|
66
67
|
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
def reset
|
|
69
|
+
@test_collector.reset
|
|
70
|
+
@target_profiles.each do |profile|
|
|
71
|
+
profile.runner_context.rules = {}
|
|
72
|
+
end
|
|
73
|
+
@rules = []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def load
|
|
70
77
|
all_controls = []
|
|
71
78
|
|
|
72
79
|
@target_profiles.each do |profile|
|
|
73
80
|
@test_collector.add_profile(profile)
|
|
81
|
+
write_lockfile(profile) if @create_lockfile
|
|
74
82
|
profile.locked_dependencies
|
|
75
83
|
profile.load_libraries
|
|
76
84
|
@attributes |= profile.runner_context.attributes
|
|
@@ -78,9 +86,29 @@ module Inspec
|
|
|
78
86
|
end
|
|
79
87
|
|
|
80
88
|
all_controls.each do |rule|
|
|
81
|
-
register_rule(rule)
|
|
89
|
+
register_rule(rule) unless rule.nil?
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def run(with = nil)
|
|
94
|
+
Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
|
|
95
|
+
load
|
|
96
|
+
run_tests(with)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def write_lockfile(profile)
|
|
100
|
+
return false if !profile.writable?
|
|
101
|
+
|
|
102
|
+
if profile.lockfile_exists?
|
|
103
|
+
Inspec::Log.debug "Using existing lockfile #{profile.lockfile_path}"
|
|
104
|
+
else
|
|
105
|
+
Inspec::Log.debug "Creating lockfile: #{profile.lockfile_path}"
|
|
106
|
+
lockfile = profile.generate_lockfile
|
|
107
|
+
File.write(profile.lockfile_path, lockfile.to_yaml)
|
|
82
108
|
end
|
|
109
|
+
end
|
|
83
110
|
|
|
111
|
+
def run_tests(with = nil)
|
|
84
112
|
@test_collector.run(with)
|
|
85
113
|
end
|
|
86
114
|
|
|
@@ -127,6 +155,7 @@ module Inspec
|
|
|
127
155
|
#
|
|
128
156
|
def add_target(target, _opts = [])
|
|
129
157
|
profile = Inspec::Profile.for_target(target,
|
|
158
|
+
cache: @cache,
|
|
130
159
|
backend: @backend,
|
|
131
160
|
controls: @controls,
|
|
132
161
|
attributes: @conf[:attributes])
|
|
@@ -134,32 +163,17 @@ module Inspec
|
|
|
134
163
|
@target_profiles << profile if supports_profile?(profile)
|
|
135
164
|
end
|
|
136
165
|
|
|
137
|
-
#
|
|
138
|
-
# This is used by inspec-shell and inspec-detect. This should
|
|
139
|
-
# probably be cleaned up a bit.
|
|
140
|
-
#
|
|
141
|
-
# @params [Hash] Options
|
|
142
|
-
# @returns [Inspec::ProfileContext]
|
|
143
|
-
#
|
|
144
|
-
def create_context(options = {})
|
|
145
|
-
meta = options[:metadata]
|
|
146
|
-
profile_id = nil
|
|
147
|
-
profile_id = meta.params[:name] unless meta.nil?
|
|
148
|
-
Inspec::ProfileContext.new(profile_id, @backend, @conf.merge(options))
|
|
149
|
-
end
|
|
150
|
-
|
|
151
166
|
def supports_profile?(profile)
|
|
152
|
-
return true if
|
|
167
|
+
return true if @ignore_supports
|
|
153
168
|
|
|
154
|
-
if !profile.
|
|
169
|
+
if !profile.supports_runtime?
|
|
155
170
|
fail 'This profile requires InSpec version '\
|
|
156
171
|
"#{profile.metadata.inspec_requirement}. You are running "\
|
|
157
172
|
"InSpec v#{Inspec::VERSION}.\n"
|
|
158
173
|
end
|
|
159
174
|
|
|
160
|
-
if !profile.
|
|
161
|
-
|
|
162
|
-
fail "This OS/platform (#{os_info}) is not supported by this profile."
|
|
175
|
+
if !profile.supports_os?
|
|
176
|
+
fail "This OS/platform (#{@backend.os[:name]}) is not supported by this profile."
|
|
163
177
|
end
|
|
164
178
|
|
|
165
179
|
true
|
|
@@ -182,6 +196,14 @@ module Inspec
|
|
|
182
196
|
new_tests
|
|
183
197
|
end
|
|
184
198
|
|
|
199
|
+
def eval_with_virtual_profile(command)
|
|
200
|
+
require 'fetchers/mock'
|
|
201
|
+
add_target({ 'inspec.yml' => 'name: inspec-shell' })
|
|
202
|
+
our_profile = @target_profiles.first
|
|
203
|
+
ctx = our_profile.runner_context
|
|
204
|
+
ctx.load(command)
|
|
205
|
+
end
|
|
206
|
+
|
|
185
207
|
private
|
|
186
208
|
|
|
187
209
|
def block_source_info(block)
|
|
@@ -231,21 +253,11 @@ module Inspec
|
|
|
231
253
|
Inspec::Log.debug "Registering rule #{rule}"
|
|
232
254
|
@rules << rule
|
|
233
255
|
checks = ::Inspec::Rule.prepare_checks(rule)
|
|
234
|
-
examples = checks.
|
|
256
|
+
examples = checks.flat_map do |m, a, b|
|
|
235
257
|
get_check_example(m, a, b)
|
|
236
|
-
end.
|
|
237
|
-
|
|
238
|
-
examples.each
|
|
239
|
-
# TODO: Remove this!! It is very dangerous to do this here.
|
|
240
|
-
# The goal of this is to make the audit DSL available to all
|
|
241
|
-
# describe blocks. Right now, these blocks are executed outside
|
|
242
|
-
# the scope of this run, thus not gaining ony of the DSL pieces.
|
|
243
|
-
# To circumvent this, the full DSL is attached to the example's
|
|
244
|
-
# scope.
|
|
245
|
-
dsl = Inspec::Resource.create_dsl(backend)
|
|
246
|
-
example.send(:include, dsl)
|
|
247
|
-
@test_collector.add_test(example, rule)
|
|
248
|
-
end
|
|
258
|
+
end.compact
|
|
259
|
+
|
|
260
|
+
examples.each { |e| @test_collector.add_test(e, rule) }
|
|
249
261
|
end
|
|
250
262
|
end
|
|
251
263
|
end
|
data/lib/inspec/shell.rb
CHANGED
|
@@ -15,18 +15,13 @@ module Inspec
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def start
|
|
18
|
-
# Create an in-memory empty runner so that we can add tests to it later.
|
|
19
|
-
# This context lasts for the duration of this "start" method call/pry
|
|
20
|
-
# session.
|
|
21
|
-
@ctx = @runner.create_context
|
|
22
|
-
configure_pry
|
|
23
|
-
|
|
24
18
|
# This will hold a single evaluation binding context as opened within
|
|
25
19
|
# the instance_eval context of the anonymous class that the profile
|
|
26
20
|
# context creates to evaluate each individual test file. We want to
|
|
27
21
|
# pretend like we are constantly appending to the same file and want
|
|
28
22
|
# to capture the local variable context from inside said class.
|
|
29
|
-
@ctx_binding = @
|
|
23
|
+
@ctx_binding = @runner.eval_with_virtual_profile('binding')
|
|
24
|
+
configure_pry
|
|
30
25
|
@ctx_binding.pry
|
|
31
26
|
end
|
|
32
27
|
|
|
@@ -51,26 +46,20 @@ module Inspec
|
|
|
51
46
|
|
|
52
47
|
# Track the rules currently registered and what their merge count is.
|
|
53
48
|
Pry.hooks.add_hook(:before_eval, 'inspec_before_eval') do
|
|
54
|
-
@current_eval_rules = @ctx.rules.each_with_object({}) do |(rule_id, rule), h|
|
|
55
|
-
h[rule_id] = Inspec::Rule.merge_count(rule)
|
|
56
|
-
end
|
|
57
49
|
@runner.reset
|
|
58
50
|
end
|
|
59
51
|
|
|
60
52
|
# After pry has evaluated a commanding within the binding context of a
|
|
61
53
|
# test file, register all the rules it discovered.
|
|
62
54
|
Pry.hooks.add_hook(:after_eval, 'inspec_after_eval') do
|
|
63
|
-
@
|
|
64
|
-
|
|
65
|
-
@current_eval_rules[rule_id] != Inspec::Rule.merge_count(rule)
|
|
66
|
-
end
|
|
67
|
-
@runner.run if @current_eval_new_tests
|
|
55
|
+
@runner.load
|
|
56
|
+
@runner.run_tests if !@runner.all_rules.empty?
|
|
68
57
|
end
|
|
69
58
|
|
|
70
59
|
# Don't print out control class inspection when the user uses DSL methods.
|
|
71
60
|
# Instead produce a result of evaluating their control.
|
|
72
61
|
Pry.config.print = proc do |_output_, value, pry|
|
|
73
|
-
next if
|
|
62
|
+
next if !@runner.all_rules.empty?
|
|
74
63
|
pry.pager.open do |pager|
|
|
75
64
|
pager.print pry.config.output_prefix
|
|
76
65
|
Pry::ColorPrinter.pp(value, pager, Pry::Terminal.width! - 1)
|
data/lib/inspec/version.rb
CHANGED
|
@@ -105,7 +105,7 @@ module Inspec::Resources
|
|
|
105
105
|
|
|
106
106
|
includes = []
|
|
107
107
|
(include_files + include_files_optional).each do |f|
|
|
108
|
-
id
|
|
108
|
+
id = Pathname.new(f).absolute? ? f : File.join(@conf_dir, f)
|
|
109
109
|
files = find_files(id, depth: 1, type: 'file')
|
|
110
110
|
|
|
111
111
|
includes.push(files) if files
|