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
@@ -1,33 +1,102 @@
1
1
  # encoding: utf-8
2
+ # author: Nolan Davidson
2
3
  # author: Christoph Hartmann
3
4
  # author: Dominik Richter
4
5
 
6
+ require 'hashie/mash'
7
+ require 'utils/database_helpers'
8
+
5
9
  module Inspec::Resources
10
+ # STABILITY: Experimental
11
+ # This resource needs further testing and refinement
12
+ #
13
+ # This requires the `sqlcmd` tool available on platform
14
+ # @see https://docs.microsoft.com/en-us/sql/relational-databases/scripting/sqlcmd-use-the-utility
15
+ # @see https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-connect-and-query-sqlcmd
6
16
  class MssqlSession < Inspec.resource(1)
7
17
  name 'mssql_session'
8
18
  desc 'Use the mssql_session InSpec audit resource to test SQL commands run against a MS Sql Server database.'
9
19
  example "
10
- sql = mssql_session('myuser','mypassword')
11
- describe sql.query('select * from sys.databases where name like \'*test*\') do
12
- its('stdout') {should_not match(/test/) }
20
+ # Using SQL authentication
21
+ sql = mssql_session(user: 'myuser', pass: 'mypassword')
22
+ describe sql.query('SELECT * FROM table').row(0).column('columnname') do
23
+ its('value') { should cmp == 1 }
24
+ end
25
+
26
+ # Passing no credentials to mssql_session forces it to use Windows authentication
27
+ sql_windows_auth = mssql_session
28
+ describe sql.query(\"SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') as \\\"login_mode\\\";\").row(0).column('login_mode') do
29
+ its('value') { should_not be_empty }
30
+ its('value') { should cmp == 1 }
13
31
  end
14
32
  "
15
33
 
16
- def initialize(user = nil, pass = nil)
17
- @user = user
18
- @pass = pass
19
- skip_resource('user and pass are required for MSSQL tests') if @user.nil? or @pass.nil?
34
+ attr_reader :user, :password, :host
35
+ def initialize(opts = {})
36
+ @user = opts[:user]
37
+ @password = opts[:password] || opts[:pass]
38
+ if opts[:pass]
39
+ warn '[DEPRECATED] use `password` option to supply password instead of `pass`'
40
+ end
41
+ @host = opts[:host] || 'localhost'
42
+ @instance = opts[:instance]
43
+
44
+ # check if sqlcmd is available
45
+ return skip_resource('sqlcmd is missing') if !inspec.command('sqlcmd').exist?
46
+ # check that database is reachable
47
+ return skip_resource("Can't connect to the MS SQL Server.") if !test_connection
20
48
  end
21
49
 
22
50
  def query(q)
23
- escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$').gsub(/\@/, '`@')
24
- cmd = inspec.command("sqlcmd -U #{@user} -P #{@pass} -Q \"#{escaped_query}\"")
25
-
26
- cmd
51
+ escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$')
52
+ # surpress 'x rows affected' in SQLCMD with 'set nocount on;'
53
+ cmd_string = "sqlcmd -Q \"set nocount on; #{escaped_query}\" -W -w 1024 -s ','"
54
+ cmd_string += " -U #{@user} -P '#{@password}'" unless @user.nil? || @password.nil?
55
+ if @instance.nil?
56
+ cmd_string += " -S #{@host}"
57
+ else
58
+ cmd_string += " -S #{@host}\\#{@instance}"
59
+ end
60
+ cmd = inspec.command(cmd_string)
61
+ out = cmd.stdout + "\n" + cmd.stderr
62
+ if cmd.exit_status != 0 || out =~ /Sqlcmd: Error/
63
+ # TODO: we need to throw an exception here
64
+ # change once https://github.com/chef/inspec/issues/1205 is in
65
+ warn "Could not execute the sql query #{out}"
66
+ DatabaseHelper::SQLQueryResult.new(cmd, Hashie::Mash.new({}))
67
+ else
68
+ DatabaseHelper::SQLQueryResult.new(cmd, parse_csv_result(cmd))
69
+ end
27
70
  end
28
71
 
29
72
  def to_s
30
- 'MSSQL'
73
+ 'MSSQL session'
74
+ end
75
+
76
+ private
77
+
78
+ def test_connection
79
+ !query('select getdate()').empty?
80
+ end
81
+
82
+ def parse_csv_result(cmd)
83
+ require 'csv'
84
+ table = CSV.parse(cmd.stdout, { headers: true })
85
+
86
+ # remove first row, since it will be a seperator line
87
+ table.delete(0)
88
+
89
+ # convert to hash
90
+ headers = table.headers
91
+
92
+ results = table.map { |row|
93
+ res = {}
94
+ headers.each { |header|
95
+ res[header.downcase] = row[header]
96
+ }
97
+ Hashie::Mash.new(res)
98
+ }
99
+ results
31
100
  end
32
101
  end
33
102
  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 Mysql < Inspec.resource(1)
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
- # license: All rights reserved
5
4
 
6
5
  require 'utils/simpleconfig'
7
6
  require 'utils/find_files'
@@ -73,7 +72,7 @@ module Inspec::Resources
73
72
  return skip_resource "Can't find file \"#{@conf_path}\""
74
73
  end
75
74
  raw_conf = read_file(@conf_path)
76
- if raw_conf.empty? && inspec.file(@conf_path).size > 0
75
+ if raw_conf.empty? && !inspec.file(@conf_path).empty?
77
76
  return skip_resource("Can't read file \"#{@conf_path}\"")
78
77
  end
79
78
 
@@ -2,22 +2,25 @@
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 MysqlSession < Inspec.resource(1)
9
9
  name 'mysql_session'
10
10
  desc 'Use the mysql_session InSpec audit resource to test SQL commands run against a MySQL database.'
11
11
  example "
12
- sql = mysql_session('my_user','password')
12
+ sql = mysql_session('my_user','password','host')
13
13
  describe sql.query('show databases like \'test\';') do
14
14
  its('stdout') { should_not match(/test/) }
15
15
  end
16
16
  "
17
17
 
18
- def initialize(user = nil, pass = nil)
18
+ def initialize(user = nil, pass = nil, host = 'localhost', port = nil, socket = nil)
19
19
  @user = user
20
20
  @pass = pass
21
+ @host = host
22
+ @port = port
23
+ @socket = socket
21
24
  init_fallback if user.nil? or pass.nil?
22
25
  skip_resource("Can't run MySQL SQL checks without authentication") if @user.nil? or @pass.nil?
23
26
  end
@@ -28,12 +31,20 @@ module Inspec::Resources
28
31
  escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$')
29
32
 
30
33
  # run the query
31
- cmd = inspec.command("mysql -u#{@user} -p#{@pass} #{db} -s -e \"#{escaped_query}\"")
34
+ command = "mysql -u#{@user} -p#{@pass}"
35
+ if !@socket.nil?
36
+ command += " -S #{@socket}"
37
+ else
38
+ command += " -h #{@host}"
39
+ end
40
+ command += " --port #{@port}" unless @port.nil?
41
+ command += " #{db} -s -e \"#{escaped_query}\""
42
+
43
+ cmd = inspec.command(command)
32
44
  out = cmd.stdout + "\n" + cmd.stderr
33
- if out =~ /Can't connect to .* MySQL server/ or
34
- out.downcase =~ /^error/
45
+ if out =~ /Can't connect to .* MySQL server/ || out.downcase =~ /^error/
35
46
  # skip this test if the server can't run the query
36
- skip_resource("Can't connect to MySQL instance for SQL checks.")
47
+ warn("Can't connect to MySQL instance for SQL checks.")
37
48
  end
38
49
 
39
50
  # return the raw command output
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'utils/nginx_parser'
6
+
7
+ # STABILITY: Experimental
8
+ # This resouce needs a proper interace to the underlying data, which is currently missing.
9
+ # Until it is added, we will keep it experimental.
10
+ #
11
+ # TODO: Support it on Windows. To do so, we need to recognize the base os and how
12
+ # it combines the file path. Calling `File.join` or similar methods may lead to errors
13
+ # when running remotely.
14
+ module Inspec::Resources
15
+ class NginxConf < Inspec.resource(1)
16
+ name 'nginx_conf'
17
+ desc 'Use the nginx_conf InSpec resource to test configuration data '\
18
+ 'for the NginX web server located in /etc/nginx/nginx.conf on '\
19
+ 'Linux and UNIX platforms.'
20
+ example "
21
+ describe nginx_conf.params ...
22
+ describe nginx_conf('/path/to/my/nginx.conf').params ...
23
+ "
24
+
25
+ attr_reader :contents
26
+
27
+ def initialize(conf_path = nil)
28
+ @conf_path = conf_path || '/etc/nginx/nginx.conf'
29
+ @contents = {}
30
+ return skip_resource 'The `nginx_conf` resource is currently not supported on Windows.' if inspec.os.windows?
31
+ end
32
+
33
+ def params
34
+ @params ||= parse_nginx(@conf_path)
35
+ rescue StandardError => e
36
+ skip_resource e.message
37
+ @params = {}
38
+ end
39
+
40
+ def to_s
41
+ "nginx_conf #{@conf_path}"
42
+ end
43
+
44
+ private
45
+
46
+ def read_content(path)
47
+ return @contents[path] if @contents.key?(path)
48
+ file = inspec.file(path)
49
+ if !file.file?
50
+ return skip_resource "Can't find file \"#{path}\""
51
+ end
52
+ @contents[path] = file.content
53
+ end
54
+
55
+ def parse_nginx(path)
56
+ return nil if inspec.os.windows?
57
+ content = read_content(path)
58
+ data = NginxConfig.parse(content)
59
+ resolve_references(data, File.dirname(path))
60
+ rescue StandardError => _
61
+ raise "Cannot parse NginX config in #{path}."
62
+ end
63
+
64
+ # Cycle through the complete parsed data structure and try to find any
65
+ # calls to `include`. In NginX, this is used to embed data from other
66
+ # files into the current data structure.
67
+ #
68
+ # The method steps through the object structure that is passed in to
69
+ # find any calls to 'include' and returns the object structure with the
70
+ # included data merged in.
71
+ #
72
+ # @param data [Hash] data structure from NginxConfig.parse
73
+ # @param rel_path [String] the relative path from which this config is read
74
+ # @return [Hash] data structure with references included
75
+ def resolve_references(data, rel_path)
76
+ # Walk through all array entries to find more references
77
+ return data.map { |x| resolve_references(x, rel_path) } if data.is_a?(Array)
78
+
79
+ # Return any data that we cannot step into to find more `include` calls
80
+ return data unless data.is_a?(Hash)
81
+
82
+ # Any call to `include` gets its data read, parsed, and merged back
83
+ # into the current data structure
84
+ if data.key?('include')
85
+ data.delete('include').flatten
86
+ .map { |x| File.expand_path(x, rel_path) }
87
+ .map { |path| parse_nginx(path) }
88
+ .map { |e| data.merge!(e) }
89
+ end
90
+
91
+ # Walk through the remaining hash fields to find more references
92
+ Hash[data.map { |k, v| [k, resolve_references(v, rel_path)] }]
93
+ end
94
+ end
95
+ 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 'utils/simpleconfig'
8
7
 
@@ -43,7 +42,7 @@ module Inspec::Resources
43
42
  end
44
43
 
45
44
  content = inspec.file(@conf_path).content
46
- if content.empty? && inspec.file(@conf_path).size > 0
45
+ if content.empty? && !inspec.file(@conf_path).empty?
47
46
  skip_resource "Can't read file \"#{@conf_path}\""
48
47
  return @params = {}
49
48
  end
@@ -51,7 +50,7 @@ module Inspec::Resources
51
50
  # parse the file
52
51
  conf = SimpleConfig.new(
53
52
  content,
54
- assignment_re: /^\s*(\S+)\s+(.*)\s*$/,
53
+ assignment_regex: /^\s*(\S+)\s+(.*)\s*$/,
55
54
  multiple_values: true,
56
55
  )
57
56
  @params = conf.params
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+ # author: Nolan Davidson
3
+ # author: Christoph Hartmann
4
+ # author: Dominik Richter
5
+
6
+ require 'hashie/mash'
7
+ require 'utils/database_helpers'
8
+ require 'htmlentities'
9
+ require 'rexml/document'
10
+ require 'csv'
11
+
12
+ module Inspec::Resources
13
+ # STABILITY: Experimental
14
+ # This resource needs further testing and refinement
15
+ #
16
+ class OracledbSession < Inspec.resource(1)
17
+ name 'oracledb_session'
18
+ desc 'Use the oracledb_session InSpec resource to test commands against an Oracle database'
19
+ example "
20
+ sql = oracledb_session(user: 'my_user', pass: 'password')
21
+ describe sql.query(\"SELECT UPPER(VALUE) AS VALUE FROM V$PARAMETER WHERE UPPER(NAME)='AUDIT_SYS_OPERATIONS'\").row(0).column('value') do
22
+ its('value') { should eq 'TRUE' }
23
+ end
24
+ "
25
+
26
+ attr_reader :user, :password, :host, :service
27
+ def initialize(opts = {})
28
+ @user = opts[:user]
29
+ @password = opts[:password] || opts[:pass]
30
+ if opts[:pass]
31
+ warn '[DEPRECATED] use `password` option to supply password instead of `pass`'
32
+ end
33
+
34
+ @host = opts[:host] || 'localhost'
35
+ @port = opts[:port] || '1521'
36
+ @service = opts[:service]
37
+
38
+ # we prefer sqlci although it is way slower than sqlplus, but it understands csv properly
39
+ @sqlcl_bin = 'sql'
40
+ @sqlplus_bin = opts[:sqlplus_bin] || 'sqlplus'
41
+
42
+ return skip_resource "Can't run Oracle checks without authentication" if @user.nil? || @password.nil?
43
+ return skip_resource 'You must provide a service name for the session' if @service.nil?
44
+ end
45
+
46
+ def query(q)
47
+ escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"')
48
+ # escape tables with $
49
+ escaped_query = escaped_query.gsub('$', '\\$')
50
+
51
+ p = nil
52
+ # use sqlplus if sqlcl is not available
53
+ if inspec.command(@sqlcl_bin).exist?
54
+ bin = @sqlcl_bin
55
+ opts = "set sqlformat csv\nSET FEEDBACK OFF"
56
+ p = :parse_csv_result
57
+ else
58
+ bin = @sqlplus_bin
59
+ opts = "SET MARKUP HTML ON\nSET FEEDBACK OFF"
60
+ p = :parse_html_result
61
+ end
62
+
63
+ command = "echo \"#{opts}\n#{verify_query(escaped_query)}\nEXIT\" | #{bin} -s #{@user}/#{@password}@//#{@host}:#{@port}/#{@service}"
64
+ cmd = inspec.command(command)
65
+
66
+ out = cmd.stdout + "\n" + cmd.stderr
67
+ if out.downcase =~ /^error/
68
+ # TODO: we need to throw an exception here
69
+ # change once https://github.com/chef/inspec/issues/1205 is in
70
+ warn "Could not execute the sql query #{out}"
71
+ DatabaseHelper::SQLQueryResult.new(cmd, Hashie::Mash.new({}))
72
+ end
73
+ DatabaseHelper::SQLQueryResult.new(cmd, send(p, cmd.stdout))
74
+ end
75
+
76
+ def to_s
77
+ 'Oracle Session'
78
+ end
79
+
80
+ private
81
+
82
+ def verify_query(query)
83
+ # ensure we have a ; at the end
84
+ query + ';' if !query.strip.end_with?(';')
85
+ query
86
+ end
87
+
88
+ def parse_csv_result(stdout)
89
+ output = stdout.delete(/\r/)
90
+ table = CSV.parse(output, { headers: true })
91
+
92
+ # convert to hash
93
+ headers = table.headers
94
+
95
+ results = table.map { |row|
96
+ res = {}
97
+ headers.each { |header|
98
+ res[header.downcase] = row[header]
99
+ }
100
+ Hashie::Mash.new(res)
101
+ }
102
+ results
103
+ end
104
+
105
+ def parse_html_result(stdout) # rubocop:disable Metrics/AbcSize
106
+ result = stdout
107
+ # make oracle html valid html by removing the p tag, it does not include a closing tag
108
+ result = result.gsub('<p>', '').gsub('</p>', '').gsub('<br>', '')
109
+ doc = REXML::Document.new result
110
+ table = doc.elements['table']
111
+ hash = []
112
+ if !table.nil?
113
+ rows = table.elements.to_a
114
+ headers = rows[0].elements.to_a('th').map { |entry| entry.text.strip }
115
+ rows.delete_at(0)
116
+
117
+ # iterate over each row, first row is header
118
+ hash = []
119
+ if !rows.nil? && !rows.empty?
120
+ hash = rows.map { |row|
121
+ res = {}
122
+ entries = row.elements.to_a('td')
123
+ # ignore if we have empty entries, oracle is adding th rows in between
124
+ return nil if entries.empty?
125
+ headers.each_with_index { |header, index|
126
+ # we need htmlentities since we do not have nokogiri
127
+ coder = HTMLEntities.new
128
+ val = coder.decode(entries[index].text).strip
129
+ res[header.downcase] = val
130
+ }
131
+ Hashie::Mash.new(res)
132
+ }.compact
133
+ end
134
+ end
135
+ hash
136
+ end
137
+ end
138
+ 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
  # Usage:
8
7
  #
@@ -26,8 +25,6 @@ module Inspec::Resources
26
25
  attr_reader :content
27
26
  def initialize(env = nil)
28
27
  @osenv = env
29
- @content = nil
30
- @content = value_for(env) unless env.nil?
31
28
  end
32
29
 
33
30
  def split
@@ -36,7 +33,12 @@ module Inspec::Resources
36
33
  path_separator = inspec.os.windows? ? ';' : ':'
37
34
  # -1 is required to catch cases like dir1::dir2:
38
35
  # where we have a trailing :
39
- @content.nil? ? [] : @content.split(path_separator, -1)
36
+ content.nil? ? [] : content.split(path_separator, -1)
37
+ end
38
+
39
+ def content
40
+ return @content if defined?(@content)
41
+ @content = value_for(@osenv) unless @osenv.nil?
40
42
  end
41
43
 
42
44
  def to_s
@@ -59,7 +61,7 @@ module Inspec::Resources
59
61
  out = inspec.command(command)
60
62
 
61
63
  unless out.exit_status == 0
62
- skip_resource "Can't read environment variables on #{os[:name]}. "\
64
+ skip_resource "Can't read environment variables on #{inspec.os.name}. "\
63
65
  "Tried `#{command}` which returned #{out.exit_status}"
64
66
  end
65
67
 
@@ -15,11 +15,12 @@ module Inspec::Resources
15
15
  example "
16
16
  describe package('nginx') do
17
17
  it { should be_installed }
18
+ it { should_not be_held } # for dpkg platforms that support holding a version from being upgraded
18
19
  its('version') { should eq 1.9.5 }
19
20
  end
20
21
  "
21
22
 
22
- def initialize(package_name = nil) # rubocop:disable Metrics/AbcSize
23
+ def initialize(package_name = nil, opts = {}) # rubocop:disable Metrics/AbcSize
23
24
  @package_name = package_name
24
25
  @name = @package_name
25
26
  @cache = nil
@@ -29,8 +30,8 @@ module Inspec::Resources
29
30
  os = inspec.os
30
31
  if os.debian?
31
32
  @pkgman = Deb.new(inspec)
32
- elsif %w{redhat suse amazon fedora}.include?(os[:family])
33
- @pkgman = Rpm.new(inspec)
33
+ elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family])
34
+ @pkgman = Rpm.new(inspec, opts)
34
35
  elsif ['arch'].include?(os[:family])
