inspec-core 5.18.14 → 5.21.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +19 -16
  3. data/inspec-core.gemspec +22 -22
  4. data/lib/inspec/base_cli.rb +2 -0
  5. data/lib/inspec/cli.rb +6 -2
  6. data/lib/inspec/dsl.rb +10 -4
  7. data/lib/inspec/enhanced_outcomes.rb +19 -0
  8. data/lib/inspec/env_printer.rb +1 -1
  9. data/lib/inspec/exceptions.rb +2 -0
  10. data/lib/inspec/formatters/base.rb +69 -16
  11. data/lib/inspec/plugin/v2/loader.rb +19 -8
  12. data/lib/inspec/plugin/v2/plugin_types/reporter.rb +1 -0
  13. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +54 -0
  14. data/lib/inspec/reporters/base.rb +1 -0
  15. data/lib/inspec/reporters/cli.rb +94 -3
  16. data/lib/inspec/reporters/json.rb +3 -1
  17. data/lib/inspec/reporters/yaml.rb +3 -1
  18. data/lib/inspec/reporters.rb +2 -1
  19. data/lib/inspec/resources/file.rb +1 -1
  20. data/lib/inspec/resources/http.rb +2 -2
  21. data/lib/inspec/resources/lxc.rb +65 -9
  22. data/lib/inspec/resources/oracledb_session.rb +13 -4
  23. data/lib/inspec/resources/podman.rb +353 -0
  24. data/lib/inspec/resources/podman_container.rb +84 -0
  25. data/lib/inspec/resources/podman_image.rb +108 -0
  26. data/lib/inspec/resources/podman_network.rb +81 -0
  27. data/lib/inspec/resources/podman_pod.rb +101 -0
  28. data/lib/inspec/resources/podman_volume.rb +87 -0
  29. data/lib/inspec/resources/service.rb +1 -1
  30. data/lib/inspec/rule.rb +54 -17
  31. data/lib/inspec/run_data/control.rb +6 -0
  32. data/lib/inspec/run_data/statistics.rb +8 -2
  33. data/lib/inspec/runner.rb +18 -8
  34. data/lib/inspec/runner_rspec.rb +3 -2
  35. data/lib/inspec/schema/exec_json.rb +78 -2
  36. data/lib/inspec/schema/output_schema.rb +4 -1
  37. data/lib/inspec/schema/profile_json.rb +46 -0
  38. data/lib/inspec/schema.rb +91 -0
  39. data/lib/inspec/utils/convert.rb +8 -0
  40. data/lib/inspec/utils/podman.rb +24 -0
  41. data/lib/inspec/utils/waivers/csv_file_reader.rb +34 -0
  42. data/lib/inspec/utils/waivers/excel_file_reader.rb +39 -0
  43. data/lib/inspec/utils/waivers/json_file_reader.rb +15 -0
  44. data/lib/inspec/version.rb +1 -1
  45. data/lib/inspec/waiver_file_reader.rb +61 -0
  46. data/lib/matchers/matchers.rb +7 -1
  47. data/lib/plugins/inspec-init/templates/profiles/alicloud/README.md +27 -0
  48. data/lib/plugins/inspec-init/templates/profiles/alicloud/controls/example.rb +10 -0
  49. data/lib/plugins/inspec-init/templates/profiles/alicloud/inputs.yml +1 -0
  50. data/lib/plugins/inspec-init/templates/profiles/alicloud/inspec.yml +14 -0
  51. data/lib/plugins/inspec-reporter-html2/README.md +1 -1
  52. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +7 -1
  53. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +10 -6
  54. data/lib/plugins/inspec-reporter-html2/templates/default.css +12 -0
  55. data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +7 -1
  56. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +5 -2
  57. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +39 -13
  58. metadata +25 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba97ee3e25e02fba9b85f797fa1a773d2e8fa7628112be422d187c0e46cfce40
