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,84 @@
1
+ # encoding: utf-8
2
+ # author: Doc Walker
3
+
4
+ require 'openssl'
5
+
6
+ class DhParams < Inspec.resource(1)
7
+ name 'dh_params'
8
+
9
+ desc '
10
+ Use the `dh_params` InSpec audit resource to test Diffie-Hellman (DH)
11
+ parameters.
12
+ '
13
+
14
+ example "
15
+ describe dh_params('/path/to/file.dh_pem') do
16
+ it { should be_dh_params }
17
+ it { should be_valid }
18
+ its('generator') { should eq 2 }
19
+ its('modulus') { should eq '00:91:a0:15:89:e5:bc:38:93:12:02:fc:...' }
20
+ its('prime_length') { should eq 2048 }
21
+ its('pem') { should eq '-----BEGIN DH PARAMETERS...' }
22
+ its('text') { should eq 'PKCS#3 DH Parameters: (2048 bit)...' }
23
+ end
24
+ "
25
+
26
+ def initialize(filename)
27
+ @dh_params_path = filename
28
+ file = inspec.file(@dh_params_path)
29
+ return skip_resource 'Unable to find DH parameters file ' \
30
+ "#{@dh_params_path}" unless file.exist?
31
+
32
+ begin
33
+ @dh_params = OpenSSL::PKey::DH.new file.content
34
+ rescue
35
+ @dh_params = nil
36
+ return skip_resource "Unable to load DH parameters #{@dh_params_path}"
37
+ end
38
+ end
39
+
40
+ # it { should be_dh_params }
41
+ def dh_params?
42
+ !@dh_params.nil?
43
+ end
44
+
45
+ # its('generator') { should eq 2 }
46
+ def generator
47
+ return if @dh_params.nil?
48
+ @dh_params.g.to_i
49
+ end
50
+
51
+ # its('modulus') { should eq '00:91:a0:15:89:e5:bc:38:93:12:02:fc:...' }
52
+ def modulus
53
+ return if @dh_params.nil?
54
+ '00:' + @dh_params.p.to_s(16).downcase.scan(/.{2}/).join(':')
55
+ end
56
+
57
+ # its('pem') { should eq '-----BEGIN DH PARAMETERS...' }
58
+ def pem
59
+ return if @dh_params.nil?
60
+ @dh_params.to_pem
61
+ end
62
+
63
+ # its('prime_length') { should be 2048 }
64
+ def prime_length
65
+ return if @dh_params.nil?
66
+ @dh_params.p.num_bits
67
+ end
68
+
69
+ # its('text') { should eq 'human-readable-text' }
70
+ def text
71
+ return if @dh_params.nil?
72
+ @dh_params.to_text
73
+ end
74
+
75
+ # it { should be_valid }
76
+ def valid?
77
+ return if @dh_params.nil?
78
+ @dh_params.params_ok?
79
+ end
80
+
81
+ def to_s
82
+ "dh_params #{@dh_params_path}"
83
+ end
84
+ end
@@ -13,9 +13,13 @@ module Inspec::Resources
13
13
  it { should be_directory }
14
14
  end
15
15
  "
16
- end
17
16
 
18
- def to_s
19
- "Directory #{@path}"
17
+ def exist?
18
+ file.exist? && file.directory?
19
+ end
20
+
21
+ def to_s
22
+ "Directory #{source_path}"
23
+ end
20
24
  end
21
25
  end
