inspec 0.29.0 → 0.30.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -2
  3. data/Rakefile +53 -0
  4. data/docs/cli.rst +442 -0
  5. data/examples/inheritance/inspec.yml +3 -0
  6. data/inspec.gemspec +1 -0
  7. data/lib/inspec/cli.rb +10 -1
  8. data/lib/inspec/completions/bash.sh.erb +45 -0
  9. data/lib/inspec/completions/zsh.sh.erb +61 -0
  10. data/lib/inspec/dependencies.rb +307 -0
  11. data/lib/inspec/dsl.rb +5 -20
  12. data/lib/inspec/env_printer.rb +149 -0
  13. data/lib/inspec/errors.rb +17 -0
  14. data/lib/inspec/metadata.rb +4 -0
  15. data/lib/inspec/profile.rb +12 -0
  16. data/lib/inspec/profile_context.rb +5 -2
  17. data/lib/inspec/shell.rb +7 -2
  18. data/lib/inspec/shell_detector.rb +90 -0
  19. data/lib/inspec/version.rb +1 -1
  20. data/lib/resources/postgres.rb +94 -12
  21. data/lib/resources/registry_key.rb +106 -27
  22. data/lib/utils/hash_map.rb +37 -0
  23. data/test/bench/startup.flat.txt +998 -0
  24. data/test/bench/startup.graph.html +71420 -0
  25. data/test/bench/startup.grind.dat +103554 -0
  26. data/test/bench/startup.stack.html +25015 -0
  27. data/test/bench/startup/startup.flat.txt +1005 -0
  28. data/test/bench/startup/startup.graph.html +71958 -0
  29. data/test/bench/startup/startup.grind.dat +101602 -0
  30. data/test/bench/startup/startup.stack.html +24516 -0
  31. data/test/cookbooks/os_prepare/metadata.rb +1 -0
  32. data/test/cookbooks/os_prepare/recipes/file.rb +5 -0
  33. data/test/cookbooks/os_prepare/recipes/registry_key.rb +13 -0
  34. data/test/docker_run.rb +3 -1
  35. data/test/functional/inheritance_test.rb +26 -13
  36. data/test/helper.rb +2 -2
  37. data/test/integration/default/file_spec.rb +16 -0
  38. data/test/integration/default/powershell_spec.rb +4 -1
  39. data/test/integration/default/registry_key_spec.rb +47 -4
  40. data/test/integration/default/secpol_spec.rb +4 -1
  41. data/test/integration/default/wmi_spec.rb +4 -1
  42. data/test/unit/mock/profiles/resource-tiny/inspec.yml +10 -0
  43. data/test/unit/mock/profiles/resource-tiny/libraries/resource.rb +3 -0
  44. data/test/unit/shell_detector_test.rb +78 -0
  45. metadata +47 -4
  46. data/docs/ctl_inspec.rst +0 -247
@@ -10,3 +10,4 @@ depends 'yum'
10
10
  depends 'runit'
11
11
  depends 'postgresql'
12
12
  depends 'httpd', '~> 0.2'
13
+ depends 'windows'
@@ -38,4 +38,9 @@ if node['platform_family'] != 'windows'
38
38
  group gid
39
39
  end
40
40
 
41
+ else
42
+
43
+ directory 'C:\Test Directory'
44
+ file 'C:\Test Directory\test file.txt'
45
+
41
46
  end
@@ -71,4 +71,17 @@ if node['platform_family'] == 'windows'
71
71
  recursive true
72
72
  action :create
73
73
  end
74
+
75
+ # used to verify pattern test
76
+ ::Chef::Recipe.send(:include, Chef::Mixin::PowershellOut)
77
+ cmd = powershell_out!('Get-WmiObject -Class Win32_UserAccount | % { $_.SID } | ConvertTo-Json')
78
+ sids = JSON.parse(cmd.stdout)
79
+ sids.each { |sid|
80
+ registry_key "HKEY_USERS\\#{sid}\\Software\\Policies\\Microsoft\\Windows\\Installer" do
81
+ values [{ name: 'AlwaysInstallElevated', type: :dword, data: 0 }]
82
+ recursive true
83
+ ignore_failure true # ignore users that have not been logged in
84
+ action :create
85
+ end
86
+ }
74
87
  end
