inspec 0.31.0 → 0.32.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 +4 -4
- data/CHANGELOG.md +56 -2
- data/Gemfile +6 -2
- data/MAINTAINERS.md +3 -1
- data/MAINTAINERS.toml +1 -1
- data/README.md +20 -1
- data/Rakefile +8 -0
- data/docs/cli.rst +18 -2
- data/docs/resources.rst +55 -3
- data/inspec.gemspec +2 -2
- data/lib/bundles/inspec-supermarket/api.rb +1 -0
- data/lib/fetchers/local.rb +12 -1
- data/lib/fetchers/tar.rb +4 -0
- data/lib/fetchers/url.rb +4 -0
- data/lib/inspec/base_cli.rb +17 -0
- data/lib/inspec/cli.rb +33 -12
- data/lib/inspec/dependencies/dependency_set.rb +50 -5
- data/lib/inspec/dependencies/lockfile.rb +94 -0
- data/lib/inspec/dependencies/requirement.rb +93 -53
- data/lib/inspec/dependencies/resolver.rb +53 -170
- data/lib/inspec/dependencies/vendor_index.rb +11 -4
- data/lib/inspec/dsl.rb +23 -15
- data/lib/inspec/errors.rb +1 -7
- data/lib/inspec/log.rb +2 -25
- data/lib/inspec/profile.rb +68 -28
- data/lib/inspec/profile_context.rb +28 -5
- data/lib/inspec/rspec_json_formatter.rb +48 -25
- data/lib/inspec/rule.rb +7 -0
- data/lib/inspec/runner.rb +26 -15
- data/lib/inspec/runner_rspec.rb +2 -6
- data/lib/inspec/shell.rb +35 -26
- data/lib/inspec/version.rb +2 -1
- data/lib/resources/host.rb +13 -6
- data/lib/resources/iis_site.rb +1 -0
- data/lib/resources/os.rb +1 -1
- data/lib/resources/package.rb +22 -6
- data/lib/resources/port.rb +1 -11
- data/lib/resources/service.rb +9 -0
- data/lib/resources/user.rb +8 -8
- metadata +14 -7
@@ -18,9 +18,9 @@ module Inspec
|
|
18
18
|
#
|
19
19
|
class VendorIndex
|
20
20
|
attr_reader :path
|
21
|
-
def initialize(path)
|
22
|
-
@path = path
|
23
|
-
FileUtils.mkdir_p(path) unless File.directory?(path)
|
21
|
+
def initialize(path = nil)
|
22
|
+
@path = path || File.join(Dir.home, '.inspec', 'cache')
|
23
|
+
FileUtils.mkdir_p(@path) unless File.directory?(@path)
|
24
24
|
end
|
25
25
|
|
26
26
|
def add(name, source, path_from)
|
@@ -42,7 +42,14 @@ module Inspec
|
|
42
42
|
path = base_path_for(name, source_url)
|
43
43
|
if File.directory?(path)
|
44
44
|
path
|
45
|
-
|
45
|
+
else
|
46
|
+
archive_entry_for(name, source_url)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def archive_entry_for(name, source_url)
|
51
|
+
path = base_path_for(name, source_url)
|
52
|
+
if File.exist?("#{path}.tar.gz")
|
46
53
|
"#{path}.tar.gz"
|
47
54
|
elsif File.exist?("#{path}.zip")
|
48
55
|
"#{path}.zip"
|
data/lib/inspec/dsl.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# copyright: 2015, Dominik Richter
|
3
|
-
# license: All rights reserved
|
4
3
|
# author: Dominik Richter
|
5
4
|
# author: Christoph Hartmann
|
5
|
+
require 'inspec/log'
|
6
6
|
|
7
7
|
module Inspec::DSL
|
8
8
|
def require_controls(id, &block)
|
@@ -32,28 +32,36 @@ module Inspec::DSL
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.load_spec_files_for_profile(bind_context, opts, &block)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
dependencies = opts[:dependencies]
|
36
|
+
profile_id = opts[:profile_id]
|
37
|
+
|
38
|
+
dep_entry = dependencies.list[profile_id]
|
39
|
+
if dep_entry.nil?
|
40
|
+
fail <<EOF
|
41
|
+
Cannot load #{profile_id} since it is not listed as a dependency
|
42
|
+
of #{bind_context.profile_name}.
|
43
|
+
|
44
|
+
Dependencies available from this context are:
|
45
|
+
|
46
|
+
#{dependencies.list.keys.join("\n ")}
|
47
|
+
EOF
|
48
|
+
end
|
49
|
+
|
50
|
+
context = load_profile_context(dep_entry.profile, opts[:backend])
|
40
51
|
|
41
52
|
# if we don't want all the rules, then just make 1 pass to get all rule_IDs
|
42
53
|
# that we want to keep from the original
|
43
|
-
filter_included_controls(context,
|
54
|
+
filter_included_controls(context, dep_entry.profile, &block) if !opts[:include_all]
|
44
55
|
|
45
56
|
# interpret the block and skip/modify as required
|
46
57
|
context.load(block) if block_given?
|
47
58
|
|
48
|
-
|
49
|
-
context.rules.values.each do |control|
|
50
|
-
bind_context.register_control(control)
|
51
|
-
end
|
59
|
+
bind_context.add_subcontext(context)
|
52
60
|
end
|
53
61
|
|
54
|
-
def self.filter_included_controls(context,
|
62
|
+
def self.filter_included_controls(context, profile, &block)
|
55
63
|
mock = Inspec::Backend.create({ backend: 'mock' })
|
56
|
-
include_ctx = Inspec::ProfileContext.
|
64
|
+
include_ctx = Inspec::ProfileContext.for_profile(profile, mock)
|
57
65
|
include_ctx.load(block) if block_given?
|
58
66
|
# remove all rules that were not registered
|
59
67
|
context.rules.keys.each do |id|
|
@@ -63,8 +71,8 @@ module Inspec::DSL
|
|
63
71
|
end
|
64
72
|
end
|
65
73
|
|
66
|
-
def self.load_profile_context(
|
67
|
-
ctx = Inspec::ProfileContext.
|
74
|
+
def self.load_profile_context(profile, backend)
|
75
|
+
ctx = Inspec::ProfileContext.for_profile(profile, backend)
|
68
76
|
profile.libraries.each do |path, content|
|
69
77
|
ctx.load(content.to_s, path, 1)
|
70
78
|
ctx.reload_dsl
|
data/lib/inspec/errors.rb
CHANGED
@@ -7,11 +7,5 @@ module Inspec
|
|
7
7
|
|
8
8
|
# dependency resolution
|
9
9
|
class CyclicDependencyError < Error; end
|
10
|
-
class
|
11
|
-
attr_reader :conflicts
|
12
|
-
def initialize(conflicts, msg = nil)
|
13
|
-
super(msg)
|
14
|
-
@conflicts = conflicts
|
15
|
-
end
|
16
|
-
end
|
10
|
+
class UnsatisfiedVersionSpecification < Error; end
|
17
11
|
end
|
data/lib/inspec/log.rb
CHANGED
@@ -1,34 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
# Copyright 2015 Dominik Richter. All rights reserved.
|
3
2
|
# author: Dominik Richter
|
4
3
|
# author: Christoph Hartmann
|
5
4
|
|
6
|
-
require '
|
5
|
+
require 'mixlib/log'
|
7
6
|
|
8
7
|
module Inspec
|
9
8
|
class Log
|
10
|
-
|
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
|
9
|
+
extend Mixlib::Log
|
33
10
|
end
|
34
11
|
end
|
data/lib/inspec/profile.rb
CHANGED
@@ -8,6 +8,7 @@ require 'inspec/polyfill'
|
|
8
8
|
require 'inspec/fetcher'
|
9
9
|
require 'inspec/source_reader'
|
10
10
|
require 'inspec/metadata'
|
11
|
+
require 'inspec/dependencies/lockfile'
|
11
12
|
require 'inspec/dependencies/dependency_set'
|
12
13
|
|
13
14
|
module Inspec
|
@@ -52,6 +53,10 @@ module Inspec
|
|
52
53
|
Metadata.finalize(@source_reader.metadata, @profile_id)
|
53
54
|
end
|
54
55
|
|
56
|
+
def name
|
57
|
+
metadata.params[:name]
|
58
|
+
end
|
59
|
+
|
55
60
|
def params
|
56
61
|
@params ||= load_params
|
57
62
|
end
|
@@ -211,6 +216,44 @@ module Inspec
|
|
211
216
|
@locked_dependencies ||= load_dependencies
|
212
217
|
end
|
213
218
|
|
219
|
+
def lockfile_exists?
|
220
|
+
File.exist?(lockfile_path)
|
221
|
+
end
|
222
|
+
|
223
|
+
def lockfile_path
|
224
|
+
File.join(cwd, 'inspec.lock')
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# TODO(ssd): Relative path handling really needs to be carefully
|
229
|
+
# thought through, especially with respect to relative paths in
|
230
|
+
# tarballs.
|
231
|
+
#
|
232
|
+
def cwd
|
233
|
+
@target.is_a?(String) && File.directory?(@target) ? @target : './'
|
234
|
+
end
|
235
|
+
|
236
|
+
def lockfile
|
237
|
+
@lockfile ||= if lockfile_exists?
|
238
|
+
Inspec::Lockfile.from_file(lockfile_path)
|
239
|
+
else
|
240
|
+
generate_lockfile
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
#
|
245
|
+
# Generate an in-memory lockfile. This won't render the lock file
|
246
|
+
# to disk, it must be explicitly written to disk by the caller.
|
247
|
+
#
|
248
|
+
# @param vendor_path [String] Path to the on-disk vendor dir
|
249
|
+
# @return [Inspec::Lockfile]
|
250
|
+
#
|
251
|
+
def generate_lockfile(vendor_path = nil)
|
252
|
+
res = Inspec::DependencySet.new(cwd, vendor_path)
|
253
|
+
res.vendor(metadata.dependencies)
|
254
|
+
Inspec::Lockfile.from_dependency_set(res)
|
255
|
+
end
|
256
|
+
|
214
257
|
private
|
215
258
|
|
216
259
|
# Create an archive name for this profile and an additional options
|
@@ -225,7 +268,7 @@ module Inspec
|
|
225
268
|
|
226
269
|
name = params[:name] ||
|
227
270
|
fail('Cannot create an archive without a profile name! Please '\
|
228
|
-
|
271
|
+
'specify the name in metadata or use --output to create the archive.')
|
229
272
|
ext = opts[:zip] ? 'zip' : 'tar.gz'
|
230
273
|
slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_')
|
231
274
|
Pathname.new(Dir.pwd).join("#{slug}.#{ext}")
|
@@ -239,34 +282,34 @@ module Inspec
|
|
239
282
|
params
|
240
283
|
end
|
241
284
|
|
285
|
+
#
|
286
|
+
# Returns a new runner for the current profile.
|
287
|
+
#
|
288
|
+
# @params [Symbol] The type of backend to use when constructing a
|
289
|
+
# new runner.
|
290
|
+
# @returns [Inspec::Runner]
|
291
|
+
#
|
292
|
+
def runner_for_profile(backend = :mock)
|
293
|
+
opts = @options.dup
|
294
|
+
opts[:ignore_supports] = true
|
295
|
+
r = Runner.new(id: @profile_id,
|
296
|
+
backend: backend,
|
297
|
+
test_collector: opts.delete(:test_collector))
|
298
|
+
r.add_profile(self, opts)
|
299
|
+
r
|
300
|
+
end
|
301
|
+
|
242
302
|
def load_checks_params(params)
|
243
303
|
params[:controls] = controls = {}
|
244
304
|
params[:groups] = groups = {}
|
245
305
|
prefix = @source_reader.target.prefix || ''
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
runner = Runner.new(
|
252
|
-
id: @profile_id,
|
253
|
-
backend: :mock,
|
254
|
-
test_collector: opts.delete(:test_collector),
|
255
|
-
)
|
256
|
-
runner.add_profile(self, opts)
|
257
|
-
runner.rules.values.each do |rule|
|
258
|
-
f = load_rule_filepath(prefix, rule)
|
259
|
-
load_rule(rule, f, controls, groups)
|
260
|
-
end
|
261
|
-
params[:attributes] = runner.attributes
|
262
|
-
else
|
263
|
-
# load from context
|
264
|
-
@runner_context.rules.values.each do |rule|
|
265
|
-
f = load_rule_filepath(prefix, rule)
|
266
|
-
load_rule(rule, f, controls, groups)
|
267
|
-
end
|
268
|
-
params[:attributes] = @runner_context.attributes
|
306
|
+
# Load from the runner_context if it exists
|
307
|
+
runner = @runner_context || runner_for_profile
|
308
|
+
runner.all_rules.each do |rule|
|
309
|
+
f = load_rule_filepath(prefix, rule)
|
310
|
+
load_rule(rule, f, controls, groups)
|
269
311
|
end
|
312
|
+
params[:attributes] = runner.attributes
|
270
313
|
params
|
271
314
|
end
|
272
315
|
|
@@ -297,10 +340,7 @@ module Inspec
|
|
297
340
|
end
|
298
341
|
|
299
342
|
def load_dependencies
|
300
|
-
|
301
|
-
res = Inspec::DependencySet.new(cwd, nil)
|
302
|
-
res.vendor(metadata.dependencies)
|
303
|
-
res
|
343
|
+
Inspec::DependencySet.from_lockfile(lockfile, cwd, nil)
|
304
344
|
end
|
305
345
|
end
|
306
346
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# author: Dominik Richter
|
3
3
|
# author: Christoph Hartmann
|
4
|
-
|
4
|
+
require 'inspec/log'
|
5
5
|
require 'inspec/rule'
|
6
6
|
require 'inspec/dsl'
|
7
7
|
require 'inspec/require_loader'
|
@@ -10,18 +10,21 @@ require 'inspec/objects/attribute'
|
|
10
10
|
|
11
11
|
module Inspec
|
12
12
|
class ProfileContext # rubocop:disable Metrics/ClassLength
|
13
|
-
|
14
|
-
|
13
|
+
def self.for_profile(profile, backend)
|
14
|
+
new(profile.name, backend, { 'profile' => profile })
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :attributes, :rules, :profile_id
|
15
18
|
def initialize(profile_id, backend, conf)
|
16
19
|
if backend.nil?
|
17
20
|
fail 'ProfileContext is initiated with a backend == nil. ' \
|
18
21
|
'This is a backend error which must be fixed upstream.'
|
19
22
|
end
|
20
|
-
|
21
23
|
@profile_id = profile_id
|
22
24
|
@backend = backend
|
23
25
|
@conf = conf.dup
|
24
26
|
@rules = {}
|
27
|
+
@subcontexts = []
|
25
28
|
@dependencies = {}
|
26
29
|
@dependencies = conf['profile'].locked_dependencies unless conf['profile'].nil?
|
27
30
|
@require_loader = ::Inspec::RequireLoader.new
|
@@ -35,6 +38,16 @@ module Inspec
|
|
35
38
|
@profile_context = ctx.new(@backend, @conf, @dependencies, @require_loader)
|
36
39
|
end
|
37
40
|
|
41
|
+
def all_rules
|
42
|
+
ret = @rules.values
|
43
|
+
ret += @subcontexts.map(&:all_rules).flatten
|
44
|
+
ret
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_subcontext(context)
|
48
|
+
@subcontexts << context
|
49
|
+
end
|
50
|
+
|
38
51
|
def load_libraries(libs)
|
39
52
|
lib_prefix = 'libraries' + File::SEPARATOR
|
40
53
|
autoloads = []
|
@@ -59,6 +72,7 @@ module Inspec
|
|
59
72
|
end
|
60
73
|
|
61
74
|
def load(content, source = nil, line = nil)
|
75
|
+
Inspec::Log.debug("Loading #{source || '<anonymous content>'} into #{self}")
|
62
76
|
@current_load = { file: source }
|
63
77
|
if content.is_a? Proc
|
64
78
|
@profile_context.instance_eval(&content)
|
@@ -167,7 +181,11 @@ module Inspec
|
|
167
181
|
end
|
168
182
|
|
169
183
|
def to_s
|
170
|
-
|
184
|
+
"Profile Context Run #{profile_name}"
|
185
|
+
end
|
186
|
+
|
187
|
+
define_method :profile_name do
|
188
|
+
profile_id
|
171
189
|
end
|
172
190
|
|
173
191
|
define_method :control do |*args, &block|
|
@@ -185,9 +203,14 @@ module Inspec
|
|
185
203
|
res = describe(*args, &block)
|
186
204
|
end
|
187
205
|
register_control(rule, &block)
|
206
|
+
|
188
207
|
res
|
189
208
|
end
|
190
209
|
|
210
|
+
define_method :add_subcontext do |context|
|
211
|
+
profile_context_owner.add_subcontext(context)
|
212
|
+
end
|
213
|
+
|
191
214
|
define_method :register_control do |control, &block|
|
192
215
|
::Inspec::Rule.set_skip_rule(control, true) if @skip_profile
|
193
216
|
|
@@ -210,12 +210,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
210
210
|
'passed' => ' ✔ ',
|
211
211
|
'unknown' => ' ? ',
|
212
212
|
'empty' => ' ',
|
213
|
-
|
214
|
-
|
215
|
-
TEST_INDICATORS = {
|
216
|
-
'failed' => ' fail: ',
|
217
|
-
'skipped' => ' skip: ',
|
218
|
-
'empty' => ' ',
|
213
|
+
'small' => ' ',
|
219
214
|
}.freeze
|
220
215
|
|
221
216
|
MULTI_TEST_CONTROL_SUMMARY_MAX_LEN = 60
|
@@ -223,18 +218,20 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
223
218
|
def initialize(*args)
|
224
219
|
@colors = COLORS
|
225
220
|
@indicators = INDICATORS
|
226
|
-
@test_indicators = TEST_INDICATORS
|
227
221
|
|
228
222
|
@format = '%color%indicator%id%summary'
|
229
223
|
@current_control = nil
|
230
224
|
@current_profile = nil
|
231
225
|
@missing_controls = []
|
226
|
+
@anonymous_tests = []
|
232
227
|
super(*args)
|
233
228
|
end
|
234
229
|
|
235
230
|
def close(_notification)
|
236
231
|
flush_current_control
|
237
232
|
output.puts('') unless @current_control.nil?
|
233
|
+
print_tests
|
234
|
+
output.puts('')
|
238
235
|
|
239
236
|
@profiles_info.each do |_id, profile|
|
240
237
|
next if profile[:already_printed]
|
@@ -274,13 +271,15 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
274
271
|
summary_status = STATUS_TYPES['unknown']
|
275
272
|
skips = []
|
276
273
|
fails = []
|
274
|
+
passes = []
|
277
275
|
@current_control[:results].each do |r|
|
278
276
|
i = STATUS_TYPES[r[:status_type]]
|
279
277
|
summary_status = i if i > summary_status
|
280
278
|
fails.push(r) if i > 0
|
279
|
+
passes.push(r) if i == STATUS_TYPES['passed']
|
281
280
|
skips.push(r) if i == STATUS_TYPES['skipped']
|
282
281
|
end
|
283
|
-
[fails, skips, STATUS_TYPES.key(summary_status)]
|
282
|
+
[fails, skips, passes, STATUS_TYPES.key(summary_status)]
|
284
283
|
end
|
285
284
|
|
286
285
|
def current_control_title
|
@@ -344,20 +343,42 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
344
343
|
lines.gsub(/\n/, "\n" + indentation)
|
345
344
|
end
|
346
345
|
|
347
|
-
def
|
346
|
+
def print_results(all)
|
348
347
|
all.each do |x|
|
349
|
-
|
350
|
-
|
348
|
+
test_status = x[:status_type]
|
349
|
+
test_color = @colors[test_status]
|
350
|
+
indicator = @indicators[x[:status]]
|
351
|
+
indicator = @indicators['empty'] if all.length == 1 || indicator.nil?
|
351
352
|
msg = x[:message] || x[:skip_message] || x[:code_desc]
|
352
353
|
print_line(
|
353
|
-
color:
|
354
|
-
indicator: indicator,
|
355
|
-
summary: format_lines(msg, @
|
354
|
+
color: test_color,
|
355
|
+
indicator: @indicators['small'] + indicator,
|
356
|
+
summary: format_lines(msg, @indicators['empty']),
|
356
357
|
id: nil, profile: nil
|
357
358
|
)
|
358
359
|
end
|
359
360
|
end
|
360
361
|
|
362
|
+
def print_tests
|
363
|
+
@anonymous_tests.each do |control|
|
364
|
+
control_result = control[:results]
|
365
|
+
title = control_result[0][:code_desc].split[0..1].join(' ')
|
366
|
+
puts ' ' + title
|
367
|
+
control_result.each do |test|
|
368
|
+
control_id = ''
|
369
|
+
test_result = test[:code_desc].split[2..-1].join(' ')
|
370
|
+
status_indicator = test[:status_type]
|
371
|
+
print_line(
|
372
|
+
color: @colors[status_indicator] || '',
|
373
|
+
indicator: @indicators['small'] + @indicators[status_indicator] || @indicators['unknown'],
|
374
|
+
summary: format_lines(test_result, @indicators['empty']),
|
375
|
+
id: control_id,
|
376
|
+
profile: control[:profile_id],
|
377
|
+
)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
361
382
|
def flush_current_control
|
362
383
|
return if @current_control.nil?
|
363
384
|
|
@@ -365,22 +386,24 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
365
386
|
@current_profile = @profiles_info[@current_control[:profile_id]]
|
366
387
|
print_current_profile if prev_profile != @current_profile
|
367
388
|
|
368
|
-
fails, skips, summary_indicator = current_control_infos
|
389
|
+
fails, skips, passes, summary_indicator = current_control_infos
|
369
390
|
summary = current_control_summary(fails, skips)
|
370
391
|
|
371
392
|
control_id = @current_control[:id].to_s
|
372
393
|
control_id += ': '
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
394
|
+
if control_id.start_with? '(generated from '
|
395
|
+
@anonymous_tests.push(@current_control)
|
396
|
+
else
|
397
|
+
print_line(
|
398
|
+
color: @colors[summary_indicator] || '',
|
399
|
+
indicator: @indicators[summary_indicator] || @indicators['unknown'],
|
400
|
+
summary: format_lines(summary, @indicators['empty']),
|
401
|
+
id: control_id,
|
402
|
+
profile: @current_control[:profile_id],
|
403
|
+
)
|
382
404
|
|
383
|
-
|
405
|
+
print_results(fails + skips + passes)
|
406
|
+
end
|
384
407
|
end
|
385
408
|
|
386
409
|
def print_target(before, after)
|