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,111 @@
1
+ # encoding: utf-8
2
+ # copyright: 2017, Chef Software, Inc. <legal@chef.io>
3
+ # author: Joshua Timberman
4
+ # author: Alex Pop
5
+
6
+ require 'utils/filter'
7
+
8
+ module Inspec::Resources
9
+ class Packages < Inspec.resource(1)
10
+ name 'packages'
11
+ desc 'Use the packages InSpec audit resource to test properties for multiple packages installed on the system'
12
+ example "
13
+ describe packages(/xserver-xorg.*/) do
14
+ its('entries') { should be_empty }
15
+ end
16
+ describe packages('vim').entries.length do
17
+ it { should be > 1 }
18
+ end
19
+ describe packages(/vi.+/).where { status != 'installed' } do
20
+ its('statuses') { should be_empty }
21
+ end
22
+ "
23
+
24
+ def initialize(pattern)
25
+ os = inspec.os
26
+ if os.debian?
27
+ @pkgs = Debs.new(inspec)
28
+ elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family])
29
+ @pkgs = Rpms.new(inspec)
30
+ else
31
+ return skip_resource "The packages resource is not yet supported on OS #{inspec.os.name}"
32
+ end
33
+
34
+ @pattern = pattern_regexp(pattern)
35
+ all_pkgs = @pkgs.build_package_list
36
+ @list = all_pkgs.find_all do |hm|
37
+ hm[:name] =~ @pattern
38
+ end
39
+ end
40
+
41
+ def to_s
42
+ "Packages #{@pattern.class == String ? @pattern : @pattern.inspect}"
43
+ end
44
+
45
+ filter = FilterTable.create
46
+ filter.add_accessor(:where)
47
+ .add_accessor(:entries)
48
+ .add(:statuses, field: 'status', style: :simple)
49
+ .add(:names, field: 'name')
50
+ .add(:versions, field: 'version')
51
+ .connect(self, :filtered_packages)
52
+
53
+ private
54
+
55
+ def pattern_regexp(p)
56
+ if p.class == String
57
+ Regexp.new(Regexp.escape(p))
58
+ elsif p.class == Regexp
59
+ p
60
+ else
61
+ raise 'Invalid name argument to packages resource, please use a "string" or /regexp/'
62
+ end
63
+ end
64
+
65
+ def filtered_packages
66
+ warn "The packages resource is not yet supported on OS #{inspec.os.name}" if resource_skipped
67
+ @list
68
+ end
69
+ end
70
+
71
+ class PkgsManagement
72
+ PackageStruct = Struct.new(:status, :name, :version)
73
+ attr_reader :inspec
74
+ def initialize(inspec)
75
+ @inspec = inspec
76
+ end
77
+ end
78
+
79
+ # Debian / Ubuntu
80
+ class Debs < PkgsManagement
81
+ def build_package_list
82
+ # use two spaces as delimiter in case any of the fields has a space in it
83
+ command = "dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${Version}\\n'"
84
+ cmd = inspec.command(command)
85
+ all = cmd.stdout.split("\n")
86
+ return [] if all.nil?
87
+ all.map do |m|
88
+ a = m.split(/ {2,}/)
89
+ a[0] = 'installed' if a[0] =~ /^.i/
90
+ a[2] = a[2].split(':').last
91
+ PackageStruct.new(*a)
92
+ end
93
+ end
94
+ end
95
+
96
+ # RedHat family
97
+ class Rpms < PkgsManagement
98
+ def build_package_list
99
+ # use two spaces as delimiter in case any of the fields has a space in it
100
+ command = "rpm -qa --queryformat '%{NAME} %{VERSION}-%{RELEASE}\\n'"
101
+ cmd = inspec.command(command)
102
+ all = cmd.stdout.split("\n")
103
+ return [] if all.nil?
104
+ all.map do |m|
105
+ a = m.split(' ')
106
+ a.unshift('installed')
107
+ PackageStruct.new(*a)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -2,13 +2,12 @@
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
  # Usage example:
8
7
  #
9
8
  # audit = command('/sbin/auditctl -l').stdout
10
9
  # options = {
