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,12 +2,24 @@
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 'shellwords'
8
7
 
9
8
  module Inspec::Resources
10
- class FileResource < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
9
+ module FilePermissionsSelector
10
+ def select_file_perms_style(os)
11
+ if os.unix?
12
+ UnixFilePermissions.new(inspec)
13
+ elsif os.windows?
14
+ WindowsFilePermissions.new(inspec)
15
+ end
16
+ end
17
+ end
18
+
19
+ class FileResource < Inspec.resource(1)
20
+ include FilePermissionsSelector
21
+ include LinuxMountParser
22
+
11
23
  name 'file'
12
24
  desc 'Use the file InSpec audit resource to test all system file types, including files, directories, symbolic links, named pipes, sockets, character devices, block devices, and doors.'
13
25
  example "
@@ -16,14 +28,16 @@ module Inspec::Resources
16
28
  it { should be_file }
17
29
  it { should be_readable }
18
30
  it { should be_writable }
31
+ it { should be_executable.by_user('root') }
19
32
  it { should be_owned_by 'root' }
20
33
  its('mode') { should cmp '0644' }
21
34
  end
22
35
  "
23
- include MountParser
24
36
 
25
37
  attr_reader :file, :mount_options
26
38
  def initialize(path)
39
+ # select permissions style
40
+ @perms_provider = select_file_perms_style(inspec.os)
27
41
  @file = inspec.backend.file(path)
28
42
  end
29
43
 
@@ -46,25 +60,28 @@ module Inspec::Resources
46
60
  end
47
61
 
48
62
  def contain(*_)
49
- fail 'Contain is not supported. Please use standard RSpec matchers.'
63
+ raise 'Contain is not supported. Please use standard RSpec matchers.'
50
64
  end
51
65
 
52
66
  def readable?(by_usergroup, by_specific_user)
53
67
  return false unless exist?
68
+ return skip_resource '`readable?` is not supported on your OS yet.' if @perms_provider.nil?
54
69
 
55
- file_permission_granted?('r', by_usergroup, by_specific_user)
70
+ file_permission_granted?('read', by_usergroup, by_specific_user)
56
71
  end
57
72
 
58
73
  def writable?(by_usergroup, by_specific_user)
59
74
  return false unless exist?
75
+ return skip_resource '`writable?` is not supported on your OS yet.' if @perms_provider.nil?
60
76
 
61
- file_permission_granted?('w', by_usergroup, by_specific_user)
77
+ file_permission_granted?('write', by_usergroup, by_specific_user)
62
78
  end
63
79
 
64
80
  def executable?(by_usergroup, by_specific_user)
65
81
  return false unless exist?
82
+ return skip_resource '`executable?` is not supported on your OS yet.' if @perms_provider.nil?
66
83
 
67
- file_permission_granted?('x', by_usergroup, by_specific_user)
84
+ file_permission_granted?('execute', by_usergroup, by_specific_user)
68
85
  end
69
86
 
70
87
  def mounted?(expected_options = nil, identical = false)
@@ -109,35 +126,65 @@ module Inspec::Resources
109
126
 
110
127
  private
111
128
 
112
- def file_permission_granted?(flag, by_usergroup, by_specific_user)
113
- unless inspec.os.unix?
114
- fail 'Checking file permissions is not supported on your os'
129
+ def file_permission_granted?(access_type, by_usergroup, by_specific_user)
130
+ raise '`file_permission_granted?` is not supported on your OS' if @perms_provider.nil?
131
+ if by_specific_user.nil? || by_specific_user.empty?
132
+ @perms_provider.check_file_permission_by_mask(file, access_type, by_usergroup, by_specific_user)
133
+ else
134
+ @perms_provider.check_file_permission_by_user(access_type, by_specific_user, source_path)
115
135
  end
136
+ end
137
+ end
116
138
 
117
- if by_specific_user.nil? || by_specific_user.empty?
118
- usergroup = usergroup_for(by_usergroup, by_specific_user)
119
- check_file_permission_by_mask(usergroup, flag)
139
+ class FilePermissions
140
+ attr_reader :inspec
141
+ def initialize(inspec)
142
+ @inspec = inspec
143
+ end
144
+ end
145
+
146
+ class UnixFilePermissions < FilePermissions
147
+ def permission_flag(access_type)
148
+ case access_type
149
+ when 'read'
150
+ 'r'
151
+ when 'write'
152
+ 'w'
153
+ when 'execute'
154
+ 'x'
120
155
  else