@@ -0,0 +1,198 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright 2017, Christoph Hartmann
4
+ #
5
+ # author: Christoph Hartmann
6
+ # author: Patrick Muench
7
+ # author: Dominik Richter
8
+
9
+ require 'utils/filter'
10
+ require 'hashie/mash'
11
+
12
+ module Inspec::Resources
13
+ class DockerContainerFilter
14
+ # use filtertable for containers
15
+ filter = FilterTable.create
16
+ filter.add_accessor(:where)
17
+ .add_accessor(:entries)
18
+ .add(:commands, field: 'command')
19
+ .add(:ids, field: 'id')
20
+ .add(:images, field: 'image')
21
+ .add(:labels, field: 'labels')
22
+ .add(:local_volumes, field: 'localvolumes')
23
+ .add(:mounts, field: 'mounts')
24
+ .add(:names, field: 'names')
25
+ .add(:networks, field: 'networks')
26
+ .add(:ports, field: 'ports')
27
+ .add(:running_for, field: 'runningfor')
28
+ .add(:sizes, field: 'size')
29
+ .add(:status, field: 'status')
30
+ .add(:exists?) { |x| !x.entries.empty? }
31
+ .add(:running?) { |x|
32
+ x.where { status.downcase.start_with?('up') }
33
+ }
34
+ filter.connect(self, :containers)
35
+
36
+ attr_reader :containers
37
+ def initialize(containers)
38
+ @containers = containers
39
+ end
40
+ end
41
+
42
+ class DockerImageFilter
43
+ filter = FilterTable.create
44
+ filter.add_accessor(:where)
45
+ .add_accessor(:entries)
46
+ .add(:ids, field: 'id')
47
+ .add(:repositories, field: 'repository')
48
+ .add(:tags, field: 'tag')
49
+ .add(:sizes, field: 'size')
50
+ .add(:digests, field: 'digest')
51
+ .add(:created, field: 'createdat')
52
+ .add(:created_since, field: 'createdsize')
53
+ .add(:exists?) { |x| !x.entries.empty? }
54
+ filter.connect(self, :images)
55
+
56
+ attr_reader :images
57
+ def initialize(images)
58
+ @images = images
59
+ end
60
+ end
61
+
62
+ # This resource helps to parse information from the docker host
63
+ # For compatability with Serverspec we also offer the following resouses:
64
+ # - docker_container
65
+ # - docker_image
66
+ class Docker < Inspec.resource(1)
67
+ name 'docker'
68
+
69
+ desc "
70
+ A resource to retrieve information about docker
71
+ "
72
+
73
+ example "
74
+ describe docker.containers do
75
+ its('images') { should_not include 'u12:latest' }
76
+ end
77
+
78
+ describe docker.images do
79
+ its('repositories') { should_not include 'inssecure_image' }
80
+ end
81
+
82
+ describe docker.version do
83
+ its('Server.Version') { should cmp >= '1.12'}
84
+ its('Client.Version') { should cmp >= '1.12'}
85
+ end
86
+
87
+ describe docker.object(id) do
88
+ its('Configuration.Path') { should eq 'value' }
89
+ end
90
+
91
+ docker.containers.ids.each do |id|
92
+ # call docker inspect for a specific container id
93
+ describe docker.object(id) do
94
+ its(%w(HostConfig Privileged)) { should cmp false }
95
+ its(%w(HostConfig Privileged)) { should_not cmp true }
96
+ end
97
+ end
98
+ "
99
+
100
+ def containers
101
+ DockerContainerFilter.new(parse_containers)
102
+ end
103
+
104
+ def images
105
+ DockerImageFilter.new(parse_images)
106
+ end
107
+
108
+ def version
109
+ return @version if defined?(@version)
110
+ data = {}
111
+ cmd = inspec.command('docker version --format \'{{ json . }}\'')
112
+ data = JSON.parse(cmd.stdout) if cmd.exit_status == 0
113
+ @version = Hashie::Mash.new(data)
114
+ rescue JSON::ParserError => _e
115
+ return Hashie::Mash.new({})
116
+ end
117
+
118
+ def info
119
+ return @info if defined?(@info)
120
+ data = {}
121
+ # docke info format is only supported for Docker 17.03+
122
+ cmd = inspec.command('docker info --format \'{{ json . }}\'')
123
+ data = JSON.parse(cmd.stdout) if cmd.exit_status == 0
124
+ @info = Hashie::Mash.new(data)
125
+ rescue JSON::ParserError => _e
126
+ return Hashie::Mash.new({})
127
+ end
128
+
129
+ # returns information about docker objects
130
+ def object(id)
131
+ return @inspect if defined?(@inspect)
132
+ data = JSON.parse(inspec.command("docker inspect #{id}").stdout)
133
+ data = data[0] if data.is_a?(Array)
134
+ @inspect = Hashie::Mash.new(data)
135
+ rescue JSON::ParserError => _e
136
+ return Hashie::Mash.new({})
137
+ end
138
+
139
+ def to_s
140
+ 'Docker Host'
141
+ end
142
+
143
+ private
144
+
145
+ def parse_containers
146
+ # @see https://github.com/moby/moby/issues/20625, works for docker 1.13+
147
+ # raw_containers = inspec.command('docker ps -a --no-trunc --format \'{{ json . }}\'').stdout
148
+ # therefore we stick with older approach
149
+ labels = %w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status}
150
+
151
+ # Networks LocalVolumes work with 1.13+ only
152
+ if !version.empty? && Gem::Version.new(version['Client']['Version']) >= Gem::Version.new('1.13')
153
+ labels.push('Networks')
154
+ labels.push('LocalVolumes')
155
+ end
156
+ # build command
157
+ format = labels.map { |label| "\"#{label}\": {{json .#{label}}}" }
158
+ raw_containers = inspec.command("docker ps -a --no-trunc --format '{#{format.join(', ')}}'").stdout
159
+ ps = []
160
+ # since docker is not outputting valid json, we need to parse each row
161
+ raw_containers.each_line { |entry|
162
+ j = JSON.parse(entry)
163
+ # convert all keys to lower_case to work well with ruby and filter table
164
+ j = j.map { |k, v|
165
+ [k.downcase, v]
166
+ }.to_h
167
+
168
+ # ensure all keys are there
169
+ j = ensure_container_keys(j)
170
+ ps.push(j)
171
+ }
172
+ ps
173
+ rescue JSON::ParserError => _e
174
+ warn 'Could not parse `docker ps` output'
175
+ []
176
+ end
177
+
178
+ def ensure_container_keys(entry)
179
+ %w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status Networks LocalVolumes}.each { |key|
180
+ entry[key.downcase] = nil if !entry.key?(key.downcase)
181
+ }
182
+ entry
183
+ end
184
+
185
+ def parse_images
186
+ # docker does not support the `json .` function here, therefore we need to emulate that behavior.
187
+ raw_images = inspec.command('docker images -a --no-trunc --format \'{ "id": {{json .ID}}, "repository": {{json .Repository}}, "tag": {{json .Tag}}, "size": {{json .Size}}, "digest": {{json .Digest}}, "createdat": {{json .CreatedAt}}, "createdsize": {{json .CreatedSince}} }\'').stdout
188
+ c_images = []
189
+ raw_images.each_line { |entry|
190
+ c_images.push(JSON.parse(entry))
191
+ }
192
+ c_images
193
+ rescue JSON::ParserError => _e
194
+ warn 'Could not parse `docker images` output'
195
+ []
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright 2017, Christoph Hartmann
4
+ #
5
+ # author: Christoph Hartmann
6
+ # author: Patrick Muench
7
+ # author: Dominik Richter
8
+
9
+ module Inspec::Resources
10
+ class DockerContainer < Inspec.resource(1)
11
+ name 'docker_container'
12
+ desc ''
13
+ example "
14
+ describe docker_container('an-echo-server') do
15
+ it { should exist }
16
+ it { should be_running }
17
+ its('id') { should_not eq '' }
18
+ its('image') { should eq 'busybox:latest' }
19
+ its('repo') { should eq 'busybox' }
20
+ its('tag') { should eq 'latest' }
21
+ its('ports') { should eq [] }
22
+ its('command') { should eq 'nc -ll -p 1234 -e /bin/cat' }
23
+ end
24
+
25
+ describe docker_container(id: 'e2c52a183358') do
26
+ it { should exist }
27
+ it { should be_running }
28
+ end
29
+ "
30
+
31
+ def initialize(opts = {})
32
+ # if a string is provided, we expect it is the name
33
+ if opts.is_a?(String)
34
+ @opts = { name: opts }
35
+ else
36
+ @opts = opts
37
+ end
38
+ end
39
+
40
+ def exist?
41
+ container_info.exists?
42
+ end
43
+
44
+ # is allways returning the full id
45
+ def id
46
+ container_info.ids[0] if container_info.entries.length == 1
47
+ end
48
+
49
+ def running?
50
+ status.downcase.start_with?('up') if container_info.entries.length == 1
51
+ end
52
+
53
+ def status
54
+ container_info.status[0] if container_info.entries.length == 1
55
+ end
56
+
57
+ def labels
58
+ container_info.labels[0] if container_info.entries.length == 1
59
+ end
60
+
61
+ def ports
62
+ container_info.ports[0] if container_info.entries.length == 1
63
+ end
64
+
65
+ def command
66
+ if container_info.entries.length == 1
67
+ cmd = container_info.commands[0]
68
+ cmd.slice(1, cmd.length - 2)
69
+ end
70
+ end
71
+
72
+ def image
73
+ container_info.images[0] if container_info.entries.length == 1
74
+ end
75
+
76
+ def repo
77
+ return if image.nil? || image_name_from_image.nil?
78
+ if image.include?('/') # host:port/ubuntu:latest
79
+ repo_part, image_part = image.split('/') # host:port, ubuntu:latest
80
+ repo_part + '/' + image_part.split(':')[0] # host:port + / + ubuntu
81
+ else
82
+ image_name_from_image.split(':')[0]
83
+ end
84
+ end
85
+
86
+ def tag
87
+ return if image_name_from_image.nil?
88
+ image_name_from_image.split(':')[1]
89
+ end
90
+
91
+ def to_s
92
+ name = @opts[:name] || @opts[:id]
93
+ "Docker Container #{name}"
94
+ end
95
+
96
+ private
97
+
98
+ def image_name_from_image
99
+ return if image.nil?
100
+ # possible image names include:
101
+ # alpine
102
+ # ubuntu:14.04
103
+ # repo.example.com:5000/ubuntu
104
+ # repo.example.com:5000/ubuntu:1404
105
+ image.include?('/') ? image.split('/')[1] : image
106
+ end
107
+
108
+ def container_info
109
+ return @info if defined?(@info)
110
+ opts = @opts
111
+ @info = inspec.docker.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) }
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright 2017, Christoph Hartmann
4
+ #
5
+ # author: Christoph Hartmann
6
+ # author: Patrick Muench
7
+ # author: Dominik Richter
8
+
9
+ module Inspec::Resources
10
+ class DockerImage < Inspec.resource(1)
11
+ name 'docker_image'
12
+ desc ''
13
+ example "
14
+ describe docker_image('alpine:latest') do
15
+ it { should exist }
16
+ its('id') { should_not eq '' }
17
+ its('image') { should eq 'alpine:latest' }
18
+ its('repo') { should eq 'alpine' }
19
+ its('tag') { should eq 'latest' }
20
+ end
21
+
22
+ describe docker_image('alpine:latest') do
23
+ it { should exist }
24
+ end
25
+
26
+ describe docker_image(id: '4a415e366388') do
27
+ it { should exist }
28
+ end
29
+ "
30
+
31
+ def initialize(opts = {})
32
+ # do sanitizion of input values
33
+ o = opts.dup
34
+ o = { image: opts } if opts.is_a?(String)
35
+ @opts = sanitize_options(o)
36
+ end
37
+
38
+ def exist?
39
+ image_info.exists?
40
+ end
41
+
42
+ def id
43
+ image_info.ids[0] if image_info.entries.size == 1
44
+ end
45
+
46
+ def image
47
+ "#{repo}:#{tag}" if image_info.entries.size == 1
48
+ end
49
+
50
+ def repo
51
+ image_info.repositories[0] if image_info.entries.size == 1
52
+ end
53
+
54
+ def tag
55
+ image_info.tags[0] if image_info.entries.size == 1
56
+ end
57
+
58
+ def to_s
59
+ img = @opts[:image] || @opts[:id]
60
+ "Docker Image #{img}"
61
+ end
62
+
63
+ private
64
+
65
+ def sanitize_options(opts)
66
+ if !opts[:image].nil?
67
+ if !opts[:image].index(':').nil?
68
+ repo, tag = opts[:image].split(':')
69
+ else
70
+ opts[:repo] = opts[:image]
71
+ opts[:image] = nil
72
+ end
73
+ opts[:repo] ||= repo
74
+ opts[:tag] ||= tag
75
+ end
76
+
77
+ if !opts[:id].nil?
78
+ if opts[:id].index(':').nil?
79
+ opts[:id] = 'sha256:' + opts[:id]
80
+ end
81
+ end
82
+
83
+ opts[:tag] ||= 'latest'
84
+ opts[:image] ||= "#{opts[:repo]}:#{opts[:tag]}" unless opts[:repo].nil?
85
+ opts
86
+ end
87
+
88
+ def image_info
89
+ return @info if defined?(@info)
90
+ opts = @opts
91
+ @info = inspec.docker.images.where {
92
+ (repository == opts[:repo] && tag == opts[:tag]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id])))
93
+ }
94
+ end
95
+ end
96
+ 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
  # The file format consists of
