inspec-core 4.41.20 → 4.52.9
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 +4 -0
- data/etc/deprecations.json +1 -1
- 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 +12 -0
- data/lib/inspec/cli.rb +21 -4
- data/lib/inspec/control_eval_context.rb +40 -39
- data/lib/inspec/dsl.rb +18 -3
- data/lib/inspec/globals.rb +5 -0
- data/lib/inspec/plugin/v1/registry.rb +1 -1
- data/lib/inspec/profile.rb +115 -2
- data/lib/inspec/resources/auditd.rb +5 -4
- data/lib/inspec/resources/cassandra.rb +64 -0
- data/lib/inspec/resources/cassandradb_conf.rb +47 -0
- data/lib/inspec/resources/cassandradb_session.rb +68 -0
- data/lib/inspec/resources/chrony_conf.rb +55 -0
- data/lib/inspec/resources/csv.rb +26 -3
- data/lib/inspec/resources/groups.rb +22 -3
- data/lib/inspec/resources/http.rb +135 -54
- data/lib/inspec/resources/ibmdb2_conf.rb +57 -0
- data/lib/inspec/resources/ibmdb2_session.rb +69 -0
- data/lib/inspec/resources/mssql_sys_conf.rb +48 -0
- data/lib/inspec/resources/opa.rb +4 -1
- data/lib/inspec/resources/oracle.rb +66 -0
- data/lib/inspec/resources/oracledb_conf.rb +40 -0
- data/lib/inspec/resources/oracledb_listener_conf.rb +123 -0
- data/lib/inspec/resources/oracledb_session.rb +25 -6
- data/lib/inspec/resources/packages.rb +21 -0
- data/lib/inspec/resources/postgres_session.rb +15 -4
- data/lib/inspec/resources/service.rb +59 -10
- data/lib/inspec/resources/ssl.rb +7 -0
- data/lib/inspec/resources/sybase_conf.rb +37 -0
- data/lib/inspec/resources/sybase_session.rb +111 -0
- data/lib/inspec/resources/users.rb +16 -2
- data/lib/inspec/resources/windows_firewall.rb +1 -1
- data/lib/inspec/resources.rb +9 -0
- data/lib/inspec/run_data/profile.rb +0 -2
- data/lib/inspec/version.rb +1 -1
- metadata +14 -2
@@ -0,0 +1,68 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class Lines
|
3
|
+
attr_reader :output
|
4
|
+
|
5
|
+
def initialize(raw, desc)
|
6
|
+
@output = raw
|
7
|
+
@desc = desc
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@desc
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class CassandradbSession < Inspec.resource(1)
|
16
|
+
name "cassandradb_session"
|
17
|
+
supports platform: "unix"
|
18
|
+
supports platform: "windows"
|
19
|
+
desc "Use the cassandradb_session InSpec resource to test commands against an Cassandra database"
|
20
|
+
example <<~EXAMPLE
|
21
|
+
cql = cassandradb_session(user: 'my_user', password: 'password', host: 'host', port: 'port')
|
22
|
+
describe cql.query("SELECT cluster_name FROM system.local") do
|
23
|
+
its('output') { should match /Test Cluster/ }
|
24
|
+
end
|
25
|
+
EXAMPLE
|
26
|
+
|
27
|
+
attr_reader :user, :password, :host, :port
|
28
|
+
|
29
|
+
def initialize(opts = {})
|
30
|
+
@user = opts[:user] || "cassandra"
|
31
|
+
@password = opts[:password] || "cassandra"
|
32
|
+
@host = opts[:host]
|
33
|
+
@port = opts[:port]
|
34
|
+
end
|
35
|
+
|
36
|
+
def query(q)
|
37
|
+
cassandra_cmd = create_cassandra_cmd(q)
|
38
|
+
cmd = inspec.command(cassandra_cmd)
|
39
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
40
|
+
if cmd.exit_status != 0 || out =~ /Unable to connect to any servers/ || out.downcase =~ /^error:.*/
|
41
|
+
raise Inspec::Exceptions::ResourceFailed, "Cassandra query with errors: #{out}"
|
42
|
+
else
|
43
|
+
Lines.new(cmd.stdout.strip, "Cassandra query: #{q}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
"Cassandra DB Session"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def create_cassandra_cmd(q)
|
54
|
+
# TODO: simple escape, must be handled by a library
|
55
|
+
# that does this securely
|
56
|
+
escaped_query = q.gsub(/\\/, "\\\\").gsub(/"/, '\\"').gsub(/\$/, '\\$')
|
57
|
+
|
58
|
+
# construct the query
|
59
|
+
command = "cqlsh"
|
60
|
+
command += " #{@host}" unless @host.nil?
|
61
|
+
command += " #{@port}" unless @port.nil?
|
62
|
+
command += " -u #{@user}"
|
63
|
+
command += " -p #{@password}"
|
64
|
+
command += " --execute '#{escaped_query}'"
|
65
|
+
command
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# chrony_conf
|
2
|
+
|
3
|
+
require "inspec/utils/simpleconfig"
|
4
|
+
require "inspec/utils/file_reader"
|
5
|
+
|
6
|
+
module Inspec::Resources
|
7
|
+
class ChronyConf < Inspec.resource(1)
|
8
|
+
name "chrony_conf"
|
9
|
+
supports platform: "unix"
|
10
|
+
desc "Use the chrony_conf InSpec audit resource to test the synchronization settings defined in the chrony.conf file. This file is typically located at /etc/chrony.conf."
|
11
|
+
example <<~EXAMPLE
|
12
|
+
describe chrony_conf do
|
13
|
+
its('server') { should_not cmp nil }
|
14
|
+
its('restrict') { should include '-4 default kod notrap nomodify nopeer noquery' }
|
15
|
+
its('pool') { should include 'pool.ntp.org iburst' }
|
16
|
+
its('driftfile') { should cmp '/var/lib/ntp/drift' }
|
17
|
+
its('allow') { should cmp nil }
|
18
|
+
its('keyfile') { should cmp '/etc/chrony.keys' }
|
19
|
+
end
|
20
|
+
EXAMPLE
|
21
|
+
|
22
|
+
include FileReader
|
23
|
+
|
24
|
+
def initialize(path = nil)
|
25
|
+
@conf_path = path || "/etc/chrony.conf"
|
26
|
+
@content = read_file_content(@conf_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(name)
|
30
|
+
param = read_params[name.to_s]
|
31
|
+
# extract first value if we have only one value in array
|
32
|
+
return param[0] if param.is_a?(Array) && (param.length == 1)
|
33
|
+
|
34
|
+
param
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"chrony.conf"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def read_params
|
44
|
+
return @params if defined?(@params)
|
45
|
+
|
46
|
+
# parse the file
|
47
|
+
conf = SimpleConfig.new(
|
48
|
+
@content,
|
49
|
+
assignment_regex: /^\s*(\S+)\s+(.*)\s*$/,
|
50
|
+
multiple_values: true
|
51
|
+
)
|
52
|
+
@params = conf.params
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/inspec/resources/csv.rb
CHANGED
@@ -11,14 +11,28 @@ module Inspec::Resources
|
|
11
11
|
describe csv('example.csv') do
|
12
12
|
its('name') { should eq(['John', 'Alice']) }
|
13
13
|
end
|
14
|
+
|
15
|
+
describe csv('example.csv', false).params do
|
16
|
+
its[[0]] { should eq (['name', 'col1', 'col2']) }
|
17
|
+
emd
|
14
18
|
EXAMPLE
|
15
19
|
|
20
|
+
def initialize(path, headers = true)
|
21
|
+
@headers = headers
|
22
|
+
super(path)
|
23
|
+
end
|
24
|
+
|
16
25
|
# override the parse method from JsonConfig
|
17
26
|
# Assuming a header row of name,col1,col2, it will output an array of hashes like so:
|
18
27
|
# [
|
19
28
|
# { 'name' => 'row1', 'col1' => 'value1', 'col2' => 'value2' },
|
20
29
|
# { 'name' => 'row2', 'col1' => 'value3', 'col2' => 'value4' }
|
21
30
|
# ]
|
31
|
+
# When headers is set to false it will return data as array of array
|
32
|
+
# [
|
33
|
+
# ['name', col1', 'col2'],
|
34
|
+
# ['row2', 'value3', 'value4']
|
35
|
+
# ]
|
22
36
|
def parse(content)
|
23
37
|
require "csv" unless defined?(CSV)
|
24
38
|
|
@@ -28,10 +42,14 @@ module Inspec::Resources
|
|
28
42
|
end
|
29
43
|
|
30
44
|
# implicit conversion of values
|
31
|
-
csv = CSV.new(content, headers:
|
45
|
+
csv = CSV.new(content, headers: @headers, converters: %i{all blank_to_nil})
|
32
46
|
|
33
47
|
# convert to hash
|
34
|
-
|
48
|
+
if @headers
|
49
|
+
csv.to_a.map(&:to_hash)
|
50
|
+
else
|
51
|
+
csv.to_a
|
52
|
+
end
|
35
53
|
rescue => e
|
36
54
|
raise Inspec::Exceptions::ResourceFailed, "Unable to parse CSV: #{e.message}"
|
37
55
|
end
|
@@ -42,7 +60,12 @@ module Inspec::Resources
|
|
42
60
|
# #value method from JsonConfig (which uses ObjectTraverser.extract_value)
|
43
61
|
# doesn't make sense here.
|
44
62
|
def value(key)
|
45
|
-
@
|
63
|
+
if @headers
|
64
|
+
@params.map { |x| x[key.first.to_s] }.compact
|
65
|
+
else
|
66
|
+
# when headers is set to false send the array as it is.
|
67
|
+
@params
|
68
|
+
end
|
46
69
|
end
|
47
70
|
|
48
71
|
private
|
@@ -22,6 +22,18 @@ module Inspec::Resources
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
# Class defined to check for members without case-sensitivity
|
26
|
+
class Members < Array
|
27
|
+
def initialize(group_members)
|
28
|
+
@group_members = group_members
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def include?(user)
|
33
|
+
!(@group_members.select { |group_member| group_member.casecmp?(user) }.empty?)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
25
37
|
class Groups < Inspec.resource(1)
|
26
38
|
include GroupManagementSelector
|
27
39
|
|
@@ -82,6 +94,7 @@ module Inspec::Resources
|
|
82
94
|
# its('gid') { should eq 0 }
|
83
95
|
# end
|
84
96
|
#
|
97
|
+
|
85
98
|
class Group < Inspec.resource(1)
|
86
99
|
include GroupManagementSelector
|
87
100
|
|
@@ -118,11 +131,13 @@ module Inspec::Resources
|
|
118
131
|
end
|
119
132
|
|
120
133
|
def members
|
121
|
-
flatten_entry(group_info, "members") || empty_value_for_members
|
134
|
+
members_list = flatten_entry(group_info, "members") || empty_value_for_members
|
135
|
+
inspec.os.windows? ? Members.new(members_list) : members_list
|
122
136
|
end
|
123
137
|
|
124
138
|
def members_array
|
125
|
-
flatten_entry(group_info, "members_array") || []
|
139
|
+
members_list = flatten_entry(group_info, "members_array") || []
|
140
|
+
inspec.os.windows? ? Members.new(members_list) : members_list
|
126
141
|
end
|
127
142
|
|
128
143
|
def local
|
@@ -150,7 +165,11 @@ module Inspec::Resources
|
|
150
165
|
def group_info
|
151
166
|
# we need a local copy for the block
|
152
167
|
group = @group.dup
|
153
|
-
|
168
|
+
if inspec.os.windows?
|
169
|
+
@groups_cache ||= inspec.groups.where { name.casecmp?(group) }
|
170
|
+
else
|
171
|
+
@groups_cache ||= inspec.groups.where { name == group }
|
172
|
+
end
|
154
173
|
end
|
155
174
|
|
156
175
|
def empty_value_for_members
|
@@ -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
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class Ibmdb2Conf < Inspec.resource(1)
|
3
|
+
name "ibmdb2_conf"
|
4
|
+
|
5
|
+
supports platform: "unix"
|
6
|
+
supports platform: "windows"
|
7
|
+
|
8
|
+
desc "Use the ibmdb2_conf InSpec audit resource to test the configuration values of IBM Db2 database."
|
9
|
+
example <<~EXAMPLE
|
10
|
+
describe ibmdb2_conf(db2_executable_file_path: "path_to_db2_binary", db_instance: "db2inst1") do
|
11
|
+
its("output") { should_not be_empty }
|
12
|
+
its("output") { should include("Audit buffer size (4KB) (AUDIT_BUF_SZ) = 0")}
|
13
|
+
end
|
14
|
+
EXAMPLE
|
15
|
+
|
16
|
+
attr_reader :output
|
17
|
+
|
18
|
+
def initialize(opts = {})
|
19
|
+
if inspec.os.platform?("unix")
|
20
|
+
@db2_executable_file_path = opts[:db2_executable_file_path]
|
21
|
+
@db_instance = opts[:db_instance]
|
22
|
+
raise Inspec::Exceptions::ResourceFailed, "Can't connect to IBM DB2 without db2_executable_file_path, db_instance options provided." if @db2_executable_file_path.nil? || @db_instance.nil?
|
23
|
+
end
|
24
|
+
@output = run_command
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"IBM Db2 Conf"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def run_command
|
34
|
+
# attach to the db2 instance and get the configuration
|
35
|
+
if inspec.os.platform?("unix")
|
36
|
+
cmd = inspec.command("#{@db2_executable_file_path} attach to #{@db_instance}\; #{@db2_executable_file_path} get database manager configuration")
|
37
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
38
|
+
|
39
|
+
# check if following specific error is there. Sourcing the db2profile to resolve the error.
|
40
|
+
if cmd.exit_status != 0 && out =~ /SQL10007N Message "-1390" could not be retrieved. Reason code: "3"/
|
41
|
+
cmd = inspec.command(". ~/sqllib/db2profile\; #{@db2_executable_file_path} attach to #{@db_instance}\; #{@db2_executable_file_path} get database manager configuration")
|
42
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
43
|
+
end
|
44
|
+
elsif inspec.os.platform?("windows")
|
45
|
+
# set-item command set the powershell to run the db2 commands.
|
46
|
+
cmd = inspec.command("set-item -path env:DB2CLP -value \"**$$**\"\; db2 get database manager configuration")
|
47
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
48
|
+
end
|
49
|
+
|
50
|
+
if cmd.exit_status != 0 || out =~ /Can't connect to IBM Db2 server/ || out.downcase =~ /^error:.*/
|
51
|
+
raise Inspec::Exceptions::ResourceFailed, "IBM Db2 query with error: #{out}"
|
52
|
+
else
|
53
|
+
cmd.stdout.gsub(/\n|\r/, ",").split(",").reject { |n| n.nil? || n.empty? }.map { |n| n.strip.gsub!(/\s+/, " ") }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class Lines
|
3
|
+
attr_reader :output
|
4
|
+
|
5
|
+
def initialize(raw, desc)
|
6
|
+
@output = raw
|
7
|
+
@desc = desc
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@desc
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Ibmdb2Session < Inspec.resource(1)
|
16
|
+
name "ibmdb2_session"
|
17
|
+
|
18
|
+
supports platform: "unix"
|
19
|
+
supports platform: "windows"
|
20
|
+
|
21
|
+
desc "Use the ibmdb2_session InSpec audit resource to test SQL commands run against a IBM Db2 database."
|
22
|
+
example <<~EXAMPLE
|
23
|
+
describe ibmdb2_session(db2_executable_file_path: "path_to_db2_binary", db_instance: "db2inst1", db_name: "sample").query('list database directory') do
|
24
|
+
its('output') { should_not match(/sample/) }
|
25
|
+
end
|
26
|
+
EXAMPLE
|
27
|
+
|
28
|
+
def initialize(opts = {})
|
29
|
+
@db_name = opts[:db_name]
|
30
|
+
if inspec.os.platform?("unix")
|
31
|
+
@db2_executable_file_path = opts[:db2_executable_file_path]
|
32
|
+
@db_instance = opts[:db_instance]
|
33
|
+
raise Inspec::Exceptions::ResourceFailed, "Can't run IBM DB2 queries without db2_executable_file_path, db_instance, db_name options provided." if @db2_executable_file_path.nil? || @db_instance.nil? || @db_name.nil?
|
34
|
+
elsif inspec.os.platform?("windows")
|
35
|
+
raise Inspec::Exceptions::ResourceFailed, "Can't run IBM DB2 queries without db_name option provided." if @db_name.nil?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def query(q)
|
40
|
+
raise Inspec::Exceptions::ResourceFailed, "#{resource_exception_message}" if resource_failed?
|
41
|
+
|
42
|
+
if inspec.os.platform?("unix")
|
43
|
+
# connect to the db and query on the database
|
44
|
+
cmd = inspec.command("#{@db2_executable_file_path} attach to #{@db_instance}\; #{@db2_executable_file_path} connect to #{@db_name}\; #{@db2_executable_file_path} #{q}\;")
|
45
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
46
|
+
|
47
|
+
# check if following specific error is there. Sourcing the db2profile to resolve the error.
|
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}\"\;")
|
50
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
51
|
+
end
|
52
|
+
elsif inspec.os.platform?("windows")
|
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}\"\;")
|
55
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
56
|
+
end
|
57
|
+
|
58
|
+
if cmd.exit_status != 0 || out =~ /Can't connect to IBM Db2 / || out.downcase =~ /^error:.*/
|
59
|
+
raise Inspec::Exceptions::ResourceFailed, "IBM Db2 connection error: #{out}"
|
60
|
+
else
|
61
|
+
Lines.new(cmd.stdout.strip, "IBM Db2 Query: #{q}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"IBM Db2 Session"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|