inspec-core 4.49.0 → 4.56.17
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 +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
|