8
7
  # - group name
@@ -107,7 +106,7 @@ module Inspec::Resources
107
106
  # iterate over each line and filter comments
108
107
  @content.split("\n").each_with_object([]) do |line, lines|
109
108
  grp_info = parse_group_line(line)
110
- lines.push(grp_info) if !grp_info.nil? && grp_info.size > 0
109
+ lines.push(grp_info) if !grp_info.nil? && !grp_info.empty?
111
110
  end
112
111
  end
113
112
 
@@ -119,7 +118,7 @@ module Inspec::Resources
119
118
  line, _idx_nl = parse_comment_line(line, opts)
120
119
  x = line.split(':')
121
120
  # abort if we have an empty or comment line
122
- return nil if x.size == 0
121
+ return nil if x.empty?
123
122
  # map data
124
123
  {
125
124
  'name' => x.at(0), # Name of the group.
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ # author: Matthew Dromazos
3
+
4
+ require 'utils/parser'
5
+
6
+ class EtcHosts < Inspec.resource(1)
7
+ name 'etc_hosts'
8
+ desc 'Use the etc_hosts InSpec audit resource to find an
9
+ ip_address and its associated hosts'
10
+ example "
11
+ describe etc_hosts.where { ip_address == '127.0.0.1' } do
12
+ its('ip_address') { should cmp '127.0.0.1' }
13
+ its('primary_name') { should cmp 'localhost' }
14
+ its('all_host_names') { should eq [['localhost', 'localhost.localdomain', 'localhost4', 'localhost4.localdomain4']] }
15
+ end
16
+ "
17
+
18
+ attr_reader :params
19
+
20
+ include CommentParser
21
+
22
+ def initialize(hosts_path = nil)
23
+ return skip_resource 'The `etc_hosts` resource is not supported on your OS.' unless inspec.os.linux? || inspec.os.windows?
24
+ @conf_path = hosts_path || default_hosts_file_path
25
+ @content = nil
26
+ @params = nil
27
+ read_content
28
+ end
29
+
30
+ filter = FilterTable.create
31
+ filter.add_accessor(:where)
32
+ .add_accessor(:entries)
33
+ .add(:ip_address, field: 'ip_address')
34
+ .add(:primary_name, field: 'primary_name')
35
+ .add(:all_host_names, field: 'all_host_names')
36
+
37
+ filter.connect(self, :params)
38
+
39
+ private
40
+
41
+ def default_hosts_file_path
42
+ inspec.os.windows? ? 'C:\windows\system32\drivers\etc\hosts' : '/etc/hosts'
43
+ end
44
+
45
+ def read_content
46
+ @content = ''
47
+ @params = {}
48
+ @content = read_file(@conf_path)
49
+ @params = parse_conf(@content)
50
+ end
51
+
52
+ def parse_conf(content)
53
+ content.map do |line|
54
+ data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
55
+ parse_line(data) unless data == ''
56
+ end.compact
57
+ end
58
+
59
+ def parse_line(line)
60
+ line_parts = line.split
61
+ return nil unless line_parts.length >= 2
62
+ {
63
+ 'ip_address' => line_parts[0],
64
+ 'primary_name' => line_parts[1],
65
+ 'all_host_names' => line_parts[1..-1],
66
+ }
67
+ end
68
+
69
+ def read_file(conf_path = @conf_path)
70
+ file = inspec.file(conf_path)
71
+ if !file.file?
72
+ return skip_resource "Can't find file. \"#{@conf_path}\""
73
+ end
74
+
75
+ raw_conf = file.content
76
+ if raw_conf.empty? && !file.empty?
77
+ return skip_resource("Could not read file contents\" #{@conf_path}\"")
78
+ end
79
+ raw_conf.lines
80
+ end
81
+ end