11
- # assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
10
+ # assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
12
11
  # multiple_values: true
13
12
  # }
14
13
  # describe parse_config(audit, options ) do
@@ -26,7 +25,7 @@ module Inspec::Resources
26
25
  output2 = command('curl http://127.0.0.1/php_status').stdout
27
26
  # php status is in format 'key : value', and we do not allow for multiple values
28
27
  options2 = {
29
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
28
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
30
29
  multiple_values: false
31
30
  }
32
31
 
@@ -76,7 +75,7 @@ module Inspec::Resources
76
75
  return skip_resource "Can't find file \"#{conf_path}\""
77
76
  end
78
77
  @content = read_file(conf_path).to_s
79
- if @content.empty? && inspec.file(conf_path).size > 0
78
+ if @content.empty? && !inspec.file(conf_path).empty?
80
79
  return skip_resource "Can't read file \"#{conf_path}\""
81
80
  end
82
81
 
@@ -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
  # The file format consists of
8
7
  # - username
@@ -62,17 +61,17 @@ module Inspec::Resources
62
61
  .add(:shells, field: 'shell')
63
62
 
64
63
  filter.add(:count) { |t, _|
65
- warn '[DEPRECATION] `passwd.count` is deprecated. Please use `passwd.entries.length` instead. It will be removed in version 1.0.0.'
64
+ warn '[DEPRECATION] `passwd.count` is deprecated. Please use `passwd.entries.length` instead. It will be removed in the next major version.'
66
65
  t.entries.length
67
66
  }
68
67
 
69
68
  filter.add(:usernames) { |t, x|
70
- warn '[DEPRECATION] `passwd.usernames` is deprecated. Please use `passwd.users` instead. It will be removed in version 1.0.0.'
69
+ warn '[DEPRECATION] `passwd.usernames` is deprecated. Please use `passwd.users` instead. It will be removed in the next major version.'
71
70
  t.users(x)
72
71
  }
73
72
 
74
73
  filter.add(:username) { |t, x|
75
- warn '[DEPRECATION] `passwd.username` is deprecated. Please use `passwd.users` instead. It will be removed in version 1.0.0.'
74
+ warn '[DEPRECATION] `passwd.username` is deprecated. Please use `passwd.users` instead. It will be removed in the next major version.'
76
75
  t.users(x)[0]
77
76
  }
78
77
 
@@ -84,7 +83,7 @@ module Inspec::Resources
84
83
  }
85
84
 
86
85
  def uid(x)
87
- warn '[DEPRECATION] `passwd.uid(arg)` is deprecated. Please use `passwd.uids(arg)` instead. It will be removed in version 1.0.0.'
86
+ warn '[DEPRECATION] `passwd.uid(arg)` is deprecated. Please use `passwd.uids(arg)` instead. It will be removed in the next major version.'
88
87
  uids(x)
89
88
  end
90
89
 
data/lib/resources/pip.rb CHANGED
@@ -7,6 +7,7 @@
7
7
  # it { should be_installed }
8
8
  # end
9
9
  #
10
+
10
11
  module Inspec::Resources
11
12
  class PipPackage < Inspec.resource(1)
12
13
  name 'pip'
@@ -15,10 +16,17 @@ module Inspec::Resources
15
16
  describe pip('Jinja2') do
16
17
  it { should be_installed }
17
18
  end
19
+
20
+ describe pip('django', '/path/to/virtualenv/bin/pip') do
21
+ it { should be_installed }
22
+ its('version') { should eq('1.11.4')}
23
+ end
18
24
  "
19
25
 
20
- def initialize(package_name)
26
+ def initialize(package_name, pip_path = nil)
21
27
  @package_name = package_name
28
+ @pip_cmd = pip_path || default_pip_path
29
+ return skip_resource 'pip not found' unless inspec.command(@pip_cmd).exist?
22
30
  end
23
31
 
24
32
  def info
@@ -26,12 +34,12 @@ module Inspec::Resources
26
34
 
27
35
  @info = {}
28
36
  @info[:type] = 'pip'