35
36
  @pkgman = Pacman.new(inspec)
36
37
  elsif ['darwin'].include?(os[:family])
@@ -46,6 +47,8 @@ module Inspec::Resources
46
47
  else
47
48
  return skip_resource 'The `package` resource is not supported on your OS yet.'
48
49
  end
50
+
51
+ evaluate_missing_requirements
49
52
  end
50
53
 
51
54
  # returns true if the package is installed
@@ -54,6 +57,13 @@ module Inspec::Resources
54
57
  info[:installed] == true
55
58
  end
56
59
 
60
+ # returns true it the package is held (if the OS supports it)
61
+ def held?(_provider = nil, _version = nil)
62
+ return false if info.nil?
63
+ return false unless info.key?(:held)
64
+ info[:held] == true
65
+ end
66
+
57
67
  # returns the package description
58
68
  def info
59
69
  return @cache if !@cache.nil?
@@ -71,6 +81,14 @@ module Inspec::Resources
71
81
  def to_s
72
82
  "System Package #{@package_name}"
73
83
  end
84
+
85
+ private
86
+
87
+ def evaluate_missing_requirements
88
+ missing_requirements_string = @pkgman.missing_requirements.uniq.join(', ')
89
+ return if missing_requirements_string.empty?
90
+ skip_resource "The following requirements are not met for this resource: #{missing_requirements_string}"
91
+ end
74
92
  end
