inspec-core 4.50.3 → 5.7.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -11
- data/etc/deprecations.json +12 -11
- data/inspec-core.gemspec +2 -2
- data/lib/bundles/inspec-supermarket/README.md +21 -2
- data/lib/bundles/inspec-supermarket/cli.rb +20 -3
- data/lib/bundles/inspec-supermarket/target.rb +3 -2
- data/lib/inspec/base_cli.rb +22 -2
- data/lib/inspec/cli.rb +15 -0
- data/lib/inspec/config.rb +5 -1
- data/lib/inspec/dependencies/requirement.rb +2 -1
- data/lib/inspec/dependency_installer.rb +74 -0
- data/lib/inspec/dependency_loader.rb +97 -0
- data/lib/inspec/dsl.rb +11 -2
- data/lib/inspec/errors.rb +7 -0
- data/lib/inspec/formatters/base.rb +31 -6
- data/lib/inspec/library_eval_context.rb +2 -0
- data/lib/inspec/metadata.rb +36 -0
- data/lib/inspec/plugin/v1/registry.rb +1 -1
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +53 -0
- data/lib/inspec/profile.rb +63 -0
- data/lib/inspec/profile_context.rb +1 -6
- data/lib/inspec/reporters/automate.rb +1 -1
- data/lib/inspec/reporters/cli.rb +1 -1
- data/lib/inspec/reporters/json.rb +31 -11
- data/lib/inspec/resource.rb +6 -0
- data/lib/inspec/resources/auditd.rb +5 -4
- data/lib/inspec/resources/bash.rb +2 -0
- data/lib/inspec/resources/cron.rb +49 -0
- data/lib/inspec/resources/file.rb +38 -0
- data/lib/inspec/resources/firewalld.rb +83 -9
- data/lib/inspec/resources/grub_conf.rb +1 -1
- data/lib/inspec/resources/http.rb +31 -2
- data/lib/inspec/resources/ibmdb2_session.rb +2 -2
- data/lib/inspec/resources/ipfilter.rb +59 -0
- data/lib/inspec/resources/ipnat.rb +58 -0
- data/lib/inspec/resources/iptables.rb +18 -2
- data/lib/inspec/resources/kernel_parameters.rb +58 -0
- data/lib/inspec/resources/mssql_session.rb +11 -3
- data/lib/inspec/resources/oracledb_session.rb +3 -1
- data/lib/inspec/resources/package.rb +74 -1
- data/lib/inspec/resources/packages.rb +21 -0
- data/lib/inspec/resources/registry_key.rb +30 -0
- data/lib/inspec/resources/selinux.rb +6 -1
- data/lib/inspec/resources/service.rb +58 -9
- data/lib/inspec/resources/ssl.rb +7 -0
- data/lib/inspec/resources/timezone.rb +65 -0
- data/lib/inspec/resources.rb +5 -16
- data/lib/inspec/runner.rb +18 -1
- data/lib/inspec/runner_rspec.rb +45 -0
- data/lib/inspec/schema/exec_json.rb +59 -58
- data/lib/inspec/schema/exec_json_min.rb +16 -16
- data/lib/inspec/schema/primitives.rb +68 -51
- data/lib/inspec/schema/profile_json.rb +27 -27
- data/lib/inspec/schema.rb +1 -0
- data/lib/inspec/ui.rb +1 -0
- data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
- data/lib/inspec/utils/filter.rb +46 -2
- data/lib/inspec/utils/run_data_filters.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec.rb +3 -0
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb +1 -1
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +4 -3
- data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
- data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
- data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
- data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
- data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
- data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
- data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +2 -0
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +3 -0
- data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -0
- data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +112 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
- metadata +25 -5
@@ -121,6 +121,10 @@ module Inspec::Resources
|
|
121
121
|
def max_redirects
|
122
122
|
opts.fetch(:max_redirects, nil)
|
123
123
|
end
|
124
|
+
|
125
|
+
def proxy
|
126
|
+
opts.fetch(:proxy, nil)
|
127
|
+
end
|
124
128
|
end
|
125
129
|
|
126
130
|
class Local < Base
|
@@ -141,12 +145,18 @@ module Inspec::Resources
|
|
141
145
|
def response
|
142
146
|
return @response if @response
|
143
147
|
|
148
|
+
Faraday.ignore_env_proxy = true if proxy == "disable"
|
149
|
+
|
144
150
|
conn = Faraday.new(url: url, headers: request_headers, params: params, ssl: { verify: ssl_verify? }) do |builder|
|
145
151
|
builder.request :url_encoded
|
146
152
|
builder.use FaradayMiddleware::FollowRedirects, limit: max_redirects unless max_redirects.nil?
|
147
153
|
builder.adapter Faraday.default_adapter
|
148
154
|
end
|
149
155
|
|
156
|
+
unless proxy == "disable" || proxy.nil?
|
157
|
+
conn.proxy = proxy
|
158
|
+
end
|
159
|
+
|
150
160
|
# set basic authentication
|
151
161
|
conn.basic_auth username, password unless username.nil? || password.nil?
|
152
162
|
|
@@ -252,6 +262,14 @@ module Inspec::Resources
|
|
252
262
|
cmd << "-X #{http_method}"
|
253
263
|
end
|
254
264
|
|
265
|
+
cmd << "--noproxy '*'" if proxy == "disable"
|
266
|
+
unless proxy == "disable" || proxy.nil?
|
267
|
+
if proxy.is_a?(Hash)
|
268
|
+
cmd << "--proxy #{proxy[:uri]} --proxy-user #{proxy[:user]}:#{proxy[:password]}"
|
269
|
+
else
|
270
|
+
cmd << "--proxy #{proxy}"
|
271
|
+
end
|
272
|
+
end
|
255
273
|
cmd << "--connect-timeout #{open_timeout}"
|
256
274
|
cmd << "--max-time #{open_timeout + read_timeout}"
|
257
275
|
cmd << "--user \'#{username}:#{password}\'" unless username.nil? || password.nil?
|
@@ -292,6 +310,17 @@ module Inspec::Resources
|
|
292
310
|
else
|
293
311
|
cmd << "'#{url}?#{params.map { |e| e.join("=") }.join("&")}'"
|
294
312
|
end
|
313
|
+
|
314
|
+
proxy_script = ""
|
315
|
+
unless proxy == "disable" || proxy.nil?
|
316
|
+
cmd << "-Proxy #{proxy[:uri]}"
|
317
|
+
cmd << "-ProxyCredential $proxyCreds"
|
318
|
+
proxy_script = <<-EOH
|
319
|
+
$secPasswd = ConvertTo-SecureString "#{proxy[:password]}" -AsPlainText -Force
|
320
|
+
$proxyCreds = New-Object System.Management.Automation.PSCredential -ArgumentList "#{proxy[:user]}",$secPasswd
|
321
|
+
EOH
|
322
|
+
end
|
323
|
+
|
295
324
|
command = cmd.join(" ")
|
296
325
|
body = "\'#{request_body}\'"
|
297
326
|
script = <<-EOH
|
@@ -302,10 +331,10 @@ module Inspec::Resources
|
|
302
331
|
foreach ($property in $Body.PSObject.Properties) {
|
303
332
|
$HashTable[$property.Name] = $property.Value
|
304
333
|
}
|
305
|
-
$response = #{command} -Body $HashTable
|
334
|
+
$response = #{command} -Body $HashTable -UseBasicParsing
|
306
335
|
$response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error
|
307
336
|
EOH
|
308
|
-
script.strip
|
337
|
+
proxy_script.strip + "\n" + script.strip
|
309
338
|
end
|
310
339
|
end
|
311
340
|
end
|
@@ -46,12 +46,12 @@ module Inspec::Resources
|
|
46
46
|
|
47
47
|
# check if following specific error is there. Sourcing the db2profile to resolve the error.
|
48
48
|
if cmd.exit_status != 0 && out =~ /SQL10007N Message "-1390" could not be retrieved. Reason code: "3"/
|
49
|
-
cmd = inspec.command(". ~/sqllib/db2profile\; #{@db2_executable_file_path} attach to #{@db_instance}\; #{@db2_executable_file_path} connect to #{@db_name}\; #{@db2_executable_file_path} #{q}\;")
|
49
|
+
cmd = inspec.command(". ~/sqllib/db2profile\; #{@db2_executable_file_path} attach to #{@db_instance}\; #{@db2_executable_file_path} connect to #{@db_name}\; #{@db2_executable_file_path} \"#{q}\"\;")
|
50
50
|
out = cmd.stdout + "\n" + cmd.stderr
|
51
51
|
end
|
52
52
|
elsif inspec.os.platform?("windows")
|
53
53
|
# set-item command set the powershell to run the db2 commands.
|
54
|
-
cmd = inspec.command("set-item -path env:DB2CLP -value \"**$$**\"\; db2 connect to #{@db_name}\; db2 #{q}\;")
|
54
|
+
cmd = inspec.command("set-item -path env:DB2CLP -value \"**$$**\"\; db2 connect to #{@db_name}\; db2 \"#{q}\"\;")
|
55
55
|
out = cmd.stdout + "\n" + cmd.stderr
|
56
56
|
end
|
57
57
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
module Inspec::Resources
|
3
|
+
class IpFilter < Inspec.resource(1)
|
4
|
+
name "ipfilter"
|
5
|
+
supports platform: "bsd"
|
6
|
+
supports platform: "solaris"
|
7
|
+
desc "Use the ipfilter InSpec audit resource to test rules that are defined for ipfilter, which maintains the IP rule set"
|
8
|
+
example <<~EXAMPLE
|
9
|
+
describe ipfilter do
|
10
|
+
it { should have_rule("pass in quick on lo0 all") }
|
11
|
+
end
|
12
|
+
EXAMPLE
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
# checks if the instance is either bsd or solaris
|
16
|
+
return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
|
17
|
+
|
18
|
+
# ensures, all calls are aborted for non-supported os
|
19
|
+
@ipfilter_cache = []
|
20
|
+
skip_resource "The `ipfilter` resource is not supported on your OS yet."
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_rule?(rule = nil)
|
24
|
+
# checks if the rule is part of the ruleset
|
25
|
+
retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
|
26
|
+
end
|
27
|
+
|
28
|
+
def retrieve_rules
|
29
|
+
# this would be true if the OS family was not bsd/solaris when checked in initliaze
|
30
|
+
return @ipfilter_cache if defined?(@ipfilter_cache)
|
31
|
+
|
32
|
+
# construct ipfstat command to read all rules
|
33
|
+
bin = find_ipfstat_or_error
|
34
|
+
ipfstat_cmd = "#{bin} -io"
|
35
|
+
cmd = inspec.command(ipfstat_cmd)
|
36
|
+
|
37
|
+
# Return empty array when command is not executed successfully
|
38
|
+
# or there is no output since no rules are active
|
39
|
+
return [] if cmd.exit_status.to_i != 0 || cmd.stdout == ""
|
40
|
+
|
41
|
+
# split rules, returns array or rules
|
42
|
+
@ipfilter_cache = cmd.stdout.split("\n").map(&:strip)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"Ipfilter"
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def find_ipfstat_or_error
|
52
|
+
%w{/usr/sbin/ipfstat /sbin/ipfstat ipfstat}.each do |cmd|
|
53
|
+
return cmd if inspec.command(cmd).exist?
|
54
|
+
end
|
55
|
+
|
56
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find `ipfstat`"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
module Inspec::Resources
|
3
|
+
class IpNat < Inspec.resource(1)
|
4
|
+
name "ipnat"
|
5
|
+
supports platform: "bsd"
|
6
|
+
supports platform: "solaris"
|
7
|
+
desc "Use the ipnat InSpec audit resource to test rules that are defined for IP NAT"
|
8
|
+
example <<~EXAMPLE
|
9
|
+
describe ipnat do
|
10
|
+
it { should have_rule("map net1 192.168.0.0/24 -> 0/32") }
|
11
|
+
end
|
12
|
+
EXAMPLE
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
# checks if the instance is either bsd or solaris
|
16
|
+
return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
|
17
|
+
|
18
|
+
# ensures, all calls are aborted for non-supported os
|
19
|
+
@ipnat_cache = []
|
20
|
+
skip_resource "The `ipnat` resource is not supported on your OS yet."
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_rule?(rule = nil)
|
24
|
+
# checks if the rule is part of the ruleset
|
25
|
+
retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
|
26
|
+
end
|
27
|
+
|
28
|
+
def retrieve_rules
|
29
|
+
# this would be true if the OS family was not bsd/solaris when checked in initliaze
|
30
|
+
return @ipnat_cache if defined?(@ipnat_cache)
|
31
|
+
|
32
|
+
# construct ipnat command to show the list of current IP NAT table entry mappings
|
33
|
+
bin = find_ipnat_or_error
|
34
|
+
ipnat_cmd = "#{bin} -l"
|
35
|
+
cmd = inspec.command(ipnat_cmd)
|
36
|
+
|
37
|
+
# Return empty array when command is not executed successfully
|
38
|
+
return [] if cmd.exit_status.to_i != 0
|
39
|
+
|
40
|
+
# split rules, returns array or rules
|
41
|
+
@ipnat_cache = cmd.stdout.split("\n").map(&:strip)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"Ipnat"
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def find_ipnat_or_error
|
51
|
+
%w{/usr/sbin/ipnat /sbin/ipnat ipnat}.each do |cmd|
|
52
|
+
return cmd if inspec.command(cmd).exist?
|
53
|
+
end
|
54
|
+
|
55
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find `ipnat`"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -33,6 +33,7 @@ module Inspec::Resources
|
|
33
33
|
def initialize(params = {})
|
34
34
|
@table = params[:table]
|
35
35
|
@chain = params[:chain]
|
36
|
+
@ignore_comments = params[:ignore_comments] || false
|
36
37
|
|
37
38
|
# we're done if we are on linux
|
38
39
|
return if inspec.os.linux?
|
@@ -59,8 +60,13 @@ module Inspec::Resources
|
|
59
60
|
cmd = inspec.command(iptables_cmd)
|
60
61
|
return [] if cmd.exit_status.to_i != 0
|
61
62
|
|
62
|
-
|
63
|
-
|
63
|
+
if @ignore_comments
|
64
|
+
# split rules, returns array or rules without any comment
|
65
|
+
@iptables_cache = remove_comments_from_rules(cmd.stdout.split("\n"))
|
66
|
+
else
|
67
|
+
# split rules, returns array or rules
|
68
|
+
@iptables_cache = cmd.stdout.split("\n").map(&:strip)
|
69
|
+
end
|
64
70
|
end
|
65
71
|
|
66
72
|
def to_s
|
@@ -69,6 +75,16 @@ module Inspec::Resources
|
|
69
75
|
|
70
76
|
private
|
71
77
|
|
78
|
+
def remove_comments_from_rules(rules)
|
79
|
+
rules.each do |rule|
|
80
|
+
next if rule.nil?
|
81
|
+
|
82
|
+
rule.gsub!(/ -m comment --comment "([^"]*)"/, "")
|
83
|
+
rule.strip
|
84
|
+
end
|
85
|
+
rules
|
86
|
+
end
|
87
|
+
|
72
88
|
def find_iptables_or_error
|
73
89
|
%w{/usr/sbin/iptables /sbin/iptables iptables}.each do |cmd|
|
74
90
|
return cmd if inspec.command(cmd).exist?
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class KernelParameters < Inspec.resource(1)
|
3
|
+
name "kernel_parameters"
|
4
|
+
supports platform: "unix"
|
5
|
+
desc "Use the kernel_parameters InSpec audit resource to test kernel parameters on Linux platforms."
|
6
|
+
example <<~EXAMPLE
|
7
|
+
describe kernel_parameters.where(parameter: /^net./ ) do
|
8
|
+
its('parameters') { should include 'net.ipv4.conf.all.forwarding' }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe kernel_parameters.where(parameter: "net.ipv4.conf.all.forwarding") do
|
12
|
+
its('values') { should eq [0] }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe kernel_parameters do
|
16
|
+
its('parameters') { should include 'net.ipv4.conf.all.forwarding' }
|
17
|
+
its('values') { should include 0 }
|
18
|
+
end
|
19
|
+
EXAMPLE
|
20
|
+
|
21
|
+
filter = FilterTable.create
|
22
|
+
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
|
23
|
+
filter.register_column(:parameters, field: "parameter")
|
24
|
+
.register_column(:values, field: "value")
|
25
|
+
filter.install_filter_methods_on_resource(self, :params)
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
# this resource is only supported on Linux
|
29
|
+
return skip_resource "The `kernel_parameters` resource is not supported on your OS." unless inspec.os.linux?
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
"Kernel Parameters"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def params
|
39
|
+
cmd = inspec.command("/sbin/sysctl -a")
|
40
|
+
cmd.exit_status != 0 ? [] : parse_kernel_paramater(cmd.stdout)
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_kernel_paramater(stdout)
|
44
|
+
result = []
|
45
|
+
stdout.split("\n").each do |out|
|
46
|
+
splitted_output = out.split("=").map(&:strip)
|
47
|
+
result.push(
|
48
|
+
{
|
49
|
+
"parameter" => splitted_output[0],
|
50
|
+
"value" => splitted_output[1].to_i,
|
51
|
+
}
|
52
|
+
)
|
53
|
+
end
|
54
|
+
result
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -76,7 +76,7 @@ module Inspec::Resources
|
|
76
76
|
if cmd.exit_status != 0 || out =~ /Sqlcmd: Error/
|
77
77
|
raise Inspec::Exceptions::ResourceFailed, "Could not execute the sql query #{out}"
|
78
78
|
else
|
79
|
-
DatabaseHelper::SQLQueryResult.new(cmd, parse_csv_result(cmd))
|
79
|
+
DatabaseHelper::SQLQueryResult.new(cmd, parse_csv_result(cmd.stdout))
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -94,9 +94,17 @@ module Inspec::Resources
|
|
94
94
|
!query("select getdate()").empty?
|
95
95
|
end
|
96
96
|
|
97
|
-
def parse_csv_result(
|
97
|
+
def parse_csv_result(stdout)
|
98
98
|
require "csv" unless defined?(CSV)
|
99
|
-
|
99
|
+
|
100
|
+
# replaces \n with \r since multiline data in older versions of database returns faulty
|
101
|
+
# formatted multiline data, example name\r\n----\r\nThis is\na multiline field\r\n
|
102
|
+
out = stdout.gsub("\n", "\r")
|
103
|
+
out = out.gsub("\r\r", "\r")
|
104
|
+
|
105
|
+
# row separator used since row delimiters \n (in linux) or \r\n (in windows)
|
106
|
+
# are converted to \r for consistency and handling faulty formatted multiline data
|
107
|
+
table = CSV.parse(out, headers: true, row_sep: "\r")
|
100
108
|
|
101
109
|
# remove first row, since it will be a seperator line
|
102
110
|
table.delete(0)
|
@@ -118,7 +118,9 @@ module Inspec::Resources
|
|
118
118
|
output = output.sub(/\r/, "").strip.gsub(",", "comma_query_sub")
|
119
119
|
converter = ->(header) { header.downcase }
|
120
120
|
CSV.parse(output, headers: true, header_converters: converter).map do |row|
|
121
|
-
|
121
|
+
next if row.entries.flatten.empty?
|
122
|
+
|
123
|
+
revised_row = row.entries.flatten.map { |entry| entry&.gsub("comma_query_sub", ",") }
|
122
124
|
Hashie::Mash.new([revised_row].to_h)
|
123
125
|
end
|
124
126
|
end
|
@@ -26,6 +26,7 @@ module Inspec::Resources
|
|
26
26
|
@cache = nil
|
27
27
|
# select package manager
|
28
28
|
@pkgman = nil
|
29
|
+
@latest_version = nil
|
29
30
|
|
30
31
|
os = inspec.os
|
31
32
|
if os.debian?
|
@@ -60,6 +61,15 @@ module Inspec::Resources
|
|
60
61
|
info[:installed] == true
|
61
62
|
end
|
62
63
|
|
64
|
+
def latest?(_provider = nil, _version = nil)
|
65
|
+
os = inspec.os
|
66
|
+
if os.solaris? || (%w{hpux aix}.include? os[:family])
|
67
|
+
raise Inspec::Exceptions::ResourceSkipped, "The `be_latest` matcher is not supported on your OS yet."
|
68
|
+
end
|
69
|
+
|
70
|
+
(!info[:only_version_no].nil? && !latest_version.nil?) && (info[:only_version_no] == latest_version)
|
71
|
+
end
|
72
|
+
|
63
73
|
# returns true it the package is held (if the OS supports it)
|
64
74
|
def held?(_provider = nil, _version = nil)
|
65
75
|
info[:held] == true
|
@@ -82,6 +92,10 @@ module Inspec::Resources
|
|
82
92
|
info[:version]
|
83
93
|
end
|
84
94
|
|
95
|
+
def latest_version
|
96
|
+
@latest_version ||= ( @pkgman.latest_version(@package_name) || info[:latest_version] )
|
97
|
+
end
|
98
|
+
|
85
99
|
def to_s
|
86
100
|
"System Package #{@package_name}"
|
87
101
|
end
|
@@ -107,6 +121,21 @@ module Inspec::Resources
|
|
107
121
|
# combined into a `ResourceSkipped` exception message.
|
108
122
|
[]
|
109
123
|
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def fetch_latest_version(cmd_string)
|
128
|
+
cmd = inspec.command(cmd_string)
|
129
|
+
if cmd.exit_status != 0
|
130
|
+
raise Inspec::Exceptions::ResourceFailed, "Failed to fetch latest version. Error: #{cmd.stderr}"
|
131
|
+
else
|
132
|
+
fetch_version_no(cmd.stdout)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def fetch_version_no(output)
|
137
|
+
output.scan(/(?:(?:\d+)[.]){2,}(?:\d+)/).max_by { |s| Gem::Version.new(s) } unless output.nil?
|
138
|
+
end
|
110
139
|
end
|
111
140
|
|
112
141
|
# Debian / Ubuntu
|
@@ -124,14 +153,21 @@ module Inspec::Resources
|
|
124
153
|
# If the package is installed and marked hold, Status is "hold ok installed"
|
125
154
|
# If the package is removed and not purged, Status is "deinstall ok config-files" with exit_status 0
|
126
155
|
# If the package is purged cmd fails with non-zero exit status
|
156
|
+
|
127
157
|
{
|
128
158
|
name: params["Package"],
|
129
159
|
installed: params["Status"].split(" ")[2] == "installed",
|
130
160
|
held: params["Status"].split(" ")[0] == "hold",
|
131
161
|
version: params["Version"],
|
132
162
|
type: "deb",
|
163
|
+
only_version_no: fetch_version_no(params["Version"]),
|
133
164
|
}
|
134
165
|
end
|
166
|
+
|
167
|
+
def latest_version(package_name)
|
168
|
+
cmd_string = "apt list #{package_name} -a"
|
169
|
+
fetch_latest_version(cmd_string)
|
170
|
+
end
|
135
171
|
end
|
136
172
|
|
137
173
|
# RHEL family
|
@@ -181,9 +217,15 @@ module Inspec::Resources
|
|
181
217
|
installed: true,
|
182
218
|
version: "#{v}-#{r}",
|
183
219
|
type: "rpm",
|
220
|
+
only_version_no: "#{v}",
|
184
221
|
}
|
185
222
|
end
|
186
223
|
|
224
|
+
def latest_version(package_name)
|
225
|
+
cmd_string = "yum list #{package_name}"
|
226
|
+
fetch_latest_version(cmd_string)
|
227
|
+
end
|
228
|
+
|
187
229
|
private
|
188
230
|
|
189
231
|
def rpm_command(package_name)
|
@@ -216,11 +258,17 @@ module Inspec::Resources
|
|
216
258
|
installed: true,
|
217
259
|
version: pkg["installed"][0]["version"],
|
218
260
|
type: "brew",
|
261
|
+
latest_version: pkg["versions"]["stable"],
|
262
|
+
only_version_no: pkg["installed"][0]["version"],
|
219
263
|
}
|
220
264
|
rescue JSON::ParserError => e
|
221
265
|
raise Inspec::Exceptions::ResourceFailed,
|
222
266
|
"Failed to parse JSON from `brew` command. Error: #{e}"
|
223
267
|
end
|
268
|
+
|
269
|
+
def latest_version(package_name)
|
270
|
+
nil
|
271
|
+
end
|
224
272
|
end
|
225
273
|
|
226
274
|
# Arch Linux
|
@@ -240,8 +288,14 @@ module Inspec::Resources
|
|
240
288
|
installed: true,
|
241
289
|
version: params["Version"],
|
242
290
|
type: "pacman",
|
291
|
+
only_version_no: fetch_version_no(params["Version"]),
|
243
292
|
}
|
244
293
|
end
|
294
|
+
|
295
|
+
def latest_version(package_name)
|
296
|
+
cmd_string = "pacman -Ss #{package_name} | grep #{package_name} | grep installed"
|
297
|
+
fetch_latest_version(cmd_string)
|
298
|
+
end
|
245
299
|
end
|
246
300
|
|
247
301
|
class HpuxPkg < PkgManagement
|
@@ -267,13 +321,20 @@ module Inspec::Resources
|
|
267
321
|
pkg_info = cmd.stdout.split("\n").delete_if { |e| e =~ /^WARNING/i }
|
268
322
|
pkg = pkg_info[0].split(" - ")[0]
|
269
323
|
|
324
|
+
version = pkg.partition("-")[2]
|
270
325
|
{
|
271
326
|
name: pkg.partition("-")[0],
|
272
327
|
installed: true,
|
273
|
-
version:
|
328
|
+
version: version,
|
274
329
|
type: "pkg",
|
330
|
+
only_version_no: fetch_version_no(version),
|
275
331
|
}
|
276
332
|
end
|
333
|
+
|
334
|
+
def latest_version(package_name)
|
335
|
+
cmd_string = "apk info #{package_name}"
|
336
|
+
fetch_latest_version(cmd_string)
|
337
|
+
end
|
277
338
|
end
|
278
339
|
|
279
340
|
class FreebsdPkg < PkgManagement
|
@@ -292,8 +353,14 @@ module Inspec::Resources
|
|
292
353
|
installed: true,
|
293
354
|
version: params["Version"],
|
294
355
|
type: "pkg",
|
356
|
+
only_version_no: params["Version"],
|
295
357
|
}
|
296
358
|
end
|
359
|
+
|
360
|
+
def latest_version(package_name)
|
361
|
+
cmd_string = "pkg version -v | grep #{package_name}"
|
362
|
+
fetch_latest_version(cmd_string)
|
363
|
+
end
|
297
364
|
end
|
298
365
|
|
299
366
|
# Determines the installed packages on Windows using the Windows package registry entries.
|
@@ -339,8 +406,14 @@ module Inspec::Resources
|
|
339
406
|
installed: true,
|
340
407
|
version: package["DisplayVersion"],
|
341
408
|
type: "windows",
|
409
|
+
only_version_no: package["DisplayVersion"],
|
342
410
|
}
|
343
411
|
end
|
412
|
+
|
413
|
+
def latest_version(package_name)
|
414
|
+
cmd_string = "Get-Package #{package_name} -AllVersions"
|
415
|
+
fetch_latest_version(cmd_string)
|
416
|
+
end
|
344
417
|
end
|
345
418
|
|
346
419
|
# AIX
|
@@ -26,6 +26,8 @@ module Inspec::Resources
|
|
26
26
|
@pkgs = Debs.new(inspec)
|
27
27
|
elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family])
|
28
28
|
@pkgs = Rpms.new(inspec)
|
29
|
+
elsif ["alpine"].include?(os[:name])
|
30
|
+
@pkgs = AlpinePkgs.new(inspec)
|
29
31
|
else
|
30
32
|
return skip_resource "The packages resource is not yet supported on OS #{inspec.os.name}"
|
31
33
|
end
|
@@ -108,4 +110,23 @@ module Inspec::Resources
|
|
108
110
|
end
|
109
111
|
end
|
110
112
|
end
|
113
|
+
|
114
|
+
# RedHat family
|
115
|
+
class AlpinePkgs < PkgsManagement
|
116
|
+
def build_package_list
|
117
|
+
command = "apk list --no-network --installed"
|
118
|
+
cmd = inspec.command(command)
|
119
|
+
all = cmd.stdout.split("\n")
|
120
|
+
return [] if all.nil? || cmd.exit_status.to_i != 0
|
121
|
+
|
122
|
+
all.map do |m|
|
123
|
+
next if m =~ /^WARNING/i
|
124
|
+
|
125
|
+
a = m.split(" ")
|
126
|
+
version = a[0].split("-")[-2]
|
127
|
+
name = a[2].gsub(/[{}^]*/, "")
|
128
|
+
PackageStruct.new("installed", name, version, a[1])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
111
132
|
end
|
@@ -105,6 +105,21 @@ module Inspec::Resources
|
|
105
105
|
children_keys(@options[:path], filter)
|
106
106
|
end
|
107
107
|
|
108
|
+
# returns hash containing users / groups and their permission
|
109
|
+
def user_permissions
|
110
|
+
return {} unless exists?
|
111
|
+
|
112
|
+
get_permissions(@options[:path])
|
113
|
+
end
|
114
|
+
|
115
|
+
# returns true if inheritance is enabled for registry key.
|
116
|
+
def inherited?
|
117
|
+
return false unless exists?
|
118
|
+
|
119
|
+
cmd = inspec.command("(Get-Acl -Path 'Registry::#{@options[:path]}').access| Where-Object {$_.IsInherited -eq $true} | measure | % { $_.Count }")
|
120
|
+
cmd.stdout.chomp == "0" ? false : true
|
121
|
+
end
|
122
|
+
|
108
123
|
# returns nil, if not existent or value
|
109
124
|
def method_missing(*keys)
|
110
125
|
# allow the use of array syntax in an `its` block so that users
|
@@ -283,6 +298,21 @@ module Inspec::Resources
|
|
283
298
|
|
284
299
|
key.start_with?("\\") ? key : "\\#{key}"
|
285
300
|
end
|
301
|
+
|
302
|
+
def get_permissions(path)
|
303
|
+
script = <<~EOH
|
304
|
+
$path = '#{path}'
|
305
|
+
$Acl = Get-Acl -Path ('Registry::' + $path)
|
306
|
+
$Result = foreach ($Access in $acl.Access) {
|
307
|
+
[PSCustomObject]@{
|
308
|
+
$Access.IdentityReference = $Access.RegistryRights.ToString()
|
309
|
+
}
|
310
|
+
}
|
311
|
+
$Result | ConvertTo-Json
|
312
|
+
EOH
|
313
|
+
result = inspec.powershell(script)
|
314
|
+
JSON.load(result.stdout).inject(&:merge) unless result.stdout.empty?
|
315
|
+
end
|
286
316
|
end
|
287
317
|
|
288
318
|
class WindowsRegistryKey < RegistryKey
|
@@ -84,8 +84,13 @@ module Inspec::Resources
|
|
84
84
|
|
85
85
|
def initialize(selinux_path = "/etc/selinux/config")
|
86
86
|
@path = selinux_path
|
87
|
-
|
87
|
+
if inspec.os.redhat? && inspec.os.name == "amazon"
|
88
|
+
lcmd = "/usr/sbin/sestatus"
|
89
|
+
else
|
90
|
+
lcmd = "sestatus"
|
91
|
+
end
|
88
92
|
|
93
|
+
cmd = inspec.command(lcmd)
|
89
94
|
if cmd.exit_status != 0
|
90
95
|
# `sestatus` command not found error message comes in stdout so handling both here
|
91
96
|
out = cmd.stdout + "\n" + cmd.stderr
|