inspec-core 5.24.7 → 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/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.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/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 +9 -14
- 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 +189 -170
- 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 +55 -47
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
|
|
@@ -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
|
@@ -375,24 +375,19 @@ module Inspec
|
|
|
375
375
|
# only_if mechanism)
|
|
376
376
|
# Double underscore: not intended to be called as part of the DSL
|
|
377
377
|
def __apply_waivers
|
|
378
|
-
@__waiver_data = nil
|
|
379
378
|
control_id = @__rule_id # TODO: control ID slugging
|
|
380
|
-
|
|
381
379
|
waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options)
|
|
382
|
-
unless waiver_files.nil? || waiver_files.empty?
|
|
383
|
-
waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files)
|
|
384
|
-
return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
|
|
385
380
|
|
|
386
|
-
|
|
387
|
-
else
|
|
388
|
-
# Support for input registry is provided for backward compatibilty with compliance phase of chef-client
|
|
389
|
-
# Chef-client sends waiver information in inputs hash
|
|
390
|
-
input_registry = Inspec::InputRegistry.instance
|
|
391
|
-
waiver_data_via_input = input_registry.inputs_by_profile.dig(__profile_id, control_id)
|
|
392
|
-
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?
|
|
393
382
|
|
|
394
|
-
|
|
395
|
-
|
|
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]
|
|
396
391
|
|
|
397
392
|
__waiver_data["skipped_due_to_waiver"] = false
|
|
398
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
|
|
data/lib/inspec/runner.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "inspec/dependencies/cache"
|
|
|
11
11
|
require "inspec/dist"
|
|
12
12
|
require "inspec/reporters"
|
|
13
13
|
require "inspec/runner_rspec"
|
|
14
|
+
require "chef-licensing"
|
|
14
15
|
# spec requirements
|
|
15
16
|
|
|
16
17
|
module Inspec
|
|
@@ -60,11 +61,13 @@ module Inspec
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
if @conf[:waiver_file]
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
Inspec.with_feature("inspec-waivers") {
|
|
65
|
+
@conf[:waiver_file].each do |file|
|
|
66
|
+
unless File.file?(file)
|
|
67
|
+
raise Inspec::Exceptions::WaiversFileDoesNotExist, "Waiver file #{file} does not exist."
|
|
68
|
+
end
|
|
66
69
|
end
|
|
67
|
-
|
|
70
|
+
}
|
|
68
71
|
end
|
|
69
72
|
|
|
70
73
|
# About reading inputs:
|
|
@@ -142,7 +145,7 @@ module Inspec
|
|
|
142
145
|
get_check_example(m, a, b)
|
|
143
146
|
end.compact
|
|
144
147
|
|
|
145
|
-
examples.map { |example| total_checks += example.
|
|
148
|
+
examples.map { |example| total_checks += example.examples.count }
|
|
146
149
|
|
|
147
150
|
unless control_describe_checks.empty?
|
|
148
151
|
# controls with empty tests are avoided
|
|
@@ -159,16 +162,42 @@ module Inspec
|
|
|
159
162
|
end
|
|
160
163
|
|
|
161
164
|
def run(with = nil)
|
|
165
|
+
ChefLicensing.check_software_entitlement! if Inspec::Dist::EXEC_NAME == "inspec"
|
|
166
|
+
|
|
167
|
+
# Validate if profiles are signed and verified
|
|
168
|
+
# Additional check is required to provide error message in case of inspec exec command (exec command can use multiple profiles as well)
|
|
169
|
+
# Only runs this block when preview flag CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING is set
|
|
170
|
+
Inspec.with_feature("inspec-mandatory-profile-signing") {
|
|
171
|
+
unless @conf.allow_unsigned_profiles?
|
|
172
|
+
verify_target_profiles_if_signed(@target_profiles)
|
|
173
|
+
end
|
|
174
|
+
}
|
|
175
|
+
|
|
162
176
|
Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
|
|
163
177
|
load
|
|
164
178
|
run_tests(with)
|
|
179
|
+
rescue ChefLicensing::SoftwareNotEntitled
|
|
180
|
+
Inspec::Log.error "License is not entitled to use InSpec."
|
|
181
|
+
Inspec::UI.new.exit(:license_not_entitled)
|
|
182
|
+
rescue ChefLicensing::Error => e
|
|
183
|
+
Inspec::Log.error e.message
|
|
184
|
+
Inspec::UI.new.exit(:usage_error)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def verify_target_profiles_if_signed(target_profiles)
|
|
188
|
+
unsigned_profiles = []
|
|
189
|
+
target_profiles.each do |profile|
|
|
190
|
+
unsigned_profiles << profile.name unless profile.verify_if_signed
|
|
191
|
+
end
|
|
192
|
+
raise Inspec::ProfileSignatureRequired, "Signature required for profile/s: #{unsigned_profiles.join(", ")}. Please provide a signed profile. Or set CHEF_ALLOW_UNSIGNED_PROFILES in the environment. Or use `--allow-unsigned-profiles` flag with InSpec CLI. " unless unsigned_profiles.empty?
|
|
165
193
|
end
|
|
166
194
|
|
|
167
195
|
def render_output(run_data)
|
|
168
196
|
return if @conf["reporter"].nil?
|
|
169
197
|
|
|
170
198
|
@conf["reporter"].each do |reporter|
|
|
171
|
-
|
|
199
|
+
enhanced_outcome_flag = @conf["enhanced_outcomes"]
|
|
200
|
+
result = Inspec::Reporters.render(reporter, run_data, enhanced_outcome_flag)
|
|
172
201
|
raise Inspec::ReporterError, "Error generating reporter '#{reporter[0]}'" if result == false
|
|
173
202
|
end
|
|
174
203
|
end
|