data/test/docker_run.rb CHANGED
@@ -10,6 +10,8 @@ class DockerRunner
10
10
  @conf_path = conf_path ||
11
11
  ENV['config']
12
12
 
13
+ docker_run_concurrency = (ENV['N'] || 5).to_i
14
+
13
15
  if @conf_path.nil?
14
16
  fail "You must provide a configuration file with docker boxes"
15
17
  end
@@ -28,7 +30,7 @@ class DockerRunner
28
30
 
29
31
  @images = docker_images_by_tag
30
32
  @image_pull_tickets = Concurrent::Semaphore.new(2)
31
- @docker_run_tickets = Concurrent::Semaphore.new(5)
33
+ @docker_run_tickets = Concurrent::Semaphore.new(docker_run_concurrency)
32
34
  end
33
35
 
34
36
  def run_all(&block)
@@ -8,19 +8,6 @@ describe 'example inheritance profile' do
8
8
  include FunctionalHelper
9
9
  let(:path) { File.join(examples_path, 'inheritance') }
10
10
 
11
- [
12
- 'archive %s --overwrite',
13
- 'check %s',
14
- 'json %s',
15
- ].each do |cmd|
16
- it cmd[/^\w/] + ' fails without --profiles-path' do
17
- out = inspec(format(cmd, path))
18
- out.stderr.must_include 'You must supply a --profiles-path to inherit'
19
- # out.stdout.must_equal '' => we still get partial output
20
- out.exit_status.must_equal 1
21
- end
22
- end
23
-
24
11
  it 'check succeeds with --profiles-path' do
25
12
  out = inspec('check ' + path + ' --profiles-path ' + examples_path)
26
13
  out.stderr.must_equal ''
@@ -28,6 +15,13 @@ describe 'example inheritance profile' do
28
15
  out.exit_status.must_equal 0
29
16
  end
30
17
 
18
+ it 'check succeeds without --profiles-path using inspec.yml' do
19
+ out = inspec('check ' + path)
20
+ out.stderr.must_equal ''
21
+ out.stdout.must_match /Valid.*true/
22
+ out.exit_status.must_equal 0
23
+ end
24
+
31
25
  it 'archive is successful with --profiles-path' do
32
26
  out = inspec('archive ' + path + ' --output ' + dst.path + ' --profiles-path ' + examples_path)
33
27
  out.stderr.must_equal ''
@@ -37,6 +31,15 @@ describe 'example inheritance profile' do
37
31
  File.exist?(dst.path).must_equal true
38
32
  end
39
33
 
34
+ it 'archive is successful without --profiles-path using inspec.yml' do
35
+ out = inspec('archive ' + path + ' --output ' + dst.path)
36
+ out.stderr.must_equal ''
37
+ out.stdout.must_include 'Generate archive '+dst.path
38
+ out.stdout.must_include 'Finished archive generation.'
39
+ out.exit_status.must_equal 0
40
+ File.exist?(dst.path).must_equal true
41
+ end
42
+
40
43
  it 'read the profile json with --profiles-path' do
41
44
  out = inspec('json ' + path + ' --profiles-path '+examples_path)
42
45
  out.stderr.must_equal ''
@@ -46,4 +49,14 @@ describe 'example inheritance profile' do
46
49
  hm['name'].must_equal 'inheritance'
47
50
  hm['controls'].length.must_equal 3
48
51
  end
52
+
53
+ it 'read the profile json without --profiles-path using inspec.yml' do
54
+ out = inspec('json ' + path)
55
+ out.stderr.must_equal ''
56
+ out.exit_status.must_equal 0
57
+ s = out.stdout
58
+ hm = JSON.load(s)
59
+ hm['name'].must_equal 'inheritance'
60
+ hm['controls'].length.must_equal 3
61
+ end
49
62
  end
