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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +933 -101
- data/Gemfile +8 -17
- data/MAINTAINERS.md +2 -1
- data/MAINTAINERS.toml +9 -4
- data/README.md +49 -28
- data/Rakefile +54 -39
- data/bin/inspec +1 -1
- data/docs/dsl_inspec.md +17 -4
- data/docs/dsl_resource.md +1 -1
- data/docs/habitat.md +192 -0
- data/docs/inspec_and_friends.md +4 -2
- data/docs/matchers.md +11 -0
- data/docs/migration.md +293 -0
- data/docs/profiles.md +124 -76
- data/docs/resources/aide_conf.md.erb +81 -0
- data/docs/resources/apache_conf.md.erb +10 -10
- data/docs/resources/apt.md.erb +13 -13
- data/docs/resources/audit_policy.md.erb +10 -10
- data/docs/resources/auditd_conf.md.erb +10 -10
- data/docs/resources/auditd_rules.md.erb +11 -11
- data/docs/resources/bash.md.erb +12 -12
- data/docs/resources/bond.md.erb +15 -15
- data/docs/resources/bridge.md.erb +11 -11
- data/docs/resources/bsd_service.md.erb +11 -11
- data/docs/resources/command.md.erb +23 -23
- data/docs/resources/crontab.md.erb +84 -0
- data/docs/resources/csv.md.erb +10 -10
- data/docs/resources/dh_params.md +191 -0
- data/docs/resources/directory.md.erb +8 -8
- data/docs/resources/docker.md.erb +160 -0
- data/docs/resources/docker_container.md.erb +89 -0
- data/docs/resources/docker_image.md.erb +86 -0
- data/docs/resources/etc_group.md.erb +16 -16
- data/docs/resources/etc_hosts.md.erb +62 -0
- data/docs/resources/file.md.erb +80 -58
- data/docs/resources/gem.md.erb +32 -13
- data/docs/resources/group.md.erb +12 -12
- data/docs/resources/grub_conf.md.erb +11 -11
- data/docs/resources/host.md.erb +27 -18
- data/docs/resources/http.md.erb +104 -0
- data/docs/resources/iis_app.md.erb +126 -0
- data/docs/resources/iis_site.md.erb +17 -17
- data/docs/resources/inetd_conf.md.erb +10 -10
- data/docs/resources/ini.md.erb +9 -9
- data/docs/resources/interface.md.erb +13 -13
- data/docs/resources/iptables.md.erb +21 -14
- data/docs/resources/json.md.erb +11 -11
- data/docs/resources/kernel_module.md.erb +73 -12
- data/docs/resources/kernel_parameter.md.erb +12 -12
- data/docs/resources/key_rsa.md.erb +70 -0
- data/docs/resources/launchd_service.md.erb +11 -11
- data/docs/resources/limits_conf.md.erb +10 -10
- data/docs/resources/login_def.md.erb +12 -12
- data/docs/resources/mount.md.erb +14 -14
- data/docs/resources/mssql_session.md.erb +62 -0
- data/docs/resources/mysql_conf.md.erb +14 -14
- data/docs/resources/mysql_session.md.erb +22 -10
- data/docs/resources/npm.md.erb +13 -13
- data/docs/resources/ntp_conf.md.erb +9 -9
- data/docs/resources/oneget.md.erb +11 -11
- data/docs/resources/oracledb_session.md.erb +53 -0
- data/docs/resources/os.md.erb +17 -17
- data/docs/resources/os_env.md.erb +12 -12
- data/docs/resources/package.md.erb +29 -16
- data/docs/resources/parse_config.md.erb +18 -18
- data/docs/resources/parse_config_file.md.erb +29 -23
- data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +17 -17
- data/docs/resources/pip.md.erb +15 -15
- data/docs/resources/port.md.erb +23 -23
- data/docs/resources/postgres_conf.md.erb +15 -13
- data/docs/resources/postgres_hba_conf.md.erb +104 -0
- data/docs/resources/postgres_ident_conf.md.erb +87 -0
- data/docs/resources/postgres_session.md.erb +28 -21
- data/docs/resources/powershell.md.erb +14 -14
- data/docs/resources/processes.md.erb +123 -0
- data/docs/resources/rabbitmq_config.md.erb +55 -0
- data/docs/resources/registry_key.md.erb +44 -21
- data/docs/resources/runit_service.md.erb +11 -11
- data/docs/resources/security_policy.md.erb +10 -10
- data/docs/resources/service.md.erb +18 -18
- data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +19 -19
- data/docs/resources/ssh_config.md.erb +13 -13
- data/docs/resources/sshd_config.md.erb +15 -15
- data/docs/resources/ssl.md.erb +13 -13
- data/docs/resources/sys_info.md.erb +10 -10
- data/docs/resources/systemd_service.md.erb +11 -11
- data/docs/resources/sysv_service.md.erb +11 -11
- data/docs/resources/upstart_service.md.erb +11 -11
- data/docs/resources/user.md.erb +20 -20
- data/docs/resources/users.md.erb +19 -19
- data/docs/resources/vbscript.md.erb +13 -13
- data/docs/resources/virtualization.md.erb +71 -0
- data/docs/resources/windows_feature.md.erb +10 -10
- data/docs/resources/windows_task.md.erb +103 -0
- data/docs/resources/wmi.md.erb +10 -10
- data/docs/resources/x509_certificate.md.erb +146 -0
- data/docs/resources/xinetd_conf.md.erb +17 -17
- data/docs/resources/xml.md.erb +75 -0
- data/docs/resources/yaml.md.erb +25 -11
- data/docs/resources/yum.md.erb +24 -16
- data/docs/resources/zfs_dataset.md.erb +63 -0
- data/docs/resources/zfs_pool.md.erb +57 -0
- data/docs/ruby_usage.md +50 -0
- data/docs/shared/matcher_cmp.md.erb +4 -6
- data/examples/inheritance/README.md +49 -3
- data/examples/inheritance/controls/example.rb +4 -1
- data/examples/inheritance/inspec.yml +3 -1
- data/examples/meta-profile/README.md +29 -3
- data/examples/meta-profile/controls/example.rb +10 -5
- data/examples/meta-profile/inspec.yml +4 -8
- data/examples/profile/README.md +1 -1
- data/examples/profile/controls/example.rb +0 -1
- data/examples/profile/controls/gordon.rb +0 -1
- data/examples/profile/inspec.yml +1 -1
- data/examples/profile-attribute/inspec.yml +1 -1
- data/examples/profile-sensitive/README.md +29 -0
- data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
- data/examples/profile-sensitive/controls/sensitive.rb +9 -0
- data/examples/profile-sensitive/inspec.yml +8 -0
- data/inspec.gemspec +10 -2
- data/lib/bundles/inspec-artifact/README.md +1 -0
- data/lib/bundles/inspec-artifact/cli.rb +284 -0
- data/lib/bundles/inspec-artifact.rb +7 -0
- data/lib/bundles/inspec-compliance/README.md +44 -15
- data/lib/bundles/inspec-compliance/api.rb +135 -29
- data/lib/bundles/inspec-compliance/cli.rb +128 -21
- data/lib/bundles/inspec-compliance/configuration.rb +8 -0
- data/lib/bundles/inspec-compliance/http.rb +26 -19
- data/lib/bundles/inspec-compliance/target.rb +50 -25
- data/lib/bundles/inspec-habitat/cli.rb +31 -0
- data/lib/bundles/inspec-habitat/log.rb +10 -0
- data/lib/bundles/inspec-habitat/profile.rb +372 -0
- data/lib/bundles/inspec-habitat.rb +12 -0
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +0 -1
- data/lib/bundles/inspec-init/templates/profile/inspec.yml +1 -1
- data/lib/bundles/inspec-supermarket/api.rb +7 -5
- data/lib/bundles/inspec-supermarket/cli.rb +2 -2
- data/lib/fetchers/git.rb +4 -2
- data/lib/fetchers/local.rb +4 -1
- data/lib/fetchers/url.rb +64 -10
- data/lib/inspec/archive/tar.rb +1 -1
- data/lib/inspec/backend.rb +26 -3
- data/lib/inspec/base_cli.rb +44 -7
- data/lib/inspec/cached_fetcher.rb +2 -2
- data/lib/inspec/cli.rb +34 -12
- data/lib/inspec/completions/fish.sh.erb +34 -0
- data/lib/inspec/control_eval_context.rb +0 -2
- data/lib/inspec/dependencies/cache.rb +0 -1
- data/lib/inspec/dependencies/dependency_set.rb +2 -2
- data/lib/inspec/dependencies/lockfile.rb +13 -6
- data/lib/inspec/dependencies/requirement.rb +6 -6
- data/lib/inspec/dependencies/resolver.rb +4 -4
- data/lib/inspec/dsl.rb +3 -5
- data/lib/inspec/env_printer.rb +9 -1
- data/lib/inspec/exceptions.rb +8 -0
- data/lib/inspec/fetcher.rb +1 -1
- data/lib/inspec/file_provider.rb +47 -11
- data/lib/inspec/library_eval_context.rb +1 -1
- data/lib/inspec/metadata.rb +62 -18
- data/lib/inspec/method_source.rb +24 -0
- data/lib/inspec/objects/attribute.rb +21 -0
- data/lib/inspec/objects/control.rb +21 -3
- data/lib/inspec/objects/each_loop.rb +2 -1
- data/lib/inspec/objects/list.rb +1 -1
- data/lib/inspec/objects/or_test.rb +17 -2
- data/lib/inspec/objects/tag.rb +27 -0
- data/lib/inspec/objects/test.rb +9 -8
- data/lib/inspec/objects.rb +1 -0
- data/lib/inspec/plugins/fetcher.rb +4 -5
- data/lib/inspec/plugins/resource.rb +1 -2
- data/lib/inspec/plugins/source_reader.rb +3 -3
- data/lib/inspec/plugins.rb +1 -1
- data/lib/inspec/polyfill.rb +0 -1
- data/lib/inspec/profile.rb +92 -23
- data/lib/inspec/profile_context.rb +1 -1
- data/lib/inspec/profile_vendor.rb +66 -0
- data/lib/inspec/resource.rb +29 -3
- data/lib/inspec/rspec_json_formatter.rb +672 -285
- data/lib/inspec/rule.rb +12 -16
- data/lib/inspec/runner.rb +26 -15
- data/lib/inspec/runner_rspec.rb +1 -0
- data/lib/inspec/runtime_profile.rb +26 -0
- data/lib/inspec/schema.rb +185 -0
- data/lib/inspec/secrets/yaml.rb +8 -1
- data/lib/inspec/secrets.rb +1 -1
- data/lib/inspec/shell.rb +92 -22
- data/lib/inspec/source_reader.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec.rb +1 -1
- data/lib/matchers/matchers.rb +67 -17
- data/lib/resources/aide_conf.rb +162 -0
- data/lib/resources/apache.rb +0 -1
- data/lib/resources/apache_conf.rb +3 -3
- data/lib/resources/apt.rb +2 -1
- data/lib/resources/audit_policy.rb +0 -1
- data/lib/resources/auditd_conf.rb +1 -2
- data/lib/resources/auditd_rules.rb +5 -5
- data/lib/resources/bond.rb +1 -1
- data/lib/resources/bridge.rb +1 -1
- data/lib/resources/command.rb +5 -3
- data/lib/resources/crontab.rb +102 -0
- data/lib/resources/csv.rb +18 -1
- data/lib/resources/dh_params.rb +84 -0
- data/lib/resources/directory.rb +7 -3
- data/lib/resources/docker.rb +198 -0
- data/lib/resources/docker_container.rb +114 -0
- data/lib/resources/docker_image.rb +96 -0
- data/lib/resources/etc_group.rb +2 -3
- data/lib/resources/etc_hosts.rb +81 -0
- data/lib/resources/file.rb +88 -30
- data/lib/resources/gem.rb +26 -6
- data/lib/resources/groups.rb +4 -5
- data/lib/resources/grub_conf.rb +3 -4
- data/lib/resources/host.rb +175 -35
- data/lib/resources/http.rb +73 -0
- data/lib/resources/iis_app.rb +103 -0
- data/lib/resources/iis_site.rb +23 -0
- data/lib/resources/inetd_conf.rb +9 -4
- data/lib/resources/interface.rb +4 -3
- data/lib/resources/json.rb +37 -14
- data/lib/resources/kernel_module.rb +77 -10
- data/lib/resources/kernel_parameter.rb +0 -1
- data/lib/resources/key_rsa.rb +67 -0
- data/lib/resources/limits_conf.rb +3 -4
- data/lib/resources/login_def.rb +2 -3
- data/lib/resources/mount.rb +34 -4
- data/lib/resources/mssql_session.rb +81 -12
- data/lib/resources/mysql.rb +0 -1
- data/lib/resources/mysql_conf.rb +1 -2
- data/lib/resources/mysql_session.rb +18 -7
- data/lib/resources/nginx_conf.rb +95 -0
- data/lib/resources/ntp_conf.rb +2 -3
- data/lib/resources/oracledb_session.rb +138 -0
- data/lib/resources/os_env.rb +7 -5
- data/lib/resources/package.rb +70 -13
- data/lib/resources/packages.rb +111 -0
- data/lib/resources/parse_config.rb +3 -4
- data/lib/resources/passwd.rb +4 -5
- data/lib/resources/pip.rb +29 -21
- data/lib/resources/port.rb +133 -14
- data/lib/resources/postgres.rb +53 -43
- data/lib/resources/postgres_conf.rb +40 -13
- data/lib/resources/postgres_hba_conf.rb +101 -0
- data/lib/resources/postgres_ident_conf.rb +79 -0
- data/lib/resources/postgres_session.rb +28 -20
- data/lib/resources/powershell.rb +2 -2
- data/lib/resources/processes.rb +61 -26
- data/lib/resources/rabbitmq_conf.rb +53 -0
- data/lib/resources/registry_key.rb +34 -11
- data/lib/resources/security_policy.rb +2 -2
- data/lib/resources/service.rb +17 -7
- data/lib/resources/ssh_conf.rb +17 -6
- data/lib/resources/ssl.rb +3 -4
- data/lib/resources/sys_info.rb +1 -1
- data/lib/resources/toml.rb +26 -0
- data/lib/resources/users.rb +41 -41
- data/lib/resources/virtualization.rb +252 -0
- data/lib/resources/windows_task.rb +106 -0
- data/lib/resources/x509_certificate.rb +143 -0
- data/lib/resources/xinetd.rb +18 -2
- data/lib/resources/xml.rb +27 -0
- data/lib/resources/yaml.rb +10 -2
- data/lib/resources/yum.rb +25 -7
- data/lib/resources/zfs_dataset.rb +60 -0
- data/lib/resources/zfs_pool.rb +49 -0
- data/lib/source_readers/flat.rb +2 -1
- data/lib/source_readers/inspec.rb +30 -11
- data/lib/utils/command_wrapper.rb +3 -3
- data/lib/utils/database_helpers.rb +77 -0
- data/lib/utils/erlang_parser.rb +192 -0
- data/lib/utils/filter.rb +2 -1
- data/lib/utils/filter_array.rb +0 -1
- data/lib/utils/find_files.rb +1 -2
- data/lib/utils/latest_version.rb +21 -0
- data/lib/utils/nginx_parser.rb +76 -0
- data/lib/utils/object_traversal.rb +5 -4
- data/lib/utils/parser.rb +23 -4
- data/lib/utils/plugin_registry.rb +3 -3
- data/lib/utils/simpleconfig.rb +25 -8
- data/lib/utils/spdx.rb +13 -0
- data/lib/utils/spdx.txt +344 -0
- metadata +168 -22
- data/docs/cli.md +0 -342
- data/docs/resources/process.md.erb +0 -73
- /data/docs/{plugin_kitchen_inspec.html.md → plugin_kitchen_inspec.md} +0 -0
|
@@ -26,7 +26,7 @@ module Inspec
|
|
|
26
26
|
def self.resolve(dependencies, cache, working_dir, backend)
|
|
27
27
|
reqs = dependencies.map do |dep|
|
|
28
28
|
req = Inspec::Requirement.from_metadata(dep, cache, cwd: working_dir, backend: backend)
|
|
29
|
-
req ||
|
|
29
|
+
req || raise("Cannot initialize dependency: #{req}")
|
|
30
30
|
end
|
|
31
31
|
new.resolve(reqs)
|
|
32
32
|
end
|
|
@@ -40,7 +40,7 @@ module Inspec
|
|
|
40
40
|
else
|
|
41
41
|
"the dependency information for #{path_string.split(' ').last}"
|
|
42
42
|
end
|
|
43
|
-
|
|
43
|
+
raise Inspec::DuplicateDep, "The dependency #{dep.name} is listed twice in #{problem_cookbook}"
|
|
44
44
|
else
|
|
45
45
|
seen_items_local << dep.name
|
|
46
46
|
end
|
|
@@ -65,13 +65,13 @@ module Inspec
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
if new_seen_items.key?(dep.resolved_source)
|
|
68
|
-
|
|
68
|
+
raise Inspec::CyclicDependencyError, "Dependency #{dep} would cause a dependency cycle (#{new_path_string})"
|
|
69
69
|
else
|
|
70
70
|
new_seen_items[dep.resolved_source] = true
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
if !dep.source_satisfies_spec?
|
|
74
|
-
|
|
74
|
+
raise Inspec::UnsatisfiedVersionSpecification, "The profile #{dep.name} from #{dep.resolved_source} has a version #{dep.source_version} which doesn't match #{dep.required_version}"
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
Inspec::Log.debug("Adding dependency #{dep.name} (#{dep.resolved_source})")
|
data/lib/inspec/dsl.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Inspec::DSL
|
|
|
19
19
|
alias include_rules include_controls
|
|
20
20
|
|
|
21
21
|
def require_resource(options = {})
|
|
22
|
-
|
|
22
|
+
raise 'You must specify a specific resource name when calling require_resource()' if options[:resource].nil?
|
|
23
23
|
|
|
24
24
|
from_profile = options[:profile] || profile_name
|
|
25
25
|
target_name = options[:as] || options[:resource]
|
|
@@ -33,12 +33,10 @@ module Inspec::DSL
|
|
|
33
33
|
|
|
34
34
|
dep_entry = dependencies.list[profile_id]
|
|
35
35
|
if dep_entry.nil?
|
|
36
|
-
|
|
37
|
-
Cannot load #{profile_id} since it is not listed as a dependency
|
|
38
|
-
of #{bind_context.profile_name}.
|
|
36
|
+
raise <<EOF
|
|
37
|
+
Cannot load #{profile_id} since it is not listed as a dependency of #{bind_context.profile_name}.
|
|
39
38
|
|
|
40
39
|
Dependencies available from this context are:
|
|
41
|
-
|
|
42
40
|
#{dependencies.list.keys.join("\n ")}
|
|
43
41
|
EOF
|
|
44
42
|
end
|
data/lib/inspec/env_printer.rb
CHANGED
|
@@ -5,6 +5,14 @@ require 'shellwords'
|
|
|
5
5
|
|
|
6
6
|
module Inspec
|
|
7
7
|
class EnvPrinter
|
|
8
|
+
attr_reader :shell
|
|
9
|
+
|
|
10
|
+
EVAL_COMMANDS = {
|
|
11
|
+
'bash' => 'eval \"$(inspec env bash)\"',
|
|
12
|
+
'fish' => 'inspec env fish > ~/.config/fish/completions/inspec.fish',
|
|
13
|
+
'zsh' => 'eval \"$(inspec env zsh)\"',
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
8
16
|
def initialize(command_class, shell = nil)
|
|
9
17
|
if !shell
|
|
10
18
|
@detected = true
|
|
@@ -56,7 +64,7 @@ module Inspec
|
|
|
56
64
|
puts <<EOF
|
|
57
65
|
# To use this, eval it in your shell
|
|
58
66
|
#
|
|
59
|
-
#
|
|
67
|
+
# #{EVAL_COMMANDS[shell]}
|
|
60
68
|
#
|
|
61
69
|
#
|
|
62
70
|
EOF
|
data/lib/inspec/fetcher.rb
CHANGED
data/lib/inspec/file_provider.rb
CHANGED
|
@@ -17,19 +17,36 @@ module Inspec
|
|
|
17
17
|
elsif File.exist?(path)
|
|
18
18
|
DirProvider.new(path)
|
|
19
19
|
else
|
|
20
|
-
|
|
20
|
+
raise "No file provider for the provided path: #{path}"
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def initialize(_path)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# List all files that are offered.
|
|
28
|
+
#
|
|
29
|
+
# @return [Array[String]] list of file paths that are included
|
|
30
|
+
def files
|
|
31
|
+
raise "Fetcher #{self} does not implement `files()`. This is required."
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Read the contents of a file. This will typically refer to a text
|
|
35
|
+
# file reading a string.
|
|
36
|
+
#
|
|
37
|
+
# @param _file [String] path of the file to be read
|
|
38
|
+
# @return [String] contents of the file described
|
|
27
39
|
def read(_file)
|
|
28
|
-
|
|
40
|
+
raise "#{self} does not implement `read(...)`. This is required."
|
|
29
41
|
end
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
# Provide a method for reading binary contents from a file.
|
|
44
|
+
# It will default to #read if not defined. For most streams that implement
|
|
45
|
+
# it, it will be the same. For some special cases, it will add change the
|
|
46
|
+
# way in which encoding of the returned data structure is handled.
|
|
47
|
+
# Does not work with alias nor alias_method.
|
|
48
|
+
def binread(file)
|
|
49
|
+
read(file)
|
|
33
50
|
end
|
|
34
51
|
|
|
35
52
|
def relative_provider
|
|
@@ -65,6 +82,12 @@ module Inspec
|
|
|
65
82
|
return nil unless File.file?(file)
|
|
66
83
|
File.read(file)
|
|
67
84
|
end
|
|
85
|
+
|
|
86
|
+
def binread(file)
|
|
87
|
+
return nil unless files.include?(file)
|
|
88
|
+
return nil unless File.file?(file)
|
|
89
|
+
File.binread(file)
|
|
90
|
+
end
|
|
68
91
|
end
|
|
69
92
|
|
|
70
93
|
class ZipProvider < FileProvider
|
|
@@ -74,9 +97,10 @@ module Inspec
|
|
|
74
97
|
@path = path
|
|
75
98
|
@contents = {}
|
|
76
99
|
@files = []
|
|
77
|
-
|
|
100
|
+
walk_zip(@path) do |io|
|
|
78
101
|
while (entry = io.get_next_entry)
|
|
79
|
-
|
|
102
|
+
name = entry.name.sub(%r{/+$}, '')
|
|
103
|
+
@files.push(name) unless name.empty?
|
|
80
104
|
end
|
|
81
105
|
end
|
|
82
106
|
end
|
|
@@ -87,10 +111,14 @@ module Inspec
|
|
|
87
111
|
|
|
88
112
|
private
|
|
89
113
|
|
|
114
|
+
def walk_zip(path, &callback)
|
|
115
|
+
::Zip::InputStream.open(path, &callback)
|
|
116
|
+
end
|
|
117
|
+
|
|
90
118
|
def read_from_zip(file)
|
|
91
119
|
return nil unless @files.include?(file)
|
|
92
120
|
res = nil
|
|
93
|
-
|
|
121
|
+
walk_zip(@path) do |io|
|
|
94
122
|
while (entry = io.get_next_entry)
|
|
95
123
|
next unless file == entry.name
|
|
96
124
|
res = io.read
|
|
@@ -108,8 +136,8 @@ module Inspec
|
|
|
108
136
|
@path = path
|
|
109
137
|
@contents = {}
|
|
110
138
|
@files = []
|
|
111
|
-
|
|
112
|
-
@files = tar.map(&:full_name)
|
|
139
|
+
walk_tar(@path) do |tar|
|
|
140
|
+
@files = tar.map(&:full_name).find_all { |x| !x.empty? }
|
|
113
141
|
end
|
|
114
142
|
end
|
|
115
143
|
|
|
@@ -119,11 +147,15 @@ module Inspec
|
|
|
119
147
|
|
|
120
148
|
private
|
|
121
149
|
|
|
150
|
+
def walk_tar(path, &callback)
|
|
151
|
+
Gem::Package::TarReader.new(Zlib::GzipReader.open(path), &callback)
|
|
152
|
+
end
|
|
153
|
+
|
|
122
154
|
def read_from_tar(file)
|
|
123
155
|
return nil unless @files.include?(file)
|
|
124
156
|
res = nil
|
|
125
157
|
# NB `TarReader` includes `Enumerable` beginning with Ruby 2.x
|
|
126
|
-
|
|
158
|
+
walk_tar(@path) do |tar|
|
|
127
159
|
tar.each do |entry|
|
|
128
160
|
next unless entry.file? && file == entry.full_name
|
|
129
161
|
res = entry.read
|
|
@@ -148,7 +180,7 @@ module Inspec
|
|
|
148
180
|
@parent = parent_provider
|
|
149
181
|
@prefix = get_prefix(parent.files)
|
|
150
182
|
if @prefix.nil?
|
|
151
|
-
|
|
183
|
+
raise "Could not determine path prefix for #{parent}"
|
|
152
184
|
end
|
|
153
185
|
@files = parent.files.find_all { |x| x.start_with?(prefix) && x != prefix }
|
|
154
186
|
.map { |x| x[prefix.length..-1] }
|
|
@@ -163,6 +195,10 @@ module Inspec
|
|
|
163
195
|
parent.read(abs_path(file))
|
|
164
196
|
end
|
|
165
197
|
|
|
198
|
+
def binread(file)
|
|
199
|
+
parent.binread(abs_path(file))
|
|
200
|
+
end
|
|
201
|
+
|
|
166
202
|
private
|
|
167
203
|
|
|
168
204
|
def get_prefix(fs)
|
data/lib/inspec/metadata.rb
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
-
# Copyright 2015 Dominik Richter
|
|
2
|
+
# Copyright 2015 Dominik Richter
|
|
3
3
|
# author: Dominik Richter
|
|
4
4
|
# author: Christoph Hartmann
|
|
5
5
|
|
|
6
6
|
require 'logger'
|
|
7
7
|
require 'rubygems/version'
|
|
8
8
|
require 'rubygems/requirement'
|
|
9
|
+
require 'semverse'
|
|
10
|
+
require 'utils/spdx'
|
|
9
11
|
|
|
10
12
|
module Inspec
|
|
11
13
|
# Extract metadata.rb information
|
|
12
14
|
class Metadata # rubocop:disable Metrics/ClassLength
|
|
13
15
|
attr_reader :ref
|
|
14
|
-
attr_accessor :params
|
|
16
|
+
attr_accessor :params, :content
|
|
15
17
|
def initialize(ref, logger = nil)
|
|
16
18
|
@ref = ref
|
|
17
19
|
@logger = logger || Logger.new(nil)
|
|
20
|
+
@content = ''
|
|
18
21
|
@params = {}
|
|
19
22
|
@missing_methods = []
|
|
20
23
|
end
|
|
@@ -68,6 +71,7 @@ module Inspec
|
|
|
68
71
|
os.method(family_check).call
|
|
69
72
|
)
|
|
70
73
|
|
|
74
|
+
# ensure we do have a string if we have a non-nil value eg. 16.06
|
|
71
75
|
release_ok = release.nil? || os[:release] == release
|
|
72
76
|
|
|
73
77
|
# we want to make sure that all matchers are true
|
|
@@ -99,7 +103,7 @@ module Inspec
|
|
|
99
103
|
end
|
|
100
104
|
|
|
101
105
|
# return all warn and errors
|
|
102
|
-
def valid
|
|
106
|
+
def valid # rubocop:disable Metrics/AbcSize
|
|
103
107
|
errors = []
|
|
104
108
|
warnings = []
|
|
105
109
|
|
|
@@ -107,11 +111,22 @@ module Inspec
|
|
|
107
111
|
next unless params[field.to_sym].nil?
|
|
108
112
|
errors.push("Missing profile #{field} in #{ref}")
|
|
109
113
|
end
|
|
110
|
-
|
|
114
|
+
|
|
115
|
+
# if version is set, ensure it is correct
|
|
116
|
+
if !params[:version].nil? && !valid_version?(params[:version])
|
|
117
|
+
errors.push('Version needs to be in SemVer format')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
%w{ title summary maintainer copyright license }.each do |field|
|
|
111
121
|
next unless params[field.to_sym].nil?
|
|
112
122
|
warnings.push("Missing profile #{field} in #{ref}")
|
|
113
123
|
end
|
|
114
124
|
|
|
125
|
+
# if version is set, ensure it is in SPDX format
|
|
126
|
+
if !params[:license].nil? && !Spdx.valid_license?(params[:license])
|
|
127
|
+
warnings.push("License '#{params[:license]}' needs to be in SPDX format. See https://spdx.org/licenses/.")
|
|
128
|
+
end
|
|
129
|
+
|
|
115
130
|
[errors, warnings]
|
|
116
131
|
end
|
|
117
132
|
|
|
@@ -121,6 +136,13 @@ module Inspec
|
|
|
121
136
|
errors.empty? && unsupported.empty?
|
|
122
137
|
end
|
|
123
138
|
|
|
139
|
+
def valid_version?(value)
|
|
140
|
+
Semverse::Version.new(value)
|
|
141
|
+
true
|
|
142
|
+
rescue Semverse::InvalidVersionFormat
|
|
143
|
+
false
|
|
144
|
+
end
|
|
145
|
+
|
|
124
146
|
def method_missing(sth, *args)
|
|
125
147
|
@logger.warn "#{ref} doesn't support: #{sth} #{args}"
|
|
126
148
|
@missing_methods.push(sth)
|
|
@@ -143,7 +165,9 @@ module Inspec
|
|
|
143
165
|
|
|
144
166
|
def self.finalize_supports_elem(elem, logger)
|
|
145
167
|
case x = elem
|
|
146
|
-
when Hash
|
|
168
|
+
when Hash
|
|
169
|
+
x[:release] = x[:release].to_s if x[:release]
|
|
170
|
+
x
|
|
147
171
|
when Array
|
|
148
172
|
logger.warn(
|
|
149
173
|
'Failed to read supports entry that is an array. Please use '\
|
|
@@ -162,7 +186,7 @@ module Inspec
|
|
|
162
186
|
|
|
163
187
|
def self.finalize_supports(supports, logger)
|
|
164
188
|
case x = supports
|
|
165
|
-
when Hash then [x]
|
|
189
|
+
when Hash then [finalize_supports_elem(x, logger)]
|
|
166
190
|
when Array then x.map { |e| finalize_supports_elem(e, logger) }.compact
|
|
167
191
|
when nil then []
|
|
168
192
|
else
|
|
@@ -174,37 +198,57 @@ module Inspec
|
|
|
174
198
|
end
|
|
175
199
|
end
|
|
176
200
|
|
|
177
|
-
def self.
|
|
201
|
+
def self.finalize_name(metadata, profile_id, original_target)
|
|
202
|
+
# profile_id always overwrites whatever already exists as the name
|
|
203
|
+
unless profile_id.to_s.empty?
|
|
204
|
+
metadata.params[:name] = profile_id.to_s
|
|
205
|
+
return
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# don't overwrite an existing name
|
|
209
|
+
return unless metadata.params[:name].nil?
|
|
210
|
+
|
|
211
|
+
# if there's a title, there is no need to set a name too
|
|
212
|
+
return unless metadata.params[:title].nil?
|
|
213
|
+
|
|
214
|
+
# create a new name based on the original target if it exists
|
|
215
|
+
metadata.params[:name] = "tests from #{original_target}" unless original_target.to_s.empty?
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def self.finalize(metadata, profile_id, options, logger = nil)
|
|
178
219
|
return nil if metadata.nil?
|
|
179
220
|
param = metadata.params || {}
|
|
180
|
-
|
|
221
|
+
options ||= {}
|
|
181
222
|
param['version'] = param['version'].to_s unless param['version'].nil?
|
|
182
223
|
metadata.params = symbolize_keys(param)
|
|
183
224
|
metadata.params[:supports] = finalize_supports(metadata.params[:supports], logger)
|
|
225
|
+
finalize_name(metadata, profile_id, options[:target])
|
|
184
226
|
|
|
185
227
|
metadata
|
|
186
228
|
end
|
|
187
229
|
|
|
188
|
-
def self.from_yaml(ref,
|
|
230
|
+
def self.from_yaml(ref, content, profile_id, logger = nil)
|
|
189
231
|
res = Metadata.new(ref, logger)
|
|
190
|
-
res.params = YAML.load(
|
|
191
|
-
|
|
232
|
+
res.params = YAML.load(content)
|
|
233
|
+
res.content = content
|
|
234
|
+
finalize(res, profile_id, {}, logger)
|
|
192
235
|
end
|
|
193
236
|
|
|
194
|
-
def self.from_ruby(ref,
|
|
237
|
+
def self.from_ruby(ref, content, profile_id, logger = nil)
|
|
195
238
|
res = Metadata.new(ref, logger)
|
|
196
|
-
res.instance_eval(
|
|
197
|
-
|
|
239
|
+
res.instance_eval(content, ref, 1)
|
|
240
|
+
res.content = content
|
|
241
|
+
finalize(res, profile_id, {}, logger)
|
|
198
242
|
end
|
|
199
243
|
|
|
200
|
-
def self.from_ref(ref,
|
|
244
|
+
def self.from_ref(ref, content, profile_id, logger = nil)
|
|
201
245
|
# NOTE there doesn't have to exist an actual file, it may come from an
|
|
202
|
-
# archive (i.e.,
|
|
246
|
+
# archive (i.e., content)
|
|
203
247
|
case File.basename(ref)
|
|
204
248
|
when 'inspec.yml'
|
|
205
|
-
from_yaml(ref,
|
|
249
|
+
from_yaml(ref, content, profile_id, logger)
|
|
206
250
|
when 'metadata.rb'
|
|
207
|
-
from_ruby(ref,
|
|
251
|
+
from_ruby(ref, content, profile_id, logger)
|
|
208
252
|
else
|
|
209
253
|
logger ||= Logger.new(nil)
|
|
210
254
|
logger.error "Don't know how to handle metadata in #{ref}"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Dominik Richter
|
|
3
|
+
# author: Christoph Hartmann
|
|
4
|
+
|
|
5
|
+
module Inspec
|
|
6
|
+
module MethodSource
|
|
7
|
+
def self.code_at(location, source_reader)
|
|
8
|
+
# TODO: logger for these cases
|
|
9
|
+
return '' if location.nil? || location[:ref].nil? || location[:line].nil?
|
|
10
|
+
return '' unless source_reader && source_reader.target
|
|
11
|
+
|
|
12
|
+
# TODO: Non-controls still need more detection
|
|
13
|
+
ref = location[:ref]
|
|
14
|
+
ref = ref.sub(source_reader.target.prefix, '')
|
|
15
|
+
src = source_reader.tests[ref]
|
|
16
|
+
return '' if src.nil?
|
|
17
|
+
|
|
18
|
+
::MethodSource.expression_at(src.lines, location[:line]).force_encoding('utf-8')
|
|
19
|
+
rescue SyntaxError => e
|
|
20
|
+
raise ::MethodSource::SourceNotFoundError,
|
|
21
|
+
"Could not parse source at #{location[:ref]}:#{location[:line]}: #{e.message}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -21,6 +21,18 @@ module Inspec
|
|
|
21
21
|
@opts[:default]
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
def title
|
|
25
|
+
@opts[:title]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def description
|
|
29
|
+
@opts[:description]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ruby_var_identifier
|
|
33
|
+
'attr_' + @name.downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
|
|
34
|
+
end
|
|
35
|
+
|
|
24
36
|
def to_hash
|
|
25
37
|
{
|
|
26
38
|
name: @name,
|
|
@@ -28,6 +40,15 @@ module Inspec
|
|
|
28
40
|
}
|
|
29
41
|
end
|
|
30
42
|
|
|
43
|
+
def to_ruby
|
|
44
|
+
res = ["#{ruby_var_identifier} = attribute('#{@name}',{"]
|
|
45
|
+
res.push " title: '#{title}'," unless title.to_s.empty?
|
|
46
|
+
res.push " default: '#{default}'," unless default.to_s.empty?
|
|
47
|
+
res.push " description: '#{description}'," unless description.to_s.empty?
|
|
48
|
+
res.push '})'
|
|
49
|
+
res.join("\n")
|
|
50
|
+
end
|
|
51
|
+
|
|
31
52
|
def to_s
|
|
32
53
|
"Attribute #{@name} with #{@value}"
|
|
33
54
|
end
|
|
@@ -2,24 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
module Inspec
|
|
4
4
|
class Control
|
|
5
|
-
attr_accessor :id, :title, :desc, :impact, :tests
|
|
5
|
+
attr_accessor :id, :title, :desc, :impact, :tests, :tags
|
|
6
6
|
def initialize
|
|
7
7
|
@tests = []
|
|
8
|
+
@tags = []
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def add_test(t)
|
|
11
12
|
@tests.push(t)
|
|
12
13
|
end
|
|
13
14
|
|
|
15
|
+
def add_tag(t)
|
|
16
|
+
@tags.push(t)
|
|
17
|
+
end
|
|
18
|
+
|
|
14
19
|
def to_hash
|
|
15
|
-
{ id: id, title: title, desc: desc, impact: impact, tests: tests.map(&:to_hash) }
|
|
20
|
+
{ id: id, title: title, desc: desc, impact: impact, tests: tests.map(&:to_hash), tags: tags.map(&:to_hash) }
|
|
16
21
|
end
|
|
17
22
|
|
|
18
23
|
def to_ruby
|
|
19
24
|
res = ["control #{id.inspect} do"]
|
|
20
25
|
res.push " title #{title.inspect}" unless title.to_s.empty?
|
|
21
|
-
res.push " desc #{desc
|
|
26
|
+
res.push " desc #{prettyprint_text(desc, 2)}" unless desc.to_s.empty?
|
|
22
27
|
res.push " impact #{impact}" unless impact.nil?
|
|
28
|
+
tags.each { |t| res.push(indent(t.to_ruby, 2)) }
|
|
23
29
|
tests.each { |t| res.push(indent(t.to_ruby, 2)) }
|
|
24
30
|
res.push 'end'
|
|
25
31
|
res.join("\n")
|
|
@@ -27,6 +33,18 @@ module Inspec
|
|
|
27
33
|
|
|
28
34
|
private
|
|
29
35
|
|
|
36
|
+
# Pretty-print a text block of InSpec code
|
|
37
|
+
#
|
|
38
|
+
# @param s [String] should not be empty
|
|
39
|
+
# @param depth [Int] indentation length for multiline text blocks
|
|
40
|
+
# @return [String] pretty-printed textblock
|
|
41
|
+
def prettyprint_text(s, depth)
|
|
42
|
+
txt = s.to_s.inspect.gsub('\n', "\n")
|
|
43
|
+
return txt if !txt.include?("\n")
|
|
44
|
+
middle = indent(txt[1..-2], depth+2)
|
|
45
|
+
txt[0] + "\n" + middle + "\n" + ' '*depth + txt[-1]
|
|
46
|
+
end
|
|
47
|
+
|
|
30
48
|
def indent(txt, d)
|
|
31
49
|
dt = ' '*d
|
|
32
50
|
dt + txt.gsub("\n", "\n"+dt)
|
data/lib/inspec/objects/list.rb
CHANGED
|
@@ -5,15 +5,30 @@ module Inspec
|
|
|
5
5
|
attr_reader :tests
|
|
6
6
|
def initialize(tests)
|
|
7
7
|
@tests = tests
|
|
8
|
+
@negated = false
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def skip
|
|
11
12
|
nil
|
|
12
13
|
end
|
|
13
14
|
|
|
15
|
+
def negate!
|
|
16
|
+
@negated = !@negated
|
|
17
|
+
end
|
|
18
|
+
|
|
14
19
|
def to_ruby
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
if @negated
|
|
21
|
+
# We don't use the describe.one wrapper when negated because:
|
|
22
|
+
# !(test1 || test2) same as (!test1 && !test2) where && is implicit in inspec
|
|
23
|
+
all_tests = @tests.map { |test|
|
|
24
|
+
test.negate!
|
|
25
|
+
test
|
|
26
|
+
}.map(&:to_ruby).join("\n")
|
|
27
|
+
return all_tests
|
|
28
|
+
else
|
|
29
|
+
all_tests = @tests.map(&:to_ruby).join("\n").gsub("\n", "\n ")
|
|
30
|
+
return format("describe.one do\n %s\nend", all_tests)
|
|
31
|
+
end
|
|
17
32
|
end
|
|
18
33
|
|
|
19
34
|
def to_hash
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# encoding:utf-8
|
|
2
|
+
|
|
3
|
+
module Inspec
|
|
4
|
+
class Tag
|
|
5
|
+
attr_accessor :key, :value
|
|
6
|
+
|
|
7
|
+
def initialize(key, value)
|
|
8
|
+
@key = key
|
|
9
|
+
@value = value
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_hash
|
|
13
|
+
{
|
|
14
|
+
name: key,
|
|
15
|
+
value: value,
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_ruby
|
|
20
|
+
"tag #{key.inspect}: #{value.inspect}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_s
|
|
24
|
+
"Tag #{key} with #{value}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/inspec/objects/test.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Inspec
|
|
|
53
53
|
# this will go in its()
|
|
54
54
|
xres = last_call
|
|
55
55
|
else
|
|
56
|
-
res += '.' + ruby_qualifier(last)
|
|
56
|
+
res += '.' + ruby_qualifier(last) unless last_call.empty?
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -66,13 +66,14 @@ module Inspec
|
|
|
66
66
|
res, xtra = describe_chain
|
|
67
67
|
itsy = xtra.nil? ? 'it' : 'its(' + xtra.to_s.inspect + ')'
|
|
68
68
|
naughty = @negated ? '_not' : ''
|
|
69
|
-
xpect = defined?(@expectation)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
xpect = if !defined?(@expectation)
|
|
70
|
+
''
|
|
71
|
+
elsif @expectation.class == Regexp
|
|
72
|
+
# without this, xpect values like / \/zones\// will not be parsed properly
|
|
73
|
+
"(#{@expectation.inspect})"
|
|
74
|
+
elsif xpect != ''
|
|
75
|
+
' ' + expectation.inspect
|
|
76
|
+
end
|
|
76
77
|
format("%sdescribe %s do\n %s { should%s %s%s }\nend",
|
|
77
78
|
vars, res, itsy, naughty, matcher, xpect)
|
|
78
79
|
end
|
data/lib/inspec/objects.rb
CHANGED