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
@@ -2,7 +2,6 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
- # license: All rights reserved
6
5
 
7
6
  RSpec::Matchers.define :be_readable do
8
7
  match do |file|
@@ -70,7 +69,7 @@ end
70
69
  # matcher to check /etc/passwd, /etc/shadow and /etc/group
71
70
  RSpec::Matchers.define :contain_legacy_plus do
72
71
  match do |file|
73
- warn '[DEPRECATION] `contain_legacy_plus` is deprecated and will be removed for InSpec 1.0. Please use `describe file(\'/etc/passwd\') do its(\'content\') { should_not match /^\+:/ } end`'
72
+ warn '[DEPRECATION] `contain_legacy_plus` is deprecated and will be removed in the next major version. Please use `describe file(\'/etc/passwd\') do its(\'content\') { should_not match /^\+:/ } end`'
74
73
  file.content =~ /^\+:/
75
74
  end
76
75
  end
@@ -78,7 +77,7 @@ end
78
77
  # verifies that no entry in an array contains a value
79
78
  RSpec::Matchers.define :contain_match do |regex|
80
79
  match do |arr|
81
- warn '[DEPRECATION] `contain_match` is deprecated and will be removed for InSpec 1.0. See https://github.com/chef/inspec/issues/738 for more details'
80
+ warn '[DEPRECATION] `contain_match` is deprecated and will be removed in the next major version. See https://github.com/chef/inspec/issues/738 for more details'
82
81
  arr.inject { |result, i|
83
82
  result = i.match(regex)
84
83
  result || i.match(/$/)
@@ -88,7 +87,6 @@ end
88
87
 
89
88
  RSpec::Matchers.define :contain_duplicates do
90
89
  match do |arr|
91
- warn '[DEPRECATION] `contain_duplicates` is deprecated and will be removed for InSpec 1.0. See https://github.com/chef/inspec/issues/738 for more details'
92
90
  dup = arr.select { |element| arr.count(element) > 1 }
93
91
  !dup.uniq.empty?
94
92
  end
@@ -105,7 +103,7 @@ RSpec::Matchers.define :be_installed do
105
103
  end
106
104
 
107
105
  chain :by do
108
- fail "[UNSUPPORTED] Please use the new resources 'gem', 'npm' or 'pip'."
106
+ raise "[UNSUPPORTED] Please use the new resources 'gem', 'npm' or 'pip'."
109
107
  end
110
108
 
111
109
  chain :with_version do |version|
@@ -121,7 +119,7 @@ RSpec::Matchers.define :be_enabled do
121
119
  end
122
120
 
123
121
  chain :with_level do |_level|
124
- fail '[UNSUPPORTED] with level is not supported'
122
+ raise '[UNSUPPORTED] with level is not supported'
125
123
  end
126
124
 
127
125
  failure_message do |service|
@@ -137,7 +135,7 @@ RSpec::Matchers.define :be_running do
137
135
  end
138
136
 
139
137
  chain :under do |_under|
140
- fail '[UNSUPPORTED] under is not supported'
138
+ raise '[UNSUPPORTED] under is not supported'
141
139
  end
142
140
 
143
141
  failure_message do |service|
@@ -178,7 +176,7 @@ RSpec::Matchers.define :be_reachable do
178
176
  end
179
177
 
180
178
  chain :with do |_attr|
181
- fail '[UNSUPPORTED] `with` is not supported in combination with `be_reachable`'
179
+ raise '[UNSUPPORTED] `with` is not supported in combination with `be_reachable`'
182
180
  end
183
181
 
184
182
  failure_message do |host|
@@ -193,7 +191,7 @@ RSpec::Matchers.define :be_resolvable do
193
191
  end
194
192
 
195
193
  chain :by do |_type|
196
- fail "[UNSUPPORTED] `by` is not supported in combination with `be_resolvable`. Please use the following syntax `host('example.com', port: 53, proto: 'udp')`."
194
+ raise "[UNSUPPORTED] `by` is not supported in combination with `be_resolvable`. Please use the following syntax `host('example.com', port: 53, proto: 'udp')`."
197
195
  end
198
196
 
199
197
  failure_message do |host|
@@ -208,18 +206,57 @@ RSpec::Matchers.define :have_rule do |rule|
208
206
  end
209
207
 
210
208
  chain :with_table do |_table|
211
- fail "[UNSUPPORTED] `with_table` is not supported in combination with `have_rule`. Please use the following syntax `iptables(table:'mangle', chain: 'input')`."
209
+ raise "[UNSUPPORTED] `with_table` is not supported in combination with `have_rule`. Please use the following syntax `iptables(table:'mangle', chain: 'input')`."
212
210
  end
213
211
 
214
212
  chain :with_chain do |_chain|
215
- fail "[UNSUPPORTED] `with_table` is not supported in combination with `with_chain`. Please use the following syntax `iptables(table:'mangle', chain: 'input')`."
213
+ raise "[UNSUPPORTED] `with_table` is not supported in combination with `with_chain`. Please use the following syntax `iptables(table:'mangle', chain: 'input')`."
216
214
  end
217
215
  end
218
216
 
219
- # unsupported
220
- RSpec::Matchers.define :contain do |_rule|
221
- match do |_resource|
222
- fail "[UNSUPPORTED] `contain` matcher. Please use the following syntax `its('content') { should match('value') }`."
217
+ # deprecated
218
+ RSpec::Matchers.define :contain do |rule|
219
+ match do |resource|
220
+ warn "[DEPRECATION] `contain` matcher. Please use the following syntax `its('content') { should include('value') }`."
221
+ expect(resource).to include(rule)
222
+ end
223
+ end
224
+
225
+ # `be_in` matcher
226
+ # You can use it in the following cases:
227
+ # - check if an item or array is included in a given array
228
+ # eg:
229
+ # describe nginx do
230
+ # its('user') { should be_in AUTHORIZED_USER_LIST }
231
+ # end
232
+ # describe nginx do
233
+ # its('module_list') { should be_in AUTHORIZED_MODULE_LIST }
234
+ # end
235
+ RSpec::Matchers.define :be_in do |list|
236
+ match do |item|
237
+ # Handle both single item and array
238
+ item.is_a?(Array) ? (item - list).empty? : list.include?(item)
239
+ end
240
+
241
+ match_when_negated do |item|
242
+ # Handle both single item and array
243
+ item.is_a?(Array) ? (item & list).empty? : !list.include?(item)
244
+ end
245
+
246
+ failure_message do |item|
247
+ if item.is_a?(Array)
248
+ "expected `#{item}` to be in the list: `#{list}` \nDiff:\n #{(item - list)}"
249
+ else
250
+ "expected `#{item}` to be in the list: `#{list}`"
251
+ end
252
+ end
253
+
254
+ failure_message_when_negated do |item|
255
+ if item.is_a?(Array)
256
+ "expected `#{item}` not to be in the list: `#{list}` \nComm:\n #{(item & list)}"
257
+ else
258
+ "expected `#{item}` not to be in the list: `#{list}`"
259
+ end
223
260
  end
224
261
  end
225
262
 
@@ -251,6 +288,13 @@ RSpec::Matchers.define :cmp do |first_expected|
251
288
  %w{true false}.include?(value.downcase)
252
289
  end
253
290
 
291
+ def version?(value)
292
+ Gem::Version.new(value)
293
+ true
294
+ rescue ArgumentError => _ex
295
+ false
296
+ end
297
+
254
298
  # expects that the values have been checked with boolean?
255
299
  def to_boolean(value)
256
300
  value.casecmp('true') == 0
@@ -260,6 +304,8 @@ RSpec::Matchers.define :cmp do |first_expected|
260
304
  # if actual and expected are strings
261
305
  if expected.is_a?(String) && actual.is_a?(String)
262
306
  return actual.casecmp(expected) == 0 if op == :==
307
+ return Gem::Version.new(actual).method(op).call(Gem::Version.new(expected)) if
308
+ version?(expected) && version?(actual)
263
309
  elsif expected.is_a?(Regexp) && (actual.is_a?(String) || actual.is_a?(Integer))
264
310
  return !actual.to_s.match(expected).nil?
265
311
  elsif expected.is_a?(String) && integer?(expected) && actual.is_a?(Integer)
@@ -305,14 +351,18 @@ RSpec::Matchers.define :cmp do |first_expected|
305
351
  end
306
352
 
307
353
  failure_message do |actual|
308
- actual = '0' + actual.to_s(8) if octal?(@expected)
354
+ actual = ('0' + actual.to_s(8)).inspect if octal?(@expected)
309
355
  "\n" + format_expectation(false) + "\n got: #{actual}\n\n(compared using `cmp` matcher)\n"
310
356
  end
311
357
 
312
358
  failure_message_when_negated do |actual|
313
- actual = '0' + actual.to_s(8) if octal?(@expected)
359
+ actual = ('0' + actual.to_s(8)).inspect if octal?(@expected)
314
360
  "\n" + format_expectation(true) + "\n got: #{actual}\n\n(compared using `cmp` matcher)\n"
315
361
  end
362
+
363
+ description do
364
+ "cmp #{@operation} #{@expected.inspect}"
365
+ end
316
366
  end
317
367
 
318
368
  # user resource matcher for serverspec compatibility
@@ -0,0 +1,162 @@
1
+ # encoding: utf-8
2
+ # author: Jen Burns, burnsjennifere@gmail.com
3
+
4
+ require 'utils/filter'
5
+ require 'utils/parser'
6
+
7
+ # rubocop:disable Metrics/ClassLength
8
+ module Inspec::Resources
9
+ class AideConf < Inspec.resource(1)
10
+ name 'aide_conf'
11
+ desc 'Use the aide_conf InSpec audit resource to test the rules established for
12
+ the file integrity tool AIDE. Controlled by the aide.conf file typically at /etc/aide.conf.'
13
+ example "
14
+ describe aide_conf do
15
+ its('selection_lines') { should include '/sbin' }
16
+ end
17
+
18
+ describe aide_conf.where { selection_line == '/bin' } do
19
+ its('rules.flatten') { should include 'r' }
20
+ end
21
+
22
+ describe aide_conf.all_have_rule('sha512') do
23
+ it { should eq true }
24
+ end
25
+ "
26
+
27
+ attr_reader :params
28
+
29
+ include CommentParser
30
+
31
+ def initialize(aide_conf_path = nil)
32
+ return skip_resource 'The `aide_conf` resource is not supported on your OS.' unless inspec.os.linux?
33
+ @conf_path = aide_conf_path || '/etc/aide.conf'
34
+ @content = nil
35
+ @rules = nil
36
+ read_content
37
+ end
38
+
39
+ def all_have_rule(rule)
40
+ # Case when file didn't exist or perms didn't allow an open
41
+ return false if @content.nil?
42
+
43
+ lines = @params.reject { |line| line['rules'].include? rule }
44
+ lines.empty?
45
+ end
46
+
47
+ filter = FilterTable.create
48
+ filter.add_accessor(:where)
49
+ .add_accessor(:entries)
50
+ .add(:selection_lines, field: 'selection_line')
51
+ .add(:rules, field: 'rules')
52
+
53
+ filter.connect(self, :params)
54
+
55
+ private
56
+
57
+ def read_content
58
+ return @content unless @content.nil?
59
+ @rules = {}
60
+
61
+ file = inspec.file(@conf_path)
62
+ if !file.file?
63
+ return skip_resource "Can't find file \"#{@conf_path}\""
64
+ end
65
+ raw_conf = file.content
66
+ if raw_conf.nil?
67
+ return skip_resource "File can't be opened or is empty \"#{@conf_path}\""
68
+ end
69
+ if raw_conf.empty? && !file.empty?
70
+ return skip_resource "Can't read file \"#{@conf_path}\""
71
+ end
72
+
73
+ # If there is a file and it contains content, continue
74
+ @content = filter_comments(inspec.file(@conf_path).content.lines)
75
+ @params = parse_conf(@content)
76
+ end
77
+
78
+ def filter_comments(data)
79
+ content = []
80
+ data.each do |line|
81
+ content_line, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
82
+ content.push(content_line)
83
+ end
84
+ content
85
+ end
86
+
87
+ def parse_conf(content)
88
+ params = []
89
+ content.each do |line|
90
+ param = parse_line(line)
91
+ if !param['selection_line'].nil?
92
+ params.push(param)
93
+ end
94
+ end
95
+ params
96
+ end
97
+
98
+ def parse_line(line)
99
+ line_and_rules = {}
100
+ # Case when line is a rule line
101
+ if line.include?(' = ')
102
+ parse_rule_line(line)
103
+ # Case when line is a selection line
104
+ elsif line.start_with?('/', '!', '=')
105
+ line_and_rules = parse_selection_line(line)
106
+ end
107
+ line_and_rules
108
+ end
109
+
110
+ def parse_rule_line(line)
111
+ line.gsub!(/\s+/, '')
112
+ rule_line_arr = line.split('=')
113
+ rules_list = rule_line_arr.last.split('+')
114
+ rule_name = rule_line_arr.first
115
+ rules_list.each_index do |i|
116
+ # Cases where rule respresents one or more other rules
117
+ if @rules.key?(rules_list[i])
118
+ rules_list[i] = @rules[rules_list[i]]
119
+ end
120
+ rules_list[i] = handle_multi_rule(rules_list, i)
121
+ end
122
+ @rules[rule_name] = rules_list.flatten
123
+ end
124
+
125
+ def parse_selection_line(line)
126
+ selec_line_arr = line.split(' ')
127
+ selection_line = selec_line_arr.first
128
+ selection_line.chop! if selection_line.end_with?('/')
129
+ rule_list = selec_line_arr.last.split('+')
130
+ rule_list.each_index do |i|
131
+ hash_list = @rules[rule_list[i]]
132
+ # Cases where rule respresents one or more other rules
133
+ if !hash_list.nil?
134
+ rule_list[i] = hash_list
135
+ end
136
+ rule_list[i] = handle_multi_rule(rule_list, i)
137
+ end
138
+ rule_list.flatten!
139
+ {
140
+ 'selection_line' => selection_line,
141
+ 'rules' => rule_list,
142
+ }
143
+ end
144
+
145
+ def handle_multi_rule(rule_list, i)
146
+ # Rules that represent multiple rules (R,L,>)
147
+ r_rules = %w{p i l n u g s m c md5}
148
+ l_rules = %w{p i l n u g}
149
+ grow_log_rules = %w{p l u g i n S}
150
+
151
+ case rule_list[i]
152
+ when 'R'
153
+ return r_rules
154
+ when 'L'
155
+ return l_rules
156
+ when '>'
157
+ return grow_log_rules
158
+ end
159
+ rule_list[i]
160
+ end
161
+ end
162
+ end
@@ -2,7 +2,6 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Christoph Hartmann
4
4
  # author: Dominik Richter
5
- # license: All rights reserved
6
5
 
7
6
  module Inspec::Resources
8
7
  class Apache < Inspec.resource(1)
@@ -2,7 +2,6 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
- # license: All rights reserved
6
5
 
7
6
  require 'utils/simpleconfig'
8
7
  require 'utils/find_files'
@@ -70,7 +69,7 @@ module Inspec::Resources
70
69
  end
71
70
 
72
71
  raw_conf = file.content
73
- if raw_conf.empty? && file.size > 0
72
+ if raw_conf.empty? && !file.empty?
74
73
  return skip_resource("Can't read file \"#{@conf_path}\"")
75
74
  end
76
75
 
@@ -82,7 +81,7 @@ module Inspec::Resources
82
81
  # parse include file parameters
83
82
  params = SimpleConfig.new(
84
83
  raw_conf,
85
- assignment_re: /^\s*(\S+)\s+(.*)\s*$/,
84
+ assignment_regex: /^\s*(\S+)\s+(.*)\s*$/,
86
85
  multiple_values: true,
87
86
  ).params
88
87
  @params.merge!(params)
@@ -107,6 +106,7 @@ module Inspec::Resources
107
106
  (include_files + include_files_optional).each do |f|
108
107
  id = Pathname.new(f).absolute? ? f : File.join(@conf_dir, f)
109
108
  files = find_files(id, depth: 1, type: 'file')
109
+ files += find_files(id, depth: 1, type: 'link')
110
110
 
111
111
  includes.push(files) if files
112
112
  end
data/lib/resources/apt.rb CHANGED
@@ -89,7 +89,8 @@ module Inspec::Resources
89
89
  active = false if raw_line != line
90
90
 
91
91
  # eg.: deb http://archive.ubuntu.com/ubuntu/ wily main restricted
92
- parse_repo = /^\s*(\S+)\s+"?([^ "\t\r\n\f]+)"?\s+(\S+)\s+(.*)$/.match(line)
92
+ # or : deb [trusted=yes] http://archive.ubuntu.com/ubuntu/ wily main restricted
93
+ parse_repo = /^\s*(\S+)\s+(?:\[\S+\])?\s*"?([^ "\t\r\n\f]+)"?\s+(\S+)\s+(.*)$/.match(line)
93
94
 
