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/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
|
|