4
- data.tar.gz: a82f51e5c6ba3e1db8c4580f92a33d91bcc2e0c2a8339850e1f168c1569b8818
3
+ metadata.gz: 7f6f86730baadd988c363a823e5e46ba68c318e48b27bbaaba614ce7c33bb71c
4
+ data.tar.gz: f02ca42e9d4e525b82e9cfa61a04b122dc3b09c1e3c96f2ec4440c3fb3053f25
5
5
  SHA512:
6
- metadata.gz: beb78893376f0b92b9cf33741f8fab471f66dd328fa1f28c87b05737da7f73266327eded07ca67f0b91a747625dfcb61011b42c06a25914d01f727b07a962456
7
- data.tar.gz: 193aeef4576e2af4466a176b80e107cd45fe0c0c0f6db290d17696cec1eb7dbf6b58b555bf8c0df4b9bb4a9d5aa8d26f542993b5be1d600a296c3ec50e531d09
6
+ metadata.gz: 597fed943b00ed280a749f05c97d01e29b4d5a85034835e9f242990588b35b740bf56a886b65202777d68b79b5cbd1faf1a48a69c1a4f1f8a00a83ecdece3527
7
+ data.tar.gz: 13aa012d46677b3cd31614dcc118c62b1aa1656f3007d6b105f7822a3c25a45b1b1284eef9fb9e761cf5a03cd8209ffe8518d54ed355c189a2f141788a000694
data/Gemfile CHANGED
@@ -23,9 +23,8 @@ group :omnibus do
23
23
  end
24
24
 
25
25
  group :test do
26
- gem "chefstyle", "~> 2.0.3"
26
+ gem "chefstyle", "~> 2.2.2"
27
27
  gem "concurrent-ruby", "~> 1.0"
28
- gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows
29
28
  gem "json_schemer", ">= 0.2.1", "< 0.2.19"
30
29
  gem "m"
31
30
  gem "minitest-sprint", "~> 1.0"
@@ -39,26 +38,30 @@ group :test do
39
38
  gem "simplecov", "~> 0.21"
40
39
  gem "simplecov_json_formatter"
41
40
  gem "webmock", "~> 3.0"
41
+
42
+ if Gem.ruby_version >= Gem::Version.new("3.0.0")
43
+ # html-proofer has a dep on io-event, which is ruby-3 only
44
+ gem "html-proofer", "~> 3.19.4", platforms: :ruby # do not attempt to run proofer on windows. Pinned to 3.19.4 as test is breaking in updated versions.
45
+ end
42
46
  end
43
47
 
44
48
  group :deploy do
45
49
  gem "inquirer"
46
50
  end
47
51
 
48
- # Only include Test Kitchen support if we are on Ruby 2.7 or higher
49
- # as chef-zero support requires Ruby 2.6
50
- # See https://github.com/inspec/inspec/pull/5341
51
- if Gem.ruby_version >= Gem::Version.new("2.7.0")
52
- group :kitchen do
53
- gem "berkshelf"
54
- gem "chef", ">= 16.0" # Required to allow net-ssh > 6
55
- gem "test-kitchen", ">= 2.8"
56
- gem "kitchen-inspec", ">= 2.0"
57
- gem "kitchen-dokken", ">= 2.11"
58
- gem "git"
52
+ group :kitchen do
53
+ gem "berkshelf"
54
+
55
+ # Chef 18 requires ruby 3
56
+ if Gem.ruby_version >= Gem::Version.new("3.0.0")
57
+ gem "chef", ">= 17.0"
58
+ else
59
+ # Ruby 2.7 presumably - TODO remove this when 2.7 is sunsetted
60
+ gem "chef", "~> 16.0"
59
61
  end
60
- end
61
62
 
62
- if Gem.ruby_version < Gem::Version.new("2.7.0")
63
- gem "activesupport", "6.1.4.4"
63
+ gem "test-kitchen", ">= 2.8"
64
+ gem "kitchen-inspec", ">= 2.0"
65
+ gem "kitchen-dokken", ">= 2.11"
66
+ gem "git"
64
67
  end