94
95
  # check if we got any result and the second param is an url
95
96
  next if parse_repo.nil? || !parse_repo[2] =~ HTTP_URL_RE
@@ -2,7 +2,6 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Christoph Hartmann
4
4
  # author: Dominik Richter
5
- # license: All rights reserved
6
5
 
7
6
  # Advanced Auditing:
8
7
  # As soon as you start applying Advanced Audit Configuration Policy, legacy policies will be completely ignored.
@@ -2,7 +2,6 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Christoph Hartmann
4
4
  # author: Dominik Richter
5
- # license: All rights reserved
6
5
 
7
6
  require 'utils/simpleconfig'
8
7
 
@@ -41,7 +40,7 @@ module Inspec::Resources
41
40
  end
42
41
 
43
42
  content = file.content
44
- if content.empty? && file.size > 0
43
+ if content.empty? && !file.empty?
45
44
  skip_resource "Can't read file '#{@conf_path}'"
46
45
  return @params = {}
47
46
  end
@@ -2,7 +2,6 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Christoph Hartmann
4
4
  # author: Dominik Richter
5
- # license: All rights reserved
6
5
 
7
6
  require 'forwardable'
8
7
  require 'utils/filter_array'
@@ -12,7 +11,7 @@ module Inspec::Resources
12
11
  def initialize(content)
