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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +65 -0
- data/.travis.yml +23 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile +33 -0
- data/LICENSE +201 -0
- data/MAINTAINERS.md +28 -0
- data/MAINTAINERS.toml +42 -0
- data/README.md +257 -0
- data/Rakefile +47 -0
- data/bin/inspec +109 -0
- data/docs/ctl_inspec.rst +195 -0
- data/docs/dsl_inspec.rst +182 -0
- data/docs/readme.rst +100 -0
- data/docs/resources.rst +4319 -0
- data/docs/template.rst +51 -0
- data/examples/test-kitchen/.kitchen.yml +20 -0
- data/examples/test-kitchen/Berksfile +3 -0
- data/examples/test-kitchen/Gemfile +21 -0
- data/examples/test-kitchen/README.md +27 -0
- data/examples/test-kitchen/metadata.rb +7 -0
- data/examples/test-kitchen/recipes/default.rb +6 -0
- data/examples/test-kitchen/recipes/nginx.rb +30 -0
- data/examples/test-kitchen/test/integration/default/web_spec.rb +28 -0
- data/inspec.gemspec +30 -0
- data/lib/inspec.rb +20 -0
- data/lib/inspec/backend.rb +42 -0
- data/lib/inspec/dsl.rb +151 -0
- data/lib/inspec/log.rb +34 -0
- data/lib/inspec/metadata.rb +79 -0
- data/lib/inspec/plugins.rb +9 -0
- data/lib/inspec/plugins/resource.rb +62 -0
- data/lib/inspec/profile.rb +138 -0
- data/lib/inspec/profile_context.rb +170 -0
- data/lib/inspec/resource.rb +76 -0
- data/lib/inspec/rspec_json_formatter.rb +27 -0
- data/lib/inspec/rule.rb +170 -0
- data/lib/inspec/runner.rb +154 -0
- data/lib/inspec/shell.rb +66 -0
- data/lib/inspec/targets.rb +9 -0
- data/lib/inspec/targets/core.rb +27 -0
- data/lib/inspec/targets/dir.rb +67 -0
- data/lib/inspec/targets/file.rb +29 -0
- data/lib/inspec/targets/folder.rb +43 -0
- data/lib/inspec/targets/tar.rb +34 -0
- data/lib/inspec/targets/url.rb +39 -0
- data/lib/inspec/targets/zip.rb +47 -0
- data/lib/inspec/version.rb +7 -0
- data/lib/matchers/matchers.rb +221 -0
- data/lib/resources/apache.rb +29 -0
- data/lib/resources/apache_conf.rb +113 -0
- data/lib/resources/apt.rb +140 -0
- data/lib/resources/audit_policy.rb +63 -0
- data/lib/resources/auditd_conf.rb +56 -0
- data/lib/resources/auditd_rules.rb +53 -0
- data/lib/resources/bond.rb +65 -0
- data/lib/resources/bridge.rb +114 -0
- data/lib/resources/command.rb +57 -0
- data/lib/resources/csv.rb +32 -0
- data/lib/resources/directory.rb +15 -0
- data/lib/resources/etc_group.rb +150 -0
- data/lib/resources/file.rb +110 -0
- data/lib/resources/gem.rb +46 -0
- data/lib/resources/group.rb +132 -0
- data/lib/resources/host.rb +143 -0
- data/lib/resources/inetd_conf.rb +56 -0
- data/lib/resources/interface.rb +127 -0
- data/lib/resources/iptables.rb +65 -0
- data/lib/resources/json.rb +64 -0
- data/lib/resources/kernel_module.rb +40 -0
- data/lib/resources/kernel_parameter.rb +55 -0
- data/lib/resources/limits_conf.rb +55 -0
- data/lib/resources/login_def.rb +60 -0
- data/lib/resources/mysql.rb +81 -0
- data/lib/resources/mysql_conf.rb +116 -0
- data/lib/resources/mysql_session.rb +52 -0
- data/lib/resources/npm.rb +44 -0
- data/lib/resources/ntp_conf.rb +58 -0
- data/lib/resources/oneget.rb +63 -0
- data/lib/resources/os.rb +22 -0
- data/lib/resources/os_env.rb +34 -0
- data/lib/resources/package.rb +169 -0
- data/lib/resources/parse_config.rb +75 -0
- data/lib/resources/passwd.rb +93 -0
- data/lib/resources/pip.rb +75 -0
- data/lib/resources/port.rb +296 -0
- data/lib/resources/postgres.rb +37 -0
- data/lib/resources/postgres_conf.rb +87 -0
- data/lib/resources/postgres_session.rb +59 -0
- data/lib/resources/processes.rb +57 -0
- data/lib/resources/registry_key.rb +54 -0
- data/lib/resources/script.rb +34 -0
- data/lib/resources/security_policy.rb +73 -0
- data/lib/resources/service.rb +379 -0
- data/lib/resources/ssh_conf.rb +75 -0
- data/lib/resources/user.rb +374 -0
- data/lib/resources/windows_feature.rb +77 -0
- data/lib/resources/yaml.rb +23 -0
- data/lib/resources/yum.rb +154 -0
- data/lib/utils/convert.rb +12 -0
- data/lib/utils/detect.rb +15 -0
- data/lib/utils/find_files.rb +36 -0
- data/lib/utils/hash.rb +13 -0
- data/lib/utils/modulator.rb +12 -0
- data/lib/utils/parser.rb +61 -0
- data/lib/utils/simpleconfig.rb +115 -0
- data/tasks/maintainers.rb +213 -0
- data/test/docker_run.rb +156 -0
- data/test/docker_test.rb +51 -0
- data/test/helper.rb +200 -0
- data/test/integration/.kitchen.yml +42 -0
- data/test/integration/Berksfile +4 -0
- data/test/integration/cookbooks/os_prepare/metadata.rb +8 -0
- data/test/integration/cookbooks/os_prepare/recipes/apt.rb +20 -0
- data/test/integration/cookbooks/os_prepare/recipes/default.rb +9 -0
- data/test/integration/cookbooks/os_prepare/recipes/file.rb +21 -0
- data/test/integration/cookbooks/os_prepare/recipes/package.rb +26 -0
- data/test/integration/default/_debug_spec.rb +1 -0
- data/test/integration/default/apt_spec.rb +42 -0
- data/test/integration/default/file_spec.rb +109 -0
- data/test/integration/default/group_spec.rb +32 -0
- data/test/integration/default/kernel_module_spec.rb +17 -0
- data/test/integration/default/kernel_parameter_spec.rb +56 -0
- data/test/integration/default/package_spec.rb +11 -0
- data/test/integration/default/service_spec.rb +28 -0
- data/test/integration/default/user_spec.rb +44 -0
- data/test/resource/command_test.rb +33 -0
- data/test/resource/dsl_test.rb +45 -0
- data/test/resource/file_test.rb +130 -0
- data/test/resource/ssh_config.rb +9 -0
- data/test/resource/sshd_config.rb +9 -0
- data/test/test-extra.yaml +11 -0
- data/test/test.yaml +11 -0
- data/test/unit/mock/cmd/Get-NetAdapter +24 -0
- data/test/unit/mock/cmd/GetUserAccount +33 -0
- data/test/unit/mock/cmd/GetWin32Group +23 -0
- data/test/unit/mock/cmd/PATH +1 -0
- data/test/unit/mock/cmd/Resolve-DnsName +26 -0
- data/test/unit/mock/cmd/Test-NetConnection +4 -0
- data/test/unit/mock/cmd/auditctl +7 -0
- data/test/unit/mock/cmd/auditpol +2 -0
- data/test/unit/mock/cmd/brew-info-jq +1 -0
- data/test/unit/mock/cmd/chage-l-root +7 -0
- data/test/unit/mock/cmd/dpkg-s-curl +21 -0
- data/test/unit/mock/cmd/dscl +5 -0
- data/test/unit/mock/cmd/etc-apt +7 -0
- data/test/unit/mock/cmd/find-etc-rc-d-name-S +12 -0
- data/test/unit/mock/cmd/find-net-interface +9 -0
- data/test/unit/mock/cmd/gem-list-local-a-q-rubocop +1 -0
- data/test/unit/mock/cmd/get-net-tcpconnection +24 -0
- data/test/unit/mock/cmd/get-netadapter-binding-bridge +4 -0
- data/test/unit/mock/cmd/get-package-firefox +30 -0
- data/test/unit/mock/cmd/get-package-ruby +18 -0
- data/test/unit/mock/cmd/get-service-dhcp +10 -0
- data/test/unit/mock/cmd/get-windows-feature +7 -0
- data/test/unit/mock/cmd/getent-hosts-example.com +1 -0
- data/test/unit/mock/cmd/getent-passwd-root +1 -0
- data/test/unit/mock/cmd/id-chartmann +1 -0
- data/test/unit/mock/cmd/id-root +1 -0
- data/test/unit/mock/cmd/initctl-show-config-ssh +3 -0
- data/test/unit/mock/cmd/initctl-status-ssh +1 -0
- data/test/unit/mock/cmd/iptables-s +6 -0
- data/test/unit/mock/cmd/launchctl-list +3 -0
- data/test/unit/mock/cmd/ls-1-etc-init.d +2 -0
- data/test/unit/mock/cmd/ls-sys-class-net-br +2 -0
- data/test/unit/mock/cmd/lsmod +2 -0
- data/test/unit/mock/cmd/lsof-np-itcp +4 -0
- data/test/unit/mock/cmd/netstat-tulpen +5 -0
- data/test/unit/mock/cmd/npm-ls-g--json-bower +9 -0
- data/test/unit/mock/cmd/pacman-qi-curl +21 -0
- data/test/unit/mock/cmd/ping-example.com +6 -0
- data/test/unit/mock/cmd/pip-show-jinja2 +11 -0
- data/test/unit/mock/cmd/ps-aux +3 -0
- data/test/unit/mock/cmd/pw-usershow-root-7 +1 -0
- data/test/unit/mock/cmd/reg_schedule +1 -0
- data/test/unit/mock/cmd/rpm-qia-curl +24 -0
- data/test/unit/mock/cmd/sbin_sysctl +1 -0
- data/test/unit/mock/cmd/secedit-export +7 -0
- data/test/unit/mock/cmd/service-e +2 -0
- data/test/unit/mock/cmd/service-sendmail-onestatus +3 -0
- data/test/unit/mock/cmd/service-sshd-status +1 -0
- data/test/unit/mock/cmd/sockstat +5 -0
- data/test/unit/mock/cmd/success +0 -0
- data/test/unit/mock/cmd/systemctl-show-all-sshd +6 -0
- data/test/unit/mock/cmd/win32_product +8 -0
- data/test/unit/mock/cmd/yum-repolist-all +52 -0
- data/test/unit/mock/files/auditd.conf +4 -0
- data/test/unit/mock/files/bond0 +37 -0
- data/test/unit/mock/files/etcgroup +3 -0
- data/test/unit/mock/files/example.csv +6 -0
- data/test/unit/mock/files/inetd.conf +2 -0
- data/test/unit/mock/files/kitchen.yml +7 -0
- data/test/unit/mock/files/limits.conf +5 -0
- data/test/unit/mock/files/login.defs +5 -0
- data/test/unit/mock/files/mysql.conf +8 -0
- data/test/unit/mock/files/mysql2.conf +2 -0
- data/test/unit/mock/files/ntp.conf +5 -0
- data/test/unit/mock/files/passwd +2 -0
- data/test/unit/mock/files/policyfile.lock.json +12 -0
- data/test/unit/mock/files/ssh_config +5 -0
- data/test/unit/mock/files/sshd_config +7 -0
- data/test/unit/mock/profiles/empty/metadata.rb +0 -0
- data/test/unit/mock/profiles/metadata/metadata.rb +1 -0
- data/test/unit/profile_context_test.rb +140 -0
- data/test/unit/profile_test.rb +49 -0
- data/test/unit/resources/apt_test.rb +46 -0
- data/test/unit/resources/audit_policy_test.rb +13 -0
- data/test/unit/resources/auditd_conf_test.rb +15 -0
- data/test/unit/resources/auditd_rules_test.rb +21 -0
- data/test/unit/resources/bond_test.rb +24 -0
- data/test/unit/resources/bridge_test.rb +56 -0
- data/test/unit/resources/csv_test.rb +35 -0
- data/test/unit/resources/etc_group_test.rb +37 -0
- data/test/unit/resources/gem_test.rb +20 -0
- data/test/unit/resources/group_test.rb +96 -0
- data/test/unit/resources/host_test.rb +38 -0
- data/test/unit/resources/inetd_conf_test.rb +15 -0
- data/test/unit/resources/interface_test.rb +54 -0
- data/test/unit/resources/iptables_test.rb +30 -0
- data/test/unit/resources/json_test.rb +36 -0
- data/test/unit/resources/kernel_module_test.rb +23 -0
- data/test/unit/resources/kernel_parameter_test.rb +13 -0
- data/test/unit/resources/limits_conf_test.rb +14 -0
- data/test/unit/resources/login_def_test.rb +16 -0
- data/test/unit/resources/mysql_conf_test.rb +14 -0
- data/test/unit/resources/npm_test.rb +20 -0
- data/test/unit/resources/ntp_conf_test.rb +16 -0
- data/test/unit/resources/oneget_test.rb +45 -0
- data/test/unit/resources/os_env_test.rb +13 -0
- data/test/unit/resources/package_test.rb +51 -0
- data/test/unit/resources/passwd_test.rb +24 -0
- data/test/unit/resources/pip_test.rb +15 -0
- data/test/unit/resources/port_test.rb +46 -0
- data/test/unit/resources/processes_test.rb +32 -0
- data/test/unit/resources/registry_key_test.rb +19 -0
- data/test/unit/resources/script_test.rb +19 -0
- data/test/unit/resources/security_policy_test.rb +16 -0
- data/test/unit/resources/service_test.rb +116 -0
- data/test/unit/resources/ssh_conf_test.rb +33 -0
- data/test/unit/resources/user_test.rb +93 -0
- data/test/unit/resources/windows_feature.rb +17 -0
- data/test/unit/resources/yaml_test.rb +34 -0
- data/test/unit/resources/yum_test.rb +68 -0
- data/test/unit/simpleconfig_test.rb +80 -0
- data/test/unit/utils/content_parser_test.rb +30 -0
- 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,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
|