data/inspec-core.gemspec CHANGED
@@ -18,32 +18,32 @@ Gem::Specification.new do |spec|
18
18
  # the gemfile and gemspec are necessary for appbundler so don't remove it
19
19
  spec.files =
20
20
  Dir.glob("{{lib,etc}/**/*,LICENSE,Gemfile,inspec-core.gemspec}")
21
- .grep_v(%r{(?<!inspec-init/templates/profiles/)(aws|azure|gcp)})
21
+ .grep_v(%r{(?<!inspec-init/templates/profiles/)(aws|azure|gcp|alicloud)})
22
22
  .grep_v(%r{lib/plugins/.*/test/})
23
23
  .reject { |f| File.directory?(f) }
24
24
 
25
25
  # Implementation dependencies
26
- spec.add_dependency "chef-telemetry", "~> 1.0", ">= 1.0.8" # 1.0.8+ removes the http dep
27
- spec.add_dependency "license-acceptance", ">= 0.2.13", "< 3.0"
28
- spec.add_dependency "thor", ">= 0.20", "< 2.0"
29
- spec.add_dependency "method_source", ">= 0.8", "< 2.0"
30
- spec.add_dependency "rubyzip", ">= 1.2.2", "< 3.0"
31
- spec.add_dependency "rspec", ">= 3.9", "<= 3.11"
32
- spec.add_dependency "rspec-its", "~> 1.2"
33
- spec.add_dependency "pry", "~> 0.13"
34
- spec.add_dependency "hashie", ">= 3.4", "< 5.0"
35
- spec.add_dependency "mixlib-log", "~> 3.0"
36
- spec.add_dependency "sslshake", "~> 1.2"
37
- spec.add_dependency "parallel", "~> 1.9"
38
- spec.add_dependency "faraday", ">= 0.9.0", "< 1.5"
39
- spec.add_dependency "faraday_middleware", "~> 1.0"
40
- spec.add_dependency "tty-table", "~> 0.10"
41
- spec.add_dependency "tty-prompt", "~> 0.17"
42
- spec.add_dependency "tomlrb", ">= 1.2", "< 2.1"
43
- spec.add_dependency "addressable", "~> 2.4"
44
- spec.add_dependency "parslet", ">= 1.5", "< 2.0" # Pinned < 2.0, see #5389
45
- spec.add_dependency "semverse", "~> 3.0"
46
- spec.add_dependency "multipart-post", "~> 2.0"
26
+ spec.add_dependency "chef-telemetry", "~> 1.0", ">= 1.0.8" # 1.0.8+ removes the http dep
27
+ spec.add_dependency "license-acceptance", ">= 0.2.13", "< 3.0"
28
+ spec.add_dependency "thor", ">= 0.20", "< 2.0"
29
+ spec.add_dependency "method_source", ">= 0.8", "< 2.0"
30
+ spec.add_dependency "rubyzip", ">= 1.2.2", "< 3.0"
31
+ spec.add_dependency "rspec", ">= 3.9", "<= 3.11"
32
+ spec.add_dependency "rspec-its", "~> 1.2"
33
+ spec.add_dependency "pry", "~> 0.13"
34
+ spec.add_dependency "hashie", ">= 3.4", "< 5.0"
35
+ spec.add_dependency "mixlib-log", "~> 3.0"
36
+ spec.add_dependency "sslshake", "~> 1.2"
37
+ spec.add_dependency "parallel", "~> 1.9"
38
+ spec.add_dependency "faraday", ">= 1", "< 3"
39
+ spec.add_dependency "faraday-follow_redirects", "~> 0.3"
40
+ spec.add_dependency "tty-table", "~> 0.10"
41
+ spec.add_dependency "tty-prompt", "~> 0.17"
42
+ spec.add_dependency "tomlrb", ">= 1.2", "< 2.1"
43
+ spec.add_dependency "addressable", "~> 2.4"
44
+ spec.add_dependency "parslet", ">= 1.5", "< 2.0" # Pinned < 2.0, see #5389
45
+ spec.add_dependency "semverse", "~> 3.0"
46
+ spec.add_dependency "multipart-post", "~> 2.0"
47
47
 
