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