inspec 1.0.0 → 1.35.1

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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +933 -101
  3. data/Gemfile +8 -17
  4. data/MAINTAINERS.md +2 -1
  5. data/MAINTAINERS.toml +9 -4
  6. data/README.md +49 -28
  7. data/Rakefile +54 -39
  8. data/bin/inspec +1 -1
  9. data/docs/dsl_inspec.md +17 -4
  10. data/docs/dsl_resource.md +1 -1
  11. data/docs/habitat.md +192 -0
  12. data/docs/inspec_and_friends.md +4 -2
  13. data/docs/matchers.md +11 -0
  14. data/docs/migration.md +293 -0
  15. data/docs/profiles.md +124 -76
  16. data/docs/resources/aide_conf.md.erb +81 -0
  17. data/docs/resources/apache_conf.md.erb +10 -10
  18. data/docs/resources/apt.md.erb +13 -13
  19. data/docs/resources/audit_policy.md.erb +10 -10
  20. data/docs/resources/auditd_conf.md.erb +10 -10
  21. data/docs/resources/auditd_rules.md.erb +11 -11
  22. data/docs/resources/bash.md.erb +12 -12
  23. data/docs/resources/bond.md.erb +15 -15
  24. data/docs/resources/bridge.md.erb +11 -11
  25. data/docs/resources/bsd_service.md.erb +11 -11
  26. data/docs/resources/command.md.erb +23 -23
  27. data/docs/resources/crontab.md.erb +84 -0
  28. data/docs/resources/csv.md.erb +10 -10
  29. data/docs/resources/dh_params.md +191 -0
  30. data/docs/resources/directory.md.erb +8 -8
  31. data/docs/resources/docker.md.erb +160 -0
  32. data/docs/resources/docker_container.md.erb +89 -0
  33. data/docs/resources/docker_image.md.erb +86 -0
  34. data/docs/resources/etc_group.md.erb +16 -16
  35. data/docs/resources/etc_hosts.md.erb +62 -0
  36. data/docs/resources/file.md.erb +80 -58
  37. data/docs/resources/gem.md.erb +32 -13
  38. data/docs/resources/group.md.erb +12 -12
  39. data/docs/resources/grub_conf.md.erb +11 -11
  40. data/docs/resources/host.md.erb +27 -18
  41. data/docs/resources/http.md.erb +104 -0
  42. data/docs/resources/iis_app.md.erb +126 -0
  43. data/docs/resources/iis_site.md.erb +17 -17
  44. data/docs/resources/inetd_conf.md.erb +10 -10
  45. data/docs/resources/ini.md.erb +9 -9
  46. data/docs/resources/interface.md.erb +13 -13
  47. data/docs/resources/iptables.md.erb +21 -14
  48. data/docs/resources/json.md.erb +11 -11
  49. data/docs/resources/kernel_module.md.erb +73 -12
  50. data/docs/resources/kernel_parameter.md.erb +12 -12
  51. data/docs/resources/key_rsa.md.erb +70 -0
  52. data/docs/resources/launchd_service.md.erb +11 -11
  53. data/docs/resources/limits_conf.md.erb +10 -10
  54. data/docs/resources/login_def.md.erb +12 -12
  55. data/docs/resources/mount.md.erb +14 -14
  56. data/docs/resources/mssql_session.md.erb +62 -0
  57. data/docs/resources/mysql_conf.md.erb +14 -14
  58. data/docs/resources/mysql_session.md.erb +22 -10
  59. data/docs/resources/npm.md.erb +13 -13
  60. data/docs/resources/ntp_conf.md.erb +9 -9
  61. data/docs/resources/oneget.md.erb +11 -11
  62. data/docs/resources/oracledb_session.md.erb +53 -0
  63. data/docs/resources/os.md.erb +17 -17
  64. data/docs/resources/os_env.md.erb +12 -12
  65. data/docs/resources/package.md.erb +29 -16
  66. data/docs/resources/parse_config.md.erb +18 -18
  67. data/docs/resources/parse_config_file.md.erb +29 -23
  68. data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +17 -17
  69. data/docs/resources/pip.md.erb +15 -15
  70. data/docs/resources/port.md.erb +23 -23
  71. data/docs/resources/postgres_conf.md.erb +15 -13
  72. data/docs/resources/postgres_hba_conf.md.erb +104 -0
  73. data/docs/resources/postgres_ident_conf.md.erb +87 -0
  74. data/docs/resources/postgres_session.md.erb +28 -21
  75. data/docs/resources/powershell.md.erb +14 -14
  76. data/docs/resources/processes.md.erb +123 -0
  77. data/docs/resources/rabbitmq_config.md.erb +55 -0
  78. data/docs/resources/registry_key.md.erb +44 -21
  79. data/docs/resources/runit_service.md.erb +11 -11
  80. data/docs/resources/security_policy.md.erb +10 -10
  81. data/docs/resources/service.md.erb +18 -18
  82. data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +19 -19
  83. data/docs/resources/ssh_config.md.erb +13 -13
  84. data/docs/resources/sshd_config.md.erb +15 -15
  85. data/docs/resources/ssl.md.erb +13 -13
  86. data/docs/resources/sys_info.md.erb +10 -10
  87. data/docs/resources/systemd_service.md.erb +11 -11
  88. data/docs/resources/sysv_service.md.erb +11 -11
  89. data/docs/resources/upstart_service.md.erb +11 -11
  90. data/docs/resources/user.md.erb +20 -20
  91. data/docs/resources/users.md.erb +19 -19
  92. data/docs/resources/vbscript.md.erb +13 -13
  93. data/docs/resources/virtualization.md.erb +71 -0
  94. data/docs/resources/windows_feature.md.erb +10 -10
  95. data/docs/resources/windows_task.md.erb +103 -0
  96. data/docs/resources/wmi.md.erb +10 -10
  97. data/docs/resources/x509_certificate.md.erb +146 -0
  98. data/docs/resources/xinetd_conf.md.erb +17 -17
  99. data/docs/resources/xml.md.erb +75 -0
  100. data/docs/resources/yaml.md.erb +25 -11
  101. data/docs/resources/yum.md.erb +24 -16
  102. data/docs/resources/zfs_dataset.md.erb +63 -0
  103. data/docs/resources/zfs_pool.md.erb +57 -0
  104. data/docs/ruby_usage.md +50 -0
  105. data/docs/shared/matcher_cmp.md.erb +4 -6
  106. data/examples/inheritance/README.md +49 -3
  107. data/examples/inheritance/controls/example.rb +4 -1
  108. data/examples/inheritance/inspec.yml +3 -1
  109. data/examples/meta-profile/README.md +29 -3
  110. data/examples/meta-profile/controls/example.rb +10 -5
  111. data/examples/meta-profile/inspec.yml +4 -8
  112. data/examples/profile/README.md +1 -1
  113. data/examples/profile/controls/example.rb +0 -1
  114. data/examples/profile/controls/gordon.rb +0 -1
  115. data/examples/profile/inspec.yml +1 -1
  116. data/examples/profile-attribute/inspec.yml +1 -1
  117. data/examples/profile-sensitive/README.md +29 -0
  118. data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
  119. data/examples/profile-sensitive/controls/sensitive.rb +9 -0
  120. data/examples/profile-sensitive/inspec.yml +8 -0
  121. data/inspec.gemspec +10 -2
  122. data/lib/bundles/inspec-artifact/README.md +1 -0
  123. data/lib/bundles/inspec-artifact/cli.rb +284 -0
  124. data/lib/bundles/inspec-artifact.rb +7 -0
  125. data/lib/bundles/inspec-compliance/README.md +44 -15
  126. data/lib/bundles/inspec-compliance/api.rb +135 -29
  127. data/lib/bundles/inspec-compliance/cli.rb +128 -21
  128. data/lib/bundles/inspec-compliance/configuration.rb +8 -0
  129. data/lib/bundles/inspec-compliance/http.rb +26 -19
  130. data/lib/bundles/inspec-compliance/target.rb +50 -25
  131. data/lib/bundles/inspec-habitat/cli.rb +31 -0
  132. data/lib/bundles/inspec-habitat/log.rb +10 -0
  133. data/lib/bundles/inspec-habitat/profile.rb +372 -0
  134. data/lib/bundles/inspec-habitat.rb +12 -0
  135. data/lib/bundles/inspec-init/templates/profile/controls/example.rb +0 -1
  136. data/lib/bundles/inspec-init/templates/profile/inspec.yml +1 -1
  137. data/lib/bundles/inspec-supermarket/api.rb +7 -5
  138. data/lib/bundles/inspec-supermarket/cli.rb +2 -2
  139. data/lib/fetchers/git.rb +4 -2
  140. data/lib/fetchers/local.rb +4 -1
  141. data/lib/fetchers/url.rb +64 -10
  142. data/lib/inspec/archive/tar.rb +1 -1
  143. data/lib/inspec/backend.rb +26 -3
  144. data/lib/inspec/base_cli.rb +44 -7
  145. data/lib/inspec/cached_fetcher.rb +2 -2
  146. data/lib/inspec/cli.rb +34 -12
  147. data/lib/inspec/completions/fish.sh.erb +34 -0
  148. data/lib/inspec/control_eval_context.rb +0 -2
  149. data/lib/inspec/dependencies/cache.rb +0 -1
  150. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  151. data/lib/inspec/dependencies/lockfile.rb +13 -6
  152. data/lib/inspec/dependencies/requirement.rb +6 -6
  153. data/lib/inspec/dependencies/resolver.rb +4 -4
  154. data/lib/inspec/dsl.rb +3 -5
  155. data/lib/inspec/env_printer.rb +9 -1
  156. data/lib/inspec/exceptions.rb +8 -0
  157. data/lib/inspec/fetcher.rb +1 -1
  158. data/lib/inspec/file_provider.rb +47 -11
  159. data/lib/inspec/library_eval_context.rb +1 -1
  160. data/lib/inspec/metadata.rb +62 -18
  161. data/lib/inspec/method_source.rb +24 -0
  162. data/lib/inspec/objects/attribute.rb +21 -0
  163. data/lib/inspec/objects/control.rb +21 -3
  164. data/lib/inspec/objects/each_loop.rb +2 -1
  165. data/lib/inspec/objects/list.rb +1 -1
  166. data/lib/inspec/objects/or_test.rb +17 -2
  167. data/lib/inspec/objects/tag.rb +27 -0
  168. data/lib/inspec/objects/test.rb +9 -8
  169. data/lib/inspec/objects.rb +1 -0
  170. data/lib/inspec/plugins/fetcher.rb +4 -5
  171. data/lib/inspec/plugins/resource.rb +1 -2
  172. data/lib/inspec/plugins/source_reader.rb +3 -3
  173. data/lib/inspec/plugins.rb +1 -1
  174. data/lib/inspec/polyfill.rb +0 -1
  175. data/lib/inspec/profile.rb +92 -23
  176. data/lib/inspec/profile_context.rb +1 -1
  177. data/lib/inspec/profile_vendor.rb +66 -0
  178. data/lib/inspec/resource.rb +29 -3
  179. data/lib/inspec/rspec_json_formatter.rb +672 -285
  180. data/lib/inspec/rule.rb +12 -16
  181. data/lib/inspec/runner.rb +26 -15
  182. data/lib/inspec/runner_rspec.rb +1 -0
  183. data/lib/inspec/runtime_profile.rb +26 -0
  184. data/lib/inspec/schema.rb +185 -0
  185. data/lib/inspec/secrets/yaml.rb +8 -1
  186. data/lib/inspec/secrets.rb +1 -1
  187. data/lib/inspec/shell.rb +92 -22
  188. data/lib/inspec/source_reader.rb +1 -1
  189. data/lib/inspec/version.rb +1 -1
  190. data/lib/inspec.rb +1 -1
  191. data/lib/matchers/matchers.rb +67 -17
  192. data/lib/resources/aide_conf.rb +162 -0
  193. data/lib/resources/apache.rb +0 -1
  194. data/lib/resources/apache_conf.rb +3 -3
  195. data/lib/resources/apt.rb +2 -1
  196. data/lib/resources/audit_policy.rb +0 -1
  197. data/lib/resources/auditd_conf.rb +1 -2
  198. data/lib/resources/auditd_rules.rb +5 -5
  199. data/lib/resources/bond.rb +1 -1
  200. data/lib/resources/bridge.rb +1 -1
  201. data/lib/resources/command.rb +5 -3
  202. data/lib/resources/crontab.rb +102 -0
  203. data/lib/resources/csv.rb +18 -1
  204. data/lib/resources/dh_params.rb +84 -0
  205. data/lib/resources/directory.rb +7 -3
  206. data/lib/resources/docker.rb +198 -0
  207. data/lib/resources/docker_container.rb +114 -0
  208. data/lib/resources/docker_image.rb +96 -0
  209. data/lib/resources/etc_group.rb +2 -3
  210. data/lib/resources/etc_hosts.rb +81 -0
  211. data/lib/resources/file.rb +88 -30
  212. data/lib/resources/gem.rb +26 -6
  213. data/lib/resources/groups.rb +4 -5
  214. data/lib/resources/grub_conf.rb +3 -4
  215. data/lib/resources/host.rb +175 -35
  216. data/lib/resources/http.rb +73 -0
  217. data/lib/resources/iis_app.rb +103 -0
  218. data/lib/resources/iis_site.rb +23 -0
  219. data/lib/resources/inetd_conf.rb +9 -4
  220. data/lib/resources/interface.rb +4 -3
  221. data/lib/resources/json.rb +37 -14
  222. data/lib/resources/kernel_module.rb +77 -10
  223. data/lib/resources/kernel_parameter.rb +0 -1
  224. data/lib/resources/key_rsa.rb +67 -0
  225. data/lib/resources/limits_conf.rb +3 -4
  226. data/lib/resources/login_def.rb +2 -3
  227. data/lib/resources/mount.rb +34 -4
  228. data/lib/resources/mssql_session.rb +81 -12
  229. data/lib/resources/mysql.rb +0 -1
  230. data/lib/resources/mysql_conf.rb +1 -2
  231. data/lib/resources/mysql_session.rb +18 -7
  232. data/lib/resources/nginx_conf.rb +95 -0
  233. data/lib/resources/ntp_conf.rb +2 -3
  234. data/lib/resources/oracledb_session.rb +138 -0
  235. data/lib/resources/os_env.rb +7 -5
  236. data/lib/resources/package.rb +70 -13
  237. data/lib/resources/packages.rb +111 -0
  238. data/lib/resources/parse_config.rb +3 -4
  239. data/lib/resources/passwd.rb +4 -5
  240. data/lib/resources/pip.rb +29 -21
  241. data/lib/resources/port.rb +133 -14
  242. data/lib/resources/postgres.rb +53 -43
  243. data/lib/resources/postgres_conf.rb +40 -13
  244. data/lib/resources/postgres_hba_conf.rb +101 -0
  245. data/lib/resources/postgres_ident_conf.rb +79 -0
  246. data/lib/resources/postgres_session.rb +28 -20
  247. data/lib/resources/powershell.rb +2 -2
  248. data/lib/resources/processes.rb +61 -26
  249. data/lib/resources/rabbitmq_conf.rb +53 -0
  250. data/lib/resources/registry_key.rb +34 -11
  251. data/lib/resources/security_policy.rb +2 -2
  252. data/lib/resources/service.rb +17 -7
  253. data/lib/resources/ssh_conf.rb +17 -6
  254. data/lib/resources/ssl.rb +3 -4
  255. data/lib/resources/sys_info.rb +1 -1
  256. data/lib/resources/toml.rb +26 -0
  257. data/lib/resources/users.rb +41 -41
  258. data/lib/resources/virtualization.rb +252 -0
  259. data/lib/resources/windows_task.rb +106 -0
  260. data/lib/resources/x509_certificate.rb +143 -0
  261. data/lib/resources/xinetd.rb +18 -2
  262. data/lib/resources/xml.rb +27 -0
  263. data/lib/resources/yaml.rb +10 -2
  264. data/lib/resources/yum.rb +25 -7
  265. data/lib/resources/zfs_dataset.rb +60 -0
  266. data/lib/resources/zfs_pool.rb +49 -0
  267. data/lib/source_readers/flat.rb +2 -1
  268. data/lib/source_readers/inspec.rb +30 -11
  269. data/lib/utils/command_wrapper.rb +3 -3
  270. data/lib/utils/database_helpers.rb +77 -0
  271. data/lib/utils/erlang_parser.rb +192 -0
  272. data/lib/utils/filter.rb +2 -1
  273. data/lib/utils/filter_array.rb +0 -1
  274. data/lib/utils/find_files.rb +1 -2
  275. data/lib/utils/latest_version.rb +21 -0
  276. data/lib/utils/nginx_parser.rb +76 -0
  277. data/lib/utils/object_traversal.rb +5 -4
  278. data/lib/utils/parser.rb +23 -4
  279. data/lib/utils/plugin_registry.rb +3 -3
  280. data/lib/utils/simpleconfig.rb +25 -8
  281. data/lib/utils/spdx.rb +13 -0
  282. data/lib/utils/spdx.txt +344 -0
  283. metadata +168 -22
  284. data/docs/cli.md +0 -342
  285. data/docs/resources/process.md.erb +0 -73
  286. /data/docs/{plugin_kitchen_inspec.html.md → plugin_kitchen_inspec.md} +0 -0
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+ # author: Gary Bright @username-is-already-taken2
3
+ # author: Chris Beard @cdbeard2016
4
+ module Inspec::Resources
5
+ class WindowsTasks < Inspec.resource(1)
6
+ name 'windows_task'
7
+ desc 'Use the windows_task InSpec audit resource to test task schedules on Microsoft Windows.'
8
+ example "
9
+ describe windows_task('\\Microsoft\\Windows\\Time Synchronization\\SynchronizeTime') do
10
+ it { should be_enabled }
11
+ end
12
+
13
+ describe windows_task('\\Microsoft\\Windows\\AppID\\PolicyConverter') do
14
+ it { should be_disabled }
15
+ end
16
+
17
+ describe windows_task('\\Microsoft\\Windows\\Defrag\\ScheduledDefrag') do
18
+ it { should exist }
19
+ end
20
+
21
+ describe windows_task('\\Microsoft\\Windows\\AppID\\PolicyConverter') do
22
+ its('logon_mode') { should eq 'Interactive/Background' }
23
+ its('last_result') { should eq '1' }
24
+ its('task_to_run') { should cmp '%Windir%\\system32\\appidpolicyconverter.exe' }
25
+ its('run_as_user') { should eq 'LOCAL SERVICE' }
26
+ end
27
+ "
28
+
29
+ def initialize(taskuri)
30
+ @taskuri = taskuri
31
+ @cache = nil
32
+
33
+ # verify that this resource is only supported on Windows
34
+ return skip_resource 'The `windows_task` resource is not supported on your OS.' unless inspec.os.windows?
35
+ end
36
+
37
+ def exists?
38
+ return true unless info.nil? || info[:uri].nil?
39
+ false
40
+ end
41
+
42
+ # rubocop:disable Style/WordArray
43
+ def enabled?
44
+ return false if info.nil? || info[:state].nil?
45
+ ['Ready', 'Running'].include?(info[:state])
46
+ end
47
+
48
+ def disabled?
49
+ return false if info.nil? || info[:state].nil?
50
+ info[:scheduled_task_state] == 'Disabled' || info[:state] == 'Disabled'
51
+ end
52
+
53
+ def logon_mode
54
+ info[:logon_mode]
55
+ end
56
+
57
+ def last_result
58
+ info[:last_result]
59
+ end
60
+
61
+ def task_to_run
62
+ info[:task_to_run].to_s.strip
63
+ end
64
+
65
+ def run_as_user
66
+ info[:run_as_user]
67
+ end
68
+
69
+ def type
70
+ info[:type] unless info.nil?
71
+ end
72
+
73
+ def info
74
+ return @cache unless @cache.nil?
75
+ # PowerShell v5 has Get-ScheduledTask cmdlet,
76
+ # _using something with backward support to v3_
77
+ # script = "Get-ScheduledTask | ? { $_.URI -eq '#{@taskuri}' } | Select-Object URI,@{N='State';E={$_.State.ToString()}} | ConvertTo-Json"
78
+
79
+ # Using schtasks as suggested by @modille but aligning property names to match cmdlet to future proof.
80
+ script = "schtasks /query /v /fo csv /tn '#{@taskuri}' | ConvertFrom-Csv | Select @{N='URI';E={$_.TaskName}},@{N='State';E={$_.Status.ToString()}},'Logon Mode','Last Result','Task To Run','Run As User','Scheduled Task State' | ConvertTo-Json -Compress"
81
+
82
+ cmd = inspec.powershell(script)
83
+
84
+ begin
85
+ params = JSON.parse(cmd.stdout)
86
+ rescue JSON::ParserError => _e
87
+ return nil
88
+ end
89
+
90
+ @cache = {
91
+ uri: params['URI'],
92
+ state: params['State'],
93
+ logon_mode: params['Logon Mode'],
94
+ last_result: params['Last Result'],
95
+ task_to_run: params['Task To Run'],
96
+ run_as_user: params['Run As User'],
97
+ scheduled_task_state: params['Scheduled Task State'],
98
+ type: 'windows-task',
99
+ }
100
+ end
101
+
102
+ def to_s
103
+ "Windows Task '#{@taskuri}'"
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+ # author: Richard Nixon
3
+ # author: Christoph Hartmann
4
+
5
+ require 'openssl'
6
+ require 'hashie/mash'
7
+
8
+ module Inspec::Resources
9
+ class X509CertificateResource < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
10
+ name 'x509_certificate'
11
+ desc 'Used to test x.509 certificates'
12
+ example "
13
+ describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do
14
+ its('subject') { should match /CN=My Website/ }
15
+ its('validity_in_days') { should be > 30 }
16
+ end
17
+
18
+ describe x509_certificate('trials/x509/cert.pem') do
19
+ it { should be_certificate }
20
+ it { should be_valid }
21
+ its('fingerprint') { should eq '62b137bdf427e7273dc2e487877b3033e4c8ce17' }
22
+ its('signature_algorithm') { should eq 'sha1WithRSAEncryption' }
23
+ its('validity_in_days') { should_not be < 100 }
24
+ its('validity_in_days') { should be >= 100 }
25
+ its('subject_dn') { should eq '/C=DE/ST=Berlin/L=Berlin/O=InSpec/OU=Chef Software, Inc/CN=inspec.io/emailAddress=support@chef.io' }
26
+ its('subject.C') { should eq 'DE' }
27
+ its('subject.emailAddress') { should_not be_empty }
28
+ its('subject.emailAddress') { should eq 'support@chef.io' }
29
+ its('issuer_dn') { should eq '/C=DE/ST=Berlin/L=Berlin/O=InSpec/OU=Chef Software, Inc/CN=inspec.io/emailAddress=support@chef.io' }
30
+ its('key_length') { should be >= 2048 }
31
+ its('extensions.subjectKeyIdentifier') { should cmp 'A5:16:0B:12:F4:48:0F:06:6C:32:29:67:98:12:DF:3D:0D:75:9D:5C' }
32
+ end
33
+ "
34
+
35
+ # @see https://tools.ietf.org/html/rfc5280#page-23
36
+ def initialize(filename)
37
+ @certpath = filename
38
+ @issuer = nil
39
+ @parsed_subject = nil
40
+ @parsed_issuer = nil
41
+ @extensions = nil
42
+
43
+ file = inspec.file(@certpath)
44
+ return skip_resource "Unable to find certificate file #{@certpath}" unless file.exist?
45
+
46
+ begin
47
+ @cert = OpenSSL::X509::Certificate.new file.content
48
+ rescue OpenSSL::X509::CertificateError
49
+ @cert = nil
50
+ return skip_resource "Unable to load certificate #{@certpath}"
51
+ end
52
+ end
53
+
54
+ # Forward these methods directly to OpenSSL::X509::Certificate instance
55
+ %w{version not_before not_after signature_algorithm public_key }.each do |m|
56
+ define_method m.to_sym do |*args|
57
+ @cert.method(m.to_sym).call(*args)
58
+ end
59
+ end
60
+
61
+ def certificate?
62
+ !@cert.nil?
63
+ end
64
+
65
+ def fingerprint
66
+ return if @cert.nil?
67
+ OpenSSL::Digest::SHA1.new(@cert.to_der).to_s
68
+ end
69
+
70
+ def serial
71
+ return if @cert.nil?
72
+ @cert.serial.to_i
73
+ end
74
+
75
+ def subject_dn
76
+ return if @cert.nil?
77
+ @cert.subject.to_s
78
+ end
79
+
80
+ def subject
81
+ return if @cert.nil?
82
+ # Return cached subject if we have already parsed it
83
+ return @parsed_subject if @parsed_subject
84
+ # Use a Mash to make it easier to access hash elements in "its('subject') {should ...}"
85
+ @parsed_subject = Hashie::Mash.new(Hash[@cert.subject.to_a.map { |k, v, _| [k, v] }])
86
+ end
87
+
88
+ def issuer_dn
89
+ return if @cert.nil?
90
+ @cert.issuer.to_s
91
+ end
92
+
93
+ def issuer
94
+ return if @cert.nil?
95
+ # Return cached subject if we have already parsed it
96
+ return @parsed_issuer if @parsed_issuer
97
+ # Use a Mash to make it easier to access hash elements in "its('issuer') {should ...}"
98
+ @parsed_issuer = Hashie::Mash.new(Hash[@cert.issuer.to_a.map { |k, v, _| [k, v] }])
99
+ end
100
+
101
+ def key_length
102
+ return if @cert.nil?
103
+ @cert.public_key.n.num_bytes * 8
104
+ end
105
+
106
+ def validity_in_days
107
+ (not_after - Time.now.utc) / 86400
108
+ end
109
+
110
+ def valid?
111
+ now = Time.now
112
+ certificate? && (now >= not_before && now <= not_after)
113
+ end
114
+
115
+ def extensions
116
+ # Return cached Mash if we already parsed the certificate extensions
117
+ return @extensions if @extensions
118
+ # Return the exception class if we failed to instantiate a Cert from file
119
+ return @cert unless @cert.respond_to? :extensions
120
+ # Use a Mash to make it easier to access hash elements in "its('entensions') {should ...}"
121
+ @extensions = Hashie::Mash.new({})
122
+ # Make sure standard extensions exist so we don't get nil for nil:NilClass
123
+ # when the user tests for extensions which aren't present
124
+ %w{
125
+ keyUsage extendedKeyUsage basicConstraints subjectKeyIdentifier
126
+ authorityKeyIdentifier subjectAltName issuerAltName authorityInfoAccess
127
+ crlDistributionPoints issuingDistributionPoint certificatePolicies
128
+ policyConstraints nameConstraints noCheck tlsfeature nsComment
129
+ }.each { |extension| @extensions[extension] ||= [] }
130
+ # Now parse the extensions into the Mash
131
+ extension_array = @cert.extensions.map(&:to_s)
132
+ extension_array.each do |extension|
133
+ kv = extension.split(/ *= */, 2)
134
+ @extensions[kv.first] = kv.last.split(/ *, */)
135
+ end
136
+ @extensions
137
+ end
138
+
139
+ def to_s
140
+ "x509_certificate #{@certpath}"
141
+ end
142
+ end
143
+ end
@@ -41,6 +41,7 @@ module Inspec::Resources
41
41
  .add(:ids, field: 'id')
