inspec-core 4.22.1 → 4.23.11
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 +2 -1
- data/inspec-core.gemspec +3 -5
- data/lib/bundles/inspec-supermarket/cli.rb +1 -1
- data/lib/inspec/base_cli.rb +11 -1
- data/lib/inspec/cli.rb +4 -2
- data/lib/inspec/config.rb +19 -1
- data/lib/inspec/input.rb +4 -3
- data/lib/inspec/input_registry.rb +7 -1
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +4 -25
- data/lib/inspec/reporters.rb +0 -3
- data/lib/inspec/reporters/automate.rb +3 -3
- data/lib/inspec/reporters/base.rb +7 -23
- data/lib/inspec/resources/apt.rb +5 -5
- data/lib/inspec/resources/bridge.rb +1 -1
- data/lib/inspec/resources/host.rb +1 -1
- data/lib/inspec/resources/mount.rb +1 -1
- data/lib/inspec/resources/mysql_session.rb +31 -8
- data/lib/inspec/resources/postgres.rb +1 -1
- data/lib/inspec/resources/postgres_session.rb +6 -4
- data/lib/inspec/resources/processes.rb +1 -1
- data/lib/inspec/resources/service.rb +1 -1
- data/lib/inspec/resources/users.rb +1 -1
- data/lib/inspec/resources/windows_firewall.rb +110 -0
- data/lib/inspec/resources/windows_firewall_rule.rb +137 -0
- data/lib/inspec/run_data/profile.rb +3 -2
- data/lib/inspec/schema/exec_json.rb +1 -1
- data/lib/inspec/shell.rb +3 -3
- data/lib/inspec/utils/parser.rb +1 -1
- data/lib/inspec/utils/run_data_filters.rb +104 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb +4 -4
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +1 -1
- data/lib/plugins/inspec-init/templates/profiles/aws/README.md +1 -1
- data/lib/plugins/inspec-reporter-html2/README.md +1 -1
- data/lib/plugins/inspec-reporter-junit/README.md +17 -0
- data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit.rb +21 -0
- data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit/reporter.rb +155 -0
- data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit/version.rb +5 -0
- data/lib/plugins/shared/core_plugin_test_helper.rb +0 -16
- metadata +17 -34
- data/README.md +0 -474
- data/lib/inspec/reporters/junit.rb +0 -77
@@ -19,7 +19,7 @@ module Inspec::Resources
|
|
19
19
|
@conf_path = File.join @conf_dir, "postgresql.conf"
|
20
20
|
else
|
21
21
|
@conf_path = nil
|
22
|
-
|
22
|
+
skip_resource "Seems like PostgreSQL is not installed on your system"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -26,12 +26,13 @@ module Inspec::Resources
|
|
26
26
|
supports platform: "windows"
|
27
27
|
desc "Use the postgres_session InSpec audit resource to test SQL commands run against a PostgreSQL database."
|
28
28
|
example <<~EXAMPLE
|
29
|
-
sql = postgres_session('username', 'password', 'host')
|
29
|
+
sql = postgres_session('username', 'password', 'host', 'port')
|
30
30
|
query('sql_query', ['database_name'])` contains the query and (optional) database to execute
|
31
31
|
|
32
32
|
# default values:
|
33
33
|
# username: 'postgres'
|
34
34
|
# host: 'localhost'
|
35
|
+
# port: 5432
|
35
36
|
# db: databse == db_user running the sql query
|
36
37
|
|
37
38
|
describe sql.query('SELECT * FROM pg_shadow WHERE passwd IS NULL;') do
|
@@ -39,15 +40,16 @@ module Inspec::Resources
|
|
39
40
|
end
|
40
41
|
EXAMPLE
|
41
42
|
|
42
|
-
def initialize(user, pass, host = nil)
|
43
|
+
def initialize(user, pass, host = nil, port = nil)
|
43
44
|
@user = user || "postgres"
|
44
45
|
@pass = pass
|
45
46
|
@host = host || "localhost"
|
47
|
+
@port = port || 5432
|
46
48
|
end
|
47
49
|
|
48
50
|
def query(query, db = [])
|
49
51
|
psql_cmd = create_psql_cmd(query, db)
|
50
|
-
cmd = inspec.command(psql_cmd)
|
52
|
+
cmd = inspec.command(psql_cmd, redact_regex: /(PGPASSWORD=').+(' psql .*)/)
|
51
53
|
out = cmd.stdout + "\n" + cmd.stderr
|
52
54
|
if cmd.exit_status != 0 || out =~ /could not connect to .*/ || out.downcase =~ /^error:.*/
|
53
55
|
Lines.new(out, "PostgreSQL query with errors: #{query}")
|
@@ -64,7 +66,7 @@ module Inspec::Resources
|
|
64
66
|
|
65
67
|
def create_psql_cmd(query, db = [])
|
66
68
|
dbs = db.map { |x| "-d #{x}" }.join(" ")
|
67
|
-
"PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h #{@host} -A -t -c #{escaped_query(query)}"
|
69
|
+
"PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h #{@host} -p #{@port} -A -t -c #{escaped_query(query)}"
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
@@ -138,7 +138,7 @@ module Inspec::Resources
|
|
138
138
|
command: 8,
|
139
139
|
}
|
140
140
|
else
|
141
|
-
command = "ps
|
141
|
+
command = "ps wwaxo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command"
|
142
142
|
regex = /^(.+?)\s+(\d+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
143
143
|
field_map = {
|
144
144
|
label: 1,
|
@@ -141,7 +141,7 @@ module Inspec::Resources
|
|
141
141
|
elsif version > 0
|
142
142
|
SysV.new(inspec, service_ctl || "/usr/sbin/service")
|
143
143
|
end
|
144
|
-
when "redhat", "fedora", "centos", "oracle", "cloudlinux"
|
144
|
+
when "redhat", "fedora", "centos", "oracle", "cloudlinux", "scientific"
|
145
145
|
version = os[:release].to_i
|
146
146
|
|
147
147
|
systemd = ((platform != "fedora" && version >= 7) ||
|
@@ -20,7 +20,7 @@ module Inspec::Resources
|
|
20
20
|
WindowsUser.new(inspec)
|
21
21
|
elsif ["darwin"].include?(os[:family])
|
22
22
|
DarwinUser.new(inspec)
|
23
|
-
elsif ["
|
23
|
+
elsif ["bsd"].include?(os[:family])
|
24
24
|
FreeBSDUser.new(inspec)
|
25
25
|
elsif ["aix"].include?(os[:family])
|
26
26
|
AixUser.new(inspec)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class WindowsFirewall < Inspec.resource(1)
|
3
|
+
name "windows_firewall"
|
4
|
+
supports platform: "windows"
|
5
|
+
desc "Check properties of the Windows Firewall for a specific profile."
|
6
|
+
example <<~EXAMPLE
|
7
|
+
describe windows_firewall("Public") do
|
8
|
+
it { should be_enabled }
|
9
|
+
its("default_inbound_action") { should_not cmp "NotConfigured" }
|
10
|
+
its("num_rules") { should be 19 }
|
11
|
+
end
|
12
|
+
EXAMPLE
|
13
|
+
|
14
|
+
def initialize(profile = "Public")
|
15
|
+
@profile = profile
|
16
|
+
@state = {}
|
17
|
+
|
18
|
+
load_profile_cmd = load_firewall_profile(profile)
|
19
|
+
cmd = inspec.powershell(load_profile_cmd)
|
20
|
+
|
21
|
+
@state = JSON.load(cmd.stdout) unless cmd.stdout.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"Windows Firewall (Profile #{@profile})"
|
26
|
+
end
|
27
|
+
|
28
|
+
def exist?
|
29
|
+
!@state.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def enabled?
|
33
|
+
@state["enabled"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_inbound_allowed?
|
37
|
+
@state["default_inbound_action"] == "Allow"
|
38
|
+
end
|
39
|
+
|
40
|
+
def default_outbound_allowed?
|
41
|
+
@state["default_outbound_action"] == "Allow"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Access to return values from Powershell via `its("PROPERTY")` and `have_PROPERTY "VALUE"`
|
45
|
+
def method_missing(method_name, *arguments, &_block)
|
46
|
+
property = normalize_for_have_access(method_name)
|
47
|
+
|
48
|
+
if method_name.to_s.start_with? "has_"
|
49
|
+
expected_value = arguments.first
|
50
|
+
respond_to_have(property, expected_value)
|
51
|
+
else
|
52
|
+
access_property(property)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def respond_to_missing?(method_name, _include_private = false)
|
57
|
+
property = normalize_for_have_access(method_name)
|
58
|
+
|
59
|
+
@state.key? property
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def normalize_for_have_access(property)
|
65
|
+
property.to_s
|
66
|
+
.delete_prefix("has_")
|
67
|
+
.delete_suffix("?")
|
68
|
+
end
|
69
|
+
|
70
|
+
def access_property(property)
|
71
|
+
@state[property]
|
72
|
+
end
|
73
|
+
|
74
|
+
def respond_to_have(property, value)
|
75
|
+
@state[property] == value
|
76
|
+
end
|
77
|
+
|
78
|
+
def load_firewall_profile(profile_name)
|
79
|
+
<<-EOH
|
80
|
+
Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M
|
81
|
+
$profile = Get-NetFirewallProfile -Name "#{profile_name}"
|
82
|
+
$count = @($profile | Get-NetFirewallRule).Count
|
83
|
+
([PSCustomObject]@{
|
84
|
+
profile_name = $profile.Name
|
85
|
+
profile = $profile.Profile.ToString()
|
86
|
+
description = $profile.Description
|
87
|
+
enabled = [bool]::Parse($profile.Enabled.ToString())
|
88
|
+
default_inbound_action = $profile.DefaultInboundAction.ToString()
|
89
|
+
default_outbound_action = $profile.DefaultOutboundAction.ToString()
|
90
|
+
|
91
|
+
allow_inbound_rules = $profile.AllowInboundRules.ToString()
|
92
|
+
allow_local_firewall_rules = $profile.AllowLocalFirewallRules.ToString()
|
93
|
+
allow_local_ipsec_rules = $profile.AllowLocalIPsecRules.ToString()
|
94
|
+
allow_user_apps = $profile.AllowUserApps.ToString()
|
95
|
+
allow_user_ports = $profile.AllowUserPorts.ToString()
|
96
|
+
allow_unicast_response_to_multicast = $profile.AllowUnicastResponseToMulticast.ToString()
|
97
|
+
|
98
|
+
notify_on_listen = $profile.NotifyOnListen.ToString()
|
99
|
+
enable_stealth_mode_for_ipsec = $profile.EnableStealthModeForIPsec.ToString()
|
100
|
+
log_max_size_kilobytes = $profile.LogMaxSizeKilobytes
|
101
|
+
log_allowed = $profile.LogAllowed.ToString()
|
102
|
+
log_blocked = $profile.LogBlocked.ToString()
|
103
|
+
log_ignored = $profile.LogIgnored.ToString()
|
104
|
+
|
105
|
+
num_rules = $count
|
106
|
+
}) | ConvertTo-Json
|
107
|
+
EOH
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class WindowsFirewallRule < Inspec.resource(1)
|
3
|
+
name "windows_firewall_rule"
|
4
|
+
supports platform: "windows"
|
5
|
+
desc "Check properties of a Windows Firewall rule."
|
6
|
+
example <<~EXAMPLE
|
7
|
+
describe windows_firewall_rule("Name") do
|
8
|
+
it { should exist }
|
9
|
+
it { should be_enabled }
|
10
|
+
|
11
|
+
it { should be_outbound}
|
12
|
+
it { should be_tcp }
|
13
|
+
it { should have_remote_port 80 }
|
14
|
+
end
|
15
|
+
EXAMPLE
|
16
|
+
|
17
|
+
def initialize(name)
|
18
|
+
@name = name
|
19
|
+
@state = {}
|
20
|
+
|
21
|
+
query = load_firewall_state(name)
|
22
|
+
cmd = inspec.powershell(query)
|
23
|
+
@state = JSON.load(cmd.stdout) unless cmd.stdout.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"Windows Firewall Rule #{@name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def exist?
|
31
|
+
!@state.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def enabled?
|
35
|
+
@state["enabled"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def allowed?
|
39
|
+
@state["action"] == "Allow"
|
40
|
+
end
|
41
|
+
|
42
|
+
def inbound?
|
43
|
+
@state["direction"] == "Inbound"
|
44
|
+
end
|
45
|
+
|
46
|
+
def outbound?
|
47
|
+
! inbound?
|
48
|
+
end
|
49
|
+
|
50
|
+
def tcp?
|
51
|
+
@state["protocol"] == "TCP"
|
52
|
+
end
|
53
|
+
|
54
|
+
def udp?
|
55
|
+
@state["protocol"] == "UDP"
|
56
|
+
end
|
57
|
+
|
58
|
+
def icmp?
|
59
|
+
@state["protocol"].start_with? "ICMP"
|
60
|
+
end
|
61
|
+
|
62
|
+
def icmpv4?
|
63
|
+
@state["protocol"] == "ICMPv4"
|
64
|
+
end
|
65
|
+
|
66
|
+
def icmpv6?
|
67
|
+
@state["protocol"] == "ICMPv6"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Access to return values from Powershell via `its("PROPERTY")` and `have_PROPERTY? "VALUE"`
|
71
|
+
def method_missing(method_name, *arguments, &_block)
|
72
|
+
property = normalize_for_have_access(method_name)
|
73
|
+
|
74
|
+
if method_name.to_s.start_with? "has_"
|
75
|
+
expected_value = arguments.first
|
76
|
+
respond_to_have(property, expected_value)
|
77
|
+
else
|
78
|
+
access_property(property)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def respond_to_missing?(method_name, _include_private = false)
|
83
|
+
property = normalize_for_have_access(method_name)
|
84
|
+
|
85
|
+
@state.key? property
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def normalize_for_have_access(property)
|
91
|
+
property.to_s
|
92
|
+
.delete_prefix("has_")
|
93
|
+
.delete_suffix("?")
|
94
|
+
end
|
95
|
+
|
96
|
+
def access_property(property)
|
97
|
+
@state[property]
|
98
|
+
end
|
99
|
+
|
100
|
+
def respond_to_have(property, value)
|
101
|
+
@state[property] == value
|
102
|
+
end
|
103
|
+
|
104
|
+
# Taken from Chef, but changed `firewall_action` to `action` for consistency
|
105
|
+
# @see https://github.com/chef/chef/blob/master/lib/chef/resource/windows_firewall_rule.rb
|
106
|
+
def load_firewall_state(rule_name)
|
107
|
+
<<-EOH
|
108
|
+
Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M
|
109
|
+
$rule = Get-NetFirewallRule -Name "#{rule_name}"
|
110
|
+
$addressFilter = $rule | Get-NetFirewallAddressFilter
|
111
|
+
$portFilter = $rule | Get-NetFirewallPortFilter
|
112
|
+
$applicationFilter = $rule | Get-NetFirewallApplicationFilter
|
113
|
+
$serviceFilter = $rule | Get-NetFirewallServiceFilter
|
114
|
+
$interfaceTypeFilter = $rule | Get-NetFirewallInterfaceTypeFilter
|
115
|
+
([PSCustomObject]@{
|
116
|
+
rule_name = $rule.Name
|
117
|
+
description = $rule.Description
|
118
|
+
displayname = $rule.DisplayName
|
119
|
+
group = $rule.Group
|
120
|
+
local_address = $addressFilter.LocalAddress
|
121
|
+
local_port = $portFilter.LocalPort
|
122
|
+
remote_address = $addressFilter.RemoteAddress
|
123
|
+
remote_port = $portFilter.RemotePort
|
124
|
+
direction = $rule.Direction.ToString()
|
125
|
+
protocol = $portFilter.Protocol
|
126
|
+
icmp_type = $portFilter.IcmpType
|
127
|
+
action = $rule.Action.ToString()
|
128
|
+
profile = $rule.Profile.ToString()
|
129
|
+
program = $applicationFilter.Program
|
130
|
+
service = $serviceFilter.Service
|
131
|
+
interface_type = $interfaceTypeFilter.InterfaceType.ToString()
|
132
|
+
enabled = [bool]::Parse($rule.Enabled.ToString())
|
133
|
+
}) | ConvertTo-Json
|
134
|
+
EOH
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -96,11 +96,12 @@ module Inspec
|
|
96
96
|
# There are probably others
|
97
97
|
:value,
|
98
98
|
:type,
|
99
|
-
:required
|
99
|
+
:required,
|
100
|
+
:sensitive
|
100
101
|
) do
|
101
102
|
include HashLikeStruct
|
102
103
|
def initialize(raw_opts_data)
|
103
|
-
%i{value type required}.each { |f| self[f] = raw_opts_data[f] }
|
104
|
+
%i{value type required sensitive}.each { |f| self[f] = raw_opts_data[f] }
|
104
105
|
end
|
105
106
|
end
|
106
107
|
end
|
@@ -74,7 +74,7 @@ module Inspec
|
|
74
74
|
},
|
75
75
|
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT])
|
76
76
|
|
77
|
-
# Based loosely on https://
|
77
|
+
# Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019
|
78
78
|
# However, concessions were made to the reality of current reporters, specifically
|
79
79
|
# with how description is omitted and version/inspec_version aren't as advertised online
|
80
80
|
PROFILE = Primitives::SchemaType.new("Exec JSON Profile", {
|
data/lib/inspec/shell.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
autoload :Pry, "pry"
|
2
2
|
|
3
3
|
module Inspec
|
4
4
|
# A pry based shell for inspec. Given a runner (with a configured backend and
|
@@ -137,7 +137,7 @@ module Inspec
|
|
137
137
|
end
|
138
138
|
|
139
139
|
info += "#{mark "Web Reference:"}\n\n"
|
140
|
-
info += "https://
|
140
|
+
info += "https://docs.chef.io/inspec/resources/#{topic}\n\n"
|
141
141
|
puts info
|
142
142
|
else
|
143
143
|
begin
|
@@ -208,7 +208,7 @@ module Inspec
|
|
208
208
|
|
209
209
|
its('content') { should_not match /^MyKey:\\s+some value/ }
|
210
210
|
|
211
|
-
For more examples, see: https://
|
211
|
+
For more examples, see: https://docs.chef.io/inspec/matchers/
|
212
212
|
|
213
213
|
EOL
|
214
214
|
end
|
data/lib/inspec/utils/parser.rb
CHANGED
@@ -0,0 +1,104 @@
|
|
1
|
+
module Inspec
|
2
|
+
module Utils
|
3
|
+
# RunDataFilters is a mixin for core Reporters and plugin reporters.
|
4
|
+
# The methods operate on the run_data Hash (prior to any conversion to a
|
5
|
+
# full RunData object).
|
6
|
+
# All methods here operate using the run_data accessor and modify
|
7
|
+
# its contents in place (if needed).
|
8
|
+
module RunDataFilters
|
9
|
+
|
10
|
+
# Long name, but we want to be clear this operates on the Hash
|
11
|
+
# This is the only method that client libraries need to call; any future
|
12
|
+
# feature growth should be handled internally here.
|
13
|
+
def apply_run_data_filters_to_hash
|
14
|
+
@config[:runtime_config] = Inspec::Config.cached || {}
|
15
|
+
apply_report_resize_options
|
16
|
+
redact_sensitive_inputs
|
17
|
+
suppress_diff_output
|
18
|
+
sort_controls
|
19
|
+
end
|
20
|
+
|
21
|
+
# Apply options such as message truncation and removal of backtraces
|
22
|
+
def apply_report_resize_options
|
23
|
+
runtime_config = @config[:runtime_config]
|
24
|
+
|
25
|
+
message_truncation = runtime_config[:reporter_message_truncation] || "ALL"
|
26
|
+
@trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
|
27
|
+
include_backtrace = runtime_config[:reporter_backtrace_inclusion].nil? ? true : runtime_config[:reporter_backtrace_inclusion]
|
28
|
+
|
29
|
+
@run_data[:profiles]&.each do |p|
|
30
|
+
p[:controls].each do |c|
|
31
|
+
c[:results]&.map! do |r|
|
32
|
+
r.delete(:backtrace) unless include_backtrace
|
33
|
+
process_message_truncation(r)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Find any inputs with :sensitive = true and replace their values with "***"
|
40
|
+
def redact_sensitive_inputs
|
41
|
+
@run_data[:profiles]&.each do |p|
|
42
|
+
p[:inputs]&.each do |i|
|
43
|
+
next unless i[:options][:sensitive]
|
44
|
+
|
45
|
+
i[:options][:value] = "***"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Optionally suppress diff output in the message field
|
51
|
+
def suppress_diff_output
|
52
|
+
return if @config[:runtime_config][:diff]
|
53
|
+
|
54
|
+
@run_data[:profiles]&.each do |p|
|
55
|
+
p[:controls]&.each do |c|
|
56
|
+
c[:results]&.each do |r|
|
57
|
+
next unless r[:message] # :message only set on failure
|
58
|
+
|
59
|
+
pos = r[:message].index("\n\nDiff:")
|
60
|
+
next unless pos # Only textual tests get Diffs
|
61
|
+
|
62
|
+
r[:message] = r[:message].slice(0, pos)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Optionally sort controls within each profile in report
|
69
|
+
def sort_controls
|
70
|
+
sort_type = @config[:runtime_config][:sort_results_by]
|
71
|
+
return unless sort_type
|
72
|
+
return if sort_type == "none"
|
73
|
+
|
74
|
+
@run_data[:profiles]&.each do |p|
|
75
|
+
p[:controls] ||= []
|
76
|
+
p[:groups] ||= []
|
77
|
+
|
78
|
+
case sort_type
|
79
|
+
when "control"
|
80
|
+
p[:controls].sort_by! { |c| c[:id] }
|
81
|
+
when "random"
|
82
|
+
p[:controls].shuffle!
|
83
|
+
when "file"
|
84
|
+
# Sort the controls by file, but preserve order within the file.
|
85
|
+
# Files are called "groups" in the run_data, and the filename is in the id.
|
86
|
+
sorted_control_ids = p[:groups].sort_by { |g| g[:id] }.map { |g| g[:controls] }.flatten
|
87
|
+
controls_by_id = {}
|
88
|
+
p[:controls].each { |c| controls_by_id[c[:id]] = c }
|
89
|
+
p[:controls] = sorted_control_ids.map { |cid| controls_by_id[cid] }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def process_message_truncation(result)
|
97
|
+
if result.key?(:message) && result[:message] != "" && @trunc > -1 && result[:message].length > @trunc
|
98
|
+
result[:message] = result[:message][0...@trunc] + "[Truncated to #{@trunc} characters]"
|
99
|
+
end
|
100
|
+
result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|