data/test/helper.rb CHANGED
@@ -145,8 +145,8 @@ class MockLoader
145
145
  'env' => cmd.call('env'),
146
146
  '${Env:PATH}' => cmd.call('$env-PATH'),
147
147
  # registry key test (winrm 1.6.0, 1.6.1)
148
- '2790db1e88204a073ed7fd3493f5445e5ce531afd0d2724a0e36c17110c535e6' => cmd.call('reg_schedule'),
149
- '25a1a38fafc289a646d30f7aa966ce0901c267798f47abf2f9440e27d31a5b7d' => cmd.call('reg_schedule'),
148
+ 'dd429dd12596fa193ba4111469b4417ecbd78a1d7ba4317c334c9111644bae44' => cmd.call('reg_schedule'),
149
+ 'Fdd429dd12596fa193ba4111469b4417ecbd78a1d7ba4317c334c9111644bae44' => cmd.call('reg_schedule'),
150
150
  'Auditpol /get /subcategory:\'User Account Management\' /r' => cmd.call('auditpol'),
151
151
  '/sbin/auditctl -l' => cmd.call('auditctl'),
152
152
  '/sbin/auditctl -s' => cmd.call('auditctl-s'),
@@ -176,4 +176,20 @@ if os.windows?
176
176
  its('basename') { should cmp 'Windows' }
177
177
  its('path') { should cmp "C:\\Windows" }
178
178
  end
179
+
180
+ describe file('C:\\Test Directory\\test file.txt') do
181
+ it { should exist }
182
+ it { should be_file }
183
+ end
184
+
185
+ describe file('C:\\Test Directory') do
186
+ it { should exist }
187
+ it { should be_directory }
188
+ end
189
+
190
+ describe file("C:/Program Files (x86)/Windows NT/Accessories/wordpad.exe") do
191
+ it { should exist }
192
+ # Only works on Windows 2012 R2
193
+ its('file_version') { should eq '6.3.9600.17415' }
194
+ end
179
195
  end
@@ -1,6 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- return unless os.windows?
3
+ unless os.windows?
4
+ STDERR.puts "\033[1;33mTODO: Not running #{__FILE__} because we are not on Windows.\033[0m"
5
+ return
6
+ end
4
7
 
5
8
  script = <<-EOH
6
9
  Write-Output 'hello'
@@ -1,6 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- return unless os.windows?
3
+ unless os.windows?
4
+ STDERR.puts "\033[1;33mTODO: Not running #{__FILE__} because we are not on Windows.\033[0m"
5
+ return
6
+ end
4
7
 
5
8
  describe registry_key('HKLM\System\Test') do
6
9
  it { should exist }
@@ -54,13 +57,53 @@ describe registry_key('HKLM\Software\Policies\Microsoft\Internet Explorer\Main')
54
57
  its('isolation64bit') { should eq 1 }
55
58
  end
56
59
 
60
+ describe registry_key('HKLM\System\CurrentControlSet\Control\Lsa\MSV1_0') do
61
+ it { should exist }
62
+ its('NTLMMinServerSec') { should eq 537_395_200 }
63
+ its('NtlmMinServerSec') { should eq 537_395_200 }
64
+ end
65
+
57
66
  describe registry_key('HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services') do
58
67
  it { should exist }
59
68
  its('MinEncryptionLevel') { should eq 3 }
60
69
  end
61
70
 
62
- describe registry_key('HKLM\System\CurrentControlSet\Control\Lsa\MSV1_0') do
71
+ # test option hash
72
+ describe registry_key({
73
+ hive: 'HKLM',
74
+ key: 'SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services'
75
+ }) do
63
76
  it { should exist }