13
12
  @content = content
14
13
  @opts = {
15
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
14
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
16
15
  multiple_values: true,
17
16
  }
18
17
  end
@@ -27,7 +26,7 @@ module Inspec::Resources
27
26
 
28
27
  def status(name)
29
28
  @status_opts = {
30
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
29
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
31
30
  multiple_values: false,
32
31
  }
33
32
  @status_content ||= inspec.command('/sbin/auditctl -s').stdout.chomp
@@ -86,6 +85,7 @@ module Inspec::Resources
86
85
  @legacy = AuditdRulesLegacy.new(@content)
87
86
  else
88
87
  parse_content
88
+ @legacy = nil
89
89
  end
90
90
  end
91
91
 
@@ -93,7 +93,7 @@ module Inspec::Resources
93
93
  # rubocop:disable Style/MethodName
94
94
  def LIST_RULES
95
95
  return @legacy.LIST_RULES if @legacy
96
- fail 'Using legacy auditd_rules LIST_RULES interface with non-legacy audit package. Please use the new syntax.'
96
+ raise 'Using legacy auditd_rules LIST_RULES interface with non-legacy audit package. Please use the new syntax.'
97
97
  end
98
98
 
99
99
  def status(name = nil)
@@ -177,7 +177,7 @@ module Inspec::Resources
177
177
 
