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
data/lib/inspec/log.rb ADDED
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ # Copyright 2015 Dominik Richter. All rights reserved.
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+
6
+ require 'rainbow/ext/string'
7
+
8
+ module Inspec
9
+ class Log
10
+ def initialize(opts = {})
11
+ @quiet = opts[:quiet] || false
12
+ end
13
+
14
+ def show(msg)
15
+ puts msg unless @quiet
16
+ end
17
+
18
+ def info(msg)
19
+ show ' . '.color(:white) + msg
20
+ end
21
+
22
+ def error(msg)
23
+ show ' ✖ '.color(:red).bright + msg
24
+ end
25
+
26
+ def warn(msg)
27
+ show ' ! '.color(:yellow).bright + msg
28
+ end
29
+
30
+ def ok(msg)
31
+ show ' ✔ '.color(:green).bright + msg
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+ # Copyright 2015 Dominik Richter. All rights reserved.
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+
6
+ require 'logger'
7
+
8
+ module Inspec
9
+ # Extract metadata.rb information
10
+ class Metadata
11
+ attr_reader :params
12
+ def initialize(logger = nil)
13
+ @logger = logger || Logger.new(nil)
14
+ @params = {}
15
+ @missing_methods = []
16
+ end
17
+
18
+ %w{
19
+ name
20
+ title
21
+ maintainer
22
+ maintainer_email
23
+ copyright
24
+ copyright_email
25
+ license
26
+ summary
27
+ description
28
+ version
29
+ }.each do |name|
30
+ define_method name.to_sym do |arg|
31
+ params[name.to_sym] = arg
32
+ end
33
+ end
34
+
35
+ def supports(sth, version = nil)
36
+ params[:supports] ||= []
37
+ params[:supports].push(
38
+ {
39
+ os: sth,
40
+ version: version,
41
+ },
42
+ )
43
+ end
44
+
45
+ def valid?
46
+ is_valid = true
47
+ %w{ name title version summary }.each do |field|
48
+ next unless params[field.to_sym].nil?
49
+ @logger.error("Missing profile #{field} in metadata.rb")
50
+ is_valid = false
51
+ end
52
+ %w{ maintainer copyright }.each do |field|
53
+ next unless params[field.to_sym].nil?
54
+ @logger.warn("Missing profile #{field} in metadata.rb")
55
+ is_valid = false
56
+ end
57
+ is_valid && @missing_methods.empty?
58
+ end
59
+
60
+ def method_missing(sth, *args)
61
+ @logger.warn "metadata.rb doesn't support: #{sth} #{args}"
62
+ @missing_methods.push(sth)
63
+ end
64
+
65
+ def self.from_file(path, profile_id, logger = nil)
66
+ logger ||= Logger.new(nil)
67
+
68
+ unless File.file?(path)
69
+ logger.error "Can't find metadata file #{path}"
70
+ return nil
71
+ end
72
+
73
+ res = Metadata.new(logger)
74
+ res.instance_eval(File.read(path), path, 1)
75
+ res.params[:name] = profile_id.to_s unless profile_id.to_s.empty?
76
+ res
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ module Inspec
6
+ module Plugins
7
+ autoload :Resource, 'inspec/plugins/resource'
8
+ end
9
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ module Inspec
6
+ module Plugins
7
+ class Resource
8
+ def self.name(name = nil)
9
+ return if name.nil?
10
+ Inspec::Plugins::Resource.__register(name, self)
11
+ end
12
+
13
+ def self.__register(name, obj)
14
+ # rubocop:disable Lint/NestedMethodDefinition
15
+ cl = Class.new(obj) do
16
+ # add some common methods
17
+ include Inspec::Plugins::ResourceCommon
18
+ def initialize(backend, name, *args)
19
+ # attach the backend to this instance
20
+ @__backend_runner__ = backend
21
+ @__resource_name__ = name
22
+ # call the resource initializer
23
+ super(*args)
24
+ end
25
+
26
+ def inspec
27
+ @__backend_runner__
28
+ end
29
+ end
30
+ # rubocop:enable Lint/NestedMethodDefinition
31
+
32
+ # add the resource to the registry by name
33
+ Inspec::Resource.registry[name] = cl
34
+ end
35
+
36
+ # Define methods which are available to all resources
37
+ # and may be overwritten.
38
+
39
+ # Print the name of the resource
40
+ def to_s
41
+ @__resource_name__
42
+ end
43
+
44
+ # Overwrite inspect to provide better output to RSpec results.
45
+ #
46
+ # @return [String] full name of the resource
47
+ def inspect
48
+ to_s
49
+ end
50
+ end
51
+
52
+ module ResourceCommon
53
+ def resource_skipped
54
+ @resource_skipped
55
+ end
56
+
57
+ def skip_resource(message)
58
+ @resource_skipped = message
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+ # Copyright 2015 Dominik Richter. All rights reserved.
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+
6
+ require 'inspec/metadata'
7
+
8
+ module Inspec
9
+ class Profile # rubocop:disable Metrics/ClassLength
10
+ def self.from_path(path, options = nil)
11
+ opt = {}
12
+ options.each { |k, v| opt[k.to_sym] = v } unless options.nil?
13
+ opt[:path] = path
14
+ Profile.new(opt)
15
+ end
16
+
17
+ attr_reader :params
18
+ attr_reader :metadata
19
+
20
+ def initialize(options = nil)
21
+ @options = options || {}
22
+ @profile_id = options[:id] || nil
23
+ @params = {}
24
+ @logger = options[:logger] || Logger.new(nil)
25
+
26
+ @path = @options[:path]
27
+ fail 'Cannot read an empty path.' if @path.nil? || @path.empty?
28
+ fail "Cannot find directory #{@path}" unless File.directory?(@path)
29
+
30
+ @metadata = read_metadata
31
+ @params = @metadata.params unless @metadata.nil?
32
+
33
+ @params[:rules] = rules = {}
34
+ @runner = Runner.new(
35
+ id: @profile_id,
36
+ backend: :mock,
37
+ )
38
+ @runner.add_tests([@path])
39
+ @runner.rules.each do |id, rule|
40
+ file = rule.instance_variable_get(:@__file)
41
+ rules[file] ||= {}
42
+ rules[file][id] = {
43
+ title: rule.title,
44
+ desc: rule.desc,
45
+ impact: rule.impact,
46
+ checks: rule.instance_variable_get(:@checks),
47
+ code: rule.instance_variable_get(:@__code),
48
+ group_title: rule.instance_variable_get(:@__group_title),
49
+ }
50
+ end
51
+ end
52
+
53
+ def info
54
+ res = @params.dup
55
+ rules = {}
56
+ res[:rules].each do |gid, group|
57
+ next if gid.to_s.empty?
58
+ path = gid.sub(File.join(@path, ''), '')
59
+ rules[path] = { title: path, rules: {} }
60
+ group.each do |id, rule|
61
+ next if id.to_s.empty?
62
+ data = rule.dup
63
+ data.delete(:checks)
64
+ data[:impact] ||= 0.5
65
+ data[:impact] = 1.0 if data[:impact] > 1.0
66
+ data[:impact] = 0.0 if data[:impact] < 0.0
67
+ rules[path][:rules][id] = data
68
+ # TODO: temporarily flatten the group down; replace this with
69
+ # proper hierarchy later on
70
+ rules[path][:title] = data[:group_title]
71
+ end
72
+ end
73
+ res[:rules] = rules
74
+ res
75
+ end
76
+
77
+ # Check if the profile is internall well-structured. The logger will be
78
+ # used to print information on errors and warnings which are found.
79
+ #
80
+ # @return [Boolean] true if no errors were found, false otherwise
81
+ def check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
82
+ no_errors = true
83
+ no_warnings = true
84
+ warn = lambda { |msg|
85
+ @logger.warn(msg)
86
+ no_warnings = false
87
+ }
88
+ error = lambda { |msg|
89
+ @logger.error(msg)
90
+ no_warnings = no_errors = false
91
+ }
92
+
93
+ @logger.info "Checking profile in #{@path}"
94
+
95
+ if @params[:name].to_s.empty?
96
+ error.call('No profile name defined')
97
+ elsif !(@params[:name].to_s =~ %r{^\S+\/\S+$})
98
+ error.call('Profile name must be defined as: OWNER/ID')
99
+ end
100
+
101
+ warn.call('No version defined') if @params[:name].to_s.empty?
102
+ warn.call('No title defined') if @params[:name].to_s.empty?
103
+ warn.call('No maintainer defined') if @params[:name].to_s.empty?
104
+ warn.call('No supports defined') if @params[:name].empty?
105
+ @logger.info 'Metadata OK.' if no_warnings
106
+
107
+ no_warnings = true
108
+ if @params[:name].empty?
109
+ warn.call('No rules were found.')
110
+ else
111
+ @logger.debug "Found #{@params[:name].length} rules."
112
+ end
113
+
114
+ # iterate over hash of groups
115
+ @params[:rules].each do |group, rules_array|
116
+ @logger.debug "Verify all rules in #{group}"
117
+ rules_array.each do |id, rule|
118
+ error.call('Avoid rules with empty IDs') if id.nil? or id.empty?
119
+ warn.call("Rule #{id} has no title") if rule[:title].to_s.empty?
120
+ warn.call("Rule #{id} has no description") if rule[:desc].to_s.empty?
121
+ warn.call("Rule #{id} has impact > 1.0") if rule[:impact].to_f > 1.0
122
+ warn.call("Rule #{id} has impact < 0.0") if rule[:impact].to_f < 0.0
123
+ warn.call("Rule #{id} has no tests defined") if rule[:checks].nil? or rule[:checks].empty?
124
+ end
125
+ end
126
+
127
+ @logger.info 'Rule definitions OK.' if no_warnings
128
+ no_errors
129
+ end
130
+
131
+ private
132
+
133
+ def read_metadata
134
+ mpath = File.join(@path, 'metadata.rb')
135
+ @metadata = Metadata.from_file(mpath, @profile_id, @logger)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'inspec/rule'
6
+ require 'inspec/dsl'
7
+ require 'rspec/core/dsl'
8
+
9
+ module Inspec
10
+ class ProfileContext # rubocop:disable Metrics/ClassLength
11
+ attr_reader :rules, :only_ifs
12
+ def initialize(profile_id, backend, profile_registry = {}, only_ifs = [])
13
+ if backend.nil?
14
+ fail 'ProfileContext is initiated with a backend == nil. ' \
15
+ 'This is a backend error which must be fixed upstream.'
16
+ end
17
+
18
+ @profile_id = profile_id
19
+ @rules = profile_registry
20
+ @only_ifs = only_ifs
21
+
22
+ dsl = create_inner_dsl(backend)
23
+ outer_dsl = create_outer_dsl(dsl)
24
+ ctx = create_context(outer_dsl)
25
+
26
+ @profile_context = ctx.new
27
+ end
28
+
29
+ def load(content, source = nil, line = nil)
30
+ @current_load = { file: source }
31
+ @profile_context.instance_eval(content, source || 'unknown', line || 1)
32
+ end
33
+
34
+ def unregister_rule(id)
35
+ full_id = Inspec::Rule.full_id(@profile_id, id)
36
+ @rules[full_id] = nil
37
+ end
38
+
39
+ def register_rule(r)
40
+ # get the full ID
41
+ r.instance_variable_set(:@__file, @current_load[:file])
42
+ r.instance_variable_set(:@__group_title, @current_load[:title])
43
+ full_id = Inspec::Rule.full_id(@profile_id, r)
44
+ if full_id.nil?
45
+ # TODO: error
46
+ return
47
+ end
48
+
49
+ # add the rule to the registry
50
+ existing = @rules[full_id]
51
+ if existing.nil?
52
+ @rules[full_id] = r
53
+ else
54
+ Inspec::Rule.merge(existing, r)
55
+ end
56
+ end
57
+
58
+ def set_header(field, val)
59
+ @current_load[field] = val
60
+ end
61
+
62
+ private
63
+
64
+ # Creates the inner DSL which includes all resources for
65
+ # creating tests. It is always connected to one target,
66
+ # which is specified via the backend argument.
67
+ #
68
+ # @param backend [BackendRunner] exposing the target to resources
69
+ # @return [InnerDSLModule]
70
+ def create_inner_dsl(backend)
71
+ Module.new do
72
+ Inspec::Resource.registry.each do |id, r|
73
+ define_method id.to_sym do |*args|
74
+ r.new(backend, id.to_s, *args)
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ # Creates the outer DSL which includes all methods for creating
81
+ # tests and control structures.
82
+ #
83
+ # @param dsl [InnerDSLModule] which contains all resources
84
+ # @return [OuterDSLClass]
85
+ def create_outer_dsl(dsl)
86
+ rule_class = Class.new(Inspec::Rule) do
87
+ include RSpec::Core::DSL
88
+ include dsl
89
+ end
90
+
91
+ # rubocop:disable Lint/NestedMethodDefinition
92
+ Class.new do
93
+ include dsl
94
+
95
+ define_method :control do |*args, &block|
96
+ id = args[0]
97
+ opts = args[1] || {}
98
+
99
+ # Skip the control if the resource triggered a skip;
100
+ # However: when this is run on a mock backend, do not skip it.
101
+ # This is e.g. relevant for JSON generation, where we need all
102
+ # controls.
103
+ return if @skip_profile && os[:family] != 'unknown'
104
+
105
+ __register_rule rule_class.new(id, opts, &block)
106
+ end
107
+
108
+ alias_method :rule, :control
109
+
110
+ define_method :describe do |*args, &block|
111
+ path = block.source_location[0]
112
+ line = block.source_location[1]
113
+ id = "#{File.basename(path)}:#{line}"
114
+ rule = rule_class.new(id, {}) do
115
+ describe(*args, &block)
116
+ end
117
+ __register_rule rule, &block
118
+ end
119
+
120
+ # TODO: mock method for attributes; import attribute handling
121
+ define_method :attributes do |_name, _options|
122
+ nil
123
+ end
124
+
125
+ def skip_rule(id)
126
+ __unregister_rule id
127
+ end
128
+
129
+ def only_if(&block)
130
+ return unless block_given?
131
+ @skip_profile = !block.call
132
+ end
133
+ end
134
+ # rubocop:enable all
135
+ end
136
+
137
+ # Creates the heart of the profile context:
138
+ # An instantiated object which has all resources registered to it
139
+ # and exposes them to the a test file. The profile context serves as a
140
+ # container for all profiles which are registered. Within the context
141
+ # profiles get access to all DSL calls for creating tests and controls.
142
+ #
143
+ # @param outer_dsl [OuterDSLClass]
144
+ # @return [ProfileContextClass]
145
+ def create_context(outer_dsl)
146
+ profile_context_owner = self
147
+
148
+ # rubocop:disable Lint/NestedMethodDefinition
149
+ Class.new(outer_dsl) do
150
+ include Inspec::DSL
151
+
152
+ define_method :title do |arg|
153
+ profile_context_owner.set_header(:title, arg)
154
+ end
155
+
156
+ define_method :__register_rule do |*args|
157
+ profile_context_owner.register_rule(*args)
158
+ end
159
+ define_method :__unregister_rule do |*args|
160
+ profile_context_owner.unregister_rule(*args)
161
+ end
162
+
163
+ def to_s
164
+ 'Profile Context Run'
165
+ end
166
+ end
167
+ # rubocop:enable all
168
+ end
169
+ end
170
+ end