64
- its('NTLMMinServerSec') { should eq 537_395_200 }
65
- its('NtlmMinServerSec') { should eq 537_395_200 }
77
+ its('MinEncryptionLevel') { should eq 3 }
78
+ end
79
+
80
+ describe registry_key({
81
+ hive: 'HKEY_LOCAL_MACHINE',
82
+ key: 'SOFTWARE\Microsoft\SystemCertificates\Root\Certificates\8C941B34EA1EA6ED9AE2BC54CF687252B4C9B561'
83
+ }) do
84
+ it { should exist }
85
+ end
86
+
87
+ # test regular expressions in our match
88
+ describe registry_key({
89
+ hive: 'HKEY_LOCAL_MACHINE',
90
+ key: 'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
91
+ }) do
92
+ its('ProductName') { should match /^[a-zA-Z0-9\(\)\s]*2012\s[rR]2[a-zA-Z0-9\(\)\s]*$/ }
93
+ end
94
+
95
+ # verify all children via a regular expression
96
+ control 'regex-test' do
97
+ title "Ensure 'Always install with elevated privileges' is set to 'Disabled'"
98
+ children = registry_key({
99
+ hive: 'HKEY_USERS'
100
+ }).children(/^S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]{3,}\\Software\\Policies\\Microsoft\\Windows\\Installer/)
101
+ describe children do
102
+ it { should_not eq []}
103
+ end
104
+ children.each { |key|
105
+ describe registry_key(key) do
106
+ its('AlwaysInstallElevated') { should cmp 0 }
107
+ end
108
+ }
66
109
  end
@@ -1,6 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- return unless os.windows?
3
+ unless os.windows?
4
+ STDERR.puts "\033[1;33mTODO: Not running #{__FILE__} because we are not on Windows.\033[0m"
5
+ return
6
+ end
4
7
 
5
8
  describe security_policy do
6
9
  its('EnableAdminAccount') { should eq 1 }
@@ -1,6 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- return unless os.windows?
3
+ unless os.windows?
4
+ STDERR.puts "\033[1;33mTODO: Not running #{__FILE__} because we are not on Windows.\033[0m"
5
+ return
6
+ end
4
7
 
5
8
  # Get-WmiObject win32_service or Get-WmiObject -class win32_service
6
9
  # returns an array of service objects
@@ -0,0 +1,10 @@
1
+ name: complete
2
+ title: complete example profile
3
+ maintainer: Chef Software, Inc.
4
+ copyright: Chef Software, Inc.
5
+ copyright_email: support@chef.io
6
+ license: Proprietary, All rights reserved
7
+ summary: Testing stub
8
+ version: 1.0.0
9
+ supports:
10
+ - os-family: linux
@@ -0,0 +1,3 @@
1
+ class Tiny < Inspec.resource(1)
2
+ name 'tiny'
3
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ # author: Steven Danna <steve@chef.io>
3
+ #
4
+ require 'helper'
5
+ require 'rbconfig'
6
+ require 'mocha/test_unit'
7
+ require 'inspec/shell_detector'
8
+
9
+ module ShellDetectorTestHelpers
10
+ def no_proc
11
+ Dir.expects(:exist?).with('/proc').returns(false)
12
+ end
13
+
14
+ def with_proc(shell)
15
+ Dir.expects(:exist?).with('/proc').returns(true)
16
+ File.expects(:readlink).with("/proc/#{ppid}/exe").returns(shell)
17
+ end
18
+
19
+ def with_ps(output)
20
+ subject.expects(:'`').with("ps -cp #{ppid} -o command=").returns(output)
21
+ end
22
+
23
+ def with_env(shell)
24
+ ENV.expects(:[]).with('SHELL').returns(shell)
25
+ end
26
+
27
+ def with_pwuid(shell)
28
+ Process.expects(:uid).returns(9999)
29
+ @mock_user = Minitest::Mock.new
30
+ @mock_user.expect :shell, shell
31
+ Etc.expects(:getpwuid).with(9999).returns(@mock_user)
32
+ end
33
+ end
34
+
35
+ describe Inspec::ShellDetector do
36
+ include ShellDetectorTestHelpers
37
+
38
+ let(:subject) { Inspec::ShellDetector.new }
39
+ let(:ppid) { Process.ppid }
40
+
41
+ # Small hack to ensure we can test on windows
42
+ it "returns nil immediately if running on windows" do
43
+ RbConfig::CONFIG.expects(:[]).with('host_os').returns('mswin')
44
+ subject.shell!.must_equal(nil)
45
+ end
46
+
47
+ describe "not on windows" do
48
+ before do
49
+ RbConfig::CONFIG.expects(:[]).with('host_os').returns('beos')
50
+ end
51
+
52
+ it "detects the shell via /proc if it exists" do
53
+ with_proc("/usr/bin/fish")
54
+ subject.shell!.must_equal("fish")
55
+ end
56
+
57
+ it "detects via `ps` if /proc doesn't exist" do
58
+ no_proc; with_ps("/usr/bin/ksh")
59
+ subject.shell!.must_equal("ksh")
60
+ end
61
+
62
+ it "detects via ENV if parent process methods failed" do
63
+ no_proc; with_ps(""); with_env("fish")
64
+ subject.shell!.must_equal("fish")
65
+ end
66
+
67
+ it "detects via getpwuid if all else fails" do
68
+ no_proc; with_ps(""); with_env(""); with_pwuid("/usr/bin/fish")
69
+ subject.shell!.must_equal("fish")
70
+ @mock_user.verify
71
+ end
72
+
73
+ it "returns nil if the shell isn't in the whitelist" do
74
+ no_proc; with_ps(""); with_env("badshell"); with_pwuid("/usr/bin/badshell")
75
+ subject.shell!.must_equal(nil)
76
+ end
77
+ end
78
+ end
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: 0.29.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-08 00:00:00.000000000 Z
11
+ date: 2016-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train
@@ -156,6 +156,20 @@ dependencies:
156
156
  - - "~>"
