inspec 0.29.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
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