121
- check_file_permission_by_user(by_specific_user, flag)
156
+ raise 'Invalid access_type provided'
122
157
  end
123
158
  end
124
159
 
125
- def check_file_permission_by_mask(usergroup, flag)
126
- mask = file.unix_mode_mask(usergroup, flag)
127
- fail 'Invalid usergroup/owner provided' if mask.nil?
160
+ def usergroup_for(usergroup, specific_user)
161
+ if usergroup == 'others'
162
+ 'other'
163
+ elsif (usergroup.nil? || usergroup.empty?) && specific_user.nil?
164
+ 'all'
165
+ else
166
+ usergroup
167
+ end
168
+ end
128
169
 
170
+ def check_file_permission_by_mask(file, access_type, usergroup, specific_user)
171
+ usergroup = usergroup_for(usergroup, specific_user)
172
+ flag = permission_flag(access_type)
173
+ mask = file.unix_mode_mask(usergroup, flag)
174
+ raise 'Invalid usergroup/owner provided' if mask.nil?
129
175
  (file.mode & mask) != 0
130
176
  end
131
177
 
132
- def check_file_permission_by_user(user, flag)
178
+ def check_file_permission_by_user(access_type, user, path)
179
+ flag = permission_flag(access_type)
133
180
  if inspec.os.linux?
134
- perm_cmd = "su -s /bin/sh -c \"test -#{flag} #{source_path}\" #{user}"
181
+ perm_cmd = "su -s /bin/sh -c \"test -#{flag} #{path}\" #{user}"
135
182
  elsif inspec.os.bsd? || inspec.os.solaris?
136
- perm_cmd = "sudo -u #{user} test -#{flag} #{source_path}"
183
+ perm_cmd = "sudo -u #{user} test -#{flag} #{path}"
137
184
  elsif inspec.os.aix?
138
- perm_cmd = "su #{user} -c test -#{flag} #{source_path}"
185
+ perm_cmd = "su #{user} -c test -#{flag} #{path}"
139
186
  elsif inspec.os.hpux?
140
- perm_cmd = "su #{user} -c \"test -#{flag} #{source_path}\""
187
+ perm_cmd = "su #{user} -c \"test -#{flag} #{path}\""
141
188
  else
142
189
  return skip_resource 'The `file` resource does not support `by_user` on your OS.'
143
190
  end
@@ -145,15 +192,26 @@ module Inspec::Resources
145
192
  cmd = inspec.command(perm_cmd)
146
193
  cmd.exit_status == 0 ? true : false
147
194
  end
195
+ end
148
196
 
149
- def usergroup_for(usergroup, specific_user)
150
- if usergroup == 'others'
151
- 'other'
152
- elsif (usergroup.nil? || usergroup.empty?) && specific_user.nil?
153
- 'all'
154
- else
155
- usergroup
156
- end
197
+ class WindowsFilePermissions < FilePermissions
198
+ def check_file_permission_by_mask(_file, _access_type, _usergroup, _specific_user)
199
+ raise '`check_file_permission_by_mask` is not supported on Windows'
200
+ end
201
+
202
+ def check_file_permission_by_user(access_type, user, path)
203
+ access_rule = case access_type
204
+ when 'read'
205
+ '@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'Read\', \'ListDirectory\')'
206
+ when 'write'
207
+ '@(\'FullControl\', \'Modify\', \'Write\')'
208
+ when 'execute'
209
+ '@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'ExecuteFile\')'
210
+ else
211
+ raise 'Invalid access_type provided'
212
+ end
213
+ cmd = inspec.command("@(@((Get-Acl '#{path}').access | Where-Object {$_.AccessControlType -eq 'Allow' -and $_.IdentityReference -eq '#{user}' }) | Where-Object {($_.FileSystemRights.ToString().Split(',') | % {$_.trim()} | ? {#{access_rule} -contains $_}) -ne $null}) | measure | % { $_.Count }")
214
+ cmd.stdout.chomp == '0' ? false : true
157
215
  end
158
216
  end
159
217
  end
data/lib/resources/gem.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # author: Christoph Hartmann
3
3
  # author: Dominik Richter
