inspec-core 4.49.0 → 4.56.17
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/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 +8 -0
- data/lib/inspec/cli.rb +12 -3
- data/lib/inspec/config.rb +5 -1
- data/lib/inspec/dependencies/requirement.rb +2 -1
- data/lib/inspec/formatters/base.rb +8 -6
- data/lib/inspec/globals.rb +5 -0
- data/lib/inspec/library_eval_context.rb +2 -0
- data/lib/inspec/plugin/v1/registry.rb +1 -1
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +10 -0
- data/lib/inspec/profile.rb +2 -0
- data/lib/inspec/profile_context.rb +1 -6
- data/lib/inspec/reporters/automate.rb +1 -1
- data/lib/inspec/reporters/json.rb +1 -1
- data/lib/inspec/resources/auditd.rb +5 -4
- data/lib/inspec/resources/bash.rb +2 -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 +135 -54
- data/lib/inspec/resources/ibmdb2_session.rb +2 -2
- 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 +2 -0
- data/lib/inspec/runner_rspec.rb +30 -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/plugins/inspec-compliance/lib/inspec-compliance/api.rb +1 -1
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +4 -3
- metadata +8 -5
@@ -11,6 +11,7 @@ module Inspec::Resources
|
|
11
11
|
class Http < Inspec.resource(1)
|
12
12
|
name "http"
|
13
13
|
supports platform: "unix"
|
14
|
+
supports platform: "windows"
|
14
15
|
desc "Use the http InSpec audit resource to test http call."
|
15
16
|
example <<~EXAMPLE
|
16
17
|
describe http('http://localhost:8080/ping', auth: {user: 'user', pass: 'test'}, params: {format: 'html'}) do
|
@@ -104,6 +105,7 @@ module Inspec::Resources
|
|
104
105
|
opts[:data]
|
105
106
|
end
|
106
107
|
|
108
|
+
# not supported on Windows
|
107
109
|
def open_timeout
|
108
110
|
opts.fetch(:open_timeout, 60)
|
109
111
|
end
|
@@ -117,7 +119,11 @@ module Inspec::Resources
|
|
117
119
|
end
|
118
120
|
|
119
121
|
def max_redirects
|
120
|
-
opts.fetch(:max_redirects,
|
122
|
+
opts.fetch(:max_redirects, nil)
|
123
|
+
end
|
124
|
+
|
125
|
+
def proxy
|
126
|
+
opts.fetch(:proxy, nil)
|
121
127
|
end
|
122
128
|
end
|
123
129
|
|
@@ -139,12 +145,18 @@ module Inspec::Resources
|
|
139
145
|
def response
|
140
146
|
return @response if @response
|
141
147
|
|
148
|
+
Faraday.ignore_env_proxy = true if proxy == "disable"
|
149
|
+
|
142
150
|
conn = Faraday.new(url: url, headers: request_headers, params: params, ssl: { verify: ssl_verify? }) do |builder|
|
143
151
|
builder.request :url_encoded
|
144
|
-
builder.use FaradayMiddleware::FollowRedirects, limit: max_redirects
|
152
|
+
builder.use FaradayMiddleware::FollowRedirects, limit: max_redirects unless max_redirects.nil?
|
145
153
|
builder.adapter Faraday.default_adapter
|
146
154
|
end
|
147
155
|
|
156
|
+
unless proxy == "disable" || proxy.nil?
|
157
|
+
conn.proxy = proxy
|
158
|
+
end
|
159
|
+
|
148
160
|
# set basic authentication
|
149
161
|
conn.basic_auth username, password unless username.nil? || password.nil?
|
150
162
|
|
@@ -162,98 +174,167 @@ module Inspec::Resources
|
|
162
174
|
attr_reader :inspec
|
163
175
|
|
164
176
|
def initialize(inspec, http_method, url, opts)
|
165
|
-
|
177
|
+
http_cmd = inspec.os.windows? ? "Invoke-WebRequest" : "curl"
|
178
|
+
unless inspec.command(http_cmd).exist?
|
166
179
|
raise Inspec::Exceptions::ResourceSkipped,
|
167
|
-
"
|
180
|
+
"#{http_cmd} is not available on the target machine"
|
168
181
|
end
|
169
|
-
|
170
|
-
@ran_curl = false
|
182
|
+
@ran_http = false
|
171
183
|
@inspec = inspec
|
172
184
|
super(http_method, url, opts)
|
173
185
|
end
|
174
186
|
|
175
187
|
def status
|
176
|
-
|
188
|
+
run_http
|
177
189
|
@status
|
178
190
|
end
|
179
191
|
|
180
192
|
def body
|
181
|
-
|
193
|
+
run_http
|
182
194
|
@body&.strip
|
183
195
|
end
|
184
196
|
|
185
197
|
def response_headers
|
186
|
-
|
198
|
+
run_http
|
187
199
|
@response_headers
|
188
200
|
end
|
189
201
|
|
190
202
|
private
|
191
203
|
|
192
|
-
def
|
193
|
-
return if @
|
204
|
+
def run_http
|
205
|
+
return if @ran_http
|
194
206
|
|
195
|
-
cmd_result = inspec.command(
|
207
|
+
cmd_result = inspec.command(http_command)
|
196
208
|
response = cmd_result.stdout
|
197
|
-
@
|
209
|
+
@ran_http = true
|
198
210
|
return if response.nil? || cmd_result.exit_status != 0
|
199
211
|
|
200
|
-
|
201
|
-
|
212
|
+
if inspec.os.windows?
|
213
|
+
response = JSON.parse(response)
|
202
214
|
|
203
|
-
|
204
|
-
|
205
|
-
loop do
|
206
|
-
break unless remainder =~ %r{^HTTP/}
|
215
|
+
@status = response["StatusCode"]
|
216
|
+
@body = response["Content"]
|
207
217
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
218
|
+
@response_headers = {}
|
219
|
+
response["Headers"].each do |name, value|
|
220
|
+
@response_headers["#{name}"] = value
|
221
|
+
end
|
222
|
+
else
|
223
|
+
# strip any carriage returns to normalize output
|
224
|
+
response.delete!("\r")
|
225
|
+
|
226
|
+
# split the prelude (status line and headers) and the body
|
227
|
+
prelude, remainder = response.split("\n\n", 2)
|
228
|
+
loop do
|
229
|
+
break unless remainder =~ %r{^HTTP/}
|
230
|
+
|
231
|
+
prelude, remainder = remainder.split("\n\n", 2)
|
232
|
+
end
|
233
|
+
@body = remainder
|
234
|
+
prelude = prelude.lines
|
235
|
+
|
236
|
+
# grab the status off of the first line of the prelude
|
237
|
+
status_line = prelude.shift
|
238
|
+
@status = status_line.split(" ", 3)[1].to_i
|
239
|
+
|
240
|
+
# parse the rest of the prelude which will be all the HTTP headers
|
241
|
+
@response_headers = {}
|
242
|
+
prelude.each do |line|
|
243
|
+
line.strip!
|
244
|
+
key, value = line.split(":", 2)
|
245
|
+
@response_headers[key] = value.strip
|
246
|
+
end
|
223
247
|
end
|
224
248
|
end
|
225
249
|
|
226
|
-
def
|
227
|
-
|
228
|
-
|
229
|
-
# Use curl's --head option when the method requested is HEAD. Otherwise,
|
230
|
-
# the user may experience a timeout when curl does not properly close
|
231
|
-
# the connection after the response is received.
|
232
|
-
if http_method.casecmp("HEAD") == 0
|
233
|
-
cmd << "--head"
|
250
|
+
def http_command # rubocop:disable Metrics/AbcSize
|
251
|
+
if inspec.os.windows?
|
252
|
+
load_powershell_command
|
234
253
|
else
|
235
|
-
cmd
|
254
|
+
cmd = ["curl -i"]
|
255
|
+
|
256
|
+
# Use curl's --head option when the method requested is HEAD. Otherwise,
|
257
|
+
# the user may experience a timeout when curl does not properly close
|
258
|
+
# the connection after the response is received.
|
259
|
+
if http_method.casecmp("HEAD") == 0
|
260
|
+
cmd << "--head"
|
261
|
+
else
|
262
|
+
cmd << "-X #{http_method}"
|
263
|
+
end
|
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
|
273
|
+
cmd << "--connect-timeout #{open_timeout}"
|
274
|
+
cmd << "--max-time #{open_timeout + read_timeout}"
|
275
|
+
cmd << "--user \'#{username}:#{password}\'" unless username.nil? || password.nil?
|
276
|
+
cmd << "--insecure" unless ssl_verify?
|
277
|
+
cmd << "--data #{Shellwords.shellescape(request_body)}" unless request_body.nil?
|
278
|
+
cmd << "--location" unless max_redirects.nil?
|
279
|
+
cmd << "--max-redirs #{max_redirects}" unless max_redirects.nil?
|
280
|
+
|
281
|
+
request_headers.each do |k, v|
|
282
|
+
cmd << "-H '#{k}: #{v}'"
|
283
|
+
end
|
284
|
+
|
285
|
+
if params.nil?
|
286
|
+
cmd << "'#{url}'"
|
287
|
+
else
|
288
|
+
cmd << "'#{url}?#{params.map { |e| e.join("=") }.join("&")}'"
|
289
|
+
end
|
290
|
+
|
291
|
+
cmd.join(" ")
|
236
292
|
end
|
293
|
+
end
|
237
294
|
|
238
|
-
|
239
|
-
cmd
|
240
|
-
cmd << "
|
241
|
-
|
242
|
-
cmd << "
|
243
|
-
|
244
|
-
cmd << "
|
245
|
-
|
295
|
+
def load_powershell_command
|
296
|
+
cmd = ["Invoke-WebRequest"]
|
297
|
+
cmd << "-Method #{http_method}"
|
298
|
+
# Missing connect-timeout
|
299
|
+
cmd << "-TimeoutSec #{open_timeout + read_timeout}"
|
300
|
+
# Insecure not supported simply https://stackoverflow.com/questions/11696944/powershell-v3-invoke-webrequest-https-error
|
301
|
+
cmd << "-MaximumRedirection #{max_redirects}" unless max_redirects.nil?
|
302
|
+
request_headers["Authorization"] = """ '\"Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(\"#{username}:#{password}\")) +'\"' """ unless username.nil? || password.nil?
|
303
|
+
request_header_string = nil
|
246
304
|
request_headers.each do |k, v|
|
247
|
-
|
305
|
+
request_header_string << " #{k} = #{v}"
|
248
306
|
end
|
249
|
-
|
307
|
+
cmd << "-Headers @{#{request_header_string.join(";")}}" unless request_header_string.nil?
|
250
308
|
if params.nil?
|
251
309
|
cmd << "'#{url}'"
|
252
310
|
else
|
253
311
|
cmd << "'#{url}?#{params.map { |e| e.join("=") }.join("&")}'"
|
254
312
|
end
|
255
313
|
|
256
|
-
|
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
|
+
|
324
|
+
command = cmd.join(" ")
|
325
|
+
body = "\'#{request_body}\'"
|
326
|
+
script = <<-EOH
|
327
|
+
$body = #{body.strip unless request_body.nil?}
|
328
|
+
$Body = $body | ConvertFrom-Json
|
329
|
+
#convert to hashtable
|
330
|
+
$HashTable = @{}
|
331
|
+
foreach ($property in $Body.PSObject.Properties) {
|
332
|
+
$HashTable[$property.Name] = $property.Value
|
333
|
+
}
|
334
|
+
$response = #{command} -Body $HashTable -UseBasicParsing
|
335
|
+
$response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error
|
336
|
+
EOH
|
337
|
+
proxy_script.strip + "\n" + script.strip
|
257
338
|
end
|
258
339
|
end
|
259
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
|
|
@@ -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
|