75
93
 
76
94
  class PkgManagement
@@ -78,6 +96,12 @@ module Inspec::Resources
78
96
  def initialize(inspec)
79
97
  @inspec = inspec
80
98
  end
99
+
100
+ def missing_requirements
101
+ # Each provider can provide an Array of missing requirements that will be
102
+ # combined into a `skip_resource` message
103
+ []
104
+ end
81
105
  end
82
106
 
83
107
  # Debian / Ubuntu
@@ -88,14 +112,17 @@ module Inspec::Resources
88
112
 
89
113
  params = SimpleConfig.new(
90
114
  cmd.stdout.chomp,
91
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
115
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
92
116
  multiple_values: false,
93
117
  ).params
118
+ # If the package is installed, Status is "install ok installed"
119
+ # If the package is installed and marked hold, Status is "hold ok installed"
94
120
  # If the package is removed and not purged, Status is "deinstall ok config-files" with exit_status 0
95
121
  # If the package is purged cmd fails with non-zero exit status
96
122
  {
97
123
  name: params['Package'],
98
- installed: params['Status'].split(' ').first == 'install',
124
+ installed: params['Status'].split(' ')[2] == 'installed',
125
+ held: params['Status'].split(' ')[0] == 'hold',
99
126
  version: params['Version'],
100
127
  type: 'deb',
101
128
  }
