inspec 1.31.0 → 1.45.9
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/.rubocop.yml +3 -0
- data/CHANGELOG.md +2599 -1
- data/Gemfile +0 -1
- data/README.md +24 -34
- data/Rakefile +0 -31
- data/docs/dsl_inspec.md +10 -0
- data/docs/matchers.md +29 -0
- data/docs/plugin_kitchen_inspec.md +18 -24
- data/docs/profiles.md +101 -81
- data/docs/resources/aide_conf.md.erb +71 -0
- data/docs/resources/apache_conf.md.erb +19 -33
- data/docs/resources/apt.md.erb +22 -36
- data/docs/resources/audit_policy.md.erb +9 -24
- data/docs/resources/auditd.md.erb +78 -0
- data/docs/resources/auditd_conf.md.erb +20 -34
- data/docs/resources/auditd_rules.md.erb +8 -24
- data/docs/resources/bash.md.erb +4 -26
- data/docs/resources/bond.md.erb +25 -40
- data/docs/resources/bridge.md.erb +5 -25
- data/docs/resources/bsd_service.md.erb +5 -25
- data/docs/resources/command.md.erb +35 -50
- data/docs/resources/cpan.md.erb +62 -0
- data/docs/resources/cran.md.erb +54 -0
- data/docs/resources/crontab.md.erb +9 -23
- data/docs/resources/csv.md.erb +12 -27
- data/docs/resources/dh_params.md +1 -0
- data/docs/resources/directory.md.erb +5 -25
- data/docs/resources/docker.md.erb +61 -58
- data/docs/resources/docker_container.md.erb +23 -19
- data/docs/resources/docker_image.md.erb +20 -16
- data/docs/resources/elasticsearch.md.erb +245 -0
- data/docs/resources/etc_fstab.md.erb +120 -0
- data/docs/resources/etc_group.md.erb +29 -45
- data/docs/resources/etc_hosts.md.erb +68 -0
- data/docs/resources/etc_hosts_allow.md.erb +68 -0
- data/docs/resources/etc_hosts_deny.md.erb +68 -0
- data/docs/resources/file.md.erb +227 -216
- data/docs/resources/firewalld.md.erb +104 -0
- data/docs/resources/gem.md.erb +21 -35
- data/docs/resources/group.md.erb +16 -30
- data/docs/resources/grub_conf.md.erb +9 -24
- data/docs/resources/host.md.erb +32 -49
- data/docs/resources/http.md.erb +38 -44
- data/docs/resources/iis_app.md.erb +25 -35
- data/docs/resources/iis_site.md.erb +26 -40
- data/docs/resources/inetd_conf.md.erb +27 -42
- data/docs/resources/ini.md.erb +9 -23
- data/docs/resources/interface.md.erb +5 -25
- data/docs/resources/iptables.md.erb +15 -29
- data/docs/resources/json.md.erb +12 -27
- data/docs/resources/kernel_module.md.erb +47 -61
- data/docs/resources/kernel_parameter.md.erb +15 -29
- data/docs/resources/key_rsa.md.erb +3 -0
- data/docs/resources/launchd_service.md.erb +5 -25
- data/docs/resources/limits_conf.md.erb +15 -29
- data/docs/resources/login_def.md.erb +15 -30
- data/docs/resources/mount.md.erb +18 -33
- data/docs/resources/mssql_session.md.erb +9 -12
- data/docs/resources/mysql_conf.md.erb +28 -32
- data/docs/resources/mysql_session.md.erb +15 -29
- data/docs/resources/nginx.md.erb +78 -0
- data/docs/resources/nginx_conf.md.erb +127 -0
- data/docs/resources/npm.md.erb +19 -35
- data/docs/resources/ntp_conf.md.erb +20 -37
- data/docs/resources/oneget.md.erb +15 -30
- data/docs/resources/oracledb_session.md.erb +9 -11
- data/docs/resources/os.md.erb +29 -43
- data/docs/resources/os_env.md.erb +23 -44
- data/docs/resources/package.md.erb +34 -36
- data/docs/resources/parse_config.md.erb +5 -25
- data/docs/resources/parse_config_file.md.erb +31 -43
- data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +24 -39
- data/docs/resources/pip.md.erb +20 -35
- data/docs/resources/port.md.erb +43 -57
- data/docs/resources/postgres_conf.md.erb +17 -31
- data/docs/resources/postgres_hba_conf.md.erb +26 -38
- data/docs/resources/postgres_ident_conf.md.erb +25 -37
- data/docs/resources/postgres_session.md.erb +15 -29
- data/docs/resources/powershell.md.erb +27 -42
- data/docs/resources/processes.md.erb +17 -33
- data/docs/resources/rabbitmq_config.md.erb +9 -24
- data/docs/resources/registry_key.md.erb +27 -42
- data/docs/resources/runit_service.md.erb +5 -25
- data/docs/resources/security_policy.md.erb +12 -27
- data/docs/resources/service.md.erb +27 -42
- data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +41 -47
- data/docs/resources/ssh_config.md.erb +19 -34
- data/docs/resources/sshd_config.md.erb +19 -34
- data/docs/resources/ssl.md.erb +39 -54
- data/docs/resources/sys_info.md.erb +12 -26
- data/docs/resources/systemd_service.md.erb +5 -25
- data/docs/resources/sysv_service.md.erb +5 -25
- data/docs/resources/upstart_service.md.erb +5 -25
- data/docs/resources/user.md.erb +29 -44
- data/docs/resources/users.md.erb +12 -26
- data/docs/resources/vbscript.md.erb +9 -24
- data/docs/resources/virtualization.md.erb +8 -23
- data/docs/resources/windows_feature.md.erb +15 -30
- data/docs/resources/windows_hotfix.md.erb +50 -0
- data/docs/resources/windows_task.md.erb +12 -26
- data/docs/resources/wmi.md.erb +9 -24
- data/docs/resources/x509_certificate.md.erb +4 -0
- data/docs/resources/xinetd_conf.md.erb +65 -80
- data/docs/resources/xml.md.erb +61 -0
- data/docs/resources/yaml.md.erb +12 -27
- data/docs/resources/yum.md.erb +37 -51
- data/docs/resources/zfs_dataset.md.erb +15 -26
- data/docs/resources/zfs_pool.md.erb +9 -20
- data/docs/ruby_usage.md +0 -1
- data/examples/meta-profile/README.md +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 +3 -3
- data/lib/bundles/inspec-artifact/cli.rb +5 -5
- data/lib/bundles/inspec-compliance/README.md +24 -22
- data/lib/bundles/inspec-compliance/api/login.rb +152 -0
- data/lib/bundles/inspec-compliance/api.rb +46 -16
- data/lib/bundles/inspec-compliance/cli.rb +58 -165
- data/lib/bundles/inspec-compliance/target.rb +2 -3
- data/lib/bundles/inspec-habitat/cli.rb +7 -2
- data/lib/bundles/inspec-habitat/profile.rb +44 -20
- data/lib/bundles/inspec-init/templates/profile/README.md +1 -1
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +1 -1
- data/lib/bundles/inspec-supermarket/api.rb +1 -1
- data/lib/inspec/backend.rb +8 -0
- data/lib/inspec/cli.rb +1 -1
- data/lib/inspec/control_eval_context.rb +16 -6
- data/lib/inspec/dependencies/requirement.rb +22 -8
- data/lib/inspec/dependencies/resolver.rb +1 -1
- data/lib/inspec/exceptions.rb +4 -0
- data/lib/inspec/file_provider.rb +38 -9
- data/lib/inspec/metadata.rb +17 -3
- data/lib/inspec/objects/attribute.rb +16 -7
- data/lib/inspec/objects/control.rb +10 -2
- data/lib/inspec/plugins/resource.rb +34 -5
- data/lib/inspec/profile.rb +10 -1
- data/lib/inspec/profile_context.rb +3 -1
- data/lib/inspec/resource.rb +17 -5
- data/lib/inspec/rspec_json_formatter.rb +48 -14
- data/lib/inspec/rule.rb +4 -0
- data/lib/inspec/runner.rb +72 -30
- data/lib/inspec/shell.rb +24 -19
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +40 -0
- data/lib/resources/aide_conf.rb +162 -0
- data/lib/resources/auditd.rb +231 -0
- data/lib/resources/auditd_rules.rb +3 -1
- data/lib/resources/command.rb +1 -1
- data/lib/resources/cpan.rb +60 -0
- data/lib/resources/cran.rb +66 -0
- data/lib/resources/docker.rb +8 -1
- data/lib/resources/docker_container.rb +19 -2
- data/lib/resources/elasticsearch.rb +172 -0
- data/lib/resources/etc_fstab.rb +102 -0
- data/lib/resources/etc_group.rb +1 -1
- data/lib/resources/etc_hosts.rb +81 -0
- data/lib/resources/etc_hosts_allow_deny.rb +122 -0
- data/lib/resources/file.rb +87 -10
- data/lib/resources/firewalld.rb +144 -0
- data/lib/resources/groups.rb +0 -1
- data/lib/resources/host.rb +30 -15
- data/lib/resources/http.rb +178 -22
- data/lib/resources/mssql_session.rb +3 -3
- data/lib/resources/mysql_conf.rb +10 -0
- data/lib/resources/mysql_session.rb +1 -1
- data/lib/resources/nginx.rb +97 -0
- data/lib/resources/nginx_conf.rb +134 -1
- data/lib/resources/oracledb_session.rb +9 -8
- data/lib/resources/os_env.rb +7 -4
- data/lib/resources/package.rb +14 -2
- data/lib/resources/packages.rb +1 -1
- data/lib/resources/pip.rb +28 -20
- data/lib/resources/port.rb +251 -3
- data/lib/resources/postgres.rb +1 -1
- data/lib/resources/postgres_session.rb +1 -1
- data/lib/resources/processes.rb +106 -20
- data/lib/resources/registry_key.rb +11 -2
- data/lib/resources/service.rb +5 -24
- data/lib/resources/ssh_conf.rb +1 -1
- data/lib/resources/ssl.rb +4 -3
- data/lib/resources/toml.rb +2 -2
- data/lib/resources/windows_hotfix.rb +35 -0
- data/lib/resources/xinetd.rb +4 -6
- data/lib/resources/xml.rb +27 -0
- data/lib/source_readers/inspec.rb +3 -3
- data/lib/utils/find_files.rb +2 -1
- data/lib/utils/nginx_parser.rb +10 -1
- data/lib/utils/object_traversal.rb +35 -10
- data/lib/utils/parser.rb +21 -3
- metadata +48 -18
- data/docs/cli.md +0 -392
|
@@ -71,7 +71,7 @@ module Inspec
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
if !dep.source_satisfies_spec?
|
|
74
|
-
raise Inspec::UnsatisfiedVersionSpecification, "The profile #{dep.name} from #{dep.resolved_source} has a version #{dep.source_version} which doesn't match #{dep.
|
|
74
|
+
raise Inspec::UnsatisfiedVersionSpecification, "The profile #{dep.name} from #{dep.resolved_source} has a version #{dep.source_version} which doesn't match #{dep.version_constraints}"
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
Inspec::Log.debug("Adding dependency #{dep.name} (#{dep.resolved_source})")
|
data/lib/inspec/exceptions.rb
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
module Inspec
|
|
5
5
|
module Exceptions
|
|
6
|
+
class AttributesFileDoesNotExist < ArgumentError; end
|
|
7
|
+
class AttributesFileNotReadable < ArgumentError; end
|
|
8
|
+
class ResourceFailed < StandardError; end
|
|
9
|
+
class ResourceSkipped < StandardError; end
|
|
6
10
|
class SecretsBackendNotFound < ArgumentError; end
|
|
7
11
|
end
|
|
8
12
|
end
|
data/lib/inspec/file_provider.rb
CHANGED
|
@@ -72,7 +72,7 @@ module Inspec
|
|
|
72
72
|
@files = if File.file?(path)
|
|
73
73
|
[path]
|
|
74
74
|
else
|
|
75
|
-
Dir[File.join(path, '**', '*')]
|
|
75
|
+
Dir[File.join(Shellwords.shellescape(path), '**', '*')]
|
|
76
76
|
end
|
|
77
77
|
@path = path
|
|
78
78
|
end
|
|
@@ -97,9 +97,10 @@ module Inspec
|
|
|
97
97
|
@path = path
|
|
98
98
|
@contents = {}
|
|
99
99
|
@files = []
|
|
100
|
-
|
|
100
|
+
walk_zip(@path) do |io|
|
|
101
101
|
while (entry = io.get_next_entry)
|
|
102
|
-
|
|
102
|
+
name = entry.name.sub(%r{/+$}, '')
|
|
103
|
+
@files.push(name) unless name.empty?
|
|
103
104
|
end
|
|
104
105
|
end
|
|
105
106
|
end
|
|
@@ -110,10 +111,14 @@ module Inspec
|
|
|
110
111
|
|
|
111
112
|
private
|
|
112
113
|
|
|
114
|
+
def walk_zip(path, &callback)
|
|
115
|
+
::Zip::InputStream.open(path, &callback)
|
|
116
|
+
end
|
|
117
|
+
|
|
113
118
|
def read_from_zip(file)
|
|
114
119
|
return nil unless @files.include?(file)
|
|
115
120
|
res = nil
|
|
116
|
-
|
|
121
|
+
walk_zip(@path) do |io|
|
|
117
122
|
while (entry = io.get_next_entry)
|
|
118
123
|
next unless file == entry.name
|
|
119
124
|
res = io.read
|
|
@@ -131,8 +136,17 @@ module Inspec
|
|
|
131
136
|
@path = path
|
|
132
137
|
@contents = {}
|
|
133
138
|
@files = []
|
|
134
|
-
|
|
135
|
-
@files = tar.
|
|
139
|
+
walk_tar(@path) do |tar|
|
|
140
|
+
@files = tar.find_all(&:file?)
|
|
141
|
+
|
|
142
|
+
# delete all entries with no name
|
|
143
|
+
@files = @files.find_all { |x| !x.full_name.empty? }
|
|
144
|
+
|
|
145
|
+
# delete all entries that have a PaxHeader
|
|
146
|
+
@files = @files.delete_if { |x| x.full_name.include?('PaxHeader/') }
|
|
147
|
+
|
|
148
|
+
# replace all items of the array simply with the relative filename of the file
|
|
149
|
+
@files.map! { |x| Pathname.new(x.full_name).relative_path_from(Pathname.new('.')).to_s }
|
|
136
150
|
end
|
|
137
151
|
end
|
|
138
152
|
|
|
@@ -142,13 +156,17 @@ module Inspec
|
|
|
142
156
|
|
|
143
157
|
private
|
|
144
158
|
|
|
159
|
+
def walk_tar(path, &callback)
|
|
160
|
+
Gem::Package::TarReader.new(Zlib::GzipReader.open(path), &callback)
|
|
161
|
+
end
|
|
162
|
+
|
|
145
163
|
def read_from_tar(file)
|
|
146
164
|
return nil unless @files.include?(file)
|
|
147
165
|
res = nil
|
|
148
166
|
# NB `TarReader` includes `Enumerable` beginning with Ruby 2.x
|
|
149
|
-
|
|
167
|
+
walk_tar(@path) do |tar|
|
|
150
168
|
tar.each do |entry|
|
|
151
|
-
next unless entry.file? && file
|
|
169
|
+
next unless entry.file? && [file, "./#{file}"].include?(entry.full_name)
|
|
152
170
|
res = entry.read
|
|
153
171
|
break
|
|
154
172
|
end
|
|
@@ -173,8 +191,19 @@ module Inspec
|
|
|
173
191
|
if @prefix.nil?
|
|
174
192
|
raise "Could not determine path prefix for #{parent}"
|
|
175
193
|
end
|
|
176
|
-
|
|
194
|
+
|
|
195
|
+
# select all files that begin with the prefix, and strip off the prefix from the file.
|
|
196
|
+
#
|
|
197
|
+
# strip off any leading top-level relative path (./) which is common in
|
|
198
|
+
# PAX-formatted tar files. Do not do any translation of the path if the
|
|
199
|
+
# path is an absolute path.
|
|
200
|
+
@files = parent.files
|
|
201
|
+
.find_all { |x| x.start_with?(prefix) && x != prefix }
|
|
177
202
|
.map { |x| x[prefix.length..-1] }
|
|
203
|
+
.map do |x|
|
|
204
|
+
path = Pathname.new(x)
|
|
205
|
+
path.absolute? ? path.to_s : path.relative_path_from(Pathname.new('.')).to_s
|
|
206
|
+
end
|
|
178
207
|
end
|
|
179
208
|
|
|
180
209
|
def abs_path(file)
|
data/lib/inspec/metadata.rb
CHANGED
|
@@ -11,6 +11,9 @@ require 'utils/spdx'
|
|
|
11
11
|
|
|
12
12
|
module Inspec
|
|
13
13
|
# Extract metadata.rb information
|
|
14
|
+
# A Metadata object may be created and finalized with invalid data.
|
|
15
|
+
# This allows the check CLI command to analyse the issues.
|
|
16
|
+
# Use valid? to determine if the metadata is coherent.
|
|
14
17
|
class Metadata # rubocop:disable Metrics/ClassLength
|
|
15
18
|
attr_reader :ref
|
|
16
19
|
attr_accessor :params, :content
|
|
@@ -112,6 +115,12 @@ module Inspec
|
|
|
112
115
|
errors.push("Missing profile #{field} in #{ref}")
|
|
113
116
|
end
|
|
114
117
|
|
|
118
|
+
if params[:name] =~ %r{[\/\\]}
|
|
119
|
+
warnings.push("Your profile name (#{params[:name]}) contains a slash " \
|
|
120
|
+
'which will not be permitted in InSpec 2.0. Please change your profile ' \
|
|
121
|
+
'name in the `inspec.yml` file.')
|
|
122
|
+
end
|
|
123
|
+
|
|
115
124
|
# if version is set, ensure it is correct
|
|
116
125
|
if !params[:version].nil? && !valid_version?(params[:version])
|
|
117
126
|
errors.push('Version needs to be in SemVer format')
|
|
@@ -180,7 +189,7 @@ module Inspec
|
|
|
180
189
|
logger.warn(
|
|
181
190
|
"Do not use deprecated `supports: #{x}` syntax. Instead use:\n"\
|
|
182
191
|
"supports:\n - os-family: #{x}\n\n")
|
|
183
|
-
{ :'os-family' => x }
|
|
192
|
+
{ :'os-family' => x } # rubocop:disable Style/HashSyntax
|
|
184
193
|
end
|
|
185
194
|
end
|
|
186
195
|
|
|
@@ -194,7 +203,7 @@ module Inspec
|
|
|
194
203
|
logger.warn(
|
|
195
204
|
"Do not use deprecated `supports: #{x}` syntax. Instead use:\n"\
|
|
196
205
|
"supports:\n - os-family: #{x}\n\n")
|
|
197
|
-
[{ :'os-family' => x }]
|
|
206
|
+
[{ :'os-family' => x }] # rubocop:disable Style/HashSyntax
|
|
198
207
|
end
|
|
199
208
|
end
|
|
200
209
|
|
|
@@ -212,7 +221,12 @@ module Inspec
|
|
|
212
221
|
return unless metadata.params[:title].nil?
|
|
213
222
|
|
|
214
223
|
# create a new name based on the original target if it exists
|
|
215
|
-
|
|
224
|
+
# Crudely slug the target to not contain slashes, to avoid breaking
|
|
225
|
+
# unit tests that look for warning sequences
|
|
226
|
+
unless original_target.to_s.empty?
|
|
227
|
+
metadata.params[:title] = "tests from #{original_target}"
|
|
228
|
+
metadata.params[:name] = metadata.params[:title].gsub(%r{[\\\/]}, '.')
|
|
229
|
+
end
|
|
216
230
|
end
|
|
217
231
|
|
|
218
232
|
def self.finalize(metadata, profile_id, options, logger = nil)
|
|
@@ -3,22 +3,31 @@
|
|
|
3
3
|
module Inspec
|
|
4
4
|
class Attribute
|
|
5
5
|
attr_accessor :name
|
|
6
|
-
|
|
6
|
+
attr_writer :value
|
|
7
|
+
|
|
8
|
+
DEFAULT_ATTRIBUTE = Class.new do
|
|
9
|
+
def method_missing(*_)
|
|
10
|
+
self
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def respond_to_missing?(_, _)
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(name, options = {})
|
|
7
19
|
@name = name
|
|
8
20
|
@opts = options
|
|
9
21
|
@value = nil
|
|
10
22
|
end
|
|
11
23
|
|
|
12
24
|
# implicit call is done by inspec to determine the value of an attribute
|
|
13
|
-
def value
|
|
14
|
-
|
|
15
|
-
@value = newvalue
|
|
16
|
-
end
|
|
17
|
-
@value || default
|
|
25
|
+
def value
|
|
26
|
+
@value.nil? ? default : @value
|
|
18
27
|
end
|
|
19
28
|
|
|
20
29
|
def default
|
|
21
|
-
@opts[:default]
|
|
30
|
+
@opts[:default] || DEFAULT_ATTRIBUTE.new
|
|
22
31
|
end
|
|
23
32
|
|
|
24
33
|
def title
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Inspec
|
|
4
4
|
class Control
|
|
5
|
-
attr_accessor :id, :title, :desc, :impact, :tests, :tags
|
|
5
|
+
attr_accessor :id, :title, :desc, :impact, :tests, :tags, :refs
|
|
6
6
|
def initialize
|
|
7
7
|
@tests = []
|
|
8
8
|
@tags = []
|
|
9
|
+
@refs = []
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def add_test(t)
|
|
@@ -20,12 +21,13 @@ module Inspec
|
|
|
20
21
|
{ id: id, title: title, desc: desc, impact: impact, tests: tests.map(&:to_hash), tags: tags.map(&:to_hash) }
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
def to_ruby
|
|
24
|
+
def to_ruby # rubocop:disable Metrics/AbcSize
|
|
24
25
|
res = ["control #{id.inspect} do"]
|
|
25
26
|
res.push " title #{title.inspect}" unless title.to_s.empty?
|
|
26
27
|
res.push " desc #{prettyprint_text(desc, 2)}" unless desc.to_s.empty?
|
|
27
28
|
res.push " impact #{impact}" unless impact.nil?
|
|
28
29
|
tags.each { |t| res.push(indent(t.to_ruby, 2)) }
|
|
30
|
+
refs.each { |t| res.push(" ref #{print_ref(t)}") }
|
|
29
31
|
tests.each { |t| res.push(indent(t.to_ruby, 2)) }
|
|
30
32
|
res.push 'end'
|
|
31
33
|
res.join("\n")
|
|
@@ -33,6 +35,12 @@ module Inspec
|
|
|
33
35
|
|
|
34
36
|
private
|
|
35
37
|
|
|
38
|
+
def print_ref(x)
|
|
39
|
+
return x.inspect if x.is_a?(String)
|
|
40
|
+
raise "Cannot process the ref: #{x}" unless x.is_a?(Hash)
|
|
41
|
+
'('+x.inspect+')'
|
|
42
|
+
end
|
|
43
|
+
|
|
36
44
|
# Pretty-print a text block of InSpec code
|
|
37
45
|
#
|
|
38
46
|
# @param s [String] should not be empty
|
|
@@ -37,14 +37,26 @@ module Inspec
|
|
|
37
37
|
Inspec::Resource.registry
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def __register(name, obj)
|
|
40
|
+
def __register(name, obj) # rubocop:disable Metrics/MethodLength
|
|
41
41
|
cl = Class.new(obj) do
|
|
42
|
+
attr_reader :resource_exception_message
|
|
43
|
+
|
|
42
44
|
def initialize(backend, name, *args)
|
|
45
|
+
@resource_skipped = false
|
|
46
|
+
@resource_failed = false
|
|
47
|
+
|
|
43
48
|
# attach the backend to this instance
|
|
44
49
|
@__backend_runner__ = backend
|
|
45
50
|
@__resource_name__ = name
|
|
51
|
+
|
|
46
52
|
# call the resource initializer
|
|
47
|
-
|
|
53
|
+
begin
|
|
54
|
+
super(*args)
|
|
55
|
+
rescue Inspec::Exceptions::ResourceSkipped => e
|
|
56
|
+
skip_resource(e.message)
|
|
57
|
+
rescue Inspec::Exceptions::ResourceFailed => e
|
|
58
|
+
fail_resource(e.message)
|
|
59
|
+
end
|
|
48
60
|
end
|
|
49
61
|
|
|
50
62
|
def self.desc(description = nil)
|
|
@@ -57,12 +69,29 @@ module Inspec
|
|
|
57
69
|
@example = example
|
|
58
70
|
end
|
|
59
71
|
|
|
72
|
+
def skip_resource(message)
|
|
73
|
+
@resource_skipped = true
|
|
74
|
+
@resource_exception_message = message
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def resource_skipped?
|
|
78
|
+
@resource_skipped
|
|
79
|
+
end
|
|
80
|
+
|
|
60
81
|
def resource_skipped
|
|
61
|
-
|
|
82
|
+
warn('[DEPRECATION] Use `resource_exception_message` for the resource skipped message. This method will be removed in InSpec 2.0.')
|
|
83
|
+
# Returning `nil` here to match previous behavior
|
|
84
|
+
return nil if @resource_skipped == false
|
|
85
|
+
@resource_exception_message
|
|
62
86
|
end
|
|
63
87
|
|
|
64
|
-
def
|
|
65
|
-
@
|
|
88
|
+
def fail_resource(message)
|
|
89
|
+
@resource_failed = true
|
|
90
|
+
@resource_exception_message = message
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def resource_failed?
|
|
94
|
+
@resource_failed
|
|
66
95
|
end
|
|
67
96
|
|
|
68
97
|
def inspec
|
data/lib/inspec/profile.rb
CHANGED
|
@@ -93,12 +93,21 @@ module Inspec
|
|
|
93
93
|
@writable = options[:writable] || false
|
|
94
94
|
@profile_id = options[:id]
|
|
95
95
|
@cache = options[:cache] || Cache.new
|
|
96
|
-
@backend = options[:backend] || Inspec::Backend.create(options.select { |k, _| k != 'target' })
|
|
97
96
|
@attr_values = options[:attributes]
|
|
98
97
|
@tests_collected = false
|
|
99
98
|
@libraries_loaded = false
|
|
100
99
|
Metadata.finalize(@source_reader.metadata, @profile_id, options)
|
|
101
100
|
|
|
101
|
+
# if a backend has already been created, clone it so each profile has its own unique backend object
|
|
102
|
+
# otherwise, create a new backend object
|
|
103
|
+
#
|
|
104
|
+
# This is necessary since we store the RuntimeProfile on the backend object. If a user runs `inspec exec`
|
|
105
|
+
# with multiple profiles, only the RuntimeProfile for the last-loaded profile will be available if
|
|
106
|
+
# we share the backend between profiles.
|
|
107
|
+
#
|
|
108
|
+
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
|
|
109
|
+
train_options = options.select { |k, _| k != 'target' } # See https://github.com/chef/inspec/pull/1646
|
|
110
|
+
@backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
|
|
102
111
|
@runtime_profile = RuntimeProfile.new(self)
|
|
103
112
|
@backend.profile = @runtime_profile
|
|
104
113
|
|
|
@@ -127,6 +127,8 @@ module Inspec
|
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def load_control_file(*args)
|
|
130
|
+
# Set `skip_file` to `false` between file loads to prevent skips from spanning multiple control files
|
|
131
|
+
control_eval_context.skip_file = false
|
|
130
132
|
load_with_context(control_eval_context, *args)
|
|
131
133
|
end
|
|
132
134
|
alias load load_control_file
|
|
@@ -177,7 +179,7 @@ module Inspec
|
|
|
177
179
|
# we need to return an attribute object, to allow dermination of default values
|
|
178
180
|
attr = Attribute.new(name, options)
|
|
179
181
|
# read value from given gived values
|
|
180
|
-
attr.value
|
|
182
|
+
attr.value = @conf['attributes'][attr.name] unless @conf['attributes'].nil?
|
|
181
183
|
@attributes.push(attr)
|
|
182
184
|
attr.value
|
|
183
185
|
end
|
data/lib/inspec/resource.rb
CHANGED
|
@@ -72,24 +72,33 @@ module Inspec
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
require 'resources/aide_conf'
|
|
75
76
|
require 'resources/apache'
|
|
76
77
|
require 'resources/apache_conf'
|
|
77
78
|
require 'resources/apt'
|
|
78
79
|
require 'resources/audit_policy'
|
|
80
|
+
require 'resources/auditd'
|
|
79
81
|
require 'resources/auditd_conf'
|
|
80
82
|
require 'resources/auditd_rules'
|
|
81
83
|
require 'resources/bash'
|
|
82
84
|
require 'resources/bond'
|
|
83
85
|
require 'resources/bridge'
|
|
84
86
|
require 'resources/command'
|
|
87
|
+
require 'resources/cran'
|
|
88
|
+
require 'resources/cpan'
|
|
85
89
|
require 'resources/crontab'
|
|
86
90
|
require 'resources/dh_params'
|
|
87
91
|
require 'resources/directory'
|
|
88
92
|
require 'resources/docker'
|
|
89
|
-
require 'resources/docker_image'
|
|
90
93
|
require 'resources/docker_container'
|
|
94
|
+
require 'resources/docker_image'
|
|
95
|
+
require 'resources/elasticsearch'
|
|
96
|
+
require 'resources/etc_fstab'
|
|
91
97
|
require 'resources/etc_group'
|
|
98
|
+
require 'resources/etc_hosts_allow_deny'
|
|
99
|
+
require 'resources/etc_hosts'
|
|
92
100
|
require 'resources/file'
|
|
101
|
+
require 'resources/firewalld'
|
|
93
102
|
require 'resources/gem'
|
|
94
103
|
require 'resources/groups'
|
|
95
104
|
require 'resources/grub_conf'
|
|
@@ -111,6 +120,7 @@ require 'resources/mssql_session'
|
|
|
111
120
|
require 'resources/mysql'
|
|
112
121
|
require 'resources/mysql_conf'
|
|
113
122
|
require 'resources/mysql_session'
|
|
123
|
+
require 'resources/nginx'
|
|
114
124
|
require 'resources/nginx_conf'
|
|
115
125
|
require 'resources/npm'
|
|
116
126
|
require 'resources/ntp_conf'
|
|
@@ -122,12 +132,12 @@ require 'resources/package'
|
|
|
122
132
|
require 'resources/packages'
|
|
123
133
|
require 'resources/parse_config'
|
|
124
134
|
require 'resources/passwd'
|
|
125
|
-
require 'resources/postgres_hba_conf'
|
|
126
|
-
require 'resources/postgres_ident_conf'
|
|
127
135
|
require 'resources/pip'
|
|
128
136
|
require 'resources/port'
|
|
129
137
|
require 'resources/postgres'
|
|
130
138
|
require 'resources/postgres_conf'
|
|
139
|
+
require 'resources/postgres_hba_conf'
|
|
140
|
+
require 'resources/postgres_ident_conf'
|
|
131
141
|
require 'resources/postgres_session'
|
|
132
142
|
require 'resources/powershell'
|
|
133
143
|
require 'resources/processes'
|
|
@@ -136,18 +146,19 @@ require 'resources/registry_key'
|
|
|
136
146
|
require 'resources/security_policy'
|
|
137
147
|
require 'resources/service'
|
|
138
148
|
require 'resources/shadow'
|
|
139
|
-
require 'resources/ssl'
|
|
140
149
|
require 'resources/ssh_conf'
|
|
150
|
+
require 'resources/ssl'
|
|
141
151
|
require 'resources/sys_info'
|
|
142
152
|
require 'resources/toml'
|
|
143
153
|
require 'resources/users'
|
|
144
154
|
require 'resources/vbscript'
|
|
145
155
|
require 'resources/virtualization'
|
|
146
156
|
require 'resources/windows_feature'
|
|
157
|
+
require 'resources/windows_hotfix'
|
|
147
158
|
require 'resources/windows_task'
|
|
148
|
-
require 'resources/xinetd'
|
|
149
159
|
require 'resources/wmi'
|
|
150
160
|
require 'resources/x509_certificate'
|
|
161
|
+
require 'resources/xinetd'
|
|
151
162
|
require 'resources/yum'
|
|
152
163
|
require 'resources/zfs_dataset'
|
|
153
164
|
require 'resources/zfs_pool'
|
|
@@ -157,3 +168,4 @@ require 'resources/json'
|
|
|
157
168
|
require 'resources/yaml'
|
|
158
169
|
require 'resources/csv'
|
|
159
170
|
require 'resources/ini'
|
|
171
|
+
require 'resources/xml'
|
|
@@ -52,11 +52,20 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
|
|
|
52
52
|
format_example(example).tap do |hash|
|
|
53
53
|
e = example.exception
|
|
54
54
|
next unless e
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
if example.metadata[:sensitive]
|
|
57
|
+
hash[:message] = '*** sensitive output suppressed ***'
|
|
58
|
+
else
|
|
59
|
+
hash[:message] = exception_message(e)
|
|
60
|
+
end
|
|
56
61
|
|
|
57
62
|
next if e.is_a? RSpec::Expectations::ExpectationNotMetError
|
|
58
63
|
hash[:exception] = e.class.name
|
|
59
64
|
hash[:backtrace] = e.backtrace
|
|
65
|
+
|
|
66
|
+
# if the exception indicates the resource author wants to skip the test,
|
|
67
|
+
# we update the test status here.
|
|
68
|
+
hash[:status] = 'skipped' if e.is_a?(Inspec::Exceptions::ResourceSkipped)
|
|
60
69
|
end
|
|
61
70
|
end
|
|
62
71
|
end
|
|
@@ -341,7 +350,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
341
350
|
# This method is called through the RSpec Formatter interface for every
|
|
342
351
|
# example found in the test suite.
|
|
343
352
|
#
|
|
344
|
-
# Within #format_example we are getting
|
|
353
|
+
# Within #format_example we are getting an example and:
|
|
345
354
|
# * if this is an example, within a control, within a profile then we want
|
|
346
355
|
# to display the profile header, display the control, and then display
|
|
347
356
|
# the example.
|
|
@@ -566,7 +575,8 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
566
575
|
end
|
|
567
576
|
|
|
568
577
|
def print_result(result)
|
|
569
|
-
|
|
578
|
+
test_skipped = result[:status] == 'skipped'
|
|
579
|
+
test_status = test_skipped ? 'skipped' : result[:status_type]
|
|
570
580
|
indicator = INDICATORS[result[:status]]
|
|
571
581
|
indicator = INDICATORS['empty'] if indicator.nil?
|
|
572
582
|
if result[:message]
|
|
@@ -613,10 +623,18 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
613
623
|
summary = profile_summary
|
|
614
624
|
return unless summary['total'] > 0
|
|
615
625
|
|
|
626
|
+
success_str = summary['passed'] == 1 ? '1 successful control' : "#{summary['passed']} successful controls"
|
|
627
|
+
failed_str = summary['failed']['total'] == 1 ? '1 control failure' : "#{summary['failed']['total']} control failures"
|
|
628
|
+
skipped_str = summary['skipped'] == 1 ? '1 control skipped' : "#{summary['skipped']} controls skipped"
|
|
629
|
+
|
|
630
|
+
success_color = summary['passed'] > 0 ? 'passed' : 'no_color'
|
|
631
|
+
failed_color = summary['failed']['total'] > 0 ? 'failed' : 'no_color'
|
|
632
|
+
skipped_color = summary['skipped'] > 0 ? 'skipped' : 'no_color'
|
|
633
|
+
|
|
616
634
|
s = format('Profile Summary: %s, %s, %s',
|
|
617
|
-
format_with_color(
|
|
618
|
-
format_with_color(
|
|
619
|
-
format_with_color(
|
|
635
|
+
format_with_color(success_color, success_str),
|
|
636
|
+
format_with_color(failed_color, failed_str),
|
|
637
|
+
format_with_color(skipped_color, skipped_str),
|
|
620
638
|
)
|
|
621
639
|
output.puts(s) if summary['total'] > 0
|
|
622
640
|
end
|
|
@@ -624,10 +642,16 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
624
642
|
def print_tests_summary
|
|
625
643
|
summary = tests_summary
|
|
626
644
|
|
|
645
|
+
failed_str = summary['failed'] == 1 ? '1 failure' : "#{summary['failed']} failures"
|
|
646
|
+
|
|
647
|
+
success_color = summary['passed'] > 0 ? 'passed' : 'no_color'
|
|
648
|
+
failed_color = summary['failed'] > 0 ? 'failed' : 'no_color'
|
|
649
|
+
skipped_color = summary['skipped'] > 0 ? 'skipped' : 'no_color'
|
|
650
|
+
|
|
627
651
|
s = format('Test Summary: %s, %s, %s',
|
|
628
|
-
format_with_color(
|
|
629
|
-
format_with_color(
|
|
630
|
-
format_with_color(
|
|
652
|
+
format_with_color(success_color, "#{summary['passed']} successful"),
|
|
653
|
+
format_with_color(failed_color, failed_str),
|
|
654
|
+
format_with_color(skipped_color, "#{summary['skipped']} skipped"),
|
|
631
655
|
)
|
|
632
656
|
|
|
633
657
|
output.puts(s)
|
|
@@ -802,7 +826,9 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
|
802
826
|
end
|
|
803
827
|
|
|
804
828
|
def update_summary(example)
|
|
805
|
-
|
|
829
|
+
test_skipped = example[:status] == 'skipped'
|
|
830
|
+
status_type = test_skipped ? 'skipped' : example[:status_type]
|
|
831
|
+
example_status = STATUS_TYPES[status_type]
|
|
806
832
|
@summary_status = example_status if example_status > @summary_status
|
|
807
833
|
fails.push(example) if example_status > 0
|
|
808
834
|
passes.push(example) if example_status == STATUS_TYPES['passed']
|
|
@@ -855,8 +881,9 @@ class InspecRspecJUnit < InspecRspecJson
|
|
|
855
881
|
private
|
|
856
882
|
|
|
857
883
|
def build_profile_xml(profile)
|
|
884
|
+
profile_name = profile[:name]
|
|
858
885
|
profile_xml = REXML::Element.new('testsuite')
|
|
859
|
-
profile_xml.add_attribute('name',
|
|
886
|
+
profile_xml.add_attribute('name', profile_name)
|
|
860
887
|
profile_xml.add_attribute('tests', count_profile_tests(profile))
|
|
861
888
|
profile_xml.add_attribute('failed', count_profile_failed_tests(profile))
|
|
862
889
|
|
|
@@ -864,17 +891,24 @@ class InspecRspecJUnit < InspecRspecJson
|
|
|
864
891
|
next if control[:results].nil?
|
|
865
892
|
|
|
866
893
|
control[:results].each do |result|
|
|
867
|
-
profile_xml.add(build_result_xml(control, result))
|
|
894
|
+
profile_xml.add(build_result_xml(profile_name, control, result))
|
|
868
895
|
end
|
|
869
896
|
end
|
|
870
897
|
|
|
871
898
|
profile_xml
|
|
872
899
|
end
|
|
873
900
|
|
|
874
|
-
def build_result_xml(control, result)
|
|
901
|
+
def build_result_xml(profile_name, control, result)
|
|
875
902
|
result_xml = REXML::Element.new('testcase')
|
|
876
903
|
result_xml.add_attribute('name', result[:code_desc])
|
|
877
|
-
|
|
904
|
+
# if there is no control title, we are likely receiving test results from a
|
|
905
|
+
# "naked" test (a test not located within a control block). Therefore, rather
|
|
906
|
+
# than outputting the auto-generated ID, i.e.
|
|
907
|
+
#
|
|
908
|
+
# "(generated from test_spec.rb:1 de0ce10e4bbbd4d0ff7a65f4234de8c1)")
|
|
909
|
+
#
|
|
910
|
+
# ... we'll output "Anonymous" instead.
|
|
911
|
+
result_xml.add_attribute('classname', control[:title].nil? ? "#{profile_name}.Anonymous" : "#{profile_name}.#{control[:id]}")
|
|
878
912
|
result_xml.add_attribute('time', result[:run_time])
|
|
879
913
|
|
|
880
914
|
if result[:status] == 'failed'
|