178
178
  # NB only in file lines
179
179
  def get_key(line)
180
- line.match(/-k ([^ ]+)/)[1]
180
+ line.match(/-k ([^ ]+)/)[1] if line.include?('-k ')
181
181
  end
182
182
 
183
183
  # NOTE there are NO precautions wrt. filenames containing spaces in auditctl
@@ -28,7 +28,7 @@ module Inspec::Resources
28
28
  @content = @file.content
29
29
  @params = SimpleConfig.new(
30
30
  @file.content,
31
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
31
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
32
32
  multiple_values: true,
33
33
  ).params if @file.exist?
34
34
  @loaded = true
@@ -115,7 +115,7 @@ module Inspec::Resources
115
115
  adapter_collection.push(info) if info[:name].casecmp(bridge_name) == 0
116
116
  end
117
117
 
118
- return nil if bridges.size == 0
118
+ return nil if bridges.empty?
119
119
  warn "[Possible Error] detected multiple bridges interfaces with the name #{bridge_name}" if bridges.size > 1
120
120
  bridges[0]
121
121
  end
@@ -2,7 +2,6 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
- # license: All rights reserved
6
5
 
7
6
  module Inspec::Resources
8
7
  class Cmd < Inspec.resource(1)
@@ -24,6 +23,9 @@ module Inspec::Resources
24
23
  attr_reader :command
