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/reporters/cli.rb
CHANGED
@@ -9,6 +9,9 @@ module Inspec::Reporters
|
|
9
9
|
"passed" => "\033[0;1;32m",
|
10
10
|
"skipped" => "\033[0;37m",
|
11
11
|
"reset" => "\033[0m",
|
12
|
+
"error" => "\033[34m",
|
13
|
+
"not_applicable" => "\033[36m",
|
14
|
+
"not_reviewed" => "\033[33m",
|
12
15
|
}.freeze
|
13
16
|
|
14
17
|
# Most currently available Windows terminals have poor support
|
@@ -18,6 +21,9 @@ module Inspec::Reporters
|
|
18
21
|
"skipped" => "[SKIP]",
|
19
22
|
"passed" => "[PASS]",
|
20
23
|
"unknown" => "[UNKN]",
|
24
|
+
"error" => "[ERR]",
|
25
|
+
"not_applicable" => "[N/A]",
|
26
|
+
"not_reviewed" => "[N/R]",
|
21
27
|
}.freeze
|
22
28
|
else
|
23
29
|
# Extended colors for everyone else
|
@@ -26,6 +32,9 @@ module Inspec::Reporters
|
|
26
32
|
"passed" => "\033[38;5;41m",
|
27
33
|
"skipped" => "\033[38;5;247m",
|
28
34
|
"reset" => "\033[0m",
|
35
|
+
"error" => "\033[0;38;5;21m",
|
36
|
+
"not_applicable" => "\033[0;38;5;117m",
|
37
|
+
"not_reviewed" => "\033[0;38;5;214m",
|
29
38
|
}.freeze
|
30
39
|
|
31
40
|
# Groovy UTF-8 characters for everyone else...
|
@@ -35,6 +44,9 @@ module Inspec::Reporters
|
|
35
44
|
"skipped" => "↺",
|
36
45
|
"passed" => "✔",
|
37
46
|
"unknown" => "?",
|
47
|
+
"error" => "ERR",
|
48
|
+
"not_applicable" => "N/A",
|
49
|
+
"not_reviewed" => "N/R",
|
38
50
|
}.freeze
|
39
51
|
end
|
40
52
|
|
@@ -63,7 +75,11 @@ module Inspec::Reporters
|
|
63
75
|
end
|
64
76
|
|
65
77
|
output("")
|
66
|
-
|
78
|
+
if enhanced_outcomes
|
79
|
+
print_control_outcomes_summary
|
80
|
+
else
|
81
|
+
print_profile_summary
|
82
|
+
end
|
67
83
|
print_tests_summary
|
68
84
|
end
|
69
85
|
|
@@ -88,6 +104,7 @@ module Inspec::Reporters
|
|
88
104
|
def print_standard_control_results(profile)
|
89
105
|
standard_controls_from_profile(profile).each do |control_from_profile|
|
90
106
|
control = Control.new(control_from_profile)
|
107
|
+
control.enhanced_outcomes = enhanced_outcomes
|
91
108
|
next if control.results.nil?
|
92
109
|
|
93
110
|
output(format_control_header(control))
|
@@ -122,7 +139,7 @@ module Inspec::Reporters
|
|
122
139
|
end
|
123
140
|
|
124
141
|
def format_control_header(control)
|
125
|
-
impact = control.impact_string
|
142
|
+
impact = enhanced_outcomes ? control.impact_string_for_enhanced_outcomes : control.impact_string
|
126
143
|
format_message(
|
127
144
|
color: impact,
|
128
145
|
indicator: impact,
|
@@ -292,6 +309,68 @@ module Inspec::Reporters
|
|
292
309
|
}
|
293
310
|
end
|
294
311
|
|
312
|
+
def control_outcomes_summary
|
313
|
+
failed = 0
|
314
|
+
passed = 0
|
315
|
+
error = 0
|
316
|
+
not_reviewed = 0
|
317
|
+
not_applicable = 0
|
318
|
+
|
319
|
+
all_unique_controls.each do |control|
|
320
|
+
next if control[:status].empty?
|
321
|
+
|
322
|
+
if control[:status] == "failed"
|
323
|
+
failed += 1
|
324
|
+
elsif control[:status] == "error"
|
325
|
+
error += 1
|
326
|
+
elsif control[:status] == "not_reviewed"
|
327
|
+
not_reviewed += 1
|
328
|
+
elsif control[:status] == "not_applicable"
|
329
|
+
not_applicable += 1
|
330
|
+
else
|
331
|
+
passed += 1
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
total = failed + passed + error + not_reviewed + not_applicable
|
336
|
+
|
337
|
+
{
|
338
|
+
"total" => total,
|
339
|
+
"failed" => failed,
|
340
|
+
"passed" => passed,
|
341
|
+
"error" => error,
|
342
|
+
"not_reviewed" => not_reviewed,
|
343
|
+
"not_applicable" => not_applicable,
|
344
|
+
}
|
345
|
+
end
|
346
|
+
|
347
|
+
def print_control_outcomes_summary
|
348
|
+
summary = control_outcomes_summary
|
349
|
+
return unless summary["total"] > 0
|
350
|
+
|
351
|
+
success_str = summary["passed"] == 1 ? "1 successful control" : "#{summary["passed"]} successful controls"
|
352
|
+
failed_str = summary["failed"] == 1 ? "1 control failure" : "#{summary["failed"]} control failures"
|
353
|
+
error_str = summary["error"] == 1 ? "1 control has error" : "#{summary["error"]} controls have error"
|
354
|
+
not_rev_str = summary["not_reviewed"] == 1 ? "1 control not reviewed" : "#{summary["not_reviewed"]} controls not reviewed"
|
355
|
+
not_app_str = summary["not_applicable"] == 1 ? "1 control not applicable" : "#{summary["not_applicable"]} controls not applicable"
|
356
|
+
|
357
|
+
success_color = summary["passed"] > 0 ? "passed" : "no_color"
|
358
|
+
failed_color = summary["failed"] > 0 ? "failed" : "no_color"
|
359
|
+
error_color = summary["error"] > 0 ? "error" : "no_color"
|
360
|
+
not_rev_color = summary["not_reviewed"] > 0 ? "not_reviewed" : "no_color"
|
361
|
+
not_app_color = summary["not_applicable"] > 0 ? "not_applicable" : "no_color"
|
362
|
+
|
363
|
+
s = format(
|
364
|
+
"Profile Summary: %s, %s, %s, %s, %s",
|
365
|
+
format_with_color(success_color, success_str),
|
366
|
+
format_with_color(failed_color, failed_str),
|
367
|
+
format_with_color(not_rev_color, not_rev_str),
|
368
|
+
format_with_color(not_app_color, not_app_str),
|
369
|
+
format_with_color(error_color, error_str)
|
370
|
+
)
|
371
|
+
output(s) if summary["total"] > 0
|
372
|
+
end
|
373
|
+
|
295
374
|
def print_profile_summary
|
296
375
|
summary = profile_summary
|
297
376
|
return unless summary["total"] > 0
|
@@ -350,6 +429,7 @@ module Inspec::Reporters
|
|
350
429
|
|
351
430
|
class Control
|
352
431
|
attr_reader :data
|
432
|
+
attr_accessor :enhanced_outcomes
|
353
433
|
|
354
434
|
def initialize(control_hash)
|
355
435
|
@data = control_hash
|
@@ -379,6 +459,10 @@ module Inspec::Reporters
|
|
379
459
|
id.start_with?("(generated from ")
|
380
460
|
end
|
381
461
|
|
462
|
+
def status
|
463
|
+
data[:status]
|
464
|
+
end
|
465
|
+
|
382
466
|
def title_for_report
|
383
467
|
# if this is an anonymous control, just grab the resource title from any result entry
|
384
468
|
return results.first[:resource_title] if anonymous?
|
@@ -392,10 +476,17 @@ module Inspec::Reporters
|
|
392
476
|
# append a failure summary if appropriate.
|
393
477
|
title_for_report += " (#{failure_count} failed)" if failure_count > 0
|
394
478
|
title_for_report += " (#{skipped_count} skipped)" if skipped_count > 0
|
395
|
-
|
396
479
|
title_for_report
|
397
480
|
end
|
398
481
|
|
482
|
+
def impact_string_for_enhanced_outcomes
|
483
|
+
if impact.nil?
|
484
|
+
"unknown"
|
485
|
+
else
|
486
|
+
status
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
399
490
|
def impact_string
|
400
491
|
if anonymous?
|
401
492
|
nil
|
@@ -114,7 +114,7 @@ module Inspec::Reporters
|
|
114
114
|
|
115
115
|
def profile_controls(profile)
|
116
116
|
(profile[:controls] || []).map { |c|
|
117
|
-
{
|
117
|
+
control_hash = {
|
118
118
|
id: c[:id],
|
119
119
|
title: c[:title],
|
120
120
|
desc: c.dig(:descriptions, :default),
|
@@ -130,6 +130,8 @@ module Inspec::Reporters
|
|
130
130
|
waiver_data: c[:waiver_data] || {},
|
131
131
|
results: profile_results(c),
|
132
132
|
}
|
133
|
+
control_hash.merge!({ status: c[:status] }) if enhanced_outcomes
|
134
|
+
control_hash
|
133
135
|
}
|
134
136
|
end
|
135
137
|
|
@@ -3,7 +3,9 @@ require "yaml"
|
|
3
3
|
module Inspec::Reporters
|
4
4
|
class Yaml < Base
|
5
5
|
def render
|
6
|
-
|
6
|
+
json_reporter_obj = Inspec::Reporters::Json.new({ run_data: run_data })
|
7
|
+
json_reporter_obj.enhanced_outcomes = enhanced_outcomes
|
8
|
+
output(json_reporter_obj.report.to_yaml, false)
|
7
9
|
end
|
8
10
|
|
9
11
|
def report
|
data/lib/inspec/reporters.rb
CHANGED
@@ -7,7 +7,7 @@ require "inspec/reporters/yaml"
|
|
7
7
|
|
8
8
|
module Inspec::Reporters
|
9
9
|
# rubocop:disable Metrics/CyclomaticComplexity
|
10
|
-
def self.render(reporter, run_data)
|
10
|
+
def self.render(reporter, run_data, enhanced_outcomes = false)
|
11
11
|
name, config = reporter.dup
|
12
12
|
config[:run_data] = run_data
|
13
13
|
case name
|
@@ -29,6 +29,7 @@ module Inspec::Reporters
|
|
29
29
|
activator.activate!
|
30
30
|
reporter = activator.implementation_class.new(config)
|
31
31
|
end
|
32
|
+
reporter.enhanced_outcomes = enhanced_outcomes
|
32
33
|
|
33
34
|
# optional send_report method on reporter
|
34
35
|
return reporter.send_report if defined?(reporter.send_report)
|
@@ -66,7 +66,7 @@ module Inspec::Resources
|
|
66
66
|
def user_permissions
|
67
67
|
return {} unless exist?
|
68
68
|
|
69
|
-
return
|
69
|
+
return skip_resource "`user_permissions` is not supported on your OS yet." unless inspec.os.windows?
|
70
70
|
|
71
71
|
@perms_provider.user_permissions(file)
|
72
72
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
require "inspec/resources/command"
|
6
6
|
require "faraday" unless defined?(Faraday)
|
7
|
-
require "
|
7
|
+
require "faraday/follow_redirects"
|
8
8
|
require "hashie"
|
9
9
|
|
10
10
|
module Inspec::Resources
|
@@ -153,7 +153,7 @@ module Inspec::Resources
|
|
153
153
|
|
154
154
|
conn = Faraday.new(url: url, headers: request_headers, params: params, ssl: { verify: ssl_verify? }) do |builder|
|
155
155
|
builder.request :url_encoded
|
156
|
-
builder.use
|
156
|
+
builder.use Faraday::FollowRedirects::Middleware, limit: max_redirects unless max_redirects.nil?
|
157
157
|
builder.adapter Faraday.default_adapter
|
158
158
|
end
|
159
159
|
|
data/lib/inspec/resources/lxc.rb
CHANGED
@@ -9,14 +9,26 @@ module Inspec::Resources
|
|
9
9
|
describe lxc("ubuntu-container") do
|
10
10
|
it { should exist }
|
11
11
|
it { should be_running }
|
12
|
+
its("name") { should eq "ubuntu-container" }
|
13
|
+
its("status") { should cmp "Running" }
|
14
|
+
its("type") { should eq "container" }
|
15
|
+
its("architecture") { should eq "x86_64" }
|
16
|
+
its("pid") { should eq 1378 }
|
17
|
+
its("created_at") { should eq "2022/08/16 12:07 UTC" }
|
18
|
+
its("last_used_at") { should eq "2022/08/17 05:06 UTC" }
|
19
|
+
its("resources") { should include "Disk usage" }
|
12
20
|
end
|
13
21
|
EXAMPLE
|
14
22
|
|
23
|
+
attr_reader :container_info, :container_name
|
24
|
+
|
15
25
|
# Resource initialization.
|
16
26
|
def initialize(container_name)
|
17
27
|
@container_name = container_name
|
18
28
|
|
19
29
|
raise Inspec::Exceptions::ResourceSkipped, "The `lxc` resource is not supported on your OS yet." unless inspec.os.linux?
|
30
|
+
|
31
|
+
@container_info = populate_container_info
|
20
32
|
end
|
21
33
|
|
22
34
|
def resource_id
|
@@ -28,17 +40,60 @@ module Inspec::Resources
|
|
28
40
|
end
|
29
41
|
|
30
42
|
def exists?
|
31
|
-
|
43
|
+
!@container_info.empty?
|
32
44
|
end
|
33
45
|
|
34
46
|
def running?
|
35
|
-
container_info
|
36
|
-
|
47
|
+
@container_info.key?("Status") && @container_info["Status"].casecmp("Running") == 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def name
|
51
|
+
@container_info["Name"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def status
|
55
|
+
@container_info["Status"]
|
56
|
+
end
|
57
|
+
|
58
|
+
def type
|
59
|
+
@container_info["Type"]
|
60
|
+
end
|
61
|
+
|
62
|
+
def architecture
|
63
|
+
@container_info["Architecture"]
|
64
|
+
end
|
65
|
+
|
66
|
+
def pid
|
67
|
+
@container_info["PID"]
|
68
|
+
end
|
69
|
+
|
70
|
+
def created_at
|
71
|
+
@container_info["Created"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def last_used_at
|
75
|
+
@container_info["Last Used"]
|
76
|
+
end
|
77
|
+
|
78
|
+
def resources
|
79
|
+
@container_info["Resources"]
|
37
80
|
end
|
38
81
|
|
39
82
|
private
|
40
83
|
|
41
|
-
|
84
|
+
def populate_container_info
|
85
|
+
lxc_util = find_lxc_or_error
|
86
|
+
lxc_info_cmd = inspec.command("#{lxc_util} info #{@container_name}")
|
87
|
+
|
88
|
+
if lxc_info_cmd.exit_status.to_i == 0
|
89
|
+
parse_command_output(lxc_info_cmd.stdout)
|
90
|
+
elsif lxc_info_cmd.stderr =~ /Error: Instance not found/
|
91
|
+
{}
|
92
|
+
else
|
93
|
+
raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve information for #{container_name}.\n#{lxc_info_cmd.stderr}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
42
97
|
def find_lxc_or_error
|
43
98
|
%w{/usr/sbin/lxc /sbin/lxc lxc}.each do |cmd|
|
44
99
|
return cmd if inspec.command(cmd).exist?
|
@@ -47,11 +102,12 @@ module Inspec::Resources
|
|
47
102
|
raise Inspec::Exceptions::ResourceFailed, "Could not find `lxc`"
|
48
103
|
end
|
49
104
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
105
|
+
def parse_command_output(output)
|
106
|
+
require "yaml" unless defined?(YAML)
|
107
|
+
YAML.load(output)
|
108
|
+
rescue Psych::SyntaxError => e
|
109
|
+
warn "Could not parse the command output.\n#{e.message}"
|
110
|
+
{}
|
55
111
|
end
|
56
112
|
end
|
57
113
|
end
|
@@ -101,22 +101,31 @@ module Inspec::Resources
|
|
101
101
|
verified_query = verify_query(escaped_query)
|
102
102
|
end
|
103
103
|
|
104
|
-
sql_prefix, sql_postfix = "", ""
|
104
|
+
sql_prefix, sql_postfix, oracle_echo_str = "", "", ""
|
105
105
|
if inspec.os.windows?
|
106
106
|
sql_prefix = %{@'\n#{format_options}\n#{verified_query}\nEXIT\n'@ | }
|
107
107
|
else
|
108
108
|
sql_postfix = %{ <<'EOC'\n#{format_options}\n#{verified_query}\nEXIT\nEOC}
|
109
|
+
# oracle_query_string is echoed to be able to extract the query output clearly
|
110
|
+
oracle_echo_str = %{echo 'oracle_query_string';}
|
111
|
+
end
|
112
|
+
|
113
|
+
# Resetting sql_postfix if system is using AIX OS and C shell installation for oracle
|
114
|
+
if inspec.os.aix?
|
115
|
+
command_to_fetch_shell = @su_user ? %{su - #{@su_user} -c "env | grep SHELL"} : %{env | grep SHELL}
|
116
|
+
shell_is_csh = inspec.command(command_to_fetch_shell).stdout&.include? "/csh"
|
117
|
+
sql_postfix = %{ <<'EOC'\n#{format_options}\n#{verified_query}\nEXIT\n'EOC'} if shell_is_csh
|
109
118
|
end
|
110
119
|
|
111
120
|
if @db_role.nil?
|
112
|
-
%{#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service}#{sql_postfix}}
|
121
|
+
%{#{oracle_echo_str}#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service}#{sql_postfix}}
|
113
122
|
elsif @su_user.nil?
|
114
|
-
%{#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service} as #{@db_role}#{sql_postfix}}
|
123
|
+
%{#{oracle_echo_str}#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service} as #{@db_role}#{sql_postfix}}
|
115
124
|
else
|
116
125
|
# oracle_query_string is echoed to be able to extract the query output clearly
|
117
126
|
# su - su_user in certain versions of oracle returns a message
|
118
127
|
# Example of msg with query output: The Oracle base remains unchanged with value /oracle\n\nVALUE\n3\n
|
119
|
-
%{su - #{@su_user} -c "
|
128
|
+
%{su - #{@su_user} -c "#{oracle_echo_str} env ORACLE_SID=#{@service} #{@bin} / as #{@db_role}#{sql_postfix}"}
|
120
129
|
end
|
121
130
|
end
|
122
131
|
|