inspec 0.9.0

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 (247) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +65 -0
  4. data/.travis.yml +23 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile +33 -0
  7. data/LICENSE +201 -0
  8. data/MAINTAINERS.md +28 -0
  9. data/MAINTAINERS.toml +42 -0
  10. data/README.md +257 -0
  11. data/Rakefile +47 -0
  12. data/bin/inspec +109 -0
  13. data/docs/ctl_inspec.rst +195 -0
  14. data/docs/dsl_inspec.rst +182 -0
  15. data/docs/readme.rst +100 -0
  16. data/docs/resources.rst +4319 -0
  17. data/docs/template.rst +51 -0
  18. data/examples/test-kitchen/.kitchen.yml +20 -0
  19. data/examples/test-kitchen/Berksfile +3 -0
  20. data/examples/test-kitchen/Gemfile +21 -0
  21. data/examples/test-kitchen/README.md +27 -0
  22. data/examples/test-kitchen/metadata.rb +7 -0
  23. data/examples/test-kitchen/recipes/default.rb +6 -0
  24. data/examples/test-kitchen/recipes/nginx.rb +30 -0
  25. data/examples/test-kitchen/test/integration/default/web_spec.rb +28 -0
  26. data/inspec.gemspec +30 -0
  27. data/lib/inspec.rb +20 -0
  28. data/lib/inspec/backend.rb +42 -0
  29. data/lib/inspec/dsl.rb +151 -0
  30. data/lib/inspec/log.rb +34 -0
  31. data/lib/inspec/metadata.rb +79 -0
  32. data/lib/inspec/plugins.rb +9 -0
  33. data/lib/inspec/plugins/resource.rb +62 -0
  34. data/lib/inspec/profile.rb +138 -0
  35. data/lib/inspec/profile_context.rb +170 -0
  36. data/lib/inspec/resource.rb +76 -0
  37. data/lib/inspec/rspec_json_formatter.rb +27 -0
  38. data/lib/inspec/rule.rb +170 -0
  39. data/lib/inspec/runner.rb +154 -0
  40. data/lib/inspec/shell.rb +66 -0
  41. data/lib/inspec/targets.rb +9 -0
  42. data/lib/inspec/targets/core.rb +27 -0
  43. data/lib/inspec/targets/dir.rb +67 -0
  44. data/lib/inspec/targets/file.rb +29 -0
  45. data/lib/inspec/targets/folder.rb +43 -0
  46. data/lib/inspec/targets/tar.rb +34 -0
  47. data/lib/inspec/targets/url.rb +39 -0
  48. data/lib/inspec/targets/zip.rb +47 -0
  49. data/lib/inspec/version.rb +7 -0
  50. data/lib/matchers/matchers.rb +221 -0
  51. data/lib/resources/apache.rb +29 -0
  52. data/lib/resources/apache_conf.rb +113 -0
  53. data/lib/resources/apt.rb +140 -0
  54. data/lib/resources/audit_policy.rb +63 -0
  55. data/lib/resources/auditd_conf.rb +56 -0
  56. data/lib/resources/auditd_rules.rb +53 -0
  57. data/lib/resources/bond.rb +65 -0
  58. data/lib/resources/bridge.rb +114 -0
  59. data/lib/resources/command.rb +57 -0
  60. data/lib/resources/csv.rb +32 -0
  61. data/lib/resources/directory.rb +15 -0
  62. data/lib/resources/etc_group.rb +150 -0
  63. data/lib/resources/file.rb +110 -0
  64. data/lib/resources/gem.rb +46 -0
  65. data/lib/resources/group.rb +132 -0
  66. data/lib/resources/host.rb +143 -0
  67. data/lib/resources/inetd_conf.rb +56 -0
  68. data/lib/resources/interface.rb +127 -0
  69. data/lib/resources/iptables.rb +65 -0
  70. data/lib/resources/json.rb +64 -0
  71. data/lib/resources/kernel_module.rb +40 -0
  72. data/lib/resources/kernel_parameter.rb +55 -0
  73. data/lib/resources/limits_conf.rb +55 -0
  74. data/lib/resources/login_def.rb +60 -0
  75. data/lib/resources/mysql.rb +81 -0
  76. data/lib/resources/mysql_conf.rb +116 -0
  77. data/lib/resources/mysql_session.rb +52 -0
  78. data/lib/resources/npm.rb +44 -0
  79. data/lib/resources/ntp_conf.rb +58 -0
  80. data/lib/resources/oneget.rb +63 -0
  81. data/lib/resources/os.rb +22 -0
  82. data/lib/resources/os_env.rb +34 -0
  83. data/lib/resources/package.rb +169 -0
  84. data/lib/resources/parse_config.rb +75 -0
  85. data/lib/resources/passwd.rb +93 -0
  86. data/lib/resources/pip.rb +75 -0
  87. data/lib/resources/port.rb +296 -0
  88. data/lib/resources/postgres.rb +37 -0
  89. data/lib/resources/postgres_conf.rb +87 -0
  90. data/lib/resources/postgres_session.rb +59 -0
  91. data/lib/resources/processes.rb +57 -0
  92. data/lib/resources/registry_key.rb +54 -0
  93. data/lib/resources/script.rb +34 -0
  94. data/lib/resources/security_policy.rb +73 -0
  95. data/lib/resources/service.rb +379 -0
  96. data/lib/resources/ssh_conf.rb +75 -0
  97. data/lib/resources/user.rb +374 -0
  98. data/lib/resources/windows_feature.rb +77 -0
  99. data/lib/resources/yaml.rb +23 -0
  100. data/lib/resources/yum.rb +154 -0
  101. data/lib/utils/convert.rb +12 -0
  102. data/lib/utils/detect.rb +15 -0
  103. data/lib/utils/find_files.rb +36 -0
  104. data/lib/utils/hash.rb +13 -0
  105. data/lib/utils/modulator.rb +12 -0
  106. data/lib/utils/parser.rb +61 -0
  107. data/lib/utils/simpleconfig.rb +115 -0
  108. data/tasks/maintainers.rb +213 -0
  109. data/test/docker_run.rb +156 -0
  110. data/test/docker_test.rb +51 -0
  111. data/test/helper.rb +200 -0
  112. data/test/integration/.kitchen.yml +42 -0
  113. data/test/integration/Berksfile +4 -0
  114. data/test/integration/cookbooks/os_prepare/metadata.rb +8 -0
  115. data/test/integration/cookbooks/os_prepare/recipes/apt.rb +20 -0
  116. data/test/integration/cookbooks/os_prepare/recipes/default.rb +9 -0
  117. data/test/integration/cookbooks/os_prepare/recipes/file.rb +21 -0
  118. data/test/integration/cookbooks/os_prepare/recipes/package.rb +26 -0
  119. data/test/integration/default/_debug_spec.rb +1 -0
  120. data/test/integration/default/apt_spec.rb +42 -0
  121. data/test/integration/default/file_spec.rb +109 -0
  122. data/test/integration/default/group_spec.rb +32 -0
  123. data/test/integration/default/kernel_module_spec.rb +17 -0
  124. data/test/integration/default/kernel_parameter_spec.rb +56 -0
  125. data/test/integration/default/package_spec.rb +11 -0
  126. data/test/integration/default/service_spec.rb +28 -0
  127. data/test/integration/default/user_spec.rb +44 -0
  128. data/test/resource/command_test.rb +33 -0
  129. data/test/resource/dsl_test.rb +45 -0
  130. data/test/resource/file_test.rb +130 -0
  131. data/test/resource/ssh_config.rb +9 -0
  132. data/test/resource/sshd_config.rb +9 -0
  133. data/test/test-extra.yaml +11 -0
  134. data/test/test.yaml +11 -0
  135. data/test/unit/mock/cmd/Get-NetAdapter +24 -0
  136. data/test/unit/mock/cmd/GetUserAccount +33 -0
  137. data/test/unit/mock/cmd/GetWin32Group +23 -0
  138. data/test/unit/mock/cmd/PATH +1 -0
  139. data/test/unit/mock/cmd/Resolve-DnsName +26 -0
  140. data/test/unit/mock/cmd/Test-NetConnection +4 -0
  141. data/test/unit/mock/cmd/auditctl +7 -0
  142. data/test/unit/mock/cmd/auditpol +2 -0
  143. data/test/unit/mock/cmd/brew-info-jq +1 -0
  144. data/test/unit/mock/cmd/chage-l-root +7 -0
  145. data/test/unit/mock/cmd/dpkg-s-curl +21 -0
  146. data/test/unit/mock/cmd/dscl +5 -0
  147. data/test/unit/mock/cmd/etc-apt +7 -0
  148. data/test/unit/mock/cmd/find-etc-rc-d-name-S +12 -0
  149. data/test/unit/mock/cmd/find-net-interface +9 -0
  150. data/test/unit/mock/cmd/gem-list-local-a-q-rubocop +1 -0
  151. data/test/unit/mock/cmd/get-net-tcpconnection +24 -0
  152. data/test/unit/mock/cmd/get-netadapter-binding-bridge +4 -0
  153. data/test/unit/mock/cmd/get-package-firefox +30 -0
  154. data/test/unit/mock/cmd/get-package-ruby +18 -0
  155. data/test/unit/mock/cmd/get-service-dhcp +10 -0
  156. data/test/unit/mock/cmd/get-windows-feature +7 -0
  157. data/test/unit/mock/cmd/getent-hosts-example.com +1 -0
  158. data/test/unit/mock/cmd/getent-passwd-root +1 -0
  159. data/test/unit/mock/cmd/id-chartmann +1 -0
  160. data/test/unit/mock/cmd/id-root +1 -0
  161. data/test/unit/mock/cmd/initctl-show-config-ssh +3 -0
  162. data/test/unit/mock/cmd/initctl-status-ssh +1 -0
  163. data/test/unit/mock/cmd/iptables-s +6 -0
  164. data/test/unit/mock/cmd/launchctl-list +3 -0
  165. data/test/unit/mock/cmd/ls-1-etc-init.d +2 -0
  166. data/test/unit/mock/cmd/ls-sys-class-net-br +2 -0
  167. data/test/unit/mock/cmd/lsmod +2 -0
  168. data/test/unit/mock/cmd/lsof-np-itcp +4 -0
  169. data/test/unit/mock/cmd/netstat-tulpen +5 -0
  170. data/test/unit/mock/cmd/npm-ls-g--json-bower +9 -0
  171. data/test/unit/mock/cmd/pacman-qi-curl +21 -0
  172. data/test/unit/mock/cmd/ping-example.com +6 -0
  173. data/test/unit/mock/cmd/pip-show-jinja2 +11 -0
  174. data/test/unit/mock/cmd/ps-aux +3 -0
  175. data/test/unit/mock/cmd/pw-usershow-root-7 +1 -0
  176. data/test/unit/mock/cmd/reg_schedule +1 -0
  177. data/test/unit/mock/cmd/rpm-qia-curl +24 -0
  178. data/test/unit/mock/cmd/sbin_sysctl +1 -0
  179. data/test/unit/mock/cmd/secedit-export +7 -0
  180. data/test/unit/mock/cmd/service-e +2 -0
  181. data/test/unit/mock/cmd/service-sendmail-onestatus +3 -0
  182. data/test/unit/mock/cmd/service-sshd-status +1 -0
  183. data/test/unit/mock/cmd/sockstat +5 -0
  184. data/test/unit/mock/cmd/success +0 -0
  185. data/test/unit/mock/cmd/systemctl-show-all-sshd +6 -0
  186. data/test/unit/mock/cmd/win32_product +8 -0
  187. data/test/unit/mock/cmd/yum-repolist-all +52 -0
  188. data/test/unit/mock/files/auditd.conf +4 -0
  189. data/test/unit/mock/files/bond0 +37 -0
  190. data/test/unit/mock/files/etcgroup +3 -0
  191. data/test/unit/mock/files/example.csv +6 -0
  192. data/test/unit/mock/files/inetd.conf +2 -0
  193. data/test/unit/mock/files/kitchen.yml +7 -0
  194. data/test/unit/mock/files/limits.conf +5 -0
  195. data/test/unit/mock/files/login.defs +5 -0
  196. data/test/unit/mock/files/mysql.conf +8 -0
  197. data/test/unit/mock/files/mysql2.conf +2 -0
  198. data/test/unit/mock/files/ntp.conf +5 -0
  199. data/test/unit/mock/files/passwd +2 -0
  200. data/test/unit/mock/files/policyfile.lock.json +12 -0
  201. data/test/unit/mock/files/ssh_config +5 -0
  202. data/test/unit/mock/files/sshd_config +7 -0
  203. data/test/unit/mock/profiles/empty/metadata.rb +0 -0
  204. data/test/unit/mock/profiles/metadata/metadata.rb +1 -0
  205. data/test/unit/profile_context_test.rb +140 -0
  206. data/test/unit/profile_test.rb +49 -0
  207. data/test/unit/resources/apt_test.rb +46 -0
  208. data/test/unit/resources/audit_policy_test.rb +13 -0
  209. data/test/unit/resources/auditd_conf_test.rb +15 -0
  210. data/test/unit/resources/auditd_rules_test.rb +21 -0
  211. data/test/unit/resources/bond_test.rb +24 -0
  212. data/test/unit/resources/bridge_test.rb +56 -0
  213. data/test/unit/resources/csv_test.rb +35 -0
  214. data/test/unit/resources/etc_group_test.rb +37 -0
  215. data/test/unit/resources/gem_test.rb +20 -0
  216. data/test/unit/resources/group_test.rb +96 -0
  217. data/test/unit/resources/host_test.rb +38 -0
  218. data/test/unit/resources/inetd_conf_test.rb +15 -0
  219. data/test/unit/resources/interface_test.rb +54 -0
  220. data/test/unit/resources/iptables_test.rb +30 -0
  221. data/test/unit/resources/json_test.rb +36 -0
  222. data/test/unit/resources/kernel_module_test.rb +23 -0
  223. data/test/unit/resources/kernel_parameter_test.rb +13 -0
  224. data/test/unit/resources/limits_conf_test.rb +14 -0
  225. data/test/unit/resources/login_def_test.rb +16 -0
  226. data/test/unit/resources/mysql_conf_test.rb +14 -0
  227. data/test/unit/resources/npm_test.rb +20 -0
  228. data/test/unit/resources/ntp_conf_test.rb +16 -0
  229. data/test/unit/resources/oneget_test.rb +45 -0
  230. data/test/unit/resources/os_env_test.rb +13 -0
  231. data/test/unit/resources/package_test.rb +51 -0
  232. data/test/unit/resources/passwd_test.rb +24 -0
  233. data/test/unit/resources/pip_test.rb +15 -0
  234. data/test/unit/resources/port_test.rb +46 -0
  235. data/test/unit/resources/processes_test.rb +32 -0
  236. data/test/unit/resources/registry_key_test.rb +19 -0
  237. data/test/unit/resources/script_test.rb +19 -0
  238. data/test/unit/resources/security_policy_test.rb +16 -0
  239. data/test/unit/resources/service_test.rb +116 -0
  240. data/test/unit/resources/ssh_conf_test.rb +33 -0
  241. data/test/unit/resources/user_test.rb +93 -0
  242. data/test/unit/resources/windows_feature.rb +17 -0
  243. data/test/unit/resources/yaml_test.rb +34 -0
  244. data/test/unit/resources/yum_test.rb +68 -0
  245. data/test/unit/simpleconfig_test.rb +80 -0
  246. data/test/unit/utils/content_parser_test.rb +30 -0
  247. metadata +555 -0
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Vulcano Security GmbH
3
+ # license: All rights reserved
4
+ # author: Dominik Richter
5
+ # author: Christoph Hartmann
6
+
7
+ require 'inspec/plugins'
8
+
9
+ module Inspec
10
+ class Resource
11
+ def self.registry
12
+ @registry ||= {}
13
+ end
14
+ end
15
+
16
+ def self.resource(version)
17
+ if version != 1
18
+ fail 'Only resource version 1 is supported!'
19
+ end
20
+ Inspec::Plugins::Resource
21
+ end
22
+ end
23
+
24
+ require 'resources/apache'
25
+ require 'resources/apache_conf'
26
+ require 'resources/apt'
27
+ require 'resources/audit_policy'
28
+ require 'resources/auditd_conf'
29
+ require 'resources/auditd_rules'
30
+ require 'resources/bond'
31
+ require 'resources/bridge'
32
+ require 'resources/command'
33
+ require 'resources/directory'
34
+ require 'resources/etc_group'
35
+ require 'resources/file'
36
+ require 'resources/gem'
37
+ require 'resources/group'
38
+ require 'resources/host'
39
+ require 'resources/inetd_conf'
40
+ require 'resources/interface'
41
+ require 'resources/iptables'
42
+ require 'resources/json'
43
+ require 'resources/kernel_module'
44
+ require 'resources/kernel_parameter'
45
+ require 'resources/limits_conf'
46
+ require 'resources/login_def'
47
+ require 'resources/mysql'
48
+ require 'resources/mysql_conf'
49
+ require 'resources/mysql_session'
50
+ require 'resources/npm'
51
+ require 'resources/ntp_conf'
52
+ require 'resources/oneget'
53
+ require 'resources/os'
54
+ require 'resources/os_env'
55
+ require 'resources/package'
56
+ require 'resources/parse_config'
57
+ require 'resources/passwd'
58
+ require 'resources/pip'
59
+ require 'resources/port'
60
+ require 'resources/postgres'
61
+ require 'resources/postgres_conf'
62
+ require 'resources/postgres_session'
63
+ require 'resources/processes'
64
+ require 'resources/registry_key'
65
+ require 'resources/script'
66
+ require 'resources/security_policy'
67
+ require 'resources/service'
68
+ require 'resources/ssh_conf'
69
+ require 'resources/user'
70
+ require 'resources/windows_feature'
71
+ require 'resources/yum'
72
+
73
+ # file formats, depend on json implementation
74
+ require 'resources/json'
75
+ require 'resources/yaml'
76
+ require 'resources/csv'
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'rspec/core'
6
+
7
+ # Extend the basic RSpec JSON Formatter
8
+ # to give us an ID in its output
9
+ # TODO: remove once RSpec has IDs in stable (probably v3.3/v4.0)
10
+ module RSpec::Core::Formatters
11
+ class JsonFormatter
12
+ private
13
+
14
+ def format_example(example)
15
+ {
16
+ description: example.description,
17
+ full_description: example.full_description,
18
+ status: example.execution_result.status.to_s,
19
+ file_path: example.metadata['file_path'],
20
+ line_number: example.metadata['line_number'],
21
+ run_time: example.execution_result.run_time,
22
+ pending_message: example.execution_result.pending_message,
23
+ id: example.metadata[:id],
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Dominik Richter
3
+ # license: All rights reserved
4
+ # author: Dominik Richter
5
+ # author: Christoph Hartmann
6
+
7
+ require 'rspec/expectations'
8
+ require 'method_source'
9
+
10
+ module Inspec
11
+ class ExpectationTarget
12
+ attr_reader :calls, :value, :block
13
+ def initialize(value, &block)
14
+ @value = value
15
+ @block = block
16
+ @calls = []
17
+ end
18
+
19
+ def to(*args, &block)
20
+ @calls.push([:to, args, block, caller])
21
+ end
22
+
23
+ def not_to(*args, &block)
24
+ @calls.push([:not_to, args, block, caller])
25
+ end
26
+
27
+ def example_group
28
+ that = self
29
+
30
+ opts = { 'caller' => calls[0][3] }
31
+ if !calls[0][3].nil? && !calls[0][3].empty? &&
32
+ (m = calls[0][3][0].match(/^([^:]*):(\d+):/))
33
+ opts['file_path'] = m[0]
34
+ opts['line_number'] = m[1]
35
+ end
36
+
37
+ RSpec::Core::ExampleGroup.describe(that.value, opts) do
38
+ that.calls.each do |method, args, block, clr|
39
+ it(nil, caller: clr) do
40
+ x = expect(that.value, &that.block).method(method)
41
+ x.call(*args, &block)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ class Rule
49
+ include ::RSpec::Matchers
50
+
51
+ def initialize(id, _opts, &block)
52
+ @id = id
53
+ @impact = nil
54
+ @__block = block
55
+ @__code = __get_block_source(&block)
56
+ @title = nil
57
+ @desc = nil
58
+ # not changeable by the user:
59
+ @profile_id = nil
60
+ @checks = []
61
+ # evaluate the given definition
62
+ instance_eval(&block) if block_given?
63
+ end
64
+
65
+ def id(*_)
66
+ # never overwrite the ID
67
+ @id
68
+ end
69
+
70
+ def impact(v = nil)
71
+ @impact = v unless v.nil?
72
+ @impact
73
+ end
74
+
75
+ def title(v = nil)
76
+ @title = v unless v.nil?
77
+ @title
78
+ end
79
+
80
+ def desc(v = nil)
81
+ @desc = unindent(v) unless v.nil?
82
+ @desc
83
+ end
84
+
85
+ def describe(value, &block)
86
+ @checks.push(['describe', [value], block])
87
+ end
88
+
89
+ def expect(value, &block)
90
+ target = ExpectationTarget.new(value, &block)
91
+ @checks.push(['expect', [value], target])
92
+ target
93
+ end
94
+
95
+ def self.merge(dst, src)
96
+ if src.id != dst.id
97
+ # TODO: register an error, this case should not happen
98
+ return
99
+ end
100
+ sp = src.instance_variable_get(:@profile_id)
101
+ dp = dst.instance_variable_get(:@profile_id)
102
+ if sp != dp
103
+ # TODO: register an error, this case should not happen
104
+ return
105
+ end
106
+ # merge all fields
107
+ dst.impact(src.impact) unless src.impact.nil?
108
+ dst.title(src.title) unless src.title.nil?
109
+ dst.desc(src.desc) unless src.desc.nil?
110
+ # merge indirect fields
111
+ # checks defined in the source will completely eliminate
112
+ # all checks that were defined in the destination
113
+ sc = src.instance_variable_get(:@checks)
114
+ unless sc.nil? || sc.empty?
115
+ dst.instance_variable_set(:@checks, sc)
116
+ end
117
+ end
118
+
119
+ # Get the full id consisting of profile id + rule id
120
+ # for the rule. If the rule's profile id is empty,
121
+ # the given profile_id will be used instead and also
122
+ # set for the rule.
123
+ def self.full_id(profile_id, rule)
124
+ if rule.is_a?(String) or rule.nil?
125
+ rid = rule
126
+ else
127
+ # As the profile context is exclusively pulled with a
128
+ # profile ID, attach it to the rule if necessary.
129
+ rid = rule.instance_variable_get(:@id)
130
+ if rid.nil?
131
+ # TODO: Message about skipping this rule
132
+ # due to missing ID
133
+ return nil
134
+ end
135
+ end
136
+ pid = rule.instance_variable_get(:@profile_id)
137
+ if pid.nil?
138
+ rule.instance_variable_set(:@profile_id, profile_id)
139
+ pid = profile_id
140
+ end
141
+ if pid.nil? or pid.empty?
142
+ return rid
143
+ else
144
+ return "#{pid}/#{rid}"
145
+ end
146
+ end
147
+
148
+ private
149
+
150
+ # Idio(ma)tic unindent
151
+ # TODO: replace this
152
+ #
153
+ # @param [String] text string which needs to be unindented
154
+ # @return [String] input with indentation removed
155
+ def unindent(text)
156
+ return '' if text.nil?
157
+ text.strip.split("\n").map(&:strip)
158
+ .map { |x| x.empty? ? "\n" : x }
159
+ .join(' ')
160
+ end
161
+
162
+ # get the rule's source code
163
+ def __get_block_source(&block)
164
+ return '' unless block_given?
165
+ block.source.to_s
166
+ rescue MethodSource::SourceNotFoundError
167
+ ''
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,154 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Dominik Richter
3
+ # license: All rights reserved
4
+ # author: Dominik Richter
5
+ # author: Christoph Hartmann
6
+
7
+ require 'uri'
8
+ require 'inspec/backend'
9
+ require 'inspec/profile_context'
10
+ require 'inspec/targets'
11
+ # spec requirements
12
+ require 'rspec'
13
+ require 'rspec/its'
14
+ require 'inspec/rspec_json_formatter'
15
+
16
+ module Inspec
17
+ class Runner # rubocop:disable Metrics/ClassLength
18
+ attr_reader :tests, :backend, :rules
19
+ def initialize(conf = {})
20
+ @rules = {}
21
+ @profile_id = conf[:id]
22
+ @conf = conf.dup
23
+ @conf[:logger] ||= Logger.new(nil)
24
+ @tests = RSpec::Core::World.new
25
+
26
+ configure_output
27
+ configure_transport
28
+ end
29
+
30
+ def normalize_map(hm)
31
+ res = {}
32
+ hm.each {|k, v|
33
+ res[k.to_s] = v
34
+ }
35
+ res
36
+ end
37
+
38
+ def configure_output
39
+ RSpec.configuration.add_formatter(@conf['format'] || 'progress')
40
+ end
41
+
42
+ def configure_transport
43
+ @backend = Inspec::Backend.create(@conf)
44
+ end
45
+
46
+ def add_tests(tests)
47
+ # retrieve the raw ruby code of all tests
48
+ items = tests.map do |test|
49
+ Inspec::Targets.resolve(test)
50
+ end.flatten
51
+
52
+ tests = items.find_all { |i| i[:type] == :test }
53
+ libs = items.find_all { |i| i[:type] == :library }
54
+
55
+ # add all tests (raw) to the runtime
56
+ tests.flatten.each do |test|
57
+ add_content(test, libs)
58
+ end
59
+ end
60
+
61
+ def create_context
62
+ Inspec::ProfileContext.new(@profile_id, @backend)
63
+ end
64
+
65
+ def add_content(test, libs)
66
+ content = test[:content]
67
+ return if content.nil? || content.empty?
68
+
69
+ # load all libraries
70
+ ctx = create_context
71
+ libs.each do |lib|
72
+ ctx.load(lib[:content].to_s, lib[:ref], lib[:line] || 1)
73
+ end
74
+
75
+ # evaluate the test content
76
+ ctx.load(content, test[:ref], test[:line] || 1)
77
+
78
+ # process the resulting rules
79
+ ctx.rules.each do |rule_id, rule|
80
+ register_rule(ctx, rule_id, rule)
81
+ end
82
+ end
83
+
84
+ def run
85
+ run_with(RSpec::Core::Runner.new(nil))
86
+ end
87
+
88
+ def run_with(rspec_runner)
89
+ rspec_runner.run_specs(@tests.ordered_example_groups)
90
+ end
91
+
92
+ private
93
+
94
+ def get_check_example(method_name, arg, block)
95
+ opts = {}
96
+ if !block.nil? && block.respond_to?(:source_location)
97
+ file_path, line = block.source_location
98
+ opts['file_path'] = file_path
99
+ opts['line_number'] = line
100
+ end
101
+
102
+ if !arg.empty? &&
103
+ arg[0].respond_to?(:resource_skipped) &&
104
+ !arg[0].resource_skipped.nil?
105
+ return RSpec::Core::ExampleGroup.describe(*arg, opts) do
106
+ it arg[0].resource_skipped
107
+ end
108
+ else
109
+ # add the resource
110
+ case method_name
111
+ when 'describe'
112
+ return RSpec::Core::ExampleGroup.describe(*arg, opts, &block)
113
+ when 'expect'
114
+ return block.example_group
115
+ else
116
+ fail "A rule was registered with #{method_name.inspect}, "\
117
+ "which isn't understood and cannot be processed."
118
+ end
119
+ end
120
+ nil
121
+ end
122
+
123
+ def register_rule(ctx, rule_id, rule)
124
+ @rules[rule_id] = rule
125
+ checks = rule.instance_variable_get(:@checks)
126
+ checks.each do |m, a, b|
127
+ # resource skipping
128
+ example = get_check_example(m, a, b)
129
+
130
+ # TODO: Remove this!! It is very dangerous to do this here.
131
+ # The goal of this is to make the audit DSL available to all
132
+ # describe blocks. Right now, these blocks are executed outside
133
+ # the scope of this run, thus not gaining ony of the DSL pieces.
134
+ # To circumvent this, the full DSL is attached to the example's
135
+ # scope.
136
+ dsl = ctx.method(:create_inner_dsl).call(backend)
137
+ example.send(:include, dsl)
138
+
139
+ set_rspec_ids(example, rule_id)
140
+ @tests.register(example)
141
+ end
142
+ end
143
+
144
+ def set_rspec_ids(example, id)
145
+ example.metadata[:id] = id
146
+ example.filtered_examples.each do |e|
147
+ e.metadata[:id] = id
148
+ end
149
+ example.children.each do |child|
150
+ set_rspec_ids(child, id)
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ module Inspec
6
+ class Shell
7
+ def initialize(runner)
8
+ @runner = runner
9
+ # load and configure pry
10
+ require 'pry'
11
+ configure_pry
12
+ end
13
+
14
+ def start
15
+ # store context to run commands in this context
16
+ @runner.add_content('binding.pry', __FILE__, __LINE__)
17
+ @runner.run
18
+ end
19
+
20
+ def configure_pry
21
+ # Remove all hooks and checks
22
+ Pry.hooks.clear_all
23
+ that = self
24
+
25
+ # Add the help command
26
+ Pry::Commands.block_command 'usage', 'Show examples' do
27
+ that.usage
28
+ end
29
+
30
+ # Add a help menu as the default intro
31
+ Pry.hooks.add_hook(:before_session, :intro) do
32
+ intro
33
+ end
34
+ end
35
+
36
+ def mark(x)
37
+ "\033[1m#{x}\033[0m"
38
+ end
39
+
40
+ def intro
41
+ puts 'Welcome to the interactive Inspec Shell'
42
+ puts "To find out how to use it, type: #{mark 'usage'}"
43
+ puts
44
+ end
45
+
46
+ def usage
47
+ ctx = @runner.backend
48
+ puts <<EOF
49
+
50
+ Welcome to the interactive Inspec Shell.
51
+
52
+ You can use resources in this environment to test the target machine.
53
+ For example:
54
+
55
+ command('uname -a').stdout
56
+ file('/proc/cpuinfo').content
57
+
58
+ You are currently running on:
59
+
60
+ OS family: #{mark ctx.os[:family] || 'unknown'}
61
+ OS release: #{mark ctx.os[:release] || 'unknown'}
62
+
63
+ EOF
64
+ end
65
+ end
66
+ end