inspec-core 5.24.24 → 6.6.0
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/Chef-EULA +9 -0
- data/Gemfile +25 -36
- data/etc/deprecations.json +0 -3
- data/etc/features.sig +6 -0
- data/etc/features.yaml +94 -0
- data/inspec-core.gemspec +21 -19
- data/lib/inspec/archive/tar.rb +0 -1
- data/lib/inspec/backend.rb +2 -0
- data/lib/inspec/base_cli.rb +80 -14
- data/lib/inspec/cached_fetcher.rb +24 -3
- data/lib/inspec/cli.rb +292 -235
- data/lib/inspec/config.rb +24 -11
- data/lib/inspec/dependencies/cache.rb +33 -0
- data/lib/inspec/dependencies/dependency_set.rb +2 -2
- data/lib/inspec/dsl.rb +1 -1
- data/lib/inspec/enhanced_outcomes.rb +1 -0
- data/lib/inspec/errors.rb +5 -0
- data/lib/inspec/exceptions.rb +1 -0
- data/lib/inspec/feature/config.rb +75 -0
- data/lib/inspec/feature/runner.rb +26 -0
- data/lib/inspec/feature.rb +34 -0
- data/lib/inspec/fetcher/git.rb +6 -21
- data/lib/inspec/fetcher/url.rb +7 -29
- data/lib/inspec/file_provider.rb +0 -1
- data/lib/inspec/globals.rb +6 -0
- data/lib/inspec/input_registry.rb +1 -5
- data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +7 -0
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +30 -2
- data/lib/inspec/profile.rb +49 -13
- data/lib/inspec/reporters/cli.rb +1 -1
- data/lib/inspec/reporters/json.rb +2 -12
- data/lib/inspec/reporters.rb +67 -54
- data/lib/inspec/resources/audit_policy.rb +2 -8
- data/lib/inspec/resources/groups.rb +0 -52
- data/lib/inspec/resources/mssql_session.rb +1 -13
- data/lib/inspec/resources/nftables.rb +1 -14
- data/lib/inspec/resources/oracledb_session.rb +11 -72
- data/lib/inspec/resources/package.rb +2 -9
- data/lib/inspec/resources/postgres_session.rb +5 -9
- data/lib/inspec/resources/sybase_session.rb +2 -11
- data/lib/inspec/resources/virtualization.rb +1 -1
- data/lib/inspec/rule.rb +13 -29
- data/lib/inspec/run_data.rb +7 -5
- data/lib/inspec/runner.rb +35 -6
- data/lib/inspec/runner_rspec.rb +12 -9
- data/lib/inspec/secrets/yaml.rb +5 -1
- data/lib/inspec/shell.rb +10 -0
- data/lib/inspec/ui.rb +4 -0
- data/lib/inspec/utils/licensing_config.rb +9 -0
- data/lib/inspec/utils/profile_ast_helpers.rb +12 -39
- data/lib/inspec/utils/waivers/csv_file_reader.rb +1 -1
- data/lib/inspec/utils/waivers/excel_file_reader.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec/waiver_file_reader.rb +18 -35
- data/lib/inspec.rb +2 -1
- data/lib/matchers/matchers.rb +3 -3
- data/lib/plugins/inspec-compliance/README.md +1 -11
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +188 -173
- data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
- data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
- data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
- data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
- data/lib/plugins/inspec-license/README.md +16 -0
- data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
- data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
- data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
- data/lib/plugins/inspec-parallel/README.md +27 -0
- data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
- data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
- data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +10 -11
- data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
- data/lib/source_readers/inspec.rb +1 -1
- metadata +53 -65
|
@@ -55,11 +55,6 @@ module Inspec::Reporters
|
|
|
55
55
|
def extract_resource_id(r)
|
|
56
56
|
# According to the RunData API, this is supposed to be an anonymous
|
|
57
57
|
# class that represents a resource, with embedded instance methods....
|
|
58
|
-
# Prefer resource object if present and exposes resource_id
|
|
59
|
-
resource_candidate = r[:resource]
|
|
60
|
-
return resource_candidate.resource_id if resource_candidate.respond_to?(:resource_id)
|
|
61
|
-
|
|
62
|
-
# Fall back to resource_title
|
|
63
58
|
resource_obj = r[:resource_title]
|
|
64
59
|
return resource_obj.resource_id if resource_obj.respond_to?(:resource_id)
|
|
65
60
|
|
|
@@ -67,13 +62,8 @@ module Inspec::Reporters
|
|
|
67
62
|
if resource_obj.is_a?(String)
|
|
68
63
|
orig_str = resource_obj
|
|
69
64
|
# Try to trim off the resource class - eg "File /some/path" => "/some/path"
|
|
70
|
-
|
|
71
|
-
trimmed_str
|
|
72
|
-
|
|
73
|
-
# Cap the resource_id to a reasonable length to avoid bloating reports
|
|
74
|
-
max_length = 256
|
|
75
|
-
candidate = trimmed_str.empty? ? orig_str : trimmed_str
|
|
76
|
-
candidate.length > max_length ? candidate[0, max_length] : candidate
|
|
65
|
+
trimmed_str = orig_str.sub(/^#{r[:resource_class]}/i, "").strip
|
|
66
|
+
trimmed_str.empty? ? orig_str : trimmed_str
|
|
77
67
|
else
|
|
78
68
|
# Boo, InSpec is crazy, and we don't know what it possibly could be.
|
|
79
69
|
# Failsafe for resource_id is empty string.
|
data/lib/inspec/reporters.rb
CHANGED
|
@@ -4,76 +4,89 @@ require "inspec/reporters/json"
|
|
|
4
4
|
require "inspec/reporters/json_automate"
|
|
5
5
|
require "inspec/reporters/automate"
|
|
6
6
|
require "inspec/reporters/yaml"
|
|
7
|
+
require "inspec/feature"
|
|
7
8
|
|
|
8
9
|
module Inspec::Reporters
|
|
9
10
|
# rubocop:disable Metrics/CyclomaticComplexity
|
|
10
11
|
def self.render(reporter, run_data, enhanced_outcomes = false)
|
|
11
12
|
name, config = reporter.dup
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
13
|
+
Inspec.with_feature("inspec-reporter-#{name}") {
|
|
14
|
+
config[:run_data] = run_data
|
|
15
|
+
case name
|
|
16
|
+
when "cli"
|
|
17
|
+
reporter = Inspec::Reporters::CLI.new(config)
|
|
18
|
+
when "json"
|
|
19
|
+
reporter = Inspec::Reporters::Json.new(config)
|
|
20
|
+
# This reporter is only used for Chef internal. We reserve the
|
|
21
|
+
# right to introduce breaking changes to this reporter at any time.
|
|
22
|
+
when "json-automate"
|
|
23
|
+
reporter = Inspec::Reporters::JsonAutomate.new(config)
|
|
24
|
+
when "automate"
|
|
25
|
+
reporter = Inspec::Reporters::Automate.new(config)
|
|
26
|
+
when "yaml"
|
|
27
|
+
reporter = Inspec::Reporters::Yaml.new(config)
|
|
28
|
+
else
|
|
29
|
+
# If we made it here, it must be a plugin, and we know it exists (because we validated it in config.rb)
|
|
30
|
+
activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
|
|
31
|
+
activator.activate!
|
|
32
|
+
reporter = activator.implementation_class.new(config)
|
|
33
|
+
end
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
if enhanced_outcomes
|
|
36
|
+
Inspec.with_feature("inspec-enhanced-outcomes") {
|
|
37
|
+
reporter.enhanced_outcomes = enhanced_outcomes
|
|
38
|
+
}
|
|
39
|
+
else
|
|
40
|
+
reporter.enhanced_outcomes = enhanced_outcomes
|
|
41
|
+
end
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
# optional send_report method on reporter
|
|
44
|
+
return reporter.send_report if defined?(reporter.send_report)
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
reporter.render
|
|
47
|
+
output = reporter.rendered_output
|
|
48
|
+
config_file = config["file"]
|
|
49
|
+
if config_file
|
|
50
|
+
config_file.gsub!("CHILD_PID", Process.pid.to_s)
|
|
51
|
+
# create destination directory if it does not exist
|
|
52
|
+
dirname = File.dirname(config_file)
|
|
53
|
+
FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
File.write(config_file, output)
|
|
56
|
+
elsif config["stdout"] == true
|
|
57
|
+
print output
|
|
58
|
+
$stdout.flush
|
|
59
|
+
end
|
|
60
|
+
}
|
|
50
61
|
end
|
|
51
62
|
|
|
52
63
|
def self.report(reporter, run_data)
|
|
53
64
|
name, config = reporter.dup
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
Inspec.with_feature("inspec-reporter-#{name}") {
|
|
66
|
+
config[:run_data] = run_data
|
|
67
|
+
case name
|
|
68
|
+
when "json"
|
|
69
|
+
reporter = Inspec::Reporters::Json.new(config)
|
|
70
|
+
when "json-automate"
|
|
71
|
+
reporter = Inspec::Reporters::JsonAutomate.new(config)
|
|
72
|
+
when "yaml"
|
|
73
|
+
reporter = Inspec::Reporters::Yaml.new(config)
|
|
74
|
+
else
|
|
75
|
+
# If we made it here, it might be a plugin
|
|
76
|
+
begin
|
|
77
|
+
activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
|
|
78
|
+
activator.activate!
|
|
79
|
+
reporter = activator.implementation_class.new(config)
|
|
80
|
+
unless reporter.respond_to(:report?)
|
|
81
|
+
return run_data
|
|
82
|
+
end
|
|
83
|
+
rescue Inspec::Plugin::V2::LoadError
|
|
84
|
+
# Must not have been a plugin - just return the run_data
|
|
69
85
|
return run_data
|
|
70
86
|
end
|
|
71
|
-
rescue Inspec::Plugin::V2::LoadError
|
|
72
|
-
# Must not have been a plugin - just return the run_data
|
|
73
|
-
return run_data
|
|
74
87
|
end
|
|
75
|
-
end
|
|
76
88
|
|
|
77
|
-
|
|
89
|
+
reporter.report
|
|
90
|
+
}
|
|
78
91
|
end
|
|
79
92
|
end
|
|
@@ -39,17 +39,11 @@ module Inspec::Resources
|
|
|
39
39
|
# expected result:
|
|
40
40
|
# Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
|
|
41
41
|
# WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
|
|
42
|
-
|
|
43
|
-
result ||= inspec.command(auditpol_cmd)
|
|
44
|
-
|
|
45
|
-
unless result.exit_status == 0
|
|
46
|
-
error = result.stdout + "\n" + result.stderr
|
|
47
|
-
raise Inspec::Exceptions::ResourceFailed, "Error while executing #{auditpol_cmd} command: #{error}"
|
|
48
|
-
end
|
|
42
|
+
result ||= inspec.command("Auditpol /get /subcategory:'#{key}' /r").stdout
|
|
49
43
|
|
|
50
44
|
# find line
|
|
51
45
|
target = nil
|
|
52
|
-
result.
|
|
46
|
+
result.each_line do |s|
|
|
53
47
|
target = s.strip if s =~ /\b.*#{key}.*\b/
|
|
54
48
|
end
|
|
55
49
|
|
|
@@ -200,60 +200,8 @@ module Inspec::Resources
|
|
|
200
200
|
# implements generic unix groups via /etc/group
|
|
201
201
|
class UnixGroup < GroupInfo
|
|
202
202
|
def groups
|
|
203
|
-
get_group_info
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
private
|
|
207
|
-
|
|
208
|
-
def get_group_info
|
|
209
|
-
# First, try to fetch group info using getent
|
|
210
|
-
group_info = fetch_group_info_using_getent
|
|
211
|
-
|
|
212
|
-
return group_info unless group_info.empty?
|
|
213
|
-
|
|
214
|
-
# If getent fails, fallback to reading group info from /etc/group using inspec.etc_group.entries
|
|
215
|
-
Inspec::Log.debug("Falling back to reading group info from /etc/group as getent is unavailable or failed.")
|
|
216
203
|
inspec.etc_group.entries
|
|
217
204
|
end
|
|
218
|
-
|
|
219
|
-
# Fetches group information using the getent utility
|
|
220
|
-
def fetch_group_info_using_getent
|
|
221
|
-
# Find getent utility on the system
|
|
222
|
-
bin = find_getent_utility
|
|
223
|
-
|
|
224
|
-
# If getent is available, fetch group info
|
|
225
|
-
return [] unless bin
|
|
226
|
-
|
|
227
|
-
cmd = inspec.command("#{bin} group")
|
|
228
|
-
return parse_group_info(cmd) if cmd.exit_status.to_i == 0
|
|
229
|
-
|
|
230
|
-
# If getent fails, log the error and return an empty array
|
|
231
|
-
Inspec::Log.debug("Failed to execute #{bin} group: #{cmd.stderr}.")
|
|
232
|
-
[]
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
# Parses group info from the command output
|
|
236
|
-
def parse_group_info(cmd)
|
|
237
|
-
cmd.stdout.strip.split("\n").map do |line|
|
|
238
|
-
name, password, gid, members = line.split(":")
|
|
239
|
-
{
|
|
240
|
-
"name" => name,
|
|
241
|
-
"password" => password,
|
|
242
|
-
"gid" => gid.to_i,
|
|
243
|
-
"members" => members,
|
|
244
|
-
}
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
# Checks if getent exists on the system
|
|
249
|
-
def find_getent_utility
|
|
250
|
-
%w{/usr/bin/getent /bin/getent getent}.each do |cmd|
|
|
251
|
-
return cmd if inspec.command(cmd).exist?
|
|
252
|
-
end
|
|
253
|
-
# Log debug information if getent is not found
|
|
254
|
-
Inspec::Log.debug("Could not find `getent` on your system.")
|
|
255
|
-
nil # Return nil if getent is not found
|
|
256
|
-
end
|
|
257
205
|
end
|
|
258
206
|
|
|
259
207
|
# OSX uses opendirectory for groups, so `/etc/group` may not be fully accurate
|
|
@@ -30,15 +30,9 @@ module Inspec::Resources
|
|
|
30
30
|
its('value') { should_not be_empty }
|
|
31
31
|
its('value') { should cmp == 1 }
|
|
32
32
|
end
|
|
33
|
-
|
|
34
|
-
# Trust the SQL Server TLS certificate when using sqlcmd
|
|
35
|
-
sql_tls = mssql_session(user: 'myuser', password: 'mypassword', trust_server_certificate: true)
|
|
36
|
-
describe sql_tls.query(\"SELECT SERVERPROPERTY('ProductVersion') as \\\"version\\\";\").row(0).column('version') do
|
|
37
|
-
its('value') { should_not be_empty }
|
|
38
|
-
end
|
|
39
33
|
EXAMPLE
|
|
40
34
|
|
|
41
|
-
attr_reader :user, :password, :host, :port, :instance, :local_mode, :db_name
|
|
35
|
+
attr_reader :user, :password, :host, :port, :instance, :local_mode, :db_name
|
|
42
36
|
def initialize(opts = {})
|
|
43
37
|
@user = opts[:user]
|
|
44
38
|
@password = opts[:password] || opts[:pass]
|
|
@@ -52,7 +46,6 @@ module Inspec::Resources
|
|
|
52
46
|
end
|
|
53
47
|
@instance = opts[:instance]
|
|
54
48
|
@db_name = opts[:db_name]
|
|
55
|
-
@trust_server_certificate = !!opts[:trust_server_certificate] # rubocop:disable Style/DoubleNegation
|
|
56
49
|
|
|
57
50
|
# check if sqlcmd is available
|
|
58
51
|
raise Inspec::Exceptions::ResourceSkipped, "sqlcmd is missing" unless inspec.command("sqlcmd").exist?
|
|
@@ -64,7 +57,6 @@ module Inspec::Resources
|
|
|
64
57
|
escaped_query = q.gsub(/\\/, "\\\\").gsub(/"/, '""').gsub(/\$/, '\\$')
|
|
65
58
|
# surpress 'x rows affected' in SQLCMD with 'set nocount on;'
|
|
66
59
|
cmd_string = "sqlcmd -Q \"set nocount on; #{escaped_query}\" -W -w 1024 -s ','"
|
|
67
|
-
cmd_string += " -C" if trust_server_certificate?
|
|
68
60
|
cmd_string += " -U '#{@user}' -P '#{@password}'" unless @user.nil? || @password.nil?
|
|
69
61
|
cmd_string += " -d '#{@db_name}'" unless @db_name.nil?
|
|
70
62
|
unless local_mode?
|
|
@@ -102,10 +94,6 @@ module Inspec::Resources
|
|
|
102
94
|
!!@local_mode # rubocop:disable Style/DoubleNegation
|
|
103
95
|
end
|
|
104
96
|
|
|
105
|
-
def trust_server_certificate?
|
|
106
|
-
@trust_server_certificate
|
|
107
|
-
end
|
|
108
|
-
|
|
109
97
|
def test_connection
|
|
110
98
|
!query("select getdate()").empty?
|
|
111
99
|
end
|
|
@@ -135,20 +135,7 @@ module Inspec::Resources
|
|
|
135
135
|
cmd = inspec.command(nftables_cmd)
|
|
136
136
|
return [] if cmd.exit_status.to_i != 0
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
# Update @nftables_cache with sanitized command output
|
|
140
|
-
@nftables_cache[idx] = cmd.stdout.gsub("\t", "").split("\n")
|
|
141
|
-
.reject { |line| line =~ /^(table|set|type|size|flags|typeof|auto-merge)/ || line =~ /^}$/ } # Reject lines that match certain patterns
|
|
142
|
-
.map { |line| line.gsub("elements = {", "").gsub("}", "").split(",") } # Use gsub to replace all occurrences of specified strings
|
|
143
|
-
.flatten # Flatten the array of arrays into a single array
|
|
144
|
-
.map(&:strip) # Remove leading and trailing whitespace from each element
|
|
145
|
-
.map { |element| sanitize_input(element) } # Sanitize each element to prevent injection attacks
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Method to sanitize input
|
|
149
|
-
def sanitize_input(input)
|
|
150
|
-
# Replace potentially dangerous characters with their escaped counterparts
|
|
151
|
-
input.gsub(/([\\'";])/, '\\\\\1')
|
|
138
|
+
@nftables_cache[idx] = cmd.stdout.gsub("\t", "").split("\n").reject { |line| line =~ /^(table|set|type|size|flags|typeof|auto-merge)/ || line =~ /^}$/ }.map { |line| line.sub("elements = {", "").sub("}", "").split(",") }.flatten.map(&:strip)
|
|
152
139
|
end
|
|
153
140
|
|
|
154
141
|
def retrieve_chain_rules
|
|
@@ -13,29 +13,14 @@ module Inspec::Resources
|
|
|
13
13
|
supports platform: "windows"
|
|
14
14
|
desc "Use the oracledb_session InSpec resource to test commands against an Oracle database"
|
|
15
15
|
example <<~EXAMPLE
|
|
16
|
-
# Using password
|
|
17
16
|
sql = oracledb_session(user: 'my_user', pass: 'password')
|
|
18
17
|
describe sql.query(\"SELECT UPPER(VALUE) AS VALUE FROM V$PARAMETER WHERE UPPER(NAME)='AUDIT_SYS_OPERATIONS'\").row(0).column('value') do
|
|
19
18
|
its('value') { should eq 'TRUE' }
|
|
20
19
|
end
|
|
21
|
-
|
|
22
|
-
# CHEF-28019: Using TNS alias (recommended for TCPS/SSL connections)
|
|
23
|
-
sql = oracledb_session(
|
|
24
|
-
user: 'my_user',
|
|
25
|
-
password: 'password',
|
|
26
|
-
tns_alias: 'MYDB_TCPS',
|
|
27
|
-
env: {
|
|
28
|
-
'TNS_ADMIN' => '/path/to/tnsnames',
|
|
29
|
-
'LD_LIBRARY_PATH' => '/opt/oracle/instantclient'
|
|
30
|
-
}
|
|
31
|
-
)
|
|
32
|
-
describe sql.query('SELECT * FROM dual').row(0).column('dummy') do
|
|
33
|
-
its('value') { should eq 'X' }
|
|
34
|
-
end
|
|
35
20
|
EXAMPLE
|
|
36
21
|
|
|
37
22
|
attr_reader :bin, :db_role, :host, :password, :port, :service,
|
|
38
|
-
:su_user, :user
|
|
23
|
+
:su_user, :user
|
|
39
24
|
|
|
40
25
|
def initialize(opts = {})
|
|
41
26
|
@user = opts[:user]
|
|
@@ -52,11 +37,6 @@ module Inspec::Resources
|
|
|
52
37
|
@db_role = opts[:as_db_role]
|
|
53
38
|
@sqlcl_bin = opts[:sqlcl_bin] || nil
|
|
54
39
|
@sqlplus_bin = opts[:sqlplus_bin] || "sqlplus"
|
|
55
|
-
|
|
56
|
-
# CHEF-28019: Support for TNS alias and environment variables
|
|
57
|
-
@tns_alias = opts[:tns_alias]
|
|
58
|
-
@env_vars = opts[:env] || {}
|
|
59
|
-
|
|
60
40
|
skip_resource "Option 'as_os_user' not available in Windows" if inspec.os.windows? && su_user
|
|
61
41
|
fail_resource "Can't run Oracle checks without authentication" unless su_user || (user || password)
|
|
62
42
|
end
|
|
@@ -77,7 +57,7 @@ module Inspec::Resources
|
|
|
77
57
|
inspec_cmd = inspec.command(command)
|
|
78
58
|
out = inspec_cmd.stdout + "\n" + inspec_cmd.stderr
|
|
79
59
|
|
|
80
|
-
if inspec_cmd.exit_status != 0 || out.downcase =~ /^error.*/
|
|
60
|
+
if inspec_cmd.exit_status != 0 || !inspec_cmd.stderr.empty? || out.downcase =~ /^error.*/
|
|
81
61
|
raise Inspec::Exceptions::ResourceFailed, "Oracle query with errors: #{out}"
|
|
82
62
|
else
|
|
83
63
|
begin
|
|
@@ -97,10 +77,8 @@ module Inspec::Resources
|
|
|
97
77
|
end
|
|
98
78
|
|
|
99
79
|
def resource_id
|
|
100
|
-
if @
|
|
101
|
-
"#{@
|
|
102
|
-
elsif @user
|
|
103
|
-
"#{@host}-#{@port}-#{@user}" # e.g., "localhost-1521-USER"
|
|
80
|
+
if @user
|
|
81
|
+
"#{@host}-#{@port}-#{@user}"
|
|
104
82
|
elsif @su_user
|
|
105
83
|
"#{@host}-#{@port}-#{@su_user}"
|
|
106
84
|
else
|
|
@@ -110,14 +88,16 @@ module Inspec::Resources
|
|
|
110
88
|
|
|
111
89
|
private
|
|
112
90
|
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
91
|
+
# 3 commands
|
|
92
|
+
# regular user password
|
|
93
|
+
# using a db_role
|
|
94
|
+
# su, using a db_role
|
|
116
95
|
def command_builder(format_options, query)
|
|
117
96
|
if @db_role.nil? || @su_user.nil?
|
|
118
97
|
verified_query = verify_query(query)
|
|
119
98
|
else
|
|
120
|
-
escaped_query =
|
|
99
|
+
escaped_query = query.gsub(/\\\\/, "\\").gsub(/"/, '\\"')
|
|
100
|
+
escaped_query = escaped_query.gsub("$", '\\$') unless escaped_query.include? "\\$"
|
|
121
101
|
verified_query = verify_query(escaped_query)
|
|
122
102
|
end
|
|
123
103
|
|
|
@@ -137,11 +117,7 @@ module Inspec::Resources
|
|
|
137
117
|
sql_postfix = %{ <<'EOC'\n#{format_options}\n#{verified_query}\nEXIT\n'EOC'} if shell_is_csh
|
|
138
118
|
end
|
|
139
119
|
|
|
140
|
-
|
|
141
|
-
if @tns_alias && !@tns_alias.to_s.empty?
|
|
142
|
-
build_tns_command(format_options, verified_query, oracle_echo_str)
|
|
143
|
-
# Original paths preserved
|
|
144
|
-
elsif @db_role.nil?
|
|
120
|
+
if @db_role.nil?
|
|
145
121
|
%{#{oracle_echo_str}#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service}#{sql_postfix}}
|
|
146
122
|
elsif @su_user.nil?
|
|
147
123
|
%{#{oracle_echo_str}#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service} as #{@db_role}#{sql_postfix}}
|
|
@@ -158,17 +134,10 @@ module Inspec::Resources
|
|
|
158
134
|
query
|
|
159
135
|
end
|
|
160
136
|
|
|
161
|
-
def escape_query(query)
|
|
162
|
-
escaped_query = query.gsub(/\\\\/, "\\").gsub(/"/, '\\"')
|
|
163
|
-
escaped_query = escaped_query.gsub("$", '\\$') unless escaped_query.include? "\\$"
|
|
164
|
-
escaped_query
|
|
165
|
-
end
|
|
166
|
-
|
|
167
137
|
def parse_csv_result(stdout)
|
|
168
138
|
output = stdout.split("oracle_query_string")[-1]
|
|
169
139
|
# comma_query_sub replaces the csv delimiter "," in the output.
|
|
170
140
|
# Handles CSV parsing of data like this (DROP,3) etc
|
|
171
|
-
|
|
172
141
|
output = output.sub(/\r/, "").strip.gsub(",", "comma_query_sub")
|
|
173
142
|
converter = ->(header) { header.downcase }
|
|
174
143
|
CSV.parse(output, headers: true, header_converters: converter).map do |row|
|
|
@@ -178,35 +147,5 @@ module Inspec::Resources
|
|
|
178
147
|
Hashie::Mash.new([revised_row].to_h)
|
|
179
148
|
end
|
|
180
149
|
end
|
|
181
|
-
|
|
182
|
-
# CHEF-28019: Build TNS alias command with environment variables
|
|
183
|
-
def build_tns_command(format_options, verified_query, oracle_echo_str)
|
|
184
|
-
env_prefix = build_env_prefix
|
|
185
|
-
connect_string = build_connect_string
|
|
186
|
-
heredoc_content = "connect #{connect_string}\n#{format_options}\n#{verified_query}\nEXIT"
|
|
187
|
-
|
|
188
|
-
if @su_user
|
|
189
|
-
cmd = %{su - #{@su_user} -c "#{oracle_echo_str} #{env_prefix} #{@bin} -s /nolog <<'INSPECSQL'\n#{heredoc_content}\nINSPECSQL"}
|
|
190
|
-
else
|
|
191
|
-
cmd = %{#{oracle_echo_str}#{bin} -s /nolog <<'INSPECSQL'\n#{heredoc_content}\nINSPECSQL}
|
|
192
|
-
cmd = "#{env_prefix} #{cmd}" unless env_prefix.empty?
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
cmd
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# CHEF-28019: Build Oracle connect string for TNS alias
|
|
199
|
-
def build_connect_string
|
|
200
|
-
connect_str = "#{@user}/#{@password}@#{@tns_alias}"
|
|
201
|
-
connect_str += " as #{@db_role}" if @db_role && !@su_user
|
|
202
|
-
connect_str
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
# CHEF-28019: Build environment variable prefix
|
|
206
|
-
def build_env_prefix
|
|
207
|
-
return "" if @env_vars.nil? || @env_vars.empty?
|
|
208
|
-
|
|
209
|
-
@env_vars.map { |k, v| "#{k}='#{v}'" }.join(" ")
|
|
210
|
-
end
|
|
211
150
|
end
|
|
212
151
|
end
|
|
@@ -376,15 +376,8 @@ module Inspec::Resources
|
|
|
376
376
|
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
|
377
377
|
]
|
|
378
378
|
|
|
379
|
-
#
|
|
380
|
-
|
|
381
|
-
# Windows, WOW6432Node does not exist since all apps register under the
|
|
382
|
-
# main Uninstall key.
|
|
383
|
-
# When os.arch is nil (e.g., train falls back to read_cmd_os in a PowerShell
|
|
384
|
-
# WinRM session where %PROCESSOR_ARCHITECTURE% is not expanded), we still
|
|
385
|
-
# include the WOW6432Node paths since virtually all modern Windows systems
|
|
386
|
-
# are 64-bit.
|
|
387
|
-
unless inspec.os.arch == "i386"
|
|
379
|
+
# add 64 bit search paths
|
|
380
|
+
if inspec.os.arch == "x86_64"
|
|
388
381
|
search_paths << 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
|
389
382
|
search_paths << 'HKCU:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
|
390
383
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# copyright: 2015, Vulcano Security GmbH
|
|
2
2
|
|
|
3
3
|
require "shellwords" unless defined?(Shellwords)
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
module Inspec::Resources
|
|
6
6
|
class Lines
|
|
7
7
|
attr_reader :output, :exit_status
|
|
@@ -55,7 +55,7 @@ module Inspec::Resources
|
|
|
55
55
|
psql_cmd = create_psql_cmd(query, db)
|
|
56
56
|
cmd = inspec.command(psql_cmd, redact_regex: %r{(:\/\/[a-z]*:).*(@)})
|
|
57
57
|
out = cmd.stdout + "\n" + cmd.stderr
|
|
58
|
-
if cmd.exit_status != 0 && ( out =~ /could not connect to/ || out =~ /password authentication failed/ ) &&
|
|
58
|
+
if cmd.exit_status != 0 && ( out =~ /could not connect to/ || out =~ /password authentication failed/ ) && out.downcase =~ /error:/
|
|
59
59
|
raise Inspec::Exceptions::ResourceFailed, "PostgreSQL connection error: #{out}"
|
|
60
60
|
elsif cmd.exit_status != 0 && out.downcase =~ /error:/
|
|
61
61
|
Lines.new(out, "PostgreSQL query with error: #{query}", cmd.exit_status)
|
|
@@ -74,10 +74,6 @@ module Inspec::Resources
|
|
|
74
74
|
Shellwords.escape(query)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
def encoded_password(password)
|
|
78
|
-
CGI.escape(password)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
77
|
def create_psql_cmd(query, db = [])
|
|
82
78
|
dbs = db.map { |x| "#{x}" }.join(" ")
|
|
83
79
|
|
|
@@ -86,14 +82,14 @@ module Inspec::Resources
|
|
|
86
82
|
# Socket connection only enabled for non-windows platforms
|
|
87
83
|
# Windows does not support unix domain sockets
|
|
88
84
|
option_port = @port.nil? ? "" : "-p #{@port}" # add explicit port if specified
|
|
89
|
-
"psql -d postgresql://#{@user}:#{
|
|
85
|
+
"psql -d postgresql://#{@user}:#{@pass}@/#{dbs}?host=#{@socket_path} #{option_port} -A -t -w -c #{escaped_query(query)}"
|
|
90
86
|
else
|
|
91
87
|
# Host in connection string establishes tcp/ip connection
|
|
92
88
|
if inspec.os.windows?
|
|
93
89
|
warn "Socket based connection not supported in windows, connecting using host" if @socket_path
|
|
94
|
-
"psql -d postgresql://#{@user}:#{
|
|
90
|
+
"psql -d postgresql://#{@user}:#{@pass}@#{@host}:#{@port}/#{dbs} -A -t -w -c \"#{query}\""
|
|
95
91
|
else
|
|
96
|
-
"psql -d postgresql://#{@user}:#{
|
|
92
|
+
"psql -d postgresql://#{@user}:#{@pass}@#{@host}:#{@port}/#{dbs} -A -t -w -c #{escaped_query(query)}"
|
|
97
93
|
end
|
|
98
94
|
end
|
|
99
95
|
end
|
|
@@ -44,19 +44,10 @@ module Inspec::Resources
|
|
|
44
44
|
# try to get a temp path
|
|
45
45
|
sql_file_path = upload_sql_file(sql)
|
|
46
46
|
|
|
47
|
-
# TODO: Find if there is better way to get the current shell
|
|
48
|
-
current_shell = inspec.command("echo $SHELL")
|
|
49
|
-
|
|
50
|
-
res = current_shell.exit_status
|
|
51
|
-
|
|
52
47
|
# isql reuires that we have a matching locale set, but does not support C.UTF-8. en_US.UTF-8 is the least evil.
|
|
53
|
-
|
|
54
|
-
command = "source #{sybase_home}/SYBASE.csh; setenv LANG en_US.UTF-8; #{bin} -s\"#{col_sep}\" -w80000 -S #{server} -U #{username} -D #{database} -P \"#{password}\" < #{sql_file_path}"
|
|
55
|
-
else
|
|
56
|
-
command = "LANG=en_US.UTF-8 SYBASE=#{sybase_home} #{bin} -s\"#{col_sep}\" -w80000 -S #{server} -U #{username} -D #{database} -P \"#{password}\" < #{sql_file_path}"
|
|
57
|
-
end
|
|
58
|
-
|
|
48
|
+
command = "LANG=en_US.UTF-8 SYBASE=#{sybase_home} #{bin} -s\"#{col_sep}\" -w80000 -S #{server} -U #{username} -D #{database} -P \"#{password}\" < #{sql_file_path}"
|
|
59
49
|
isql_cmd = inspec.command(command)
|
|
50
|
+
|
|
60
51
|
# Check for isql errors
|
|
61
52
|
res = isql_cmd.exit_status
|
|
62
53
|
raise Inspec::Exceptions::ResourceFailed.new("isql exited with code #{res} and stderr '#{isql_cmd.stderr}', stdout '#{isql_cmd.stdout}'") unless res == 0
|
|
@@ -223,7 +223,7 @@ module Inspec::Resources
|
|
|
223
223
|
elsif cgroup_content =~ %r{^\d+:[^:]+:/(kubepods)/.+$}
|
|
224
224
|
@virtualization_data[:system] = $1
|
|
225
225
|
@virtualization_data[:role] = "guest"
|
|
226
|
-
elsif /container=podman/.match?(
|
|
226
|
+
elsif /container=podman/.match?(file_read("/proc/1/environ"))
|
|
227
227
|
@virtualization_data[:system] = "podman"
|
|
228
228
|
@virtualization_data[:role] = "guest"
|
|
229
229
|
elsif lxc_version_exists? && cgroup_content =~ %r{\d:[^:]+:/$}
|
data/lib/inspec/rule.rb
CHANGED
|
@@ -46,22 +46,11 @@ module Inspec
|
|
|
46
46
|
return unless block_given?
|
|
47
47
|
|
|
48
48
|
begin
|
|
49
|
-
|
|
50
|
-
# If the control is waived with run: false, skip the block entirely
|
|
51
|
-
# to avoid eager resource evaluation (e.g., `command('find /').stdout`
|
|
52
|
-
# executing expensive commands for waived controls).
|
|
53
|
-
__apply_waivers
|
|
54
|
-
|
|
55
|
-
unless @__skip_rule[:result] && @__skip_rule[:type] == :waiver
|
|
56
|
-
instance_eval(&block)
|
|
49
|
+
instance_eval(&block)
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# run:true / no-run-key waivers do not set a skip flag. Kept for
|
|
62
|
-
# defensive correctness in case waiver state changes during eval.
|
|
63
|
-
__apply_waivers
|
|
64
|
-
end
|
|
51
|
+
# By applying waivers *after* the instance eval, we assure that
|
|
52
|
+
# waivers have higher precedence than only_if.
|
|
53
|
+
__apply_waivers
|
|
65
54
|
|
|
66
55
|
rescue SystemStackError, StandardError => e
|
|
67
56
|
# We've encountered an exception while trying to eval the code inside the
|
|
@@ -386,24 +375,19 @@ module Inspec
|
|
|
386
375
|
# only_if mechanism)
|
|
387
376
|
# Double underscore: not intended to be called as part of the DSL
|
|
388
377
|
def __apply_waivers
|
|
389
|
-
@__waiver_data = nil
|
|
390
378
|
control_id = @__rule_id # TODO: control ID slugging
|
|
391
|
-
|
|
392
379
|
waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options)
|
|
393
|
-
unless waiver_files.nil? || waiver_files.empty?
|
|
394
|
-
waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files)
|
|
395
|
-
return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
|
|
396
380
|
|
|
397
|
-
|
|
398
|
-
else
|
|
399
|
-
# Support for input registry is provided for backward compatibilty with compliance phase of chef-client
|
|
400
|
-
# Chef-client sends waiver information in inputs hash
|
|
401
|
-
input_registry = Inspec::InputRegistry.instance
|
|
402
|
-
waiver_data_via_input = input_registry.inputs_by_profile.dig(__profile_id, control_id)
|
|
403
|
-
return unless waiver_data_via_input && waiver_data_via_input.has_value? && waiver_data_via_input.value.is_a?(Hash)
|
|
381
|
+
waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files) unless waiver_files.nil?
|
|
404
382
|
|
|
405
|
-
|
|
406
|
-
|
|
383
|
+
return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
|
|
384
|
+
|
|
385
|
+
# An InSpec Input is a datastructure that tracks a profile parameter
|
|
386
|
+
# over time. Its value can be set by many sources, and it keeps a
|
|
387
|
+
# log of each "set" event so that when it is collapsed to a value,
|
|
388
|
+
# it can determine the correct (highest priority) value.
|
|
389
|
+
# Store in an instance variable for.. later reading???
|
|
390
|
+
@__waiver_data = waiver_data_by_profile[control_id]
|
|
407
391
|
|
|
408
392
|
__waiver_data["skipped_due_to_waiver"] = false
|
|
409
393
|
__waiver_data["message"] = ""
|
data/lib/inspec/run_data.rb
CHANGED
|
@@ -27,11 +27,13 @@ module Inspec
|
|
|
27
27
|
) do
|
|
28
28
|
include HashLikeStruct
|
|
29
29
|
def initialize(raw_run_data)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
self.
|
|
33
|
-
self.
|
|
34
|
-
self.
|
|
30
|
+
@raw_run_data = raw_run_data
|
|
31
|
+
|
|
32
|
+
self.controls = @raw_run_data[:controls].map { |c| Inspec::RunData::Control.new(c) }
|
|
33
|
+
self.profiles = @raw_run_data[:profiles].map { |p| Inspec::RunData::Profile.new(p) }
|
|
34
|
+
self.statistics = Inspec::RunData::Statistics.new(@raw_run_data[:statistics])
|
|
35
|
+
self.platform = Inspec::RunData::Platform.new(@raw_run_data[:platform])
|
|
36
|
+
self.version = @raw_run_data[:version]
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
|