29
- cmd = inspec.command("#{pip_cmd} show #{@package_name}")
37
+ cmd = inspec.command("#{@pip_cmd} show #{@package_name}")
30
38
  return @info if cmd.exit_status != 0
31
39
 
32
40
  params = SimpleConfig.new(
33
41
  cmd.stdout,
34
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
42
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
35
43
  multiple_values: false,
36
44
  ).params
37
45
  @info[:name] = params['Name']
@@ -54,28 +62,28 @@ module Inspec::Resources
54
62
 
55
63
  private
56
64
 
57
- def pip_cmd
65
+ def default_pip_path
66
+ return 'pip' unless inspec.os.windows?
67
+
58
68
  # Pip is not on the default path for Windows, therefore we do some logic
59
69
  # to find the binary on Windows
60
- if inspec.os.windows?
61
- # we need to detect the pip command on Windows
62
- cmd = inspec.command('New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json')
63
- begin
64
- paths = JSON.parse(cmd.stdout)
65
- # use pip if it on system path
66
- pipcmd = paths['Pip']
67
- # calculate path on windows
68
- if defined?(paths['Python']) && pipcmd.nil?
69
- pipdir = paths['Python'].split('\\')
70
- # remove python.exe
71
- pipdir.pop
72
- pipcmd = pipdir.push('Scripts').push('pip.exe').join('/')
73
- end
74
- rescue JSON::ParserError => _e
75
- return nil
70
+ cmd = inspec.command('New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json')
71
+ begin
72
+ paths = JSON.parse(cmd.stdout)
73
+ # use pip if it on system path
74
+ pipcmd = paths['Pip']
75
+ # calculate path on windows
76
+ if defined?(paths['Python']) && pipcmd.nil?
77
+ pipdir = paths['Python'].split('\\')
78
+ # remove python.exe
79
+ pipdir.pop
80
+ pipcmd = pipdir.push('Scripts').push('pip.exe').join('/')
76
81
  end
82
+ rescue JSON::ParserError => _e
83
+ return nil
77
84
  end
78
- pipcmd || 'pip'
85
+
86
+ pipcmd
79
87
  end
80
88
  end
81
89
  end
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'utils/parser'
6
6
  require 'utils/filter'
7
+ require 'ipaddr'
7
8
 
8
9
  # TODO: currently we return local ip only
9
10
  # TODO: improve handling of same port on multiple interfaces
@@ -45,7 +46,7 @@ module Inspec::Resources
45
46
  .add(:protocols, field: 'protocol', style: :simple)
46
47
  .add(:processes, field: 'process', style: :simple)
47
48
  .add(:pids, field: 'pid', style: :simple)
48
- .add(:listening?) { |x| x.entries.length > 0 }
49
+ .add(:listening?) { |x| !x.entries.empty? }
49
50
  filter.connect(self, :info)
50
51
 
51
52
  def to_s
@@ -169,7 +170,7 @@ module Inspec::Resources
169
170
  ports = []
170
171
 
171
172
  # check that lsof is available, otherwise fail
172
- fail 'Please ensure `lsof` is available on the machine.' if !inspec.command(@lsof.to_s).exist?
173
+ raise 'Please ensure `lsof` is available on the machine.' if !inspec.command(@lsof.to_s).exist?
173
174
 
174
175
  # -F p=pid, c=command, P=protocol name, t=type, n=internet addresses
175
176
  # see 'OUTPUT FOR OTHER PROGRAMS' in LSOF(8)
@@ -263,10 +264,34 @@ module Inspec::Resources
263
264
  end
264
265
 
265
266
  # extract port information from netstat
266
- class LinuxPorts < PortsInfo
267
+ class LinuxPorts < PortsInfo # rubocop:disable Metrics/ClassLength
268
+ ALLOWED_PROTOCOLS = %w{tcp tcp6 udp udp6}.freeze
269
+
267
270
  def info
