inspec-core 5.18.14 → 5.21.29

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.
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
data/lib/inspec/schema.rb CHANGED
@@ -111,6 +111,43 @@ module Inspec
111
111
  },
112
112
  }.freeze
113
113
 
114
+ CONTROL_ENHANCED_OUTCOME = {
115
+ "type" => "object",
116
+ "additionalProperties" => false,
117
+ "properties" => {
118
+ "id" => { "type" => "string" },
119
+ "title" => { "type" => %w{string null} },
120
+ "desc" => { "type" => %w{string null} },
121
+ "descriptions" => { "type" => %w{array} },
122
+ "impact" => { "type" => "number" },
123
+ "status" => {
124
+ "enum" => %w{passed failed not_applicable not_reviewed error},
125
+ "description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"),
126
+ },
127
+ "refs" => REFS,
128
+ "tags" => TAGS,
129
+ "code" => { "type" => "string" },
130
+ "source_location" => {
131
+ "type" => "object",
132
+ "properties" => {
133
+ "ref" => { "type" => "string" },
134
+ "line" => { "type" => "number" },
135
+ },
136
+ },
137
+ "results" => { "type" => "array", "items" => RESULT },
138
+ "waiver_data" => {
139
+ "type" => "object",
140
+ "properties" => {
141
+ "skipped_due_to_waiver" => { "type" => "string" },
142
+ "run" => { "type" => "boolean" },
143
+ "message" => { "type" => "string" },
144
+ "expiration_date" => { "type" => "string" },
145
+ "justification" => { "type" => "string" },
146
+ },
147
+ },
148
+ },
149
+ }.freeze
150
+
114
151
  SUPPORTS = {
115
152
  "type" => "object",
116
153
  "additionalProperties" => false,
@@ -173,6 +210,45 @@ module Inspec
173
210
  },
174
211
  }.freeze
175
212
 
213
+ PROFILE_ENHANCED_OUTCOME = {
214
+ "type" => "object",
215
+ "additionalProperties" => false,
216
+ "properties" => {
217
+ "name" => { "type" => "string" },
218
+ "version" => { "type" => "string", "optional" => true },
219
+ "sha256" => { "type" => "string", "optional" => false },
220
+
221
+ "title" => { "type" => "string", "optional" => true },
222
+ "maintainer" => { "type" => "string", "optional" => true },
223
+ "copyright" => { "type" => "string", "optional" => true },
224
+ "copyright_email" => { "type" => "string", "optional" => true },
225
+ "license" => { "type" => "string", "optional" => true },
226
+ "summary" => { "type" => "string", "optional" => true },
227
+ "status" => { "type" => "string", "optional" => false },
228
+ "status_message" => { "type" => "string", "optional" => true },
229
+ # skip_message is deprecated, status_message should be used to store the reason for skipping
230
+ "skip_message" => { "type" => "string", "optional" => true },
231
+
232
+ "supports" => {
233
+ "type" => "array",
234
+ "items" => SUPPORTS,
235
+ "optional" => true,
236
+ },
237
+ "controls" => {
238
+ "type" => "array",
239
+ "items" => CONTROL_ENHANCED_OUTCOME,
240
+ },
241
+ "groups" => {
242
+ "type" => "array",
243
+ "items" => CONTROL_GROUP,
244
+ },
245
+ "attributes" => { # TODO: rename to inputs, refs #3802
246
+ "type" => "array",
247
+ # TODO: more detailed specification needed
248
+ },
249
+ },
250
+ }.freeze
251
+
176
252
  EXEC_JSON = {
177
253
  "type" => "object",
178
254
  "additionalProperties" => false,
@@ -187,6 +263,20 @@ module Inspec
187
263
  },
188
264
  }.freeze
189
265
 