42
42
  .add(:socket_types, field: 'socket_type')
43
43
  .add(:types, field: 'type')
44
+ .add(:protocols, field: 'protocol')
44
45
  .add(:wait, field: 'wait')
45
46
  .add(:disabled?) { |x| x.where('disable' => 'no').services.empty? }
46
47
  .add(:enabled?) { |x| x.where('disable' => 'yes').services.empty? }
@@ -56,7 +57,7 @@ module Inspec::Resources
56
57
  end
57
58
 
58
59
  @contents[path] = file.content
59
- if @contents[path].empty? && file.size > 0
60
+ if @contents[path].nil? || @contents[path].empty?
60
61
  return skip_resource "Can't read file \"#{path}\""
61
62
  end
62
63
 
@@ -91,8 +92,23 @@ module Inspec::Resources
91
92
  params
92
93
  end
93
94
 
95
+ # Method used to derive the default protocol used from the socket_type
96
+ def default_protocol(type)
97
+ case type
98
+ when 'stream'
99
+ 'tcp'
100
+ when 'dgram'
101
+ 'udp'
102
+ else
103
+ 'unknown'
104
+ end
105
+ end
106
+
94
107
  def service_lines
95
- @services ||= params['services'].values.flatten.map(&:params)
108
+ @services ||= params['services'].values.flatten.map { |service|
109
+ service.params['protocol'] ||= default_protocol(service.params['socket_type'])
110
+ service.params
111
+ }
96
112
  end
