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.
- checksums.yaml +4 -4
- data/Gemfile +19 -16
- data/inspec-core.gemspec +22 -22
- data/lib/inspec/base_cli.rb +2 -0
- data/lib/inspec/cli.rb +6 -2
- data/lib/inspec/dsl.rb +10 -4
- data/lib/inspec/enhanced_outcomes.rb +19 -0
- data/lib/inspec/env_printer.rb +1 -1
- data/lib/inspec/exceptions.rb +2 -0
- data/lib/inspec/formatters/base.rb +69 -16
- data/lib/inspec/plugin/v2/loader.rb +19 -8
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +1 -0
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +54 -0
- data/lib/inspec/reporters/base.rb +1 -0
- data/lib/inspec/reporters/cli.rb +94 -3
- data/lib/inspec/reporters/json.rb +3 -1
- data/lib/inspec/reporters/yaml.rb +3 -1
- data/lib/inspec/reporters.rb +2 -1
- data/lib/inspec/resources/file.rb +1 -1
- data/lib/inspec/resources/http.rb +2 -2
- data/lib/inspec/resources/lxc.rb +65 -9
- data/lib/inspec/resources/oracledb_session.rb +13 -4
- data/lib/inspec/resources/podman.rb +353 -0
- data/lib/inspec/resources/podman_container.rb +84 -0
- data/lib/inspec/resources/podman_image.rb +108 -0
- data/lib/inspec/resources/podman_network.rb +81 -0
- data/lib/inspec/resources/podman_pod.rb +101 -0
- data/lib/inspec/resources/podman_volume.rb +87 -0
- data/lib/inspec/resources/service.rb +1 -1
- data/lib/inspec/rule.rb +54 -17
- data/lib/inspec/run_data/control.rb +6 -0
- data/lib/inspec/run_data/statistics.rb +8 -2
- data/lib/inspec/runner.rb +18 -8
- data/lib/inspec/runner_rspec.rb +3 -2
- data/lib/inspec/schema/exec_json.rb +78 -2
- data/lib/inspec/schema/output_schema.rb +4 -1
- data/lib/inspec/schema/profile_json.rb +46 -0
- data/lib/inspec/schema.rb +91 -0
- data/lib/inspec/utils/convert.rb +8 -0
- data/lib/inspec/utils/podman.rb +24 -0
- data/lib/inspec/utils/waivers/csv_file_reader.rb +34 -0
- data/lib/inspec/utils/waivers/excel_file_reader.rb +39 -0
- data/lib/inspec/utils/waivers/json_file_reader.rb +15 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec/waiver_file_reader.rb +61 -0
- data/lib/matchers/matchers.rb +7 -1
- data/lib/plugins/inspec-init/templates/profiles/alicloud/README.md +27 -0
- data/lib/plugins/inspec-init/templates/profiles/alicloud/controls/example.rb +10 -0
- data/lib/plugins/inspec-init/templates/profiles/alicloud/inputs.yml +1 -0
- data/lib/plugins/inspec-init/templates/profiles/alicloud/inspec.yml +14 -0
- data/lib/plugins/inspec-reporter-html2/README.md +1 -1
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +7 -1
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +10 -6
- data/lib/plugins/inspec-reporter-html2/templates/default.css +12 -0
- data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +7 -1
- data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +5 -2
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +39 -13
- 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
|
|
data/lib/inspec/utils/convert.rb
CHANGED
@@ -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
|
data/lib/inspec/version.rb
CHANGED
@@ -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
|
data/lib/matchers/matchers.rb
CHANGED
@@ -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
|
-
|
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 @@
|
|
1
|
+
|
@@ -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/
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
status
|
7
|
-
|
8
|
-
status
|
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
|
-
|
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 = "#{
|
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
|
-
|
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
|
-
|
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,
|
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,
|
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,
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
@status_mapping[control_id].include? "
|
95
|
-
|
96
|
-
|
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} "
|