271
+ ports_via_ss || ports_via_netstat
272
+ end
273
+
274
+ def ports_via_ss
275
+ return nil unless inspec.command('ss').exist?
276
+
277
+ cmd = inspec.command('ss -tulpen')
278
+ return nil unless cmd.exit_status.to_i.zero?
279
+
280
+ ports = []
281
+
282
+ cmd.stdout.each_line do |line|
283
+ parsed_line = parse_ss_line(line)
284
+ ports << parsed_line unless parsed_line.nil?
285
+ end
286
+
287
+ ports
288
+ end
289
+
290
+ def ports_via_netstat
291
+ return nil unless inspec.command('netstat').exist?
292
+
268
293
  cmd = inspec.command('netstat -tulpen')
269
- return nil if cmd.exit_status.to_i != 0
294
+ return nil unless cmd.exit_status.to_i.zero?
270
295
 
271
296
  ports = []
272
297
  # parse all lines
@@ -286,10 +311,38 @@ module Inspec::Resources
286
311
  ip6 = /^(\S+):(\d+)$/.match(net_addr)
287
312
  ip6addr = ip6[1]
288
313
  ip6addr = '::' if ip6addr =~ /^:::$/
289
- # build uri
290
- ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
291
- # replace []
292
- host = ip_addr.host[1..ip_addr.host.size-2]
314
+
315
+ # v6 addresses need to end in a double-colon when using
316
+ # shorthand notation. netstat ends with a single colon.
317
+ # IPAddr will fail to properly parse an address unless it
318
+ # uses a double-colon for short-hand notation.
319
+ ip6addr += ':' if ip6addr =~ /\w:$/
320
+
321
+ begin
322
+ ip_parser = IPAddr.new(ip6addr)
323
+ rescue IPAddr::InvalidAddressError
324
+ # This IP is not parsable. There appears to be a bug in netstat
325
+ # output that truncates link-local IP addresses:
326
+ # example: udp6 0 0 fe80::42:acff:fe11::123 :::* 0 54550 3335/ntpd
327
+ # actual link address: inet6 fe80::42:acff:fe11:5/64 scope link
328
+ #
329
+ # in this example, the "5" is truncated making the netstat output
330
+ # an invalid IP address.
331
+ return [nil, nil]
332
+ end
333
+
334
+ # Check to see if this is a IPv4 address in a tcp6/udp6 line.
335
+ # If so, don't put brackets around the IP or URI won't know how
336
+ # to properly handle it.
337
+ # example: tcp6 0 0 127.0.0.1:8005 :::* LISTEN
338
+ if ip_parser.ipv4?
339
+ ip_addr = URI("addr://#{ip6addr}:#{ip6[2]}")
340
+ host = ip_addr.host
341
+ else
342
+ ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
343
+ # strip []
344
+ host = ip_addr.host[1..ip_addr.host.size-2]
345
+ end
293
346
  else
294
347
  ip_addr = URI('addr://'+net_addr)
295
348
  host = ip_addr.host
@@ -317,6 +370,7 @@ module Inspec::Resources
317
370
 
318
371
  # extract host and port information
319
372
  host, port = parse_net_address(parsed[4], protocol)
373
+ return {} if host.nil?
320
374
 
321
375
  # extract PID
322
376
  process = parsed[9].split('/')
@@ -332,6 +386,66 @@ module Inspec::Resources
332
386
  'pid' => pid,
333
387
  }
334
388
  end