266
+ EXEC_JSON_ENHANCED_OUTCOME = {
267
+ "type" => "object",
268
+ "additionalProperties" => false,
269
+ "properties" => {
270
+ "platform" => PLATFORM,
271
+ "profiles" => {
272
+ "type" => "array",
273
+ "items" => PROFILE_ENHANCED_OUTCOME,
274
+ },
275
+ "statistics" => STATISTICS,
276
+ "version" => { "type" => "string" },
277
+ },
278
+ }.freeze
279
+
190
280
  MIN_CONTROL = {
191
281
  "type" => "object",
192
282
  "additionalProperties" => false,
@@ -228,6 +318,7 @@ module Inspec
228
318
  LIST = {
229
319
  "exec-json" => EXEC_JSON,
230
320
  "exec-jsonmin" => EXEC_JSONMIN,
321
+ "exec-json-enhanced-outcome" => EXEC_JSON_ENHANCED_OUTCOME,
231
322
  "platforms" => PLATFORMS,
232
323
  }.freeze
233
324
 
@@ -5,4 +5,12 @@ module Converter
5
5
  val = val.to_i if val =~ /^\d+$/
6
6
  val
7
7
  end
8
+
9
+ def self.to_boolean(value)
10
+ if ["true", "True", "TRUE", true, "yes", "y", "YES", "Y"].include? value
11
+ true
12
+ elsif ["false", "False", "FALSE", false, "no", "n", "NO", "N"].include? value
13
+ false
14
+ end
15
+ end
8
16
  end
@@ -0,0 +1,24 @@
1
+ require "inspec/resources/command"
2
+
3
+ module Inspec
4
+ module Utils
5
+ module Podman
6
+ def podman_running?
7
+ inspec.command("podman version").exit_status == 0
8
+ end
9
+
10
+ # Generates the template in this format using labels hash: "\"id\": {{json .ID}}, \"name\": {{json .Name}}",
11
+ def generate_go_template(labels)
12
+ (labels.map { |k, v| "\"#{k}\": {{json .#{v}}}" }).join(", ")
13
+ end
14
+
15
+ def parse_command_output(output)
16
+ require "json" unless defined?(JSON)
17
+ JSON.parse(output)
18
+ rescue JSON::ParserError => _e
19
+ warn "Could not parse the command output"
20
+ {}
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ require "csv" unless defined?(CSV)
2
+
3
+ module Waivers
4
+ class CSVFileReader
5
+ def self.resolve(path)
6
+ return nil unless File.file?(path)
7
+
8
+ @headers ||= []
9
+ fetch_data(path)
10
+ end
11
+
12
+ def self.fetch_data(path)
13
+ waiver_data_hash = {}
14
+ CSV.foreach(path, headers: true) do |row|
15
+ row_hash = row.to_hash
16
+ @headers = row_hash.keys if @headers.empty?
17
+ control_id = row_hash["control_id"]
18
+ # delete keys and values not required in final hash
19
+ row_hash.delete("control_id")
20
+ row_hash.delete_if { |k, v| k.nil? || v.nil? }
21
+
22
+ waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank?
23
+ end
24
+
25
+ waiver_data_hash
26
+ rescue CSV::MalformedCSVError => e
27
+ raise "Error reading InSpec waivers in CSV: #{e}"
28
+ end
29
+
30
+ def self.headers
31
+ @headers
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ require "roo"
2
+ require "roo-xls"
3
+
4
+ module Waivers
5
+ class ExcelFileReader
6
+ def self.resolve(path)
7
+ return nil unless File.file?(path)
8
+
9
+ @headers ||= []
10
+ fetch_data(path)
11
+ end
12
+
13
+ def self.fetch_data(path)
14
+ waiver_data_hash = {}
15
+ file_extension = File.extname(path) == ".xlsx" ? :xlsx : :xls
16
+ excel_file = Roo::Spreadsheet.open(path, extension: file_extension)
17
+ excel_file.sheet(0).parse(headers: true).each_with_index do |row, index|
18
+ if index == 0
19
+ @headers = row.keys
20
+ else
21
+ row_hash = row
22
+ control_id = row_hash["control_id"]
23
+ # delete keys and values not required in final hash
24
+ row_hash.delete("control_id")
25
+ row_hash.delete_if { |k, v| k.nil? || v.nil? }
26
+ end
27
+
28
+ waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank?
29
+ end
30
+ waiver_data_hash
31
+ rescue Exception => e
32
+ raise "Error reading InSpec waivers in Excel: #{e}"
33
+ end
34
+
35
+ def self.headers
36
+ @headers
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ module Waivers
2
+ class JSONFileReader
3
+ def self.resolve(path)
4
+ return nil unless File.file?(path)
5
+
6
+ fetch_data(path)
7
+ end
8
+
9
+ def self.fetch_data(path)
10
+ JSON.parse(File.read(path))
11
+ rescue JSON::ParserError => e
12
+ raise "Error reading InSpec waivers in JSON: #{e}"
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = "5.18.14".freeze
2
+ VERSION = "5.21.29".freeze
3
3
  end
@@ -0,0 +1,61 @@
1
+ require "inspec/secrets/yaml"
2
+ require "inspec/utils/waivers/csv_file_reader"
3
+ require "inspec/utils/waivers/json_file_reader"
4
+
5
+ module Inspec
6
+ class WaiverFileReader
7
+
8
+ def self.fetch_waivers_by_profile(profile_id, files)
9
+ read_waivers_from_file(profile_id, files) if @waivers_data.nil? || @waivers_data[profile_id].nil?
10
+ @waivers_data[profile_id]
11
+ end
12
+
13
+ def self.read_waivers_from_file(profile_id, files)
14
+ @waivers_data ||= {}
15
+ output = {}
16
+
17
+ files.each do |file_path|
18
+ file_extension = File.extname(file_path)
19
+ data = nil
20
+ if [".yaml", ".yml"].include? file_extension
21
+ data = Secrets::YAML.resolve(file_path)
22
+ data = data.inputs unless data.nil?
23
+ validate_json_yaml(data)
24
+ elsif file_extension == ".csv"
25
+ data = Waivers::CSVFileReader.resolve(file_path)
26
+ headers = Waivers::CSVFileReader.headers
27
+ validate_headers(headers)
28
+ elsif file_extension == ".json"
29
+ data = Waivers::JSONFileReader.resolve(file_path)
30
+ validate_json_yaml(data)
31
+ end
32
+ output.merge!(data) if !data.nil? && data.is_a?(Hash)
33
+
34
+ if data.nil?
35
+ raise Inspec::Exceptions::WaiversFileNotReadable,
36
+ "Cannot find parser for waivers file '#{file_path}'. " \
37
+ "Check to make sure file has the appropriate extension."
38
+ end
39
+ end
40
+
41
+ @waivers_data[profile_id] = output
42
+ end
43
+
44
+ def self.validate_headers(headers, json_yaml = false)
45
+ required_fields = json_yaml ? %w{justification} : %w{control_id justification}
46
+ all_fields = %w{control_id justification expiration_date run}
47
+
48
+ Inspec::Log.warn "Missing column headers: #{(required_fields - headers)}" unless (required_fields - headers).empty?
49
+ Inspec::Log.warn "Invalid column header: Column can't be nil" if headers.include? nil
50
+ Inspec::Log.warn "Extra column headers: #{(headers - all_fields)}" unless (headers - all_fields).empty?
51
+ end
52
+
53
+ def self.validate_json_yaml(data)
54
+ headers = []
55
+ data.each_value do |value|
56
+ headers.push value.keys
57
+ end
58
+ validate_headers(headers.flatten.uniq, true)
59
+ end
60
+ end
61
+ end
@@ -225,7 +225,11 @@ RSpec::Matchers.define :cmp do |first_expected| # rubocop:disable Metrics/BlockL
225
225
  end
226
226
 
227
227
  def boolean?(value)
228
- %w{true false}.include?(value.downcase)
228
+ if value.respond_to?("downcase")
229
+ %w{true false}.include?(value.downcase)
230
+ else
231
+ value.is_a?(TrueClass) || value.is_a?(FalseClass)
232
+ end
229
233
  end
230
234
 
231
235
  def version?(value)
@@ -252,6 +256,8 @@ RSpec::Matchers.define :cmp do |first_expected| # rubocop:disable Metrics/BlockL
252
256
  return actual.send(op, expected.to_i)
253
257
  elsif expected.is_a?(String) && boolean?(expected) && [true, false].include?(actual)
254
258
  return actual.send(op, to_boolean(expected))
259
+ elsif boolean?(expected) && %w{true false}.include?(actual)
260
+ return actual.send(op, expected.to_s)
255
261
  elsif expected.is_a?(Integer) && actual.is_a?(String) && integer?(actual)
256
262
  return actual.to_i.send(op, expected)
257
263
  elsif expected.is_a?(Float) && float?(actual)
@@ -0,0 +1,27 @@
1
+ # Example InSpec Profile For AliCloud
2
+
3
+ This example shows the implementation of an InSpec profile for AliCloud.
4
+
5
+ The related control will simply be skipped if this is not provided. See the [InSpec DSL documentation](https://docs.chef.io/inspec/dsl_inspec/) for more details on conditional execution using `only_if`.
6
+
7
+ ## Run the test
8
+
9
+ ```bash
10
+ $ cd my-alicloud-sample-profile/
11
+ $ inspec exec . -t alicloud://
12
+ ```
13
+
14
+ ```
15
+ Profile: Ali Cloud InSpec Profile (my-alicloud-profile)
16
+ Version: 0.1.0
17
+ Target: alicloud://ap-south-1
18
+ ✔ ali-cloud-instances-1.0: Ensure AliCloud ECS Instances has correct attributes.
19
+ ✔ AliCloud ECS Instances (All) is expected to exist
20
+ ✔ AliCloud ECS Instances (All) entries.count is expected to be >= 1
21
+ Profile: AliCloud Resource Pack (inspec-alicloud)
22
+ Version: 0.10.8
23
+ Target: alicloud://ap-south-1
24
+ No tests executed.
25
+ Profile Summary: 1 successful controls, 0 control failures, 0 controls skipped
26
+ Test Summary: 1 successful, 0 failures, 0 skipped
27
+ ```
@@ -0,0 +1,10 @@
1
+ title "Test AliCloud Instances count"
2
+
3
+ control "ali-cloud-instances-1.0" do
4
+ impact 1.0
5
+ title "Ensure AliCloud ECS Instances Class has correct attributes."
6
+ describe alicloud_ecs_instances do
7
+ it { should exist }
8
+ its("entries.count") { should be >= 1 }
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ name: my-alicloud-sample-profile
2
+ title: Ali Cloud InSpec Profile
3
+ maintainer: The Authors
4
+ copyright: The Authors
5
+ copyright_email: you@example.com
6
+ license: Apache-2.0
7
+ summary: An InSpec Compliance Profile For Ali CLoud
8
+ version: 0.1.0
9
+ inspec_version: '~> 5'
10
+ depends:
11
+ - name: inspec-alicloud
12
+ url: https://github.com/inspec/inspec-alicloud/archive/main.tar.gz
13
+ supports:
14
+ - platform: alicloud
@@ -50,4 +50,4 @@ Specifies the full path to the location of a JavaScript file that will be read a
50
50
 
51
51
  ## Developing This Plugin
52
52
 
53
- This plugin is part of the Chef InSpec source code. While it has its own tests, the general contribution policy is dictated by the Chef InSpec project at https://github.com/inspec/inspec/blob/master/CONTRIBUTING.md
53
+ This plugin is part of the Chef InSpec source code. While it has its own tests, the general contribution policy is dictated by the Chef InSpec project at https://github.com/inspec/inspec/blob/main/CONTRIBUTING.md
@@ -36,8 +36,14 @@
36
36
  <caption>Control Statistics</caption>
37
37
  <tr><th colspan="2"><h4 id="statistics-label">Control Statistics</h4></th></tr>
38
38
  <tr class= "passed"><th>Passed:</th><td><%= run_data.statistics.controls.passed.total %></td></tr>
39
- <tr class= "skipped"><th>Skipped:</th><td><%= run_data.statistics.controls.skipped.total %></td></tr>
40
39
  <tr class= "failed"><th>Failed:</th><td><%= run_data.statistics.controls.failed.total %></td></tr>
40
+ <% if enhanced_outcomes %>
41
+ <tr class= "not_reviewed"><th>Not Reviewed:</th><td><%= run_data.statistics.controls.not_reviewed.total %></td></tr>
42
+ <tr class= "not_applicable"><th>Not Applicable:</th><td><%= run_data.statistics.controls.not_applicable.total %></td></tr>
43
+ <tr class= "error"><th>Error:</th><td><%= run_data.statistics.controls.error.total %></td></tr>
44
+ <% else %>
45
+ <tr class= "skipped"><th>Skipped:</th><td><%= run_data.statistics.controls.skipped.total %></td></tr>
46
+ <% end %>
41
47
  <tr class= "duration"><th>Duration:</th><td><%= run_data.statistics.duration %> seconds</td></tr>
42
48
  <tr class= "date"><th>Time Finished:</th><td><%= Time.now %></td></tr>
43
49
  </table>
@@ -1,11 +1,15 @@
1
1
  <% slugged_id = control.id.tr(" ", "_") %>
2
2
  <%
3
- # Determine status of control
4
- status = "passed"
5
- if control.results.any? { |r| r.status == "failed" }
6
- status = "failed"
7
- elsif control.results.any? { |r| r.status == "skipped" }
8
- status = "skipped"
3
+ if enhanced_outcomes
4
+ status = control.status
5
+ else
6
+ # Determine status of control
7
+ status = "passed"
8
+ if control.results.any? { |r| r.status == "failed" }
9
+ status = "failed"
10
+ elsif control.results.any? { |r| r.status == "skipped" }
11
+ status = "skipped"
12
+ end
9
13
  end
10
14
  %>
11
15
 
@@ -60,6 +60,18 @@ pre code {
60
60
  .result-metadata .status-skipped div {
61
61
  background-color: grey;
62
62
  }
63
+ .control-metadata .status-error div,
64
+ .result-metadata .status-error div {
65
+ background-color: rgb(63, 15, 183);
66
+ }
67
+ .control-metadata .status-not_applicable div,
68
+ .result-metadata .status-not_applicable div {
69
+ background-color: rgb(135, 206, 250);
70
+ }
71
+ .control-metadata .status-not_reviewed div,
72
+ .result-metadata .status-not_reviewed div {
73
+ background-color: rgb(255, 194, 0);
74
+ }
63
75
  .result-metadata,
64
76
  .control-metadata {
65
77
  margin: 0 0 0 5%;
@@ -1,8 +1,14 @@
1
1
  <div class="selector-panel">
2
2
  <p id="selector-instructions">Display controls that are:</p>
3
3
  <input class="selector-checkbox" id="passed-checkbox" type="checkbox" checked="checked"/><label for="passed-checkbox">Passed</label>
4
- <input class="selector-checkbox" id="skipped-checkbox" type="checkbox" checked="checked"/><label for="skipped-checkbox">Skipped</label>
5
4
  <input class="selector-checkbox" id="failed-checkbox" type="checkbox" checked="checked"/><label for="failed-checkbox">Failed</label>
5
+ <% if enhanced_outcomes %>
6
+ <input class="selector-checkbox" id="not_reviewed-checkbox" type="checkbox" checked="checked"/><label for="not_reviewed-checkbox">Not Reviewed</label>
7
+ <input class="selector-checkbox" id="not_applicable-checkbox" type="checkbox" checked="checked"/><label for="not_applicable-checkbox">Not Applicable</label>
8
+ <input class="selector-checkbox" id="error-checkbox" type="checkbox" checked="checked"/><label for="error-checkbox">Error</label>
9
+ <% else %>
10
+ <input class="selector-checkbox" id="skipped-checkbox" type="checkbox" checked="checked"/><label for="skipped-checkbox">Skipped</label>
11
+ <% end %>
6
12
  <p id="selector-instructions">Display profiles that are:</p>
7
13
  <input class="profile-selector-checkbox" id="child-profile-checkbox" type="checkbox" /><label for="child-profile-checkbox">Dependent Profiles</label>
8
14
  </div>
@@ -59,11 +59,14 @@ module InspecPlugins
59
59
  # Read name and version from metadata and use them to form the filename
60
60
  profile_md = artifact.read_profile_metadata(profile_path)
61
61
 
62
- artifact_filename = "#{profile_md["name"]}-#{profile_md["version"]}.#{SIGNED_PROFILE_SUFFIX}"
62
+ # Behave same as archive filename for iaf filename
63
+ slug = profile_md["name"].downcase.strip.tr(" ", "-").gsub(/[^\w-]/, "_")
64
+ filename = "#{slug}-#{profile_md["version"]}"
65
+ artifact_filename = "#{filename}.#{SIGNED_PROFILE_SUFFIX}"
63
66
 
64
67
  # Generating tar.gz file using archive method of Inspec Cli
65
68
  Inspec::InspecCLI.new.archive(profile_path, "error")
66
- tarfile = "#{profile_md["name"]}-#{profile_md["version"]}.tar.gz"
69
+ tarfile = "#{filename}.tar.gz"
67
70
  tar_content = IO.binread(tarfile)
68
71
  FileUtils.rm(tarfile)
69
72
 
@@ -20,6 +20,9 @@ module InspecPlugins::StreamingReporterProgressBar
20
20
  "passed" => "\033[0;1;32m",
21
21
  "skipped" => "\033[0;37m",
22
22
  "reset" => "\033[0m",
23
+ "error" => "\033[34m",
24
+ "not_applicable" => "\033[36m",
25
+ "not_reviewed" => "\033[33m",
23
26
  }.freeze
24
27
 
25
28
  # Most currently available Windows terminals have poor support
@@ -28,6 +31,9 @@ module InspecPlugins::StreamingReporterProgressBar
28
31
  "failed" => "[FAIL]",
29
32
  "skipped" => "[SKIP]",
30
33
  "passed" => "[PASS]",
34
+ "error" => " [ERROR] ",
35
+ "not_applicable" => " [N/A] ",
36
+ "not_reviewed" => " [N/R] ",
31
37
  }.freeze
32
38
  else
33
39
  # Extended colors for everyone else
@@ -36,6 +42,9 @@ module InspecPlugins::StreamingReporterProgressBar
36
42
  "passed" => "\033[38;5;41m",
37
43
  "skipped" => "\033[38;5;247m",
38
44
  "reset" => "\033[0m",
45
+ "error" => "\033[0;38;5;21m",
46
+ "not_applicable" => "\033[0;38;5;117m",
47
+ "not_reviewed" => "\033[0;38;5;214m",
39
48
  }.freeze
40
49
 
41
50
  # Groovy UTF-8 characters for everyone else...
@@ -44,6 +53,9 @@ module InspecPlugins::StreamingReporterProgressBar
44
53
  "failed" => "× [FAILED] ",
45
54
  "skipped" => "↺ [SKIPPED]",
46
55
  "passed" => "✔ [PASSED] ",
56
+ "error" => "× [ERROR] ",
57
+ "not_applicable" => " [N/A] ",
58
+ "not_reviewed" => " [N/R] ",
47
59
  }.freeze