157
157
  - !ruby/object:Gem::Version
158
158
  version: '3.4'
159
+ - !ruby/object:Gem::Dependency
160
+ name: molinillo
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.5'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '0.5'
159
173
  - !ruby/object:Gem::Dependency
160
174
  name: mocha
161
175
  requirement: !ruby/object:Gem::Requirement
@@ -190,7 +204,7 @@ files:
190
204
  - README.md
191
205
  - Rakefile
192
206
  - bin/inspec
193
- - docs/ctl_inspec.rst
207
+ - docs/cli.rst
194
208
  - docs/dsl_inspec.rst
195
209
  - docs/dsl_resource.rst
196
210
  - docs/inspec_and_friends.rst
@@ -273,8 +287,13 @@ files:
273
287
  - lib/inspec/backend.rb
274
288
  - lib/inspec/base_cli.rb
275
289
  - lib/inspec/cli.rb
290
+ - lib/inspec/completions/bash.sh.erb
291
+ - lib/inspec/completions/zsh.sh.erb
292
+ - lib/inspec/dependencies.rb
276
293
  - lib/inspec/describe.rb
277
294
  - lib/inspec/dsl.rb
295
+ - lib/inspec/env_printer.rb
296
+ - lib/inspec/errors.rb
278
297
  - lib/inspec/expect.rb
279
298
  - lib/inspec/fetcher.rb
280
299
  - lib/inspec/log.rb
@@ -307,6 +326,7 @@ files:
307
326
  - lib/inspec/secrets.rb
308
327
  - lib/inspec/secrets/yaml.rb
309
328
  - lib/inspec/shell.rb
329
+ - lib/inspec/shell_detector.rb
310
330
  - lib/inspec/source_reader.rb
311
331
  - lib/inspec/version.rb
312
332
  - lib/matchers/matchers.rb
@@ -377,6 +397,7 @@ files:
377
397
  - lib/utils/filter_array.rb
378
398
  - lib/utils/find_files.rb
379
399
  - lib/utils/hash.rb
400
+ - lib/utils/hash_map.rb
380
401
  - lib/utils/json_log.rb
381
402
  - lib/utils/modulator.rb
382
403
  - lib/utils/object_traversal.rb
@@ -384,6 +405,14 @@ files:
384
405
  - lib/utils/plugin_registry.rb