25
24
 
26
25
  def initialize(cmd)
26
+ if cmd.nil?
27
+ raise 'InSpec `command` was called with `nil` as the argument. This is not supported. Please provide a valid command instead.'
28
+ end
27
29
  @command = cmd
28
30
  end
29
31
 
@@ -45,12 +47,12 @@ module Inspec::Resources
45
47
 
46
48
  def exist?
47
49
  # silent for mock resources
48
- return false if inspec.os[:name].to_s == 'unknown'
50
+ return false if inspec.os.name.nil?
49
51
 
50
52
  if inspec.os.linux?
51
53
  res = inspec.backend.run_command("bash -c 'type \"#{@command}\"'")
52
54
  elsif inspec.os.windows?
53
- res = inspec.backend.run_command("where.exe \"#{@command}\"")
55
+ res = inspec.backend.run_command("Get-Command \"#{@command}\"")
54
56
  elsif inspec.os.unix?
55
57
  res = inspec.backend.run_command("type \"#{@command}\"")
56
58
  else
@@ -0,0 +1,102 @@
1
+ # encoding: utf-8
2
+ # author: Adam Leff
3
+
4
+ require 'utils/parser'
5
+ require 'utils/filter'
6
+
7
+ module Inspec::Resources
8
+ class Crontab < Inspec.resource(1)
9
+ name 'crontab'
10
+ desc 'Use the crontab InSpec audit resource to test the contents of the crontab for a given user which contains information about scheduled tasks owned by that user.'
11
+ example "
12
+ describe crontab('root') do
13
+ its('commands') { should include '/path/to/some/script' }
14
+ end
15
+
16
+ describe crontab('myuser').commands('/home/myuser/build.sh') do
17
+ its('hours') { should cmp '*' }
18
+ its('minutes') { should cmp '*' }
19
+ end
20
+
21
+ describe crontab.where({'hour' => '*', 'minute' => '*'}) do
22
+ its('entries.length') { should cmp '0' }
23
+ end
24
+
25
+ describe crontab.where { command =~ /a partial command string/ } do
26
+ its('entries.length') { should cmp 1 }
27
+ end
28
+ "
29
+
30
+ attr_reader :params
31
+
32
+ include CommentParser
33
+
34
+ def initialize(user = nil)
35
+ @user = user
36
+ @params = read_crontab
37
+
38
+ return skip_resource 'The `crontab` resource is not supported on your OS.' unless inspec.os.unix?
39
+ end
40
+
41
+ def read_crontab
42
+ inspec.command(crontab_cmd).stdout.lines.map { |l| parse_crontab_line(l) }.compact
43
+ end
44
+
45
+ def parse_crontab_line(l)
46
+ data, = parse_comment_line(l, comment_char: '#', standalone_comments: false)
47
+ return nil if data.nil? || data.empty?
48
+
49
+ case data
50
+ when /@hourly .*/
51
+ { 'minute' => '0', 'hour' => '*', 'day' => '*', 'month' => '*', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
52
+ when /@(midnight|daily) .*/
53
+ { 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
54
+ when /@weekly .*/
55
+ { 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '0', 'command' => data.split(/\s+/, 2).at(1) }
56
+ when /@monthly ./
57
+ { 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '*', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
58
+ when /@(annually|yearly) .*/
59
+ { 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '1', 'weekday' => '*', 'command' => data.split(/\s+/, 2).at(1) }
60
+ when /@reboot .*/
61
+ { 'minute' => '-1', 'hour' => '-1', 'day' => '-1', 'month' => '-1', 'weekday' => '-1', 'command' => data.split(/\s+/, 2).at(1) }
62
+ else
63
+ elements = data.split(/\s+/, 6)
64
+ {
65
+ 'minute' => elements.at(0),
66
+ 'hour' => elements.at(1),
67
+ 'day' => elements.at(2),
68
+ 'month' => elements.at(3),
69
+ 'weekday' => elements.at(4),
70
+ 'command' => elements.at(5),
71
+ }
72
+ end
73
+ end
74
+
75
+ def crontab_cmd
76
+ @user.nil? ? 'crontab -l' : "crontab -l -u #{@user}"
77
+ end
78
+
79
+ filter = FilterTable.create
80
+ filter.add_accessor(:where)
81
+ .add_accessor(:entries)
82
+ .add(:minutes, field: 'minute')
83
+ .add(:hours, field: 'hour')
84
+ .add(:days, field: 'day')
85
+ .add(:months, field: 'month')
86
+ .add(:weekdays, field: 'weekday')
87
+ .add(:commands, field: 'command')
88
+
89
+ # rebuild the crontab line from raw content
90
+ filter.add(:content) { |t, _|
91
+ t.entries.map do |e|
92
+ [e.minute, e.hour, e.day, e.month, e.weekday, e.command].join(' ')
93
+ end.join("\n")
94
+ }
95
+
96
+ filter.connect(self, :params)
97
+
98
+ def to_s
99
+ @user.nil? ? 'crontab for current user' : "crontab for user #{@user}"
100
+ end
101
+ end
102
+ end
data/lib/resources/csv.rb CHANGED
@@ -15,19 +15,36 @@ module Inspec::Resources
15
15
  end
16
16
  "
17
17
 
18
- # override file load and parse hash from csv
18
+ # override the parse method from JsonConfig
19
+ # Assuming a header row of name,col1,col2, it will output an array of hashes like so:
20
+ # [
21
+ # { 'name' => 'row1', 'col1' => 'value1', 'col2' => 'value2' },
22
+ # { 'name' => 'row2', 'col1' => 'value3', 'col2' => 'value4' }
23
+ # ]
19
24
  def parse(content)
20
25
  require 'csv'
26
+
21
27
  # convert empty field to nil
22
28
  CSV::Converters[:blank_to_nil] = lambda do |field|
23
29
  field && field.empty? ? nil : field
24
30
  end
31
+
25
32
  # implicit conversion of values
26
33
  csv = CSV.new(content, headers: true, converters: [:all, :blank_to_nil])
34
+
27
35
  # convert to hash
28
36
  csv.to_a.map(&:to_hash)
29
37
  end
30
38
 
39
+ # override the value method from JsonConfig
40
+ # The format of the CSV hash as created by #parse is very different
41
+ # than what the YAML, JSON, and INI resources create, so using the
42
+ # #value method from JsonConfig (which uses ObjectTraverser.extract_value)
43
+ # doesn't make sense here.
44
+ def value(key)
45
+ @params.map { |x| x[key.first.to_s] }.compact
46
+ end
47
+
31
48
  def to_s
32
49
  "Csv #{@path}"
33
50
  end