@@ -104,14 +131,31 @@ module Inspec::Resources
104
131
 
105
132
  # RHEL family
106
133
  class Rpm < PkgManagement
134
+ def initialize(inspec, opts)
135
+ super(inspec)
136
+
137
+ @dbpath = opts.fetch(:rpm_dbpath, nil)
138
+ end
139
+
140
+ def missing_requirements
141
+ missing_requirements = []
142
+
143
+ unless @dbpath.nil? || inspec.directory(@dbpath).directory?
144
+ missing_requirements << "RPMDB #{@dbpath} does not exist"
145
+ end
146
+
147
+ missing_requirements
148
+ end
149
+
107
150
  def info(package_name)
108
- cmd = inspec.command("rpm -qia #{package_name}")
151
+ rpm_cmd = rpm_command(package_name)
152
+ cmd = inspec.command(rpm_cmd)
109
153
  # CentOS does not return an error code if the package is not installed,
110
154
  # therefore we need to check for emptyness
111
155
  return nil if cmd.exit_status.to_i != 0 || cmd.stdout.chomp.empty?
112
156
  params = SimpleConfig.new(
113
157
  cmd.stdout.chomp,
114
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
158
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
115
159
  multiple_values: false,
116
160
  ).params
117
161
  # On some (all?) systems, the linebreak before the vendor line is missing
