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
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} "