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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +323 -6
  3. data/Gemfile +10 -0
  4. data/README.md +1 -1
  5. data/Rakefile +4 -55
  6. data/docs/.gitignore +2 -0
  7. data/docs/README.md +40 -0
  8. data/docs/cli.md +342 -0
  9. data/docs/dsl_inspec.md +245 -0
  10. data/docs/dsl_resource.md +93 -0
  11. data/docs/inspec_and_friends.md +105 -0
  12. data/docs/matchers.md +136 -0
  13. data/docs/plugin_kitchen_inspec.html.md +55 -0
  14. data/docs/profiles.md +285 -0
  15. data/docs/resources/apache_conf.md.erb +75 -0
  16. data/docs/resources/apt.md.erb +84 -0
  17. data/docs/resources/audit_policy.md.erb +61 -0
  18. data/docs/resources/auditd_conf.md.erb +79 -0
  19. data/docs/resources/auditd_rules.md.erb +132 -0
  20. data/docs/resources/bash.md.erb +84 -0
  21. data/docs/resources/bond.md.erb +97 -0
  22. data/docs/resources/bridge.md.erb +67 -0
  23. data/docs/resources/bsd_service.md.erb +76 -0
  24. data/docs/resources/command.md.erb +151 -0
  25. data/docs/resources/csv.md.erb +62 -0
  26. data/docs/resources/directory.md.erb +43 -0
  27. data/docs/resources/etc_group.md.erb +116 -0
  28. data/docs/resources/etc_passwd.md.erb +155 -0
  29. data/docs/resources/etc_shadow.md.erb +149 -0
  30. data/docs/resources/file.md.erb +460 -0
  31. data/docs/resources/gem.md.erb +73 -0
  32. data/docs/resources/group.md.erb +74 -0
  33. data/docs/resources/grub_conf.md.erb +115 -0
  34. data/docs/resources/host.md.erb +85 -0
  35. data/docs/resources/iis_site.md.erb +142 -0
  36. data/docs/resources/inetd_conf.md.erb +99 -0
  37. data/docs/resources/ini.md.erb +69 -0
  38. data/docs/resources/interface.md.erb +66 -0
  39. data/docs/resources/iptables.md.erb +70 -0
  40. data/docs/resources/json.md.erb +76 -0
  41. data/docs/resources/kernel_module.md.erb +60 -0
  42. data/docs/resources/kernel_parameter.md.erb +72 -0
  43. data/docs/resources/launchd_service.md.erb +76 -0
  44. data/docs/resources/limits_conf.md.erb +80 -0
  45. data/docs/resources/login_def.md.erb +77 -0
  46. data/docs/resources/mount.md.erb +83 -0
  47. data/docs/resources/mysql_conf.md.erb +102 -0
  48. data/docs/resources/mysql_session.md.erb +63 -0
  49. data/docs/resources/npm.md.erb +75 -0
  50. data/docs/resources/ntp_conf.md.erb +76 -0
  51. data/docs/resources/oneget.md.erb +67 -0
  52. data/docs/resources/os.md.erb +154 -0
  53. data/docs/resources/os_env.md.erb +98 -0
  54. data/docs/resources/package.md.erb +115 -0
  55. data/docs/resources/parse_config.md.erb +122 -0
  56. data/docs/resources/parse_config_file.md.erb +143 -0
  57. data/docs/resources/pip.md.erb +74 -0
  58. data/docs/resources/port.md.erb +150 -0
  59. data/docs/resources/postgres_conf.md.erb +90 -0
  60. data/docs/resources/postgres_session.md.erb +75 -0
  61. data/docs/resources/powershell.md.erb +116 -0
  62. data/docs/resources/process.md.erb +73 -0
  63. data/docs/resources/registry_key.md.erb +149 -0
  64. data/docs/resources/runit_service.md.erb +76 -0
  65. data/docs/resources/security_policy.md.erb +61 -0
  66. data/docs/resources/service.md.erb +135 -0
  67. data/docs/resources/ssh_config.md.erb +94 -0
  68. data/docs/resources/sshd_config.md.erb +97 -0
  69. data/docs/resources/ssl.md.erb +133 -0
  70. data/docs/resources/sys_info.md.erb +55 -0
  71. data/docs/resources/systemd_service.md.erb +76 -0
  72. data/docs/resources/sysv_service.md.erb +76 -0
  73. data/docs/resources/upstart_service.md.erb +76 -0
  74. data/docs/resources/user.md.erb +154 -0
  75. data/docs/resources/users.md.erb +140 -0
  76. data/docs/resources/vbscript.md.erb +69 -0
  77. data/docs/resources/windows_feature.md.erb +61 -0
  78. data/docs/resources/wmi.md.erb +95 -0
  79. data/docs/resources/xinetd_conf.md.erb +170 -0
  80. data/docs/resources/yaml.md.erb +69 -0
  81. data/docs/resources/yum.md.erb +103 -0
  82. data/docs/ruby_usage.md +154 -0
  83. data/docs/shared/matcher_be.md.erb +1 -0
  84. data/docs/shared/matcher_cmp.md.erb +45 -0
  85. data/docs/shared/matcher_eq.md.erb +3 -0
  86. data/docs/shared/matcher_include.md.erb +1 -0
  87. data/docs/shared/matcher_match.md.erb +1 -0
  88. data/docs/shell.md +150 -0
  89. data/examples/meta-profile/inspec.yml +2 -4
  90. data/inspec.gemspec +1 -1
  91. data/lib/bundles/inspec-compliance/api.rb +28 -18
  92. data/lib/bundles/inspec-compliance/cli.rb +19 -27
  93. data/lib/bundles/inspec-compliance/target.rb +15 -2
  94. data/lib/fetchers/git.rb +4 -0
  95. data/lib/fetchers/local.rb +16 -1
  96. data/lib/fetchers/mock.rb +4 -0
  97. data/lib/fetchers/url.rb +46 -20
  98. data/lib/inspec/base_cli.rb +14 -0
  99. data/lib/inspec/cached_fetcher.rb +67 -0
  100. data/lib/inspec/cli.rb +22 -8
  101. data/lib/inspec/control_eval_context.rb +12 -2
  102. data/lib/inspec/dependencies/{vendor_index.rb → cache.rb} +5 -4
  103. data/lib/inspec/dependencies/dependency_set.rb +8 -14
  104. data/lib/inspec/dependencies/lockfile.rb +5 -13
  105. data/lib/inspec/dependencies/requirement.rb +31 -41
  106. data/lib/inspec/dependencies/resolver.rb +3 -3
  107. data/lib/inspec/dsl.rb +16 -4
  108. data/lib/inspec/errors.rb +1 -0
  109. data/lib/inspec/fetcher.rb +25 -1
  110. data/lib/inspec/objects/each_loop.rb +5 -2
  111. data/lib/inspec/objects/test.rb +8 -2
  112. data/lib/inspec/plugins/fetcher.rb +13 -12
  113. data/lib/inspec/plugins/resource.rb +3 -0
  114. data/lib/inspec/profile.rb +62 -26
  115. data/lib/inspec/profile_context.rb +44 -8
  116. data/lib/inspec/resource.rb +18 -3
  117. data/lib/inspec/rspec_json_formatter.rb +91 -40
  118. data/lib/inspec/rule.rb +45 -2
  119. data/lib/inspec/runner.rb +51 -39
  120. data/lib/inspec/shell.rb +5 -16
  121. data/lib/inspec/version.rb +1 -1
  122. data/lib/resources/apache_conf.rb +1 -1
  123. data/lib/resources/gem.rb +1 -0
  124. data/lib/resources/groups.rb +190 -0
  125. data/lib/resources/oneget.rb +1 -0
  126. data/lib/resources/os.rb +1 -1
  127. data/lib/resources/package.rb +3 -1
  128. data/lib/resources/parse_config.rb +18 -1
  129. data/lib/resources/pip.rb +1 -1
  130. data/lib/resources/security_policy.rb +54 -0
  131. data/lib/resources/ssl.rb +9 -11
  132. data/lib/resources/sys_info.rb +1 -1
  133. data/lib/resources/users.rb +81 -64
  134. metadata +91 -17
  135. data/docs/cli.rst +0 -442
  136. data/docs/dsl_inspec.rst +0 -259
  137. data/docs/dsl_resource.rst +0 -90
  138. data/docs/inspec_and_friends.rst +0 -85
  139. data/docs/matchers.rst +0 -137
  140. data/docs/profiles.rst +0 -169
  141. data/docs/readme.rst +0 -105
  142. data/docs/resources.rst +0 -4836
  143. data/docs/ruby_usage.rst +0 -145
  144. data/docs/shell.rst +0 -130
  145. data/docs/template.rst +0 -51
  146. 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[:summary] = {
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: example.full_description,
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 ||= Hash[@profiles.map { |x| profile_info(x) }]
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 dump_summary(summary)
137
- super(summary)
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
- @profiles_info.each do |_name, profile|
144
- total += profile[:controls].length
145
- profile[:controls].each do |_control_name, control|
146
- next unless control[:results]
147
- if control[:results].any? { |r| r[:status] == 'failed' }
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 control[:results].any? { |r| r[:status] == 'skipped' }
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
- # TODO: provide this information in the output
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.values.find { |p| profile_contains_example?(p, example) }
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].key?(example[:id])
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
- p = example2profile(example, profiles)
192
- p[:controls][example[:id]] if p && p[:controls]
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 |_id, profile|
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
- res = @output_hash[:summary]
270
- passed = res[:example_count] - res[:failure_count] - res[:skip_count]
271
- s = format('Summary: %s%d successful%s, %s%d failures%s, %s%d skipped%s',
272
- COLORS['passed'], passed, COLORS['reset'],
273
- COLORS['failed'], res[:failure_count], COLORS['reset'],
274
- COLORS['skipped'], res[:skip_count], COLORS['reset'])
275
- output.puts(s)
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
- msg = x[:message] || x[:skip_message] || x[:code_desc]
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 run(with = nil)
68
- Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
69
- Inspec::Log.debug "Backend is #{@backend}"
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 profile.metadata.nil? || @ignore_supports
167
+ return true if @ignore_supports
153
168
 
154
- if !profile.metadata.supports_runtime?
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.metadata.supports_transport?(@backend)
161
- os_info = @backend.os[:name].to_s
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.map do |m, a, b|
256
+ examples = checks.flat_map do |m, a, b|
235
257
  get_check_example(m, a, b)
236
- end.flatten.compact
237
-
238
- examples.each do |example|
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 = @ctx.load('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
- @current_eval_new_tests =
64
- @runner.register_rules(@ctx) do |rule_id, rule|
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 @current_eval_new_tests
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)
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '0.34.1'.freeze
7
+ VERSION = '1.0.0'.freeze
8
8
  end
@@ -105,7 +105,7 @@ module Inspec::Resources
105
105
 
106
106
  includes = []
107
107
  (include_files + include_files_optional).each do |f|
108
- id = File.join(@conf_dir, f)
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
data/lib/resources/gem.rb CHANGED
@@ -9,6 +9,7 @@ module Inspec::Resources
9
9
  example "
10
10
  describe gem('rubocop') do
11
11
  it { should be_installed }
12
+ its('version') { should eq '0.33.0' }
12
13
  end
13
14
  "
14
15