48
60
  end
49
61
 
@@ -71,29 +83,43 @@ module InspecPlugins::StreamingReporterProgressBar
71
83
  control_id = notification.example.metadata[:id]
72
84
  title = notification.example.metadata[:title]
73
85
  full_description = notification.example.metadata[:full_description]
74
- control_impact = notification.example.metadata[:impact]
86
+
87
+ # No-op exception occurs in case of not_applicable_if
88
+ if (full_description.include? "No-op") && notification.example.exception
89
+ full_description += notification.example.exception.message
90
+ end
91
+
75
92
  set_status_mapping(control_id, status)
76
- show_progress(control_id, title, full_description, control_impact) if control_ended?(control_id)
93
+ collect_notifications(notification, control_id, status)
94
+ control_ended = control_ended?(control_id)
95
+ if control_ended
96
+ control_outcome = add_enhanced_outcomes(control_id) if enhanced_outcomes
97
+ show_progress(control_id, title, full_description, control_outcome)
98
+ end
77
99
  end
78
100
 
79
- def show_progress(control_id, title, full_description, control_impact)
101
+ def show_progress(control_id, title, full_description, control_outcome)
80
102
  @bar ||= ProgressBar.new(controls_count, :bar, :counter, :percentage)
81
103
  sleep 0.1
82
104
  @bar.increment!
83
- @bar.puts format_it(control_id, title, full_description, control_impact)
105
+ @bar.puts format_it(control_id, title, full_description, control_outcome)
84
106
  rescue StandardError => e
85
107
  raise "Exception in Progress Bar streaming reporter: #{e}"
86
108
  end
87
109
 
88
- def format_it(control_id, title, full_description, control_impact)
89
- control_status = if @status_mapping[control_id].include? "failed"
90
- "failed"
91
- elsif @status_mapping[control_id].include? "passed"
92
- "passed"
93
- else
94
- @status_mapping[control_id].include? "skipped"
95
- "skipped"
96
- end
110
+ def format_it(control_id, title, full_description, control_outcome)
111
+ if control_outcome
112
+ control_status = control_outcome
113
+ else
114
+ control_status = if @status_mapping[control_id].include? "failed"
115
+ "failed"
116
+ elsif @status_mapping[control_id].include? "passed"
117
+ "passed"
118
+ else
119
+ @status_mapping[control_id].include? "skipped"
120
+ "skipped"
121
+ end
122
+ end
97
123
  indicator = INDICATORS[control_status]
98
124
  message_to_format = ""
99
125
  message_to_format += "#{indicator} "