48
48
  spec.add_dependency "train-core", "~> 3.10"
49
49
  end
@@ -205,6 +205,8 @@ module Inspec
205
205
  long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error."
206
206
  option :reporter_include_source, type: :boolean, default: false,
207
207
  desc: "Include full source code of controls in the CLI report"
208
+ option :enhanced_outcomes, type: :boolean,
209
+ desc: "Show enhanced outcomes in output"
208
210
  end
209
211
 
210
212
  def self.help(*args)
data/lib/inspec/cli.rb CHANGED
@@ -415,6 +415,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
415
415
  desc: "Load one or more input files, a YAML file with values for the shell to use"
416
416
  option :input, type: :array, banner: "name1=value1 name2=value2",
417
417
  desc: "Specify one or more inputs directly on the command line to the shell, as --input NAME=VALUE. Accepts single-quoted YAML and JSON structures."
418
+ option :enhanced_outcomes, type: :boolean,
419
+ desc: "Show enhanced outcomes in output"
418
420
  def shell_func
419
421
  o = config
420
422
  deprecate_target_id(config)
@@ -461,11 +463,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
461
463
  pretty_handle_exception(e)
462
464
  end
463
465
 
466
+ option :enhanced_outcomes, type: :boolean,
467
+ desc: "Show enhanced outcomes output"
464
468
  desc "schema NAME", "print the JSON schema", hide: true
465
469
  def schema(name)
466
470
  require "inspec/schema/output_schema"
467
-
468
- puts Inspec::Schema::OutputSchema.json(name)
471
+ o = config
472
+ puts Inspec::Schema::OutputSchema.json(name, o)
469
473
  rescue StandardError => e
470
474
  puts e
471
475
  puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}"
data/lib/inspec/dsl.rb CHANGED
@@ -91,13 +91,19 @@ module Inspec::DSL
91
91
  if profile_version
92
92
  new_profile_id = "#{profile_id}-#{profile_version}"
93
93
  else
94
+ # This scary regex is used to match version following semantic Versioning (SemVer). Thanks to https://ihateregex.io/expr/semver/
95
+ regex_for_semver = /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/
94
96
  dependencies.list.keys.each do |key|
95
- # If dep profile does not contain a source version, key does not contain a version as well. In that case new_profile_id will be always nil and instead profile_id would be used to fetch profile from dependency list.
96
- profile_id_key = key.split("-")
97
- profile_id_key.pop
98
- new_profile_id = key if profile_id_key.join("-") == profile_id
97
+ # 1. Fetching VERSION from a profile dependency name which is in a format NAME-VERSION.
98
+ # 2. Matching original profile dependency name with profile name used with include or require control DSL.
99
+ fetching_semver = key.match(regex_for_semver).to_s
100
+ unless fetching_semver.nil? || fetching_semver.empty?
101
+ profile_id_key = key.split("-#{fetching_semver}")[0]
102
+ new_profile_id = key if profile_id_key == profile_id
103
+ end
99
104
  end
100
105
  end
106
+ # If dep profile does not contain a source version, key does not contain a version as well. In that case new_profile_id will be always nil and instead profile_id would be used to fetch profile from dependency list.
101
107
  dep_entry = new_profile_id ? dependencies.list[new_profile_id] : dependencies.list[profile_id]
102
108
 
103
109
  if dep_entry.nil?
@@ -0,0 +1,19 @@
1
+ module Inspec
2
+ module EnhancedOutcomes
3
+
4
+ def self.determine_status(results, impact)
5
+ # No-op exception occurs in case of not_applicable_if
6
+ if results.any? { |r| !r[:exception].nil? && !r[:backtrace].nil? && r[:resource_class] != "noop" }
7
+ "error"
8
+ elsif !impact.nil? && impact.to_f == 0.0
9
+ "not_applicable"
10
+ elsif results.all? { |r| r[:status] == "skipped" }
11
+ "not_reviewed"
12
+ elsif results.any? { |r| r[:status] == "failed" }
13
+ "failed"
14
+ else
15
+ "passed"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -35,7 +35,7 @@ module Inspec
35
35
  private
