inspec-core 4.21.1 → 4.22.22
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 +1 -1
- data/inspec-core.gemspec +2 -4
- data/lib/bundles/inspec-supermarket/cli.rb +1 -1
- data/lib/inspec/base_cli.rb +2 -2
- data/lib/inspec/config.rb +0 -1
- data/lib/inspec/exceptions.rb +1 -0
- data/lib/inspec/input_registry.rb +2 -1
- data/lib/inspec/metadata.rb +6 -1
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +11 -5
- data/lib/inspec/profile.rb +30 -9
- data/lib/inspec/reporters.rb +0 -3
- data/lib/inspec/reporters/automate.rb +3 -3
- data/lib/inspec/reporters/base.rb +11 -5
- data/lib/inspec/reporters/cli.rb +1 -0
- data/lib/inspec/reporters/json.rb +9 -4
- data/lib/inspec/resources/apt.rb +2 -0
- 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 +1 -1
- data/lib/inspec/resources/service.rb +2 -2
- 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.rb +1 -1
- data/lib/inspec/run_data/profile.rb +4 -4
- data/lib/inspec/runner.rb +8 -2
- data/lib/inspec/runner_rspec.rb +4 -1
- data/lib/inspec/schema.rb +2 -0
- data/lib/inspec/schema/exec_json.rb +4 -3
- data/lib/inspec/schema/primitives.rb +1 -1
- data/lib/inspec/utils/parser.rb +1 -1
- 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-reporter-html2/templates/profile.html.erb +5 -2
- data/lib/plugins/inspec-reporter-junit/README.md +15 -0
- data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit.rb +12 -0
- data/lib/{inspec/reporters/junit.rb → plugins/inspec-reporter-junit/lib/inspec-reporter-junit/reporter.rb} +22 -26
- data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit/version.rb +5 -0
- metadata +16 -34
- data/README.md +0 -474
@@ -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
|
|
@@ -47,7 +47,7 @@ module Inspec::Resources
|
|
47
47
|
|
48
48
|
def query(query, db = [])
|
49
49
|
psql_cmd = create_psql_cmd(query, db)
|
50
|
-
cmd = inspec.command(psql_cmd)
|
50
|
+
cmd = inspec.command(psql_cmd, redact_regex: /(PGPASSWORD=').+(' psql .*)/)
|
51
51
|
out = cmd.stdout + "\n" + cmd.stderr
|
52
52
|
if cmd.exit_status != 0 || out =~ /could not connect to .*/ || out.downcase =~ /^error:.*/
|
53
53
|
Lines.new(out, "PostgreSQL query with errors: #{query}")
|
@@ -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) ||
|
@@ -154,7 +154,7 @@ module Inspec::Resources
|
|
154
154
|
end
|
155
155
|
when "wrlinux"
|
156
156
|
SysV.new(inspec, service_ctl)
|
157
|
-
when "mac_os_x"
|
157
|
+
when "mac_os_x", "darwin"
|
158
158
|
LaunchCtl.new(inspec, service_ctl)
|
159
159
|
when "freebsd"
|
160
160
|
BSDInit.new(inspec, service_ctl)
|
@@ -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
|
data/lib/inspec/run_data.rb
CHANGED
@@ -47,7 +47,7 @@ module Inspec
|
|
47
47
|
# core reporters have been migrated to plugins. It is probable that new data elements
|
48
48
|
# and new Hash compatibility behavior will be added during the core reporter plugin
|
49
49
|
# conversion process.
|
50
|
-
SCHEMA_VERSION = "0.
|
50
|
+
SCHEMA_VERSION = "0.2.0".freeze
|
51
51
|
|
52
52
|
def self.compatible_schema?(constraints)
|
53
53
|
reqs = Gem::Requirement.create(constraints)
|
@@ -15,7 +15,7 @@ module Inspec
|
|
15
15
|
:summary,
|
16
16
|
:supports, # complex local
|
17
17
|
:parent_profile,
|
18
|
-
:
|
18
|
+
:status_message,
|
19
19
|
:waiver_data, # Undocumented but used in JSON reporter - should not be?
|
20
20
|
:title,
|
21
21
|
:version
|
@@ -40,7 +40,7 @@ module Inspec
|
|
40
40
|
title
|
41
41
|
version
|
42
42
|
parent_profile
|
43
|
-
|
43
|
+
status_message
|
44
44
|
waiver_data
|
45
45
|
}.each do |field|
|
46
46
|
self[field] = raw_prof_data[field]
|
@@ -51,11 +51,11 @@ module Inspec
|
|
51
51
|
class Profile
|
52
52
|
# Good candidate for keyword_init, but that is not in 2.4
|
53
53
|
Dependency = Struct.new(
|
54
|
-
:name, :path, :status, :
|
54
|
+
:name, :path, :status, :status_message, :git, :url, :compliance, :supermarket, :branch, :tag, :commit, :version, :relative_path
|
55
55
|
) do
|
56
56
|
include HashLikeStruct
|
57
57
|
def initialize(raw_dep_data)
|
58
|
-
%i{name path status
|
58
|
+
%i{name path status status_message git url supermarket compliance branch tag commit version relative_path}.each { |f| self[f] = raw_dep_data[f] }
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
data/lib/inspec/runner.rb
CHANGED
@@ -115,8 +115,14 @@ module Inspec
|
|
115
115
|
@test_collector.add_profile(requirement.profile)
|
116
116
|
end
|
117
117
|
|
118
|
-
|
119
|
-
|
118
|
+
begin
|
119
|
+
tests = profile.collect_tests
|
120
|
+
all_controls += tests unless tests.nil?
|
121
|
+
rescue Inspec::Exceptions::ProfileLoadFailed => e
|
122
|
+
Inspec::Log.error "Failed to load profile #{profile.name}: #{e}"
|
123
|
+
profile.set_status_message e.to_s
|
124
|
+
next
|
125
|
+
end
|
120
126
|
end
|
121
127
|
|
122
128
|
all_controls.each do |rule|
|
data/lib/inspec/runner_rspec.rb
CHANGED
@@ -90,9 +90,12 @@ module Inspec
|
|
90
90
|
return @rspec_exit_code if @formatter.results.empty?
|
91
91
|
|
92
92
|
stats = @formatter.results[:statistics][:controls]
|
93
|
+
load_failures = @formatter.results[:profiles]&.select { |p| p[:status] == "failed" }&.any?
|
93
94
|
skipped = @formatter.results.dig(:profiles, 0, :status) == "skipped"
|
94
|
-
if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped
|
95
|
+
if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped && !load_failures
|
95
96
|
0
|
97
|
+
elsif load_failures
|
98
|
+
@conf["distinct_exit"] ? 102 : 1
|
96
99
|
elsif stats[:failed][:total] > 0
|
97
100
|
@conf["distinct_exit"] ? 100 : 1
|
98
101
|
elsif stats[:skipped][:total] > 0 || skipped
|
data/lib/inspec/schema.rb
CHANGED
@@ -147,6 +147,8 @@ module Inspec
|
|
147
147
|
"license" => { "type" => "string", "optional" => true },
|
148
148
|
"summary" => { "type" => "string", "optional" => true },
|
149
149
|
"status" => { "type" => "string", "optional" => false },
|
150
|
+
"status_message" => { "type" => "string", "optional" => true },
|
151
|
+
# skip_message is deprecated, status_message should be used to store the reason for skipping
|
150
152
|
"skip_message" => { "type" => "string", "optional" => true },
|
151
153
|
|
152
154
|
"supports" => {
|
@@ -83,7 +83,7 @@ module Inspec
|
|
83
83
|
"required" => %w{name sha256 supports attributes groups controls},
|
84
84
|
# Name is mandatory in inspec.yml.
|
85
85
|
# supports, controls, groups, and attributes are always present, even if empty
|
86
|
-
# sha256, status,
|
86
|
+
# sha256, status, status_message
|
87
87
|
"properties" => {
|
88
88
|
# These are provided in inspec.yml
|
89
89
|
"name" => Primitives::STRING,
|
@@ -100,10 +100,11 @@ module Inspec
|
|
100
100
|
"description" => Primitives::STRING,
|
101
101
|
"inspec_version" => Primitives::STRING,
|
102
102
|
|
103
|
-
# These are generated at runtime, and all except skip_message are guaranteed
|
103
|
+
# These are generated at runtime, and all except status_message and skip_message are guaranteed
|
104
104
|
"sha256" => Primitives::STRING,
|
105
105
|
"status" => Primitives::STRING,
|
106
|
-
"
|
106
|
+
"status_message" => Primitives::STRING, # If skipped or failed to load, why
|
107
|
+
"skip_message" => Primitives::STRING, # Deprecated field storing reason for skipping. status_message should be used instead.
|
107
108
|
"controls" => Primitives.array(CONTROL.ref),
|
108
109
|
"groups" => Primitives.array(Primitives::CONTROL_GROUP.ref),
|
109
110
|
"attributes" => Primitives.array(Primitives::INPUT),
|
data/lib/inspec/utils/parser.rb
CHANGED
data/lib/inspec/version.rb
CHANGED
@@ -22,7 +22,7 @@ module InspecPlugins
|
|
22
22
|
# return all compliance profiles available for the user
|
23
23
|
# the user is either specified in the options hash or by default
|
24
24
|
# the username of the account is used that is logged in
|
25
|
-
def self.profiles(config, profile_filter = nil) # rubocop:disable PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
|
25
|
+
def self.profiles(config, profile_filter = nil) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
|
26
26
|
owner = config["owner"] || config["user"]
|
27
27
|
|
28
28
|
# Chef Compliance
|
@@ -81,13 +81,13 @@ module InspecPlugins
|
|
81
81
|
mapped_profiles.select! do |p|
|
82
82
|
(!ver || p["version"] == ver) && (!id || p["name"] == id)
|
83
83
|
end
|
84
|
-
|
84
|
+
[msg, mapped_profiles]
|
85
85
|
when "401"
|
86
86
|
msg = "401 Unauthorized. Please check your token."
|
87
|
-
|
87
|
+
[msg, []]
|
88
88
|
else
|
89
89
|
msg = "An unexpected error occurred (HTTP #{response_code}): #{response.message}"
|
90
|
-
|
90
|
+
[msg, []]
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|