inspec-core 5.22.29 → 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 +10 -1
- data/etc/features.sig +6 -0
- data/etc/features.yaml +94 -0
- data/inspec-core.gemspec +14 -5
- 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 +300 -230
- data/lib/inspec/config.rb +24 -2
- data/lib/inspec/dependencies/cache.rb +33 -0
- 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/globals.rb +6 -0
- 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 +373 -12
- data/lib/inspec/reporters/cli.rb +1 -1
- data/lib/inspec/reporters.rb +67 -54
- data/lib/inspec/resources/security_policy.rb +7 -2
- data/lib/inspec/run_data.rb +7 -5
- data/lib/inspec/runner.rb +34 -5
- 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 +372 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec/waiver_file_reader.rb +68 -27
- data/lib/inspec.rb +2 -1
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -168
- 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-reporter-html2/templates/control.html.erb +7 -6
- data/lib/plugins/inspec-reporter-html2/templates/default.js +6 -6
- data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +6 -2
- 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
- metadata +54 -13
data/lib/inspec/config.rb
CHANGED
@@ -7,6 +7,7 @@ require "forwardable" unless defined?(Forwardable)
|
|
7
7
|
require "thor" unless defined?(Thor)
|
8
8
|
require "base64" unless defined?(Base64)
|
9
9
|
require "inspec/plugin/v2/filter"
|
10
|
+
require "inspec/feature"
|
10
11
|
|
11
12
|
module Inspec
|
12
13
|
class Config
|
@@ -25,6 +26,12 @@ module Inspec
|
|
25
26
|
shell_command
|
26
27
|
}.freeze
|
27
28
|
|
29
|
+
AUDIT_LOG_OPTIONS = %w{
|
30
|
+
audit_log_location
|
31
|
+
enable_audit_log
|
32
|
+
audit_log_app_name
|
33
|
+
}.freeze
|
34
|
+
|
28
35
|
KNOWN_VERSIONS = [
|
29
36
|
"1.1",
|
30
37
|
"1.2",
|
@@ -80,6 +87,10 @@ module Inspec
|
|
80
87
|
puts
|
81
88
|
end
|
82
89
|
|
90
|
+
def allow_unsigned_profiles?
|
91
|
+
self["allow_unsigned_profiles"] || ENV["CHEF_ALLOW_UNSIGNED_PROFILES"]
|
92
|
+
end
|
93
|
+
|
83
94
|
# return all telemetry options from config
|
84
95
|
# @return [Hash]
|
85
96
|
def telemetry_options
|
@@ -109,11 +120,14 @@ module Inspec
|
|
109
120
|
def unpack_train_credentials
|
110
121
|
# Internally, use indifferent access while we build the creds
|
111
122
|
credentials = Thor::CoreExt::HashWithIndifferentAccess.new({})
|
112
|
-
|
113
123
|
# Helper methods prefixed with _utc_ (Unpack Train Credentials)
|
114
124
|
|
115
125
|
credentials.merge!(_utc_generic_credentials)
|
116
126
|
|
127
|
+
# Only runs this block when preview flag CHEF_PREVIEW_AUDIT_LOGGING is set
|
128
|
+
Inspec.with_feature("inspec-audit-logging") {
|
129
|
+
credentials.merge!(_utc_merge_audit_log_options)
|
130
|
+
}
|
117
131
|
_utc_determine_backend(credentials)
|
118
132
|
transport_name = credentials[:backend].to_s
|
119
133
|
|
@@ -154,13 +168,21 @@ module Inspec
|
|
154
168
|
|
155
169
|
private
|
156
170
|
|
171
|
+
def _utc_merge_audit_log_options
|
172
|
+
@final_options.select { |option, _value| AUDIT_LOG_OPTIONS.include?(option) }
|
173
|
+
end
|
174
|
+
|
157
175
|
def _utc_merge_transport_options(credentials, transport_name)
|
158
176
|
# Ask Train for the names of the transport options
|
159
177
|
transport_options = Train.options(transport_name).keys.map(&:to_s)
|
160
178
|
|
161
179
|
# If there are any options with those (unprefixed) names, merge them in.
|
180
|
+
# e.g., 'host'
|
162
181
|
unprefixed_transport_options = final_options.select do |option_name, _value|
|
163
|
-
|
182
|
+
# We currently want this option only to be set if CHEF_PREVIEW_AUDIT_LOGGING is set
|
183
|
+
# and we already merging the audit_log_options within _utc_merge_audit_log_options method which only invoke if feature flag is on
|
184
|
+
# we don't want audit log option to get set from here again so this is a safe check and can be removed when we remove preview feature flag.
|
185
|
+
transport_options.include? option_name unless option_name.match?("enable_audit_log")
|
164
186
|
end
|
165
187
|
credentials.merge!(unprefixed_transport_options)
|
166
188
|
|
@@ -70,5 +70,38 @@ module Inspec
|
|
70
70
|
def base_path_for(cache_key)
|
71
71
|
File.join(@path, cache_key)
|
72
72
|
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# For given cache key, return true if the
|
76
|
+
# cache path is locked
|
77
|
+
def locked?(key)
|
78
|
+
locked = false
|
79
|
+
path = base_path_for(key)
|
80
|
+
# For archive there is no need to lock the directory so we skip those and return false for archive formatted cache
|
81
|
+
if File.directory?(path)
|
82
|
+
locked = File.exist?("#{path}/.lock")
|
83
|
+
end
|
84
|
+
locked
|
85
|
+
end
|
86
|
+
|
87
|
+
def lock(cache_path)
|
88
|
+
lock_file_path = File.join(cache_path, ".lock")
|
89
|
+
begin
|
90
|
+
FileUtils.mkdir_p(cache_path)
|
91
|
+
Inspec::Log.debug("Locking cache ..... #{cache_path}")
|
92
|
+
FileUtils.touch(lock_file_path)
|
93
|
+
rescue Errno::EACCES
|
94
|
+
raise "Permission denied while creating cache lock #{cache_path}/.lock."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def unlock(cache_path)
|
99
|
+
Inspec::Log.debug("Unlocking cache..... #{cache_path}")
|
100
|
+
begin
|
101
|
+
FileUtils.rm("#{cache_path}/.lock") if File.exist?("#{cache_path}/.lock")
|
102
|
+
rescue Errno::EACCES
|
103
|
+
raise "Permission denied while removing cache lock #{cache_path}/.lock"
|
104
|
+
end
|
105
|
+
end
|
73
106
|
end
|
74
107
|
end
|
data/lib/inspec/errors.rb
CHANGED
data/lib/inspec/exceptions.rb
CHANGED
@@ -12,5 +12,7 @@ module Inspec
|
|
12
12
|
class ProfileSigningKeyNotFound < ArgumentError; end
|
13
13
|
class WaiversFileNotReadable < ArgumentError; end
|
14
14
|
class WaiversFileDoesNotExist < ArgumentError; end
|
15
|
+
class WaiversFileInvalidFormatting < ArgumentError; end
|
16
|
+
class InvalidAuditLogOption < ArgumentError; end
|
15
17
|
end
|
16
18
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "inspec/iaf_file" # Uses some of the same encryption routines
|
2
|
+
|
3
|
+
module Inspec
|
4
|
+
class Feature
|
5
|
+
class Config
|
6
|
+
|
7
|
+
VERIFICATION_KEY_NAME = "progress-2022-05-04".freeze
|
8
|
+
|
9
|
+
attr_reader :cfg_data, :valid
|
10
|
+
|
11
|
+
def initialize(conf_path = nil)
|
12
|
+
# If conf path is nil, read from source installation
|
13
|
+
conf_path ||= File.join(Inspec.src_root, "etc", "features.yaml")
|
14
|
+
|
15
|
+
# Verify path and sig file exists or else throw exception
|
16
|
+
sig_path = conf_path.sub(/\.yaml/, ".sig")
|
17
|
+
[conf_path, sig_path].each do |file|
|
18
|
+
raise Inspec::FeatureConfigMissingError.new("No such file #{file}") unless File.exist?(file)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Verify sig matches contents
|
22
|
+
validation_key_path = Inspec::IafFile.find_validation_key(VERIFICATION_KEY_NAME)
|
23
|
+
verification_key = Inspec::IafFile::KEY_ALG.new File.read validation_key_path
|
24
|
+
signature = Base64.decode64 File.read sig_path
|
25
|
+
digest = Inspec::IafFile::ARTIFACT_DIGEST.new
|
26
|
+
unless verification_key.verify digest, signature, File.read(conf_path)
|
27
|
+
# If not load default empty config and raise exception
|
28
|
+
@cfg_data = load_error_data
|
29
|
+
raise Inspec::FeatureConfigTamperedError.new("Feature yaml file does not match signature - tampered?")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Read YAML data from path
|
33
|
+
@cfg_data = YAML.load_file(conf_path)
|
34
|
+
@features_by_name = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_each_feature
|
38
|
+
cfg_data["features"].each do |feature_name, raw_info|
|
39
|
+
feat = @features_by_name[feature_name] ||= Inspec::Feature.new(feature_name.to_sym, raw_info)
|
40
|
+
yield(feat)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](feature_name)
|
45
|
+
raw_info = cfg_data["features"][feature_name]
|
46
|
+
return nil unless raw_info
|
47
|
+
|
48
|
+
@features_by_name[feature_name] ||= Inspec::Feature.new(feature_name.to_sym, raw_info)
|
49
|
+
end
|
50
|
+
|
51
|
+
def feature_name?(query)
|
52
|
+
cfg_data["features"].key?(query.to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
def features
|
56
|
+
@features ||= load_features
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def load_features
|
62
|
+
feats = []
|
63
|
+
with_each_feature { |f| feats << f }
|
64
|
+
feats
|
65
|
+
end
|
66
|
+
|
67
|
+
# Default data for when the config is in an error state.
|
68
|
+
def load_error_data
|
69
|
+
{
|
70
|
+
"features": {},
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Inspec
|
2
|
+
class Feature
|
3
|
+
class Runner
|
4
|
+
def self.with_feature(feature_name, opts = {}, &feature_implementation)
|
5
|
+
config = opts[:config] || Inspec::Feature::Config.new
|
6
|
+
logger = opts[:logger] || Inspec::Log
|
7
|
+
|
8
|
+
# Emit log message saying we're running a feature
|
9
|
+
logger.debug("Prepping to run feature '#{feature_name}'")
|
10
|
+
|
11
|
+
# Validate that the feature is recognized
|
12
|
+
feature = config[feature_name]
|
13
|
+
unless feature
|
14
|
+
logger.warn "Unrecognized feature name '#{feature_name}'"
|
15
|
+
end
|
16
|
+
|
17
|
+
# If the feature is not recognized
|
18
|
+
# If the feature has no env_preview flag set in config
|
19
|
+
# If the feature is previewable
|
20
|
+
if feature.nil? || feature&.no_preview? || feature&.previewable?
|
21
|
+
yield feature_implementation
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative "feature/config"
|
2
|
+
require_relative "feature/runner"
|
3
|
+
|
4
|
+
module Inspec
|
5
|
+
def self.with_feature(feature_name, opts = {}, &feature_implementation)
|
6
|
+
Inspec::Feature::Runner.with_feature(feature_name, opts, &feature_implementation)
|
7
|
+
end
|
8
|
+
|
9
|
+
class Feature
|
10
|
+
attr_reader :name, :description, :env_preview
|
11
|
+
def initialize(feature_name, feature_yaml_opts)
|
12
|
+
@name = feature_name
|
13
|
+
feature_yaml_opts ||= {}
|
14
|
+
@description = feature_yaml_opts["description"]
|
15
|
+
@env_preview = feature_yaml_opts["env_preview"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def previewable?
|
19
|
+
# If the feature is previewable in config (features.yaml) & has an environment value set to use previewed feature
|
20
|
+
!!env_preview && !env_preview_value.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_preview?
|
24
|
+
env_preview.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def env_preview_value
|
28
|
+
# Examples: If feature name is "inspec-test-feature"
|
29
|
+
# ENV used for this feature preview would be CHEF_PREVIEW_TEST_FEATURE
|
30
|
+
env_preview_feature_name = name.to_s.split("inspec-")[-1]
|
31
|
+
ENV["CHEF_PREVIEW_#{env_preview_feature_name.gsub("-", "_").upcase}"]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/inspec/fetcher/git.rb
CHANGED
@@ -127,6 +127,11 @@ module Inspec::Fetcher
|
|
127
127
|
%i{branch tag ref}.map { |opt_name| update_ivar_from_opt(opt_name, opts) }.any?
|
128
128
|
end
|
129
129
|
|
130
|
+
# Git fetcher is sensitive to cache contention so it needs cache locking mechanism.
|
131
|
+
def requires_locking?
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
130
135
|
private
|
131
136
|
|
132
137
|
def resolved_ref
|
data/lib/inspec/globals.rb
CHANGED
@@ -23,4 +23,10 @@ module Inspec
|
|
23
23
|
require "rbconfig" unless defined?(RbConfig)
|
24
24
|
RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
|
25
25
|
end
|
26
|
+
|
27
|
+
def self.log_dir
|
28
|
+
log_dir_path = "#{config_dir}/logs"
|
29
|
+
FileUtils.mkdir_p(log_dir_path) unless File.directory?(log_dir_path)
|
30
|
+
log_dir_path
|
31
|
+
end
|
26
32
|
end
|
@@ -105,6 +105,13 @@ module Inspec
|
|
105
105
|
file_provider = Inspec::FileProvider.for_path(archive_path)
|
106
106
|
file_provider.relative_provider
|
107
107
|
end
|
108
|
+
|
109
|
+
# Returns false by default
|
110
|
+
# This is used to regulate cache contention.
|
111
|
+
# Fetchers that are sensitive to cache contention should return true.
|
112
|
+
def requires_locking?
|
113
|
+
false
|
114
|
+
end
|
108
115
|
end
|
109
116
|
end
|
110
117
|
end
|
@@ -12,6 +12,7 @@ module Inspec::Plugin::V2::PluginType
|
|
12
12
|
@control_checks_count_map = {}
|
13
13
|
@controls_count = nil
|
14
14
|
@notifications = {}
|
15
|
+
@enhanced_outcome_control_wise = {}
|
15
16
|
end
|
16
17
|
|
17
18
|
private
|
@@ -29,16 +30,43 @@ module Inspec::Plugin::V2::PluginType
|
|
29
30
|
|
30
31
|
# method to identify when the control ended running
|
31
32
|
# this will be useful in executing operations on control's level end
|
32
|
-
def control_ended?(control_id)
|
33
|
+
def control_ended?(notification, control_id)
|
33
34
|
set_control_checks_count_map_value
|
34
35
|
unless @control_checks_count_map[control_id].nil?
|
35
36
|
@control_checks_count_map[control_id] -= 1
|
36
|
-
@control_checks_count_map[control_id] == 0
|
37
|
+
control_ended = @control_checks_count_map[control_id] == 0
|
38
|
+
# after a control has ended it checks for certain operations, like enhanced outcomes
|
39
|
+
run_control_operations(notification, control_id) if control_ended
|
40
|
+
control_ended
|
37
41
|
else
|
38
42
|
false
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
46
|
+
def run_control_operations(notification, control_id)
|
47
|
+
check_for_enhanced_outcomes(notification, control_id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_for_enhanced_outcomes(notification, control_id)
|
51
|
+
if enhanced_outcomes
|
52
|
+
control_outcome = add_enhanced_outcomes(control_id)
|
53
|
+
@enhanced_outcome_control_wise[control_id] = control_outcome
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_message(indicator, control_id, title, full_description)
|
58
|
+
message_to_format = ""
|
59
|
+
message_to_format += "#{indicator} "
|
60
|
+
message_to_format += "#{control_id.to_s.strip.dup.force_encoding(Encoding::UTF_8)} "
|
61
|
+
message_to_format += "#{title.gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8)} " if title
|
62
|
+
message_to_format += "#{full_description.gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8)} " unless title
|
63
|
+
message_to_format
|
64
|
+
end
|
65
|
+
|
66
|
+
def control_outcome(control_id)
|
67
|
+
@enhanced_outcome_control_wise[control_id]
|
68
|
+
end
|
69
|
+
|
42
70
|
# method to identify total no. of controls
|
43
71
|
def controls_count
|
44
72
|
@controls_count ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_controls_count
|