inspec-core 5.22.72 → 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 +24 -38
- data/etc/features.sig +6 -0
- data/etc/features.yaml +94 -0
- data/inspec-core.gemspec +16 -15
- data/lib/inspec/backend.rb +2 -0
- data/lib/inspec/base_cli.rb +80 -4
- 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 +2 -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 +5 -0
- data/lib/inspec/fetcher/url.rb +7 -29
- 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 +46 -3
- data/lib/inspec/reporters/cli.rb +1 -1
- data/lib/inspec/reporters.rb +67 -54
- data/lib/inspec/resources/groups.rb +0 -52
- data/lib/inspec/resources/nftables.rb +1 -14
- data/lib/inspec/resources/oracledb_session.rb +3 -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 +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 +9 -3
- 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 +2 -1
- 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 +68 -27
- 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 +45 -25
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
|
@@ -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
|
@@ -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
|
@@ -57,7 +57,7 @@ module Inspec::Resources
|
|
57
57
|
inspec_cmd = inspec.command(command)
|
58
58
|
out = inspec_cmd.stdout + "\n" + inspec_cmd.stderr
|
59
59
|
|
60
|
-
if inspec_cmd.exit_status != 0 || out.downcase =~ /^error.*/
|
60
|
+
if inspec_cmd.exit_status != 0 || !inspec_cmd.stderr.empty? || out.downcase =~ /^error.*/
|
61
61
|
raise Inspec::Exceptions::ResourceFailed, "Oracle query with errors: #{out}"
|
62
62
|
else
|
63
63
|
begin
|
@@ -96,7 +96,8 @@ module Inspec::Resources
|
|
96
96
|
if @db_role.nil? || @su_user.nil?
|
97
97
|
verified_query = verify_query(query)
|
98
98
|
else
|
99
|
-
escaped_query =
|
99
|
+
escaped_query = query.gsub(/\\\\/, "\\").gsub(/"/, '\\"')
|
100
|
+
escaped_query = escaped_query.gsub("$", '\\$') unless escaped_query.include? "\\$"
|
100
101
|
verified_query = verify_query(escaped_query)
|
101
102
|
end
|
102
103
|
|
@@ -133,17 +134,10 @@ module Inspec::Resources
|
|
133
134
|
query
|
134
135
|
end
|
135
136
|
|
136
|
-
def escape_query(query)
|
137
|
-
escaped_query = query.gsub(/\\\\/, "\\").gsub(/"/, '\\"')
|
138
|
-
escaped_query = escaped_query.gsub("$", '\\$') unless escaped_query.include? "\\$"
|
139
|
-
escaped_query
|
140
|
-
end
|
141
|
-
|
142
137
|
def parse_csv_result(stdout)
|
143
138
|
output = stdout.split("oracle_query_string")[-1]
|
144
139
|
# comma_query_sub replaces the csv delimiter "," in the output.
|
145
140
|
# Handles CSV parsing of data like this (DROP,3) etc
|
146
|
-
|
147
141
|
output = output.sub(/\r/, "").strip.gsub(",", "comma_query_sub")
|
148
142
|
converter = ->(header) { header.downcase }
|
149
143
|
CSV.parse(output, headers: true, header_converters: converter).map do |row|
|
@@ -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
|
data/lib/inspec/runner_rspec.rb
CHANGED
@@ -177,16 +177,18 @@ module Inspec
|
|
177
177
|
next unless streaming_reporters.include? streaming_reporter_name
|
178
178
|
|
179
179
|
# Activate the plugin so the formatter ID gets registered with RSpec, presumably
|
180
|
-
activator = reg.find_activator(plugin_type: :streaming_reporter, activator_name: streaming_reporter_name.to_sym)
|
181
|
-
activator.activate!
|
182
180
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
181
|
+
Inspec.with_feature("inspec-reporter-#{streaming_reporter_name}") {
|
182
|
+
activator = reg.find_activator(plugin_type: :streaming_reporter, activator_name: streaming_reporter_name.to_sym)
|
183
|
+
activator.activate!
|
184
|
+
# We cannot pass in a nil output path. Rspec only accepts a valid string or a IO object.
|
185
|
+
if file_target&.[]("file").nil?
|
186
|
+
RSpec.configuration.add_formatter(activator.implementation_class)
|
187
|
+
else
|
188
|
+
RSpec.configuration.add_formatter(activator.implementation_class, file_target["file"])
|
189
|
+
end
|
190
|
+
@conf["reporter"].delete(streaming_reporter_name)
|
191
|
+
}
|
190
192
|
end
|
191
193
|
end
|
192
194
|
|
@@ -196,6 +198,7 @@ module Inspec
|
|
196
198
|
def configure_output
|
197
199
|
RSpec.configuration.output_stream = $stdout
|
198
200
|
@formatter = RSpec.configuration.add_formatter(Inspec::Formatters::Base)
|
201
|
+
|
199
202
|
@formatter.enhanced_outcomes = @conf.final_options["enhanced_outcomes"]
|
200
203
|
RSpec.configuration.add_formatter(Inspec::Formatters::ShowProgress, $stderr) if @conf[:show_progress]
|
201
204
|
set_optional_formatters
|
data/lib/inspec/secrets/yaml.rb
CHANGED
@@ -24,12 +24,18 @@ module Secrets
|
|
24
24
|
@inputs = ::YAML.load_file(target)
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
# In case of empty yaml file raise the warning else raise the parsing error.
|
28
|
+
if !@inputs || @inputs.empty?
|
29
|
+
Inspec::Log.warn("Unable to parse #{target}: YAML file is empty.")
|
29
30
|
@inputs = nil
|
31
|
+
elsif !@inputs.is_a?(Hash)
|
32
|
+
# Exits with usage error.
|
33
|
+
Inspec::Log.error("Unable to parse #{target}: invalid YAML or contents is not a Hash")
|
34
|
+
Inspec::UI.new.exit(:usage_error)
|
30
35
|
end
|
31
36
|
rescue => e
|
32
|
-
|
37
|
+
# Any other error related to Yaml parsing will be raised here.
|
38
|
+
raise "Error reading YAML file #{target}: #{e}"
|
33
39
|
end
|
34
40
|
end
|
35
41
|
end
|
data/lib/inspec/shell.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require "chef-licensing"
|
2
|
+
require "inspec/dist"
|
3
|
+
|
1
4
|
autoload :Pry, "pry"
|
2
5
|
|
3
6
|
module Inspec
|
@@ -10,6 +13,7 @@ module Inspec
|
|
10
13
|
end
|
11
14
|
|
12
15
|
def start
|
16
|
+
ChefLicensing.check_software_entitlement! if Inspec::Dist::EXEC_NAME == "inspec"
|
13
17
|
# This will hold a single evaluation binding context as opened within
|
14
18
|
# the instance_eval context of the anonymous class that the profile
|
15
19
|
# context creates to evaluate each individual test file. We want to
|
@@ -18,6 +22,12 @@ module Inspec
|
|
18
22
|
@ctx_binding = @runner.eval_with_virtual_profile("binding")
|
19
23
|
configure_pry
|
20
24
|
@ctx_binding.pry
|
25
|
+
rescue ChefLicensing::SoftwareNotEntitled
|
26
|
+
Inspec::Log.error "License is not entitled to use InSpec."
|
27
|
+
Inspec::UI.new.exit(:license_not_entitled)
|
28
|
+
rescue ChefLicensing::Error => e
|
29
|
+
Inspec::Log.error e.message
|
30
|
+
Inspec::UI.new.exit(:usage_error)
|
21
31
|
end
|
22
32
|
|
23
33
|
def configure_pry # rubocop:disable Metrics/AbcSize
|
data/lib/inspec/ui.rb
CHANGED
@@ -32,9 +32,13 @@ module Inspec
|
|
32
32
|
EXIT_FATAL_DEPRECATION = 3
|
33
33
|
EXIT_GEM_DEPENDENCY_LOAD_ERROR = 4
|
34
34
|
EXIT_BAD_SIGNATURE = 5
|
35
|
+
EXIT_SIGNATURE_REQUIRED = 6
|
35
36
|
EXIT_LICENSE_NOT_ACCEPTED = 172
|
37
|
+
EXIT_LICENSE_NOT_ENTITLED = 173
|
38
|
+
EXIT_LICENSE_NOT_SET = 174
|
36
39
|
EXIT_FAILED_TESTS = 100
|
37
40
|
EXIT_SKIPPED_TESTS = 101
|
41
|
+
EXIT_TERMINATED_BY_CTL_C = 130
|
38
42
|
|
39
43
|
attr_reader :io
|
40
44
|
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative "../log"
|
2
|
+
require "chef-licensing"
|
3
|
+
ChefLicensing.configure do |config|
|
4
|
+
config.chef_product_name = "InSpec"
|
5
|
+
config.chef_entitlement_id = "3ff52c37-e41f-4f6c-ad4d-365192205968"
|
6
|
+
config.chef_executable_name = "inspec"
|
7
|
+
config.license_server_url = "https://services.chef.io/licensing"
|
8
|
+
config.logger = Inspec::Log
|
9
|
+
end
|
@@ -19,7 +19,7 @@ module Waivers
|
|
19
19
|
row_hash.delete("control_id")
|
20
20
|
row_hash.delete_if { |k, v| k.nil? || v.nil? }
|
21
21
|
|
22
|
-
waiver_data_hash[control_id] = row_hash if control_id && !
|
22
|
+
waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank?
|
23
23
|
end
|
24
24
|
|
25
25
|
waiver_data_hash
|
@@ -25,7 +25,7 @@ module Waivers
|
|
25
25
|
row_hash.delete_if { |k, v| k.nil? || v.nil? }
|
26
26
|
end
|
27
27
|
|
28
|
-
waiver_data_hash[control_id] = row_hash if control_id && !
|
28
|
+
waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank?
|
29
29
|
end
|
30
30
|
waiver_data_hash
|
31
31
|
rescue Exception => e
|
data/lib/inspec/version.rb
CHANGED