385
406
  - lib/utils/simpleconfig.rb
386
407
  - tasks/maintainers.rb
408
+ - test/bench/startup.flat.txt
409
+ - test/bench/startup.graph.html
410
+ - test/bench/startup.grind.dat
411
+ - test/bench/startup.stack.html
412
+ - test/bench/startup/startup.flat.txt
413
+ - test/bench/startup/startup.graph.html
414
+ - test/bench/startup/startup.grind.dat
415
+ - test/bench/startup/startup.stack.html
387
416
  - test/cookbooks/os_prepare/attributes/default.rb
388
417
  - test/cookbooks/os_prepare/files/empty.iso
389
418
  - test/cookbooks/os_prepare/files/example.csv
@@ -577,6 +606,8 @@ files:
577
606
  - test/unit/mock/profiles/library/inspec.yml
578
607
  - test/unit/mock/profiles/library/libraries/gordonlib.rb
579
608
  - test/unit/mock/profiles/library/libraries/testlib.rb
609
+ - test/unit/mock/profiles/resource-tiny/inspec.yml
610
+ - test/unit/mock/profiles/resource-tiny/libraries/resource.rb
580
611
  - test/unit/mock/profiles/simple-metadata/inspec.yml
581
612
  - test/unit/mock/profiles/skippy-profile-os/controls/one.rb
582
613
  - test/unit/mock/profiles/skippy-profile-os/inspec.yml
@@ -638,6 +669,7 @@ files:
638
669
  - test/unit/resources/xinetd_test.rb
639
670
  - test/unit/resources/yaml_test.rb
640
671
  - test/unit/resources/yum_test.rb
672
+ - test/unit/shell_detector_test.rb
641
673
  - test/unit/source_reader_test.rb
642
674
  - test/unit/source_readers/flat_test.rb
643
675
  - test/unit/source_readers/inspec_test.rb
@@ -667,11 +699,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
667
699
  version: '0'
668
700
  requirements: []
669
701
  rubyforge_project:
670
- rubygems_version: 2.4.6
702
+ rubygems_version: 2.5.1
671
703
  signing_key:
672
704
  specification_version: 4
673
705
  summary: Infrastructure and compliance testing.
674
706
  test_files:
707
+ - test/bench/startup.flat.txt
708
+ - test/bench/startup.graph.html
709
+ - test/bench/startup.grind.dat
710
+ - test/bench/startup.stack.html
711
+ - test/bench/startup/startup.flat.txt
712
+ - test/bench/startup/startup.graph.html
713
+ - test/bench/startup/startup.grind.dat
714
+ - test/bench/startup/startup.stack.html
675
715
  - test/cookbooks/os_prepare/attributes/default.rb
676
716
  - test/cookbooks/os_prepare/files/empty.iso
677
717
  - test/cookbooks/os_prepare/files/example.csv
@@ -865,6 +905,8 @@ test_files:
865
905
  - test/unit/mock/profiles/library/inspec.yml
866
906
  - test/unit/mock/profiles/library/libraries/gordonlib.rb
867
907
  - test/unit/mock/profiles/library/libraries/testlib.rb
908
+ - test/unit/mock/profiles/resource-tiny/inspec.yml
909
+ - test/unit/mock/profiles/resource-tiny/libraries/resource.rb
868
910
  - test/unit/mock/profiles/simple-metadata/inspec.yml
869
911
  - test/unit/mock/profiles/skippy-profile-os/controls/one.rb
870
912
  - test/unit/mock/profiles/skippy-profile-os/inspec.yml
@@ -926,6 +968,7 @@ test_files:
926
968
  - test/unit/resources/xinetd_test.rb
927
969
  - test/unit/resources/yaml_test.rb
928
970
  - test/unit/resources/yum_test.rb
971
+ - test/unit/shell_detector_test.rb
929
972
  - test/unit/source_reader_test.rb
930
973
  - test/unit/source_readers/flat_test.rb
931
974
  - test/unit/source_readers/inspec_test.rb