97
113
  end
98
114
  end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ # author: Jonathan Morley
3
+
4
+ module Inspec::Resources
5
+ class XmlConfig < JsonConfig
6
+ name 'xml'
7
+ desc 'Use the xml InSpec resource to test configuration data in an XML file'
8
+ example "
9
+ describe xml('default.xml') do
10
+ its('key/sub_key') { should eq(['value']) }
11
+ end
12
+ "
13
+
14
+ def parse(content)
15
+ require 'rexml/document'
16
+ REXML::Document.new(content)
17
+ end
18
+
19
+ def value(key)
20
+ REXML::XPath.each(@params, key.first.to_s).map(&:text)
21
+ end
22
+
23
+ def to_s
24
+ "XML #{@path}"
25
+ end
26
+ end
27
+ end
@@ -14,8 +14,16 @@ module Inspec::Resources
14
14
  name 'yaml'
15
15
  desc 'Use the yaml InSpec audit resource to test configuration data in a YAML file.'
16
16
  example "
17
- describe yaml do
18
- its('name') { should eq 'foo' }
17
+ describe yaml('config.yaml') do
18
+ its(['driver', 'name']) { should eq 'vagrant' }
19
+ end
20
+
21
+ describe yaml({ command: 'retrieve_data.py --yaml' }) do
22
+ its('state') { should eq 'open' }
23
+ end
24
+
25
+ describe yaml({ content: \"key1: value1\nkey2: value2\" }) do
26
+ its('key2') { should cmp 'value2' }
19
27
  end
20
28
  "
21
29
 
data/lib/resources/yum.rb CHANGED
@@ -2,8 +2,6 @@
2
2
  # author: Christoph Hartmann
3
3
  # author: Dominik Richter
4
4
 
5
- require 'resources/file'
6
-
7
5
  # Usage:
8
6
  # describe yum do
9
7
  # its('repos') { should exist }
@@ -22,6 +20,7 @@ require 'resources/file'
22
20
  # describe yum.repo('epel') do
23
21
  # it { should exist }
24
22
  # it { should be_enabled }
23
+ # its('baseurl') { should include 'mycompany.biz' }
25
24
  # end
26
25
  #
27
26
  # deprecated:
@@ -33,7 +32,7 @@ require 'resources/file'
33
32
  module Inspec::Resources
34
33
  class Yum < Inspec.resource(1)
35
34
  name 'yum'
36
- desc 'Use the yum InSpec audit resource to test packages in the Yum repository.'
35
+ desc 'Use the yum InSpec audit resource to test the configuration of Yum repositories.'
37
36
  example "
38
37
  describe yum.repo('name') do
39
38
  it { should exist }
@@ -121,19 +120,38 @@ module Inspec::Resources
121
120
  def info
122
121
  return @cache if defined?(@cache)
123
122
  selection = @yum.repositories.select { |e| e['id'] == @reponame || shortname(e['id']) == @reponame }
124
- @cache = selection[0] if !selection.nil? && selection.length == 1
123
+ @cache = selection.empty? ? {} : selection.first
125
124
  @cache
126
125
  end
127
126
 
128
127
  def exist?
129
- !info.nil?
128
+ !info.empty?
130
129
  end
131
130
 
132
131
  def enabled?
133
- repo = info
134
- return false if repo.nil?
132
+ return false unless exist?
135
133
  info['status'] == 'enabled'
136
134
  end
135
+
136
+ # provide a method for each of the repo metadata items we know about
137
+ [
138
+ :baseurl,
139
+ :expire,
140
+ :filename,
141
+ :mirrors,
142
+ :pkgs,
143
+ :size,
144
+ :status,
145
+ :updated,
146
+ ].each do |key|
147
+ define_method key do
148
+ info[key.to_s]
149
+ end
150
+ end
151
+
152
+ def to_s
153
+ "YumRepo #{@reponame}"
154
+ end
137
155
  end
138
156
 
139
157
  # for compatability with serverspec
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ # author: Joseph Benden
3
+
4
+ module Inspec::Resources
5
+ class ZfsDataset < Inspec.resource(1)
6
+ name 'zfs_dataset'
7
+ desc "
8
+ Use the zfs_dataset InSpec audit resource to test if the named
9
+ ZFS Dataset is present and/or has certain properties.
10
+ "
11
+ example "
12
+ describe zfs_dataset('tank/tmp') do
13
+ its('exec') { should eq('off') }
14
+ its('setuid') { should eq('off') }
15
+ end
16
+ "
17
+
18
+ def initialize(zfs_dataset)
19
+ return skip_resource 'The `zfs_dataset` resource is not supported on your OS yet.' if !inspec.os.bsd?
20
+ @zfs_dataset = zfs_dataset
21
+
22
+ @params = gather
23
+ end
24
+
25
+ # method called by 'it { should exist }'
26
+ def exists?
27
+ inspec.command("/sbin/zfs get -Hp all #{@zfs_dataset}").exit_status == 0
28
+ end
29
+
30
+ def mounted?
31
+ return false if !exists?
32
+ inspec.mount(@params['mountpoint']).mounted?
33
+ end
34
+
35
+ def to_s
36
+ "ZFS Dataset #{@zfs_dataset}"
37
+ end
38
+
39
+ def gather
40
+ cmd = inspec.command("/sbin/zfs get -Hp all #{@zfs_dataset}")
41
+ return nil if cmd.exit_status.to_i != 0
42
+
43
+ # parse data
44
+ cmd.stdout.chomp.split("\n").each_with_object(Hash.new(0)) do |line, h|
45
+ t = line.split("\t")
46
+ h[t[1].to_s] = t[2].to_s
47
+ end
48
+ end
49
+
50
+ # override method
51
+ def exec
52
+ @params['exec']
53
+ end
54
+
55
+ # expose all parameters
56
+ def method_missing(name)
57
+ @params[name.to_s]
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ # author: Joseph Benden
3
+
4
+ module Inspec::Resources
5
+ class ZfsPool < Inspec.resource(1)
6
+ name 'zfs_pool'
7
+ desc "
8
+ Use the zfs_pool InSpec audit resource to test if the named
9
+ ZFS Pool is present and/or has certain properties.
10
+ "
11
+ example "
12
+ describe zfs_pool('tank') do
13
+ its('failmode') { should eq('continue') }
14
+ end
15
+ "
16
+
17
+ def initialize(zfs_pool)
18
+ return skip_resource 'The `zfs_pool` resource is not supported on your OS yet.' if !inspec.os.bsd?
19
+ @zfs_pool = zfs_pool
20
+
21
+ @params = gather
22
+ end
23
+
24
+ # method called by 'it { should exist }'
25
+ def exists?
26
+ inspec.command("/sbin/zpool get -Hp all #{@zfs_pool}").exit_status == 0
27
+ end
28
+
29
+ def to_s
30
+ "ZFS Pool #{@zfs_pool}"
31
+ end
32
+
33
+ def gather
34
+ cmd = inspec.command("/sbin/zpool get -Hp all #{@zfs_pool}")
35
+ return nil if cmd.exit_status.to_i != 0
36
+
37
+ # parse data
38
+ cmd.stdout.chomp.split("\n").each_with_object(Hash.new(0)) do |line, h|
39
+ t = line.split("\t")
40
+ h[t[1].to_s] = t[2].to_s
41
+ end
42
+ end
43
+
44
+ # expose all parameters
45
+ def method_missing(name)
46
+ @params[name.to_s]
47
+ end
48
+ end
49
+ end
@@ -20,13 +20,14 @@ module SourceReaders
20
20
  new(target, files)
21
21
  end
22
22
 
23
- attr_reader :metadata, :tests, :libraries
23
+ attr_reader :metadata, :tests, :libraries, :data_files
24
24
 
25
25
  def initialize(target, files)
26
26
  @target = target
27
27
  @metadata = ::Inspec::Metadata.new(nil)
28
28
  @tests = load_tests(files)
29
29
  @libraries = {}
30
+ @data_files = {}
30
31
  end
31
32
 
32
33
  private
@@ -23,33 +23,52 @@ module SourceReaders
23
23
  nil
24
24
  end
25
25
 
26
- attr_reader :metadata, :tests, :libraries
26
+ attr_reader :metadata, :tests, :libraries, :data_files
27
27
 
28
+ # This create a new instance of an InSpec profile source reader
29
+ #
30
+ # @param [FileProvider] target An instance of a FileProvider object that can list files and read them
31
+ # @param [String] metadata_source eg. inspec.yml or metadata.rb
28
32
  def initialize(target, metadata_source)
29
- @target = target
30
- @metadata = Inspec::Metadata.from_ref(
33
+ @target = target
34
+ @metadata = load_metadata(metadata_source)
35
+ @tests = load_tests
36
+ @libraries = load_libs
37
+ @data_files = load_data_files
38
+ end
39
+
40
+ private
41
+
42
+ def load_metadata(metadata_source)
43
+ Inspec::Metadata.from_ref(
31
44
  metadata_source,
32
45
  @target.read(metadata_source),
33
46
  nil)
34
-
35
- @tests = load_tests
36
- @libraries = load_libs
47
+ rescue Psych::SyntaxError => e
48
+ raise "Unable to parse inspec.yml: line #{e.line}, #{e.problem} #{e.context}"
49
+ rescue => e
50
+ raise "Unable to parse #{metadata_source}: #{e.class} -- #{e.message}"
37
51
  end
38
52
 
39
- private
40
-
41
53
  def load_tests
42
54
  tests = @target.files.find_all do |path|
43
- path.start_with?('controls', 'test') && path.end_with?('.rb')
55
+ path.start_with?('controls') && path.end_with?('.rb')
44
56
  end
45
- Hash[tests.map { |x| [x, @target.read(x)] }]
57
+ Hash[tests.map { |x| [x, @target.read(x)] }.delete_if { |_file, contents| contents.nil? }]
46
58
  end
47
59
 
48
60
  def load_libs
49
61
  tests = @target.files.find_all do |path|
50
62
  path.start_with?('libraries') && path.end_with?('.rb')
51
63
  end
52
- Hash[tests.map { |x| [x, @target.read(x)] }]
64
+ Hash[tests.map { |x| [x, @target.read(x)] }.delete_if { |_file, contents| contents.nil? }]
65
+ end
66
+
67
+ def load_data_files
68
+ files = @target.files.find_all do |path|
69
+ path.start_with?('files' + File::SEPARATOR)
70
+ end
71
+ Hash[files.map { |x| [x, @target.read(x)] }.delete_if { |_file, contents| contents.nil? }]
53
72
  end
54
73
  end
55
74
  end
@@ -9,20 +9,20 @@ class CommandWrapper
9
9
 
10
10
  def self.wrap(cmd, options)
11
11
  unless options.is_a?(Hash)
12
- fail 'All options for the command wrapper must be provided as a hash. '\
12
+ raise 'All options for the command wrapper must be provided as a hash. '\
13
13
  "You entered: #{options.inspect}. Please consult the documentation."
14
14
  end
15
15
 
16
16
  wrap = options[:wrap]
17
17
  if !wrap.nil? && !wrap.is_a?(Proc)
18
- fail "Called command wrapper with wrap: #{wrap.inspect}. It must be called with a Proc."
18
+ raise "Called command wrapper with wrap: #{wrap.inspect}. It must be called with a Proc."
19
19
  elsif !wrap.nil?
20
20
  return wrap.call(cmd)
21
21
  end
22
22
 
23
23
  shell = options[:shell]
24
24
  unless UNIX_SHELLS.include?(shell)
25
- fail "Don't know how to wrap commands for shell: #{shell.inspect}."
25
+ raise "Don't know how to wrap commands for shell: #{shell.inspect}."
26
26
  end
27
27
 
28
28
  path = options[:path] || shell