@@ -133,6 +177,17 @@ module Inspec::Resources
133
177
  type: 'rpm',
134
178
  }
135
179
  end
180
+
181
+ private
182
+
183
+ def rpm_command(package_name)
184
+ cmd = ''
185
+ cmd += 'rpm -qia'
186
+ cmd += " --dbpath #{@dbpath}" if @dbpath
187
+ cmd += ' ' + package_name
188
+
189
+ cmd
190
+ end
136
191
  end
137
192
 
138
193
  # MacOS / Darwin implementation
@@ -143,11 +198,13 @@ module Inspec::Resources
143
198
  # parse data
144
199
  pkg = JSON.parse(cmd.stdout)[0]
145
200
  {
146
- name: pkg.name.to_s,
201
+ name: pkg['name'],
147
202
  installed: true,
148
- version: pkg.installed.version.to_s,
203
+ version: pkg['installed'][0]['version'],
149
204
  type: 'brew',
150
205
  }
206
+ rescue JSON::ParserError => _e
207
+ return nil
151
208
  end
152
209
  end
153
210
 
@@ -159,7 +216,7 @@ module Inspec::Resources
159
216
 
160
217
  params = SimpleConfig.new(
161
218
  cmd.stdout.chomp,
162
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
219
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
163
220
  multiple_values: false,
164
221
  ).params
165
222
 
@@ -204,7 +261,7 @@ module Inspec::Resources
204
261
  # Find the package
205
262
  cmd = inspec.command <<-EOF.gsub(/^\s*/, '')
206
263
  Get-ItemProperty (@("#{search_paths.join('", "')}") | Where-Object { Test-Path $_ }) |
207
- Where-Object { $_.DisplayName -like "#{package_name}*" -or $_.PSChildName -like "#{package_name}" } |
264
+ Where-Object { $_.DisplayName -like "#{package_name}" -or $_.PSChildName -like "#{package_name}" } |
208
265
  Select-Object -Property DisplayName,DisplayVersion | ConvertTo-Json
209
266
  EOF
210
267
 
@@ -259,7 +316,7 @@ module Inspec::Resources
259
316
 
260
317
  params = SimpleConfig.new(
261
318
  cmd.stdout.chomp,
262
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
319
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
263
320
  multiple_values: false,
264
321
  ).params
265
322
 
@@ -280,7 +337,7 @@ module Inspec::Resources
280
337
 
281
338
  params = SimpleConfig.new(
282
339
  cmd.stdout.chomp,
283
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
340
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
284
341
  multiple_values: false,
285
342
  ).params
286
343