inspec-core 5.22.3 → 5.22.36
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -19
- data/inspec-core.gemspec +6 -4
- data/lib/inspec/cli.rb +22 -5
- data/lib/inspec/config.rb +32 -10
- data/lib/inspec/fetcher/git.rb +43 -28
- data/lib/inspec/formatters/base.rb +1 -1
- data/lib/inspec/profile.rb +340 -18
- data/lib/inspec/resources/host.rb +4 -16
- data/lib/inspec/resources/security_policy.rb +7 -2
- data/lib/inspec/rule.rb +5 -0
- data/lib/inspec/utils/profile_ast_helpers.rb +372 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec/waiver_file_reader.rb +5 -3
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +2 -2
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +7 -6
- data/lib/plugins/inspec-reporter-html2/templates/default.js +6 -6
- metadata +11 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbe23835cae58303aa3ac665ecd59e269752bbe4f6448e33db3219df0f04dbc8
|
4
|
+
data.tar.gz: 9a6d1e0e7758054f4d2746355a0c5538ede8af58a4220b3160d999f5e3093f34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c074f69651db5a586e4fee3b1a621ef9cc43d05634136dae9e0008a22c2d74d0515fe2938f83df42aace4ad089bbab3e95cf3d70bf3825b88aaca5e8e0696518
|
7
|
+
data.tar.gz: f01196343c6f7109cb44ced5f3a23a2b3f7b543f88a48d34d0b49c2b2a47dae31c599af4f51febca1afecbc1355f471c543c2ac8484c5c1a669ea4fec41cfe45
|
data/Gemfile
CHANGED
@@ -11,6 +11,10 @@ gem "inspec-bin", path: "./inspec-bin"
|
|
11
11
|
|
12
12
|
gem "ffi", ">= 1.9.14", "!= 1.13.0", "!= 1.14.2"
|
13
13
|
|
14
|
+
# We have a build issue 2023-11-13 with unf_ext 0.0.9 so we are pinning to 0.0.8.2
|
15
|
+
# See https://github.com/knu/ruby-unf_ext/issues/74 https://buildkite.com/chef/inspec-inspec-inspec-5-omnibus-release/builds/22
|
16
|
+
gem "unf_ext", "= 0.0.8.2"
|
17
|
+
|
14
18
|
# inspec tests depend text output that changed in the 3.10 release
|
15
19
|
# but our runtime dep is still 3.9+
|
16
20
|
gem "rspec", ">= 3.10"
|
@@ -25,11 +29,11 @@ end
|
|
25
29
|
group :test do
|
26
30
|
gem "chefstyle", "~> 2.2.2"
|
27
31
|
gem "concurrent-ruby", "~> 1.0"
|
28
|
-
gem "json_schemer", ">= 0.2.1", "< 0.
|
32
|
+
gem "json_schemer", ">= 0.2.1", "< 2.0.1"
|
29
33
|
gem "m"
|
30
34
|
gem "minitest-sprint", "~> 1.0"
|
31
35
|
gem "minitest", "5.15.0"
|
32
|
-
gem "mocha", "~>
|
36
|
+
gem "mocha", "~> 2.1"
|
33
37
|
gem "nokogiri", "~> 1.9"
|
34
38
|
gem "pry-byebug"
|
35
39
|
gem "pry", "~> 0.10"
|
@@ -47,20 +51,3 @@ end
|
|
47
51
|
group :deploy do
|
48
52
|
gem "inquirer"
|
49
53
|
end
|
50
|
-
|
51
|
-
group :kitchen do
|
52
|
-
gem "berkshelf"
|
53
|
-
|
54
|
-
# Chef 18 requires ruby 3
|
55
|
-
if Gem.ruby_version >= Gem::Version.new("3.0.0")
|
56
|
-
gem "chef", ">= 17.0"
|
57
|
-
else
|
58
|
-
# Ruby 2.7 presumably - TODO remove this when 2.7 is sunsetted
|
59
|
-
gem "chef", "~> 16.0"
|
60
|
-
end
|
61
|
-
|
62
|
-
gem "test-kitchen", ">= 2.8"
|
63
|
-
gem "kitchen-inspec", ">= 2.0"
|
64
|
-
gem "kitchen-dokken", ">= 2.11"
|
65
|
-
gem "git"
|
66
|
-
end
|
data/inspec-core.gemspec
CHANGED
@@ -25,13 +25,15 @@ Gem::Specification.new do |spec|
|
|
25
25
|
# Implementation dependencies
|
26
26
|
spec.add_dependency "chef-telemetry", "~> 1.0", ">= 1.0.8" # 1.0.8+ removes the http dep
|
27
27
|
spec.add_dependency "license-acceptance", ">= 0.2.13", "< 3.0"
|
28
|
-
|
28
|
+
# TODO: We should remove the thor pinning in next upcoming releases currently it's breaking our unit test in cli_args_test for aliases due to
|
29
|
+
# recent changes made in thor library REF: https://github.com/rails/thor/releases/tag/v1.3.0 & https://github.com/rails/thor/pull/800
|
30
|
+
spec.add_dependency "thor", ">= 0.20", "< 1.3.0"
|
29
31
|
spec.add_dependency "method_source", ">= 0.8", "< 2.0"
|
30
32
|
spec.add_dependency "rubyzip", ">= 1.2.2", "< 3.0"
|
31
|
-
spec.add_dependency "rspec", ">= 3.9", "<= 3.
|
33
|
+
spec.add_dependency "rspec", ">= 3.9", "<= 3.12"
|
32
34
|
spec.add_dependency "rspec-its", "~> 1.2"
|
33
35
|
spec.add_dependency "pry", "~> 0.13"
|
34
|
-
spec.add_dependency "hashie", ">= 3.4", "<
|
36
|
+
spec.add_dependency "hashie", ">= 3.4", "< 6.0"
|
35
37
|
spec.add_dependency "mixlib-log", "~> 3.0"
|
36
38
|
spec.add_dependency "sslshake", "~> 1.2"
|
37
39
|
spec.add_dependency "parallel", "~> 1.9"
|
@@ -41,7 +43,7 @@ Gem::Specification.new do |spec|
|
|
41
43
|
spec.add_dependency "tty-prompt", "~> 0.17"
|
42
44
|
spec.add_dependency "tomlrb", ">= 1.2", "< 2.1"
|
43
45
|
spec.add_dependency "addressable", "~> 2.4"
|
44
|
-
spec.add_dependency "parslet", ">= 1.5", "<
|
46
|
+
spec.add_dependency "parslet", ">= 1.5", "< 3.0" # Pinned < 2.0, see #5389
|
45
47
|
spec.add_dependency "semverse", "~> 3.0"
|
46
48
|
spec.add_dependency "multipart-post", "~> 2.0"
|
47
49
|
|
data/lib/inspec/cli.rb
CHANGED
@@ -68,8 +68,14 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
68
68
|
desc: "A list of controls to include. Ignore all other tests."
|
69
69
|
option :tags, type: :array,
|
70
70
|
desc: "A list of tags to filter controls and include only those. Ignore all other tests."
|
71
|
+
option :legacy_export, type: :boolean, default: false,
|
72
|
+
desc: "Run with legacy export."
|
71
73
|
profile_options
|
72
74
|
def json(target)
|
75
|
+
# Config initialisation is needed before deprecation warning can be issued
|
76
|
+
# Deprecator calls config get method to fetch the config value
|
77
|
+
# Without config initialisation, the config value is not set and hence calling config get through deprecator will set the value of config as blank, making options of json command inaccessible.
|
78
|
+
config
|
73
79
|
# This deprecation warning is ignored currently.
|
74
80
|
Inspec.deprecate(:renamed_to_inspec_export)
|
75
81
|
export(target, true)
|
@@ -86,6 +92,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
86
92
|
desc: "For --what=profile, a list of controls to include. Ignore all other tests."
|
87
93
|
option :tags, type: :array,
|
88
94
|
desc: "For --what=profile, a list of tags to filter controls and include only those. Ignore all other tests."
|
95
|
+
option :legacy_export, type: :boolean, default: false,
|
96
|
+
desc: "Run with legacy export."
|
89
97
|
profile_options
|
90
98
|
def export(target, as_json = false)
|
91
99
|
o = config
|
@@ -121,16 +129,17 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
121
129
|
|
122
130
|
case what
|
123
131
|
when "profile"
|
132
|
+
profile_info = o[:legacy_export] ? profile.info : profile.info_from_parse
|
124
133
|
if format == "json"
|
125
134
|
require "json" unless defined?(JSON)
|
126
135
|
# Write JSON
|
127
136
|
Inspec::Utils::JsonProfileSummary.produce_json(
|
128
|
-
info:
|
137
|
+
info: profile_info,
|
129
138
|
write_path: dst
|
130
139
|
)
|
131
140
|
elsif format == "yaml"
|
132
141
|
Inspec::Utils::YamlProfileSummary.produce_yaml(
|
133
|
-
info:
|
142
|
+
info: profile_info,
|
134
143
|
write_path: dst
|
135
144
|
)
|
136
145
|
end
|
@@ -152,6 +161,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
152
161
|
desc: "The output format to use. Valid values: `json` and `doc`. Default value: `doc`."
|
153
162
|
option :with_cookstyle, type: :boolean,
|
154
163
|
desc: "Enable or disable cookstyle checks.", default: false
|
164
|
+
option :legacy_check, type: :boolean, default: false,
|
165
|
+
desc: "Run with legacy check."
|
155
166
|
profile_options
|
156
167
|
def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
157
168
|
o = config
|
@@ -166,7 +177,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
166
177
|
|
167
178
|
# run check
|
168
179
|
profile = Inspec::Profile.for_target(path, o)
|
169
|
-
result = profile.check
|
180
|
+
result = o[:legacy_check] ? profile.legacy_check : profile.check
|
170
181
|
|
171
182
|
if o["format"] == "json"
|
172
183
|
puts JSON.generate(result)
|
@@ -244,6 +255,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
244
255
|
desc: "Fallback to using local archives if fetching fails."
|
245
256
|
option :ignore_errors, type: :boolean, default: false,
|
246
257
|
desc: "Ignore profile warnings."
|
258
|
+
option :check, type: :boolean, default: false,
|
259
|
+
desc: "Run profile check before archiving."
|
260
|
+
option :export, type: :boolean, default: false,
|
261
|
+
desc: "Export the profile to inspec.json and include in archive"
|
262
|
+
option :legacy_export, type: :boolean, default: false,
|
263
|
+
desc: "Export the profile in legacy mode to inspec.json and include in archive"
|
247
264
|
def archive(path, log_level = nil)
|
248
265
|
o = config
|
249
266
|
diagnose(o)
|
@@ -264,7 +281,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
264
281
|
o[:logger].warn "Archiving a profile that contains gem dependencies, but InSpec cannot package gems with the profile! Please archive your ~/.inspec/gems directory separately."
|
265
282
|
end
|
266
283
|
|
267
|
-
result = profile.check
|
284
|
+
result = profile.check if o[:check]
|
268
285
|
|
269
286
|
if result && !o[:ignore_errors] == false
|
270
287
|
o[:logger].info "Profile check failed. Please fix the profile before generating an archive."
|
@@ -514,7 +531,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
514
531
|
end
|
515
532
|
|
516
533
|
def run_command(opts)
|
517
|
-
runner = Inspec::Runner.new(
|
534
|
+
runner = Inspec::Runner.new(opts)
|
518
535
|
res = runner.eval_with_virtual_profile(opts[:command])
|
519
536
|
runner.load
|
520
537
|
|
data/lib/inspec/config.rb
CHANGED
@@ -448,6 +448,22 @@ module Inspec
|
|
448
448
|
# Reporter options may be defined top-level.
|
449
449
|
options.merge!(config_file_reporter_options)
|
450
450
|
|
451
|
+
# when sent reporter from compliance-mode (via chef-client), the reporter is a symbol
|
452
|
+
if @cli_opts.key?(:reporter) && @cli_opts["reporter"].nil?
|
453
|
+
@cli_opts["reporter"] = @cli_opts[:reporter]
|
454
|
+
@cli_opts.delete(:reporter)
|
455
|
+
elsif @cli_opts.key?(:reporter) && @cli_opts.key?("reporter") && @cli_opts["reporter"].is_a?(Array)
|
456
|
+
# combine reporter and "reporter" options into "reporter" option
|
457
|
+
@cli_opts["reporter"] = @cli_opts[:reporter] + @cli_opts["reporter"]
|
458
|
+
end
|
459
|
+
|
460
|
+
if @cli_opts["reporter"]
|
461
|
+
# Add reporter_cli_opts in options to capture reporter cli opts separately
|
462
|
+
options.merge!({ "reporter_cli_opts" => @cli_opts["reporter"] })
|
463
|
+
# Delete reporter from cli_opts to avoid direct merging of reporter info of cli and config
|
464
|
+
@cli_opts.delete("reporter")
|
465
|
+
end
|
466
|
+
|
451
467
|
# Highest precedence: merge in any options defined via the CLI
|
452
468
|
options.merge!(@cli_opts)
|
453
469
|
|
@@ -476,13 +492,13 @@ module Inspec
|
|
476
492
|
end
|
477
493
|
|
478
494
|
def finalize_parse_reporters(options) # rubocop:disable Metrics/AbcSize
|
479
|
-
#
|
480
|
-
options["
|
495
|
+
# Default to cli report for ad-hoc runners
|
496
|
+
options["reporter_cli_opts"] = ["cli"] if (options["reporter"].nil? || options["reporter"].empty?) && options["reporter_cli_opts"].nil?
|
481
497
|
|
482
|
-
#
|
483
|
-
if options["
|
498
|
+
# Parse out reporter_cli_opts to proper report format
|
499
|
+
if options["reporter_cli_opts"].is_a?(Array)
|
484
500
|
reports = {}
|
485
|
-
options["
|
501
|
+
options["reporter_cli_opts"].each do |report|
|
486
502
|
reporter_name, destination = report.split(":", 2)
|
487
503
|
if destination.nil? || destination.strip == "-"
|
488
504
|
reports[reporter_name] = { "stdout" => true }
|
@@ -494,7 +510,12 @@ module Inspec
|
|
494
510
|
reports[reporter_name]["target_id"] = options["target_id"] if options["target_id"]
|
495
511
|
end
|
496
512
|
end
|
497
|
-
|
513
|
+
|
514
|
+
if options["reporter"].nil? || options["reporter"].empty?
|
515
|
+
options["reporter"] = reports
|
516
|
+
else
|
517
|
+
options["reporter"].merge!(reports)
|
518
|
+
end
|
498
519
|
end
|
499
520
|
|
500
521
|
# add in stdout if not specified
|
@@ -507,6 +528,10 @@ module Inspec
|
|
507
528
|
end
|
508
529
|
|
509
530
|
validate_reporters!(options["reporter"])
|
531
|
+
|
532
|
+
# Delete reporter_cli_opts after graceful merging of cli and config reporters
|
533
|
+
options.delete("reporter_cli_opts")
|
534
|
+
|
510
535
|
options
|
511
536
|
end
|
512
537
|
|
@@ -548,15 +573,12 @@ module Inspec
|
|
548
573
|
class Defaults
|
549
574
|
DEFAULTS = {
|
550
575
|
exec: {
|
551
|
-
"reporter" => ["cli"],
|
552
576
|
"show_progress" => false,
|
553
577
|
"color" => true,
|
554
578
|
"create_lockfile" => true,
|
555
579
|
"backend_cache" => true,
|
556
580
|
},
|
557
|
-
shell: {
|
558
|
-
"reporter" => ["cli"],
|
559
|
-
},
|
581
|
+
shell: {},
|
560
582
|
}.freeze
|
561
583
|
|
562
584
|
def self.for_command(command_name)
|
data/lib/inspec/fetcher/git.rb
CHANGED
@@ -41,6 +41,7 @@ module Inspec::Fetcher
|
|
41
41
|
@ref = opts[:ref]
|
42
42
|
@remote_url = expand_local_path(remote_url)
|
43
43
|
@repo_directory = nil
|
44
|
+
@resolved_ref = nil
|
44
45
|
@relative_path = opts[:relative_path] if opts[:relative_path] && !opts[:relative_path].empty?
|
45
46
|
end
|
46
47
|
|
@@ -70,7 +71,7 @@ module Inspec::Fetcher
|
|
70
71
|
if @relative_path
|
71
72
|
perform_relative_path_fetch(destination_path, working_dir)
|
72
73
|
else
|
73
|
-
Inspec::Log.debug("Checkout of #{resolved_ref} successful. " \
|
74
|
+
Inspec::Log.debug("Checkout of #{resolved_ref.nil? ? @remote_url : resolved_ref} successful. " \
|
74
75
|
"Moving checkout to #{destination_path}")
|
75
76
|
FileUtils.cp_r(working_dir + "/.", destination_path)
|
76
77
|
end
|
@@ -80,14 +81,14 @@ module Inspec::Fetcher
|
|
80
81
|
end
|
81
82
|
|
82
83
|
def perform_relative_path_fetch(destination_path, working_dir)
|
83
|
-
Inspec::Log.debug("Checkout of #{resolved_ref} successful. " \
|
84
|
+
Inspec::Log.debug("Checkout of #{resolved_ref.nil? ? @remote_url : resolved_ref} successful. " \
|
84
85
|
"Moving #{@relative_path} to #{destination_path}")
|
85
86
|
unless File.exist?("#{working_dir}/#{@relative_path}")
|
86
87
|
# Cleanup the destination path - otherwise we'll have an empty dir
|
87
88
|
# in the cache, which is enough to confuse the cache reader
|
88
89
|
# This is a courtesy, assuming we're writing to the cache; if we're
|
89
90
|
# vendoring to something more complex, don't bother.
|
90
|
-
FileUtils.
|
91
|
+
FileUtils.rm_r(destination_path) if Dir.exist?(destination_path)
|
91
92
|
|
92
93
|
raise Inspec::FetcherFailure, "Cannot find relative path '#{@relative_path}' " \
|
93
94
|
"within profile in git repo specified by '#{@remote_url}'"
|
@@ -96,9 +97,16 @@ module Inspec::Fetcher
|
|
96
97
|
end
|
97
98
|
|
98
99
|
def cache_key
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
cache_key = if @relative_path && !resolved_ref.nil?
|
101
|
+
OpenSSL::Digest.hexdigest("SHA256", resolved_ref + @relative_path)
|
102
|
+
elsif @relative_path && resolved_ref.nil?
|
103
|
+
OpenSSL::Digest.hexdigest("SHA256", @remote_url + @relative_path)
|
104
|
+
elsif resolved_ref.nil?
|
105
|
+
OpenSSL::Digest.hexdigest("SHA256", @remote_url)
|
106
|
+
else
|
107
|
+
resolved_ref
|
108
|
+
end
|
109
|
+
cache_key
|
102
110
|
end
|
103
111
|
|
104
112
|
def archive_path
|
@@ -106,7 +114,11 @@ module Inspec::Fetcher
|
|
106
114
|
end
|
107
115
|
|
108
116
|
def resolved_source
|
109
|
-
|
117
|
+
if resolved_ref.nil?
|
118
|
+
source = { git: @remote_url }
|
119
|
+
else
|
120
|
+
source = { git: @remote_url, ref: resolved_ref }
|
121
|
+
end
|
110
122
|
source[:relative_path] = @relative_path if @relative_path
|
111
123
|
source
|
112
124
|
end
|
@@ -125,33 +137,27 @@ module Inspec::Fetcher
|
|
125
137
|
elsif @tag
|
126
138
|
resolve_ref(@tag)
|
127
139
|
else
|
128
|
-
resolve_ref
|
140
|
+
resolve_ref
|
129
141
|
end
|
130
142
|
end
|
131
143
|
|
132
|
-
def
|
133
|
-
command_string =
|
144
|
+
def resolve_ref(ref_name = nil)
|
145
|
+
command_string = if ref_name.nil?
|
146
|
+
# Running git ls-remote command helps to raise error if git URL is invalid and avoids cache_key creation
|
147
|
+
"git ls-remote \"#{@remote_url}\""
|
148
|
+
else
|
149
|
+
"git ls-remote \"#{@remote_url}\" \"#{ref_name}*\""
|
150
|
+
end
|
134
151
|
cmd = shellout(command_string)
|
135
|
-
unless cmd.exitstatus == 0
|
136
|
-
|
152
|
+
raise(Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - error running '#{command_string}': #{cmd.stderr}") unless cmd.exitstatus == 0
|
153
|
+
|
154
|
+
if ref_name.nil?
|
155
|
+
ref = nil
|
137
156
|
else
|
138
|
-
ref = cmd.stdout
|
157
|
+
ref = parse_ls_remote(cmd.stdout, ref_name)
|
139
158
|
unless ref
|
140
|
-
raise
|
159
|
+
raise Inspec::FetcherFailure, "Profile git dependency failed - unable to resolve #{ref_name} to a specific git commit for #{@remote_url}"
|
141
160
|
end
|
142
|
-
|
143
|
-
ref
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def resolve_ref(ref_name)
|
148
|
-
command_string = "git ls-remote \"#{@remote_url}\" \"#{ref_name}*\""
|
149
|
-
cmd = shellout(command_string)
|
150
|
-
raise(Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - error running '#{command_string}': #{cmd.stderr}") unless cmd.exitstatus == 0
|
151
|
-
|
152
|
-
ref = parse_ls_remote(cmd.stdout, ref_name)
|
153
|
-
unless ref
|
154
|
-
raise Inspec::FetcherFailure, "Profile git dependency failed - unable to resolve #{ref_name} to a specific git commit for #{@remote_url}"
|
155
161
|
end
|
156
162
|
|
157
163
|
ref
|
@@ -200,7 +206,14 @@ module Inspec::Fetcher
|
|
200
206
|
|
201
207
|
def checkout(dir = @repo_directory)
|
202
208
|
clone(dir)
|
203
|
-
|
209
|
+
# In case of branch, tag or git reference is not provided by User the resolved_ref will always be nil
|
210
|
+
# and will always checkout the default HEAD branch, else it will checkout specific branch, tag or git reference.
|
211
|
+
if resolved_ref.nil?
|
212
|
+
git_cmd("checkout", dir)
|
213
|
+
else
|
214
|
+
git_cmd("checkout #{resolved_ref}", dir)
|
215
|
+
end
|
216
|
+
|
204
217
|
@repo_directory
|
205
218
|
end
|
206
219
|
|
@@ -208,6 +221,8 @@ module Inspec::Fetcher
|
|
208
221
|
cmd = shellout("git #{cmd}", cwd: dir)
|
209
222
|
cmd.error!
|
210
223
|
cmd.status
|
224
|
+
rescue Mixlib::ShellOut::ShellCommandFailed => e
|
225
|
+
raise Inspec::FetcherFailure, "Error while running git command. #{e.message} "
|
211
226
|
rescue Errno::ENOENT
|
212
227
|
raise Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - to use git sources, you must have git installed."
|
213
228
|
end
|
@@ -160,7 +160,7 @@ module Inspec::Formatters
|
|
160
160
|
end
|
161
161
|
|
162
162
|
# added this additionally because stats summary is also used for determining exit code in runner rspec
|
163
|
-
skipped += 1 if control[:results].any? { |r| r[:status] == "skipped" }
|
163
|
+
skipped += 1 if control[:results] && (control[:results].any? { |r| r[:status] == "skipped" })
|
164
164
|
|
165
165
|
end
|
166
166
|
total = error + not_applicable + not_reviewed + failed + passed
|