36
36
 
37
37
  def print_completion_for_shell
38
- erb = ERB.new(File.read(completion_template_path), nil, "-")
38
+ erb = ERB.new(File.read(completion_template_path), trim_mode: "-")
39
39
  puts erb.result(TemplateContext.new(@command_class).get_bindings)
40
40
  end
41
41
 
@@ -10,5 +10,7 @@ module Inspec
10
10
  class SecretsBackendNotFound < ArgumentError; end
11
11
  class ProfileValidationKeyNotFound < ArgumentError; end
12
12
  class ProfileSigningKeyNotFound < ArgumentError; end
13
+ class WaiversFileNotReadable < ArgumentError; end
14
+ class WaiversFileDoesNotExist < ArgumentError; end
13
15
  end
14
16
  end
@@ -1,12 +1,13 @@
1
1
  require "rspec/core"
2
2
  require "rspec/core/formatters/base_formatter"
3
3
  require "set" unless defined?(Set)
4
+ require "inspec/enhanced_outcomes"
4
5
 
5
6
  module Inspec::Formatters
6
7
  class Base < RSpec::Core::Formatters::BaseFormatter
7
8
  RSpec::Core::Formatters.register self, :close, :dump_summary, :stop
8
9
 
9
- attr_accessor :backend, :run_data
10
+ attr_accessor :backend, :run_data, :enhanced_outcomes
10
11
 
11
12
  def initialize(output)
12
13
  super(output)
@@ -17,6 +18,7 @@ module Inspec::Formatters
17
18
  @backend = nil
18
19
  @all_controls_count = nil
19
20
  @control_checks_count_map = {}
21
+ @enhanced_outcomes = nil
20
22
  end
21
23
 
22
24
  # RSpec Override: #dump_summary
@@ -50,7 +52,6 @@ module Inspec::Formatters
50
52
  else
51
53
  hash[:message] = exception_message(e)
52
54
  end
53
-
54
55
  next if e.is_a? RSpec::Expectations::ExpectationNotMetError
55
56
 
56
57
  hash[:exception] = e.class.name
@@ -68,6 +69,8 @@ module Inspec::Formatters
68
69
  # flesh out the profiles key with additional profile information
69
70
  run_data[:profiles] = profiles_info
70
71
 
72
+ add_enhanced_outcomes_to_controls if enhanced_outcomes
73
+
71
74
  # add the platform information for this particular target