4
+ # author: Joe Nuspl
4
5
 
5
6
  module Inspec::Resources
6
7
  class GemPackage < Inspec.resource(1)
@@ -13,23 +14,42 @@ module Inspec::Resources
13
14
  end
14
15
  "
15
16
 
16
- def initialize(package_name)
17
+ attr_reader :gem_binary
18
+
19
+ def initialize(package_name, gem_binary = nil)
17
20
  @package_name = package_name
21
+ @gem_binary = case gem_binary
22
+ when nil
23
+ 'gem'
24
+ when :chef
25
+ if inspec.os.windows?
26
+ 'c:\opscode\chef\embedded\bin\gem.bat'
27
+ else
28
+ '/opt/chef/embedded/bin/gem'
29
+ end
30
+ when :chef_server
31
+ '/opt/opscode/embedded/bin/gem'
32
+ else
33
+ gem_binary
34
+ end
35
+ skip_resource 'Unable to retrieve gem information' if info.empty?
18
36
  end
19
37
 
20
38
  def info
21
39
  return @info if defined?(@info)
22
40
 
23
- cmd = inspec.command("gem list --local -a -q \^#{@package_name}\$")
41
+ cmd = inspec.command("#{@gem_binary} list --local -a -q \^#{@package_name}\$")
42
+ return {} unless cmd.exit_status.zero?
43
+
44
+ # extract package name and version
45
+ # parses data like winrm (1.3.4, 1.3.3)
46
+ params = /^\s*([^\(]*?)\s*\((.*?)\)\s*$/.match(cmd.stdout.chomp)
24
47
  @info = {
25
- installed: cmd.exit_status == 0,
48
+ installed: !params.nil?,
26
49
  type: 'gem',
27
50
  }
28
51
  return @info unless @info[:installed]
29
52
 
30
- # extract package name and version
31
- # parses data like winrm (1.3.4, 1.3.3)
32
- params = /^\s*([^\(]*?)\s*\((.*?)\)\s*$/.match(cmd.stdout.chomp)
33
53
  versions = params[2].split(',')
34
54
  @info[:name] = params[1]
35
55
  @info[:version] = versions[0]
@@ -90,7 +90,6 @@ module Inspec::Resources
90
90
 
91
91
  def initialize(groupname)
92
92
  @group = groupname
93
- @group = @group.downcase unless inspec.os.windows?
94
93
 
95
94
  # select group manager
96
95
  @group_provider = select_group_manager(inspec.os)
@@ -99,18 +98,18 @@ module Inspec::Resources
99
98
 
100
99
  # verifies if a group exists
101
100
  def exists?
102
- group_info.entries.size > 0
101
+ !group_info.entries.empty?
103
102
  end
104
103
 
105
104
  def gid
106
105
  gids = group_info.gids
107
- if gids.size == 0
106
+ if gids.empty?
108
107
  nil
109
108
  # the default case should be one group
110
109
  elsif gids.size == 1
111
110
  gids.entries[0]
112
111
  else
113
- fail 'found more than one group with the same name, please use `groups` resource'
112
+ raise 'found more than one group with the same name, please use `groups` resource'
114
113
  end
115
114
  end
116
115
 
@@ -144,7 +143,7 @@ module Inspec::Resources
144
143
  end
145
144
 
146
145
  def groups
147
- fail 'group provider must implement the `groups` method'
146
+ raise 'group provider must implement the `groups` method'
148
147
  end
149
148
  end
150
149
 
@@ -1,6 +1,5 @@
1
1
  # encoding: utf-8
2
2
  # author: Thomas Cate
3
- # license: All rights reserved
4
3
 
5
4
  require 'utils/simpleconfig'
6
5
 
@@ -38,11 +37,11 @@ class GrubConfig < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
38
37
  @conf_path = path || '/boot/grub/grub.cfg'
39
38
  @defaults_path = '/etc/default/grub'
40
39
  @version = 'grub2'
41
- elsif os[:name] == 'amazon' # rubocop:disable Style/GuardClause
40
+ elsif os[:name] == 'amazon'
42
41
  @conf_path = path || '/etc/grub.conf'
43
42
  @version = 'legacy'
44
43
  else
45
- fail UnknownGrubConfig
44
+ raise UnknownGrubConfig
46
45
  end
47
46
  end
48
47
 
@@ -145,7 +144,7 @@ class GrubConfig < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
145
144
 
146
145
  content = file.content
147
146
 
148
- if content.empty? && file.size > 0
147
+ if content.empty? && !file.empty?
149
148
  skip_resource "Can't read file '#{@conf_path}'"
150
149
  return @params = {}
151
150
  end
@@ -10,7 +10,7 @@
10
10
  # end
11
11
  #
12
12
  # To verify a hostname with protocol and port
13
- # describe host('example.com', port: 53, proto: 'udp') do
13
+ # describe host('example.com', port: 443, protocol: 'tcp') do
14
14
  # it { should be_reachable }
15
15
  # end
16
16
  #
@@ -24,6 +24,8 @@
24
24
  # it { should be_resolvable.by('dns') }
25
25
  # end
26
26
 
27
+ require 'resolv'
28
+
27
29
  module Inspec::Resources
28
30
  class Host < Inspec.resource(1)
29
31
  name 'host'
@@ -31,26 +33,51 @@ module Inspec::Resources
31
33
  example "
32
34
  describe host('example.com') do
33
35
  it { should be_reachable }
36
+ it { should be_resolvable }
37
+ its('ipaddress') { should include '12.34.56.78' }
34
38
  end
35
39
 
36
- describe host('example.com', port: '80') do
40
+ describe host('example.com', port: '80', protocol: 'tcp') do
37
41
  it { should be_reachable }
38
42
  end
39
43
  "
40
44
 
45
+ attr_reader :hostname, :port, :protocol
46
+
41
47
  def initialize(hostname, params = {})
42
48
  @hostname = hostname
43
- @port = params[:port] || nil
44
- @proto = params[:proto] || nil
49
+ @port = params[:port]
50
+
51
+ if params[:proto]
52
+ warn '[DEPRECATION] The `proto` parameter is deprecated. Use `protocol` instead.'
53
+ @protocol = params[:proto]
54
+ else
55
+ @protocol = params.fetch(:protocol, 'icmp')
56
+ end
57
+
58
+ return skip_resource 'Invalid protocol: only `tcp` and `icmp` protocols are support for the `host` resource.' unless
59
+ %w{icmp tcp}.include?(@protocol)
45
60
 
46
61
  @host_provider = nil
47
62
  if inspec.os.linux?
48
63
  @host_provider = LinuxHostProvider.new(inspec)
49
64
  elsif inspec.os.windows?
50
65
  @host_provider = WindowsHostProvider.new(inspec)
66
+ elsif inspec.os.darwin?
67
+ @host_provider = DarwinHostProvider.new(inspec)
51
68
  else
52
69
  return skip_resource 'The `host` resource is not supported on your OS yet.'
53
70
  end
71
+
72
+ missing_requirements = @host_provider.missing_requirements(protocol)
73
+ unless missing_requirements.empty?
74
+ return skip_resource "The following requirements are not met for this resource: #{missing_requirements.join(', ')}"
75
+ end
76
+ end
77
+
78
+ def proto
79
+ warn '[DEPRECATION] The `proto` method is deprecated. Use `protocol` instead.'
80
+ protocol
54
81
  end
55
82
 
56
83
  # if we get the IP address, the host is resolvable
@@ -59,9 +86,25 @@ module Inspec::Resources
59
86
  resolve.nil? || resolve.empty? ? false : true
60
87
  end
61
88
 
62
- def reachable?(port = nil, proto = nil, timeout = nil)
63
- fail "Use `host` resource with host('#{@hostname}', port: #{port}, proto: '#{proto}') parameters." if !port.nil? || !proto.nil? || !timeout.nil?
64
- ping.nil? ? false : ping
89
+ def reachable?
90
+ # ping checks do not require port or protocol
91
+ return ping.fetch(:success, false) if protocol == 'icmp'
92
+
93
+ # if either port or protocol are specified but not both, we cannot proceed.
94
+ if port.nil? || protocol.nil?
95
+ raise "Protocol required with port. Use `host` resource with host('#{hostname}', port: 1234, proto: 'tcp') parameters." if port.nil? || protocol.nil?
96
+ end
97
+
98
+ # perform the protocol-specific reachability test
99
+ ping.fetch(:success, false)
100
+ end
101
+
102
+ def connection
103
+ ping[:connection]
104
+ end
105
+
106
+ def socket
107
+ ping[:socket]
65
108
  end
66
109
 
67
110
  # returns all A records of the IP address, will return an array
@@ -70,19 +113,21 @@ module Inspec::Resources
70
113
  end
71
114
 
72
115
  def to_s
73
- "Host #{@hostname}"
116
+ "Host #{hostname}"
74
117
  end
75
118
 
76
119
  private
77
120
 
78
121
  def ping
79
122
  return @ping_cache if defined?(@ping_cache)
80
- @ping_cache = @host_provider.ping(@hostname, @port, @proto) if !@host_provider.nil?
123
+ return {} if @host_provider.nil?
124
+
125
+ @ping_cache = @host_provider.ping(hostname, port, protocol)
81
126
  end
82
127
 
83
128
  def resolve
84
129
  return @ip_cache if defined?(@ip_cache)
85
- @ip_cache = @host_provider.resolve(@hostname) if !@host_provider.nil?
130
+ @ip_cache = @host_provider.resolve(hostname) if !@host_provider.nil?
86
131
  end
87
132
  end
88
133
 
@@ -91,25 +136,128 @@ module Inspec::Resources
91
136
  def initialize(inspec)
92
137
  @inspec = inspec
93
138
  end
139
+
140
+ def missing_requirements(_protocol)
141
+ # each provider can return an array of missing requirements that can
142
+ # be enumerated in a skip_resource message
143
+ []
144
+ end
94
145
  end
95
146
 
96
- class LinuxHostProvider < HostProvider
97
- # ping is difficult to achieve, since we are not sure
98
- def ping(hostname, _port = nil, _proto = nil)
99
- # fall back to ping, but we can only test ICMP packages with ping
100
- # therefore we have to skip the test, if we do not have everything on the node to run the test
101
- ping = inspec.command("ping -w 1 -c 1 #{hostname}")
102
- ping.exit_status.to_i != 0 ? false : true
147
+ class UnixHostProvider < HostProvider
148
+ def resolve_with_dig(hostname)
149
+ addresses = []
150
+
151
+ # look for IPv4 addresses
152
+ cmd = inspec.command("dig +short A #{hostname}")
153
+ cmd.stdout.lines.each do |line|
154
+ matched = line.chomp.match(Resolv::IPv4::Regex)
155
+ addresses << matched.to_s unless matched.nil?
156
+ end
157
+
158
+ # look for IPv6 addresses
159
+ cmd = inspec.command("dig +short AAAA #{hostname}")
160
+ cmd.stdout.lines.each do |line|
161
+ matched = line.chomp.match(Resolv::IPv6::Regex)
162
+ addresses << matched.to_s unless matched.nil?
163
+ end
164
+
165
+ addresses.empty? ? nil : addresses
166
+ end
167
+
168
+ def resolve_with_getent(hostname)
169
+ cmd = inspec.command("getent ahosts #{hostname}")
170
+ return nil unless cmd.exit_status.to_i.zero?
171
+
172
+ # getent ahosts output is formatted like so:
173
+ # $ getent ahosts www.google.com
174
+ # 172.217.8.4 STREAM www.google.com
175
+ # 172.217.8.4 DGRAM
176
+ # 172.217.8.4 RAW
177
+ # 2607:f8b0:4004:803::2004 STREAM
178
+ # 2607:f8b0:4004:803::2004 DGRAM
179
+ # 2607:f8b0:4004:803::2004 RAW
180
+ addresses = []
181
+ cmd.stdout.lines.each do |line|
182
+ ip, = line.split(/\s+/, 2)
183
+ next unless ip.match(Resolv::IPv4::Regex) || ip.match(Resolv::IPv6::Regex)
184
+ addresses << ip unless addresses.include?(ip)
185
+ end
186
+
187
+ addresses
188
+ end
189
+ end
190
+
191
+ class DarwinHostProvider < UnixHostProvider
192
+ def missing_requirements(protocol)
193
+ missing = []
194
+
195
+ if protocol == 'tcp'
196
+ missing << 'netcat must be installed' unless inspec.command('nc').exist?
197
+ end
198
+
199
+ missing
200
+ end
201
+
202
+ def ping(hostname, port, protocol)
203
+ if protocol == 'tcp'
204
+ resp = inspec.command("nc -vz -G 1 #{hostname} #{port}")
205
+ else
206
+ resp = inspec.command("ping -W 1 -c 1 #{hostname}")
207
+ end
208
+
209
+ {
210
+ success: resp.exit_status.to_i.zero?,
211
+ connection: resp.stderr,
212
+ socket: resp.stdout,
213
+ }
103
214
  end
104
215
 
105
216
  def resolve(hostname)
106
- # TODO: we rely on getent hosts for now, but it prefers to return IPv6, only then IPv4
107
- cmd = inspec.command("getent hosts #{hostname}")
108
- return nil if cmd.exit_status.to_i != 0
217
+ resolve_with_dig(hostname)
218
+ end
219
+ end
220
+
221
+ class LinuxHostProvider < UnixHostProvider
222
+ def missing_requirements(protocol)
223
+ missing = []
109
224
 
110
- # extract ip adress
111
- resolve = /^\s*(?<ip>\S+)\s+(.*)\s*$/.match(cmd.stdout.chomp)
112
- [resolve[1]] if resolve
225
+ if protocol == 'tcp' && (!inspec.command('nc').exist? && !inspec.command('ncat').exist?)
226
+ missing << 'netcat must be installed'
227
+ end
228
+
229
+ missing
230
+ end
231
+
232
+ def ping(hostname, port, protocol)
233
+ if protocol == 'tcp'
234
+ resp = inspec.command(tcp_check_command(hostname, port))
235
+ else
236
+ # fall back to ping, but we can only test ICMP packages with ping
237
+ resp = inspec.command("ping -w 1 -c 1 #{hostname}")
238
+ end
239
+
240
+ {
241
+ success: resp.exit_status.to_i.zero?,
242
+ connection: resp.stderr,
243
+ socket: resp.stdout,
244
+ }
245
+ end
246
+
247
+ def tcp_check_command(hostname, port)
248
+ if inspec.command('nc').exist?
249
+ base_cmd = 'nc'
250
+ elsif inspec.command('ncat').exist?
251
+ base_cmd = 'ncat'
252
+ else
253
+ return
254
+ end
255
+
256
+ "echo | #{base_cmd} -v -w 1 #{hostname} #{port}"
257
+ end
258
+
259
+ def resolve(hostname)
260
+ resolve_with_getent(hostname)
113
261
  end
114
262
  end
115
263
 
@@ -118,13 +266,10 @@ module Inspec::Resources
118
266
  # @see http://blogs.technet.com/b/josebda/archive/2015/04/18/windows-powershell-equivalents-for-common-networking-commands-ipconfig-ping-nslookup.aspx
119
267
  # @see http://blogs.technet.com/b/heyscriptingguy/archive/2014/03/19/creating-a-port-scanner-with-windows-powershell.aspx
120
268
  class WindowsHostProvider < HostProvider
121
- def ping(hostname, port = nil, proto = nil)
122
- # TODO: abort if we cannot run it via udp
123
- return nil if proto == 'udp'
124
-
269
+ def ping(hostname, port = nil, _proto = nil)
125
270
  # ICMP: Test-NetConnection www.microsoft.com
126
271
  # TCP and port: Test-NetConnection -ComputerName www.microsoft.com -RemotePort 80
127
- request = "Test-NetConnection -ComputerName #{hostname}"
272
+ request = "Test-NetConnection -ComputerName #{hostname} -WarningAction SilentlyContinue"
128
273
  request += " -RemotePort #{port}" unless port.nil?
129
274
  request += '| Select-Object -Property ComputerName, TcpTestSucceeded, PingSucceeded | ConvertTo-Json'
130
275
  cmd = inspec.command(request)
@@ -132,15 +277,10 @@ module Inspec::Resources
132
277
  begin
133
278
  ping = JSON.parse(cmd.stdout)
134
279
  rescue JSON::ParserError => _e
135
- return nil
280
+ return {}
136
281
  end
137
282
 
138
- # Logic being if you provided a port you wanted to check it was open
139
- if port.nil?
140
- ping['PingSucceeded']
141
- else
142
- ping['TcpTestSucceeded']
143
- end
283
+ { success: port.nil? ? ping['PingSucceeded'] : ping['TcpTestSucceeded'] }
144
284
  end
145
285
 
146
286
  def resolve(hostname)