389
+
390
+ def parse_ss_line(line)
391
+ parsed = line.split(/\s+/, 7)
392
+
393
+ # ss only returns "tcp" and "udp" as the protocol. However, netstat would return
394
+ # "tcp6" and "udp6" as necessary. In order to maintain backward compatibility, we
395
+ # will manually modify the protocol value if the line we're parsing is an IPv6
396
+ # entry.
397
+ process_info = parsed[6]
398
+ protocol = parsed[0]
399
+ protocol += '6' if process_info.include?('v6only:1')
400
+ return nil unless ALLOWED_PROTOCOLS.include?(protocol)
401
+
402
+ # parse the Local Address:Port
403
+ # examples:
404
+ # *:22
405
+ # :::22
406
+ # 10.0.2.15:1234
407
+ # ::ffff:10.0.2.15:9300
408
+ # fe80::a00:27ff:fe32:ed09%enp0s3:9200
409
+ parsed_net_address = parsed[4].match(/(\S+):(\*|\d+)$/)
410
+ return nil if parsed_net_address.nil?
411
+ host = parsed_net_address[1]
412
+ port = parsed_net_address[2]
413
+ return nil if host.nil? && port.nil?
414
+
415
+ # For backward compatibility with the netstat output, ensure the
416
+ # port is stored as an integer
417
+ port = port.to_i
418
+
419
+ # for those "v4-but-listed-in-v6" entries, strip off the
420
+ # leading IPv6 value at the beginning
421
+ # example: ::ffff:10.0.2.15:9200
422
+ host.delete!('::ffff:') if host.start_with?('::ffff:')
423
+
424
+ # if there's an interface name in the local address, which is common for
425
+ # IPv6 listeners, strip that out too.
426
+ # example: fe80::a00:27ff:fe32:ed09%enp0s3
427
+ host = host.split('%').first
428
+
429
+ # if host is "*", replace with "0.0.0.0" to maintain backward compatibility with
430
+ # the netstat-provided data
431
+ host = '0.0.0.0' if host == '*'
432
+
433
+ # parse the process name from the processes information
434
+ process_match = parsed[6].match(/users:\(\(\"(\S+)\"/)
435
+ process = process_match.nil? ? nil : process_match[1]
436
+
437
+ # parse the PID from the processes information
438
+ pid_match = parsed[6].match(/pid=(\d+)/)
439
+ pid = pid_match.nil? ? nil : pid_match[1].to_i
440
+
441
+ {
442
+ 'port' => port,
443
+ 'address' => host,
444
+ 'protocol' => protocol,
445
+ 'process' => process,
446
+ 'pid' => pid,
447
+ }
448
+ end
335
449
  end
336
450
 
337
451
  # extracts information from sockstat
@@ -434,13 +548,17 @@ module Inspec::Resources
434
548
  # the last . to :
435
549
  local_addr[local_addr.rindex('.')] = ':'
436
550
  host, port = parse_net_address(local_addr, protocol)
437
- {
438
- 'port' => port,
439
- 'address' => host,
440
- 'protocol' => protocol,
441
- }
551
+ if host.nil?
552
+ nil
553
+ else
554
+ {
555
+ 'port' => port,
556
+ 'address' => host,
557
+ 'protocol' => protocol,
558
+ }
559
+ end
442
560
  }
443
- ports
561
+ ports.compact
444
562
  end
445
563
  end
446
564
 
@@ -476,6 +594,7 @@ module Inspec::Resources
476
594
  local_addr[local_addr.rindex('.')] = ':'
477
595
  # extract host and port information
478
596
  host, port = parse_net_address(local_addr, protocol)
597
+ return {} if host.nil?
479
598
  # map data
480
599
  {
481
600
  'port' => port,
@@ -2,67 +2,50 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
- # license: All rights reserved
5
+ # author: Aaron Lippold
6
6
 
7
7
  module Inspec::Resources
8
8
  class Postgres < Inspec.resource(1)
9
9
  name 'postgres'
10
10
 
11
- attr_reader :service, :data_dir, :conf_dir, :conf_path
11
+ attr_reader :service, :data_dir, :conf_dir, :conf_path, :version, :cluster
12
12
  def initialize
13
- os = inspec.os
14
- if os.debian?
13
+ if inspec.os.debian?
15
14
  #
16
15
  # https://wiki.debian.org/PostgreSql
17
16
  #
18
17
  # Debian allows multiple versions of postgresql to be
19
18
  # installed as well as multiple "clusters" to be configured.
20
19
  #
21
- version = version_from_dir('/etc/postgresql')
22
- cluster = cluster_from_dir("/etc/postgresql/#{version}")
23
- @conf_dir = "/etc/postgresql/#{version}/#{cluster}"
24
- @data_dir = "/var/lib/postgresql/#{version}/#{cluster}"
25
- elsif os.redhat?
26
- #
27
- # /var/lib/pgsql/data is the default data directory on RHEL6
28
- # and RHEL7. However, PR #824 explicitly added version-based
29
- # directories. Thus, we call #version_from_dir unless it looks
30
- # like we are using unversioned directories.
31
- #
32
- # TODO(ssd): This has the potential to be noisy because of the
33
- # warning in version_from_dir. We should determine which case
34
- # is more common and only warn in the less common case.
35
- #
36
- version = if inspec.directory('/var/lib/pgsql/data').exist?
37
- warn 'Found /var/lib/pgsql/data. Assuming postgresql install uses un-versioned directories.'
38
- nil
39
- else
40
- version_from_dir('/var/lib/pgsql/')
41
- end
42
-
43
- @data_dir = File.join('/var/lib/pgsql/', version.to_s, 'data')
44
- elsif os[:name] == 'arch'
45
- #
46
- # https://wiki.archlinux.org/index.php/PostgreSQL
47
- #
48
- # The archlinux wiki points to /var/lib/postgresql/data as the
49
- # main data directory.
50
- #
51
- @data_dir = '/var/lib/postgres/data'
20
+ @version = version_from_psql || version_from_dir('/etc/postgresql')
21
+ @cluster = cluster_from_dir("/etc/postgresql/#{@version}")
22
+ @conf_dir = "/etc/postgresql/#{@version}/#{@cluster}"
23
+ @data_dir = "/var/lib/postgresql/#{@version}/#{@cluster}"
52
24
  else
53
- #
54
- # According to https://www.postgresql.org/docs/9.5/static/creating-cluster.html
55
- #
56
- # > There is no default, although locations such as
57
- # > /usr/local/pgsql/data or /var/lib/pgsql/data are popular.
58
- #
59
- @data_dir = '/var/lib/pgsql/data'
25
+ @version = version_from_psql
26
+ if @version.nil?
27
+ if inspec.directory('/var/lib/pgsql/data').exist?
28
+ warn 'Unable to determine PostgreSQL version: psql did not return
29
+ a version number and unversioned data directories were found.'
30
+ nil
31
+ else
32
+ @version = version_from_dir('/var/lib/pgsql')
33
+ end
34
+ end
35
+ @data_dir = locate_data_dir_location_by_version(@version)
60
36
  end
61
37
 
62
38
  @service = 'postgresql'
39
+ @service += "-#{@version}" if @version.to_f >= 9.4
63
40
  @conf_dir ||= @data_dir
41
+
64
42
  verify_dirs
65
- @conf_path = File.join @conf_dir, 'postgresql.conf'
43
+ if !@version.nil? && !@conf_dir.empty?
44
+ @conf_path = File.join @conf_dir, 'postgresql.conf'
45
+ else
46
+ @conf_path = nil
47
+ return skip_resource 'Seems like PostgreSQL is not installed on your system'
48
+ end
66
49
  end
67
50
 
68
51
  def to_s
@@ -81,6 +64,33 @@ module Inspec::Resources
81
64
  end
82
65
  end
83
66
 
67
+ def version_from_psql
68
+ return unless inspec.command('psql').exist?
69
+ inspec.command("psql --version | awk '{ print $NF }' | awk -F. '{ print $1\".\"$2 }'").stdout.strip
70
+ end
71
+
72
+ def locate_data_dir_location_by_version(ver = @version)
73
+ data_dir_loc = nil
74
+ dir_list = [
75
+ "/var/lib/pgsql/#{ver}/data",
76
+ '/var/lib/pgsql/data',
77
+ '/var/lib/postgres/data',
78
+ '/var/lib/postgresql/data',
79
+ ]
80
+
81
+ dir_list.each do |dir|
82
+ data_dir_loc = dir if inspec.directory(dir).exist?
83
+ break
84
+ end
85
+
86
+ if data_dir_loc.nil?
87
+ warn 'Unable to find the PostgreSQL data_dir in expected location(s), please
88
+ execute "psql -t -A -p <port> -h <host> -c "show hba_file";" as the PostgreSQL
89
+ DBA to find the non-starndard data_dir location.'
90
+ end
91
+ data_dir_loc
92
+ end
93
+
84
94
  def version_from_dir(dir)
85
95
  dirs = inspec.command("ls -d #{dir}/*/").stdout
86
96
  entries = dirs.lines.count