72
75
  run_data[:platform] = {
73
76
  name: platform(:name),
@@ -110,6 +113,20 @@ module Inspec::Formatters
110
113
 
111
114
  private
112
115
 
116
+ def add_enhanced_outcomes_to_controls
117
+ all_unique_controls.each do |control|
118
+ control[:status] = determine_control_enhanced_outcome(control)
119
+ end
120
+ end
121
+
122
+ def determine_control_enhanced_outcome(control)
123
+ if control[:results]
124
+ Inspec::EnhancedOutcomes.determine_status(control[:results], control[:impact])
125
+ else
126
+ "passed"
127
+ end
128
+ end
129
+
113
130
  def all_unique_controls
114
131
  unique_controls = Set.new
115
132
  run_data[:profiles].each do |profile|
@@ -120,25 +137,59 @@ module Inspec::Formatters
120
137
  end
121
138
 
122
139
  def statistics
140
+ error = 0
141
+ not_applicable = 0
142
+ not_reviewed = 0
123
143
  failed = 0
124
- skipped = 0
125
144
  passed = 0
145
+ skipped = 0
146
+ enhanced_outcomes_summary = {}
147
+ if enhanced_outcomes
148
+ all_unique_controls.each do |control|
149
+
150
+ if control[:status] == "error"
151
+ error += 1
152
+ elsif control[:status] == "not_applicable"
153
+ not_applicable += 1
154
+ elsif control[:status] == "not_reviewed"
155
+ not_reviewed += 1
156
+ elsif control[:status] == "failed"
157
+ failed += 1
158
+ elsif control[:status] == "passed"
159
+ passed += 1
160
+ end
126
161
 
127
- all_unique_controls.each do |control|
128
- next unless control[:results]
129
-
130
- if control[:results].any? { |r| r[:status] == "failed" }
131
- failed += 1
132
- elsif control[:results].any? { |r| r[:status] == "skipped" }
133
- skipped += 1
134
- else
135
- passed += 1
136
- end
137
- end
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" }
138
164
 
139
- total = failed + passed + skipped
165
+ end
166
+ total = error + not_applicable + not_reviewed + failed + passed
167
+ enhanced_outcomes_summary = {
168
+ not_applicable: {
169
+ total: not_applicable,
170
+ },
171
+ not_reviewed: {
172
+ total: not_reviewed,
173
+ },
174
+ error: {
175
+ total: error,
176
+ },
177
+ }
178
+ else
179
+ all_unique_controls.each do |control|
180
+ next unless control[:results]
140
181
 
141
- {
182
+ if control[:results].any? { |r| r[:status] == "failed" }
183
+ failed += 1
184
+ elsif control[:results].any? { |r| r[:status] == "skipped" }
185
+ skipped += 1
186
+ else
187
+ passed += 1
188
+ end
189
+ end
190
+ total = failed + passed + skipped
191
+ end
192
+ final_summary = {
142
193
  total: total,
143
194
  passed: {
144
195
  total: passed,
@@ -150,6 +201,8 @@ module Inspec::Formatters
150
201
  total: failed,
151
202
  },
152
203
  }
204
+
205
+ final_summary.merge!(enhanced_outcomes_summary)
153
206
  end
154
207
 
155
208
  def exception_message(exception)
@@ -178,7 +178,19 @@ module Inspec::Plugin::V2
178
178
  # TODO: enforce first-level version pinning
179
179
  plugin_deps = [Gem::Dependency.new(plugin_gem_name.to_s, version_constraint)]
180
180
  managed_gem_set = Gem::Resolver::VendorSet.new
181
- list_managed_gems.each { |spec| managed_gem_set.add_vendor_gem(spec.name, spec.gem_dir) }
181
+
182
+ list_managed_gems.each do |spec|
183
+ if Gem::Specification.load spec.gem_dir
184
+ managed_gem_set.add_vendor_gem(spec.name, spec.gem_dir)
185
+ else
186
+ # In case of invalid gemspec as mentioned in this PR https://github.com/brianmario/yajl-ruby/pull/223
187
+ # the add_vendor_gem breaks. So this is patch to fix the loading issue.
188
+ # Horribly, chdir to gemspec path to honor . in gemspec
189
+ Dir.chdir(spec.gem_dir) do |dir|
190
+ managed_gem_set.add_vendor_gem(spec.name, spec.gem_dir)
191
+ end
192
+ end
193
+ end
182
194
 
183
195
  # TODO: Next two lines merge our managed gems with the other gems available
184
196
  # in our "local universe" - which may be the system, or it could be in a Bundler microcosm,
@@ -278,7 +290,12 @@ module Inspec::Plugin::V2
278
290
  when :user_gem
279
291
  status.entry_point = status.name.to_s
280
292
  status.version = plugin_entry[:version]
281
- status.description = fetch_plugin_specs(status.name.to_s)&.summary
293
+ # Fetch the summary of the gem from local gemspec file instead of remote call using Gem::SpecFetcher.fetcher.
294
+ unless plugin_entry[:version].nil? # safe check very rare case.
295
+ version_string = plugin_entry[:version].gsub(/[=,~,>,<]/, "").strip
296
+ plugin_name_with_version = "#{status.name}-#{version_string}"
297
+ status.description = fetch_gemspec(File.join(plugin_gem_path, "gems", plugin_name_with_version, "/", status.name.to_s + ".gemspec"))&.summary
298
+ end
282
299
  when :path
283
300
  status.entry_point = plugin_entry[:installation_path]
284
301
  end
@@ -287,12 +304,6 @@ module Inspec::Plugin::V2
287
304
  end
288
305
  end
289
306
 
290
- def fetch_plugin_specs(plugin_name)
291
- fetcher = Gem::SpecFetcher.fetcher
292
- plugin_dependency = Gem::Dependency.new(plugin_name)
293
- fetcher.spec_for_dependency(plugin_dependency).flatten.first
294
- end
295
-
296
307
  def fixup_train_plugin_status(status)
297
308
  status.api_generation = :'train-1'
298
309
  if status.installation_type == :user_gem
@@ -7,6 +7,7 @@ module Inspec::Plugin::V2::PluginType
7
7
  include Inspec::Utils::RunDataFilters
8
8
 
9
9
  attr_reader :run_data
10
+ attr_accessor :enhanced_outcomes
10
11
 
11
12
  def initialize(config)
12
13
  @config = config
@@ -11,6 +11,7 @@ module Inspec::Plugin::V2::PluginType
11
11
  @running_controls_list = []
12
12
  @control_checks_count_map = {}
13
13
  @controls_count = nil
14
+ @notifications = {}
14
15
  end
15
16
 
16
17
  private
@@ -49,5 +50,58 @@ module Inspec::Plugin::V2::PluginType
49
50
  @control_checks_count_map = RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_control_checks_count_map
50
51
  end
51
52
  end
53
+
54
+ def enhanced_outcomes
55
+ @enhanced_outcomes ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.enhanced_outcomes
56
+ end
57
+
58
+ def add_enhanced_outcomes(control_id)
59
+ if control_has_error(@notifications[control_id])
60
+ "error"
61
+ elsif control_has_impact_zero(@notifications[control_id])
62
+ "not_applicable"
63
+ elsif control_has_all_tests_skipped(@notifications[control_id])
64
+ "not_reviewed"
65
+ elsif control_has_any_tests_failed(@notifications[control_id])
66
+ "failed"
67
+ else
68
+ "passed"
69
+ end
70
+ end
71
+
72
+ def control_has_error(notifications)
73
+ notifications.any? do |notification_data|
74
+ notification, _status = notification_data
75
+ !notification.example.exception.nil? && !(notification.example.exception.is_a? RSpec::Expectations::ExpectationNotMetError) && !notification.example.exception.backtrace.nil? && (!notification.description.include? "No-op") # No-op exception occurs in case of not_applicable_if
76
+ end
77
+ end
78
+
79
+ def control_has_all_tests_skipped(notifications)
80
+ notifications.all? do |notification_data|
81
+ _notification, status = notification_data
82
+ status == "skipped"
83
+ end
84
+ end
85
+
86
+ def control_has_any_tests_failed(notifications)
87
+ notifications.any? do |notification_data|
88
+ _notification, status = notification_data
89
+ status == "failed"
90
+ end
91
+ end
92
+
93
+ def control_has_impact_zero(notifications)
94
+ notification_data = notifications.first
95
+ notification_impact = notification_data.first.example.metadata[:impact]
96
+ notification_data && !notification_impact.nil? && notification_impact.to_f == 0.0
97
+ end
98
+
99
+ def collect_notifications(notification, control_id, status)
100
+ if @notifications[control_id].nil?
101
+ @notifications[control_id] = [[notification, status]]
102
+ else
103
+ @notifications[control_id].push([notification, status])
104
+ end
105
+ end
52
106
  end
53
107
  end
@@ -5,6 +5,7 @@ module Inspec::Reporters
5
5
  include Inspec::Utils::RunDataFilters
6
6
 
7
7
  attr_reader :run_data
8
+ attr_accessor :enhanced_outcomes
8
9
 
9
10
  def initialize(config)
10
11
  @config = config