inspec-core 5.22.40 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/Chef-EULA +9 -0
  3. data/Gemfile +10 -5
  4. data/etc/features.sig +6 -0
  5. data/etc/features.yaml +94 -0
  6. data/inspec-core.gemspec +14 -5
  7. data/lib/inspec/backend.rb +2 -0
  8. data/lib/inspec/base_cli.rb +80 -4
  9. data/lib/inspec/cached_fetcher.rb +24 -3
  10. data/lib/inspec/cli.rb +292 -235
  11. data/lib/inspec/config.rb +24 -11
  12. data/lib/inspec/dependencies/cache.rb +33 -0
  13. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  14. data/lib/inspec/dsl.rb +1 -1
  15. data/lib/inspec/enhanced_outcomes.rb +1 -0
  16. data/lib/inspec/errors.rb +5 -0
  17. data/lib/inspec/exceptions.rb +2 -0
  18. data/lib/inspec/feature/config.rb +75 -0
  19. data/lib/inspec/feature/runner.rb +26 -0
  20. data/lib/inspec/feature.rb +34 -0
  21. data/lib/inspec/fetcher/git.rb +5 -0
  22. data/lib/inspec/globals.rb +6 -0
  23. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +7 -0
  24. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +30 -2
  25. data/lib/inspec/profile.rb +46 -3
  26. data/lib/inspec/reporters/cli.rb +1 -1
  27. data/lib/inspec/reporters.rb +67 -54
  28. data/lib/inspec/rule.rb +9 -14
  29. data/lib/inspec/run_data.rb +7 -5
  30. data/lib/inspec/runner.rb +35 -6
  31. data/lib/inspec/runner_rspec.rb +12 -9
  32. data/lib/inspec/secrets/yaml.rb +9 -3
  33. data/lib/inspec/shell.rb +10 -0
  34. data/lib/inspec/ui.rb +4 -0
  35. data/lib/inspec/utils/licensing_config.rb +9 -0
  36. data/lib/inspec/utils/waivers/csv_file_reader.rb +1 -1
  37. data/lib/inspec/utils/waivers/excel_file_reader.rb +1 -1
  38. data/lib/inspec/version.rb +1 -1
  39. data/lib/inspec/waiver_file_reader.rb +68 -27
  40. data/lib/inspec.rb +2 -1
  41. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -168
  42. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
  43. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  44. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
  45. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
  46. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
  47. data/lib/plugins/inspec-license/README.md +16 -0
  48. data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
  49. data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
  50. data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
  51. data/lib/plugins/inspec-parallel/README.md +27 -0
  52. data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
  53. data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
  54. data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
  55. data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
  56. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
  57. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
  58. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
  59. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
  60. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
  61. data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
  62. data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
  63. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +6 -2
  64. data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
  65. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
  66. metadata +53 -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
- transport_options.include? option_name # e.g., 'host'
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
 
@@ -448,15 +470,6 @@ module Inspec
448
470
  # Reporter options may be defined top-level.
449
471
  options.merge!(config_file_reporter_options)
450
472
 
451
- # when sent reporter from compliance-mode (via chef-client), the reporter is a symbol
452
- if @cli_opts.key?(:reporter) && @cli_opts["reporter"].nil?
453
- @cli_opts["reporter"] = @cli_opts[:reporter]
454
- @cli_opts.delete(:reporter)
455
- elsif @cli_opts.key?(:reporter) && @cli_opts.key?("reporter") && @cli_opts["reporter"].is_a?(Array)
456
- # combine reporter and "reporter" options into "reporter" option
457
- @cli_opts["reporter"] = @cli_opts[:reporter] + @cli_opts["reporter"]
458
- end
459
-
460
473
  if @cli_opts["reporter"]
461
474
  # Add reporter_cli_opts in options to capture reporter cli opts separately
462
475
  options.merge!({ "reporter_cli_opts" => @cli_opts["reporter"] })
@@ -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
@@ -26,7 +26,7 @@ module Inspec
26
26
  dep_list = {}
27
27
  dependencies.each do |d|
28
28
  # if depedent profile does not have a source version then only name is used in dependency hash
29
- key_name = ((d.source_version.nil? || d.source_version.empty?) ? "#{d.name}" : "#{d.name}-#{d.source_version}") rescue "#{d.name}"
29
+ key_name = (d.source_version.blank? ? "#{d.name}" : "#{d.name}-#{d.source_version}") rescue "#{d.name}"
30
30
  dep_list[key_name] = d
31
31
  end
32
32
  new(cwd, cache, dep_list, backend)
@@ -42,7 +42,7 @@ module Inspec
42
42
  dep_list = {}
43
43
  dep_tree.each do |d|
44
44
  # if depedent profile does not have a source version then only name is used in dependency hash
45
- key_name = ((d.source_version.nil? || d.source_version.empty?) ? "#{d.name}" : "#{d.name}-#{d.source_version}") rescue "#{d.name}"
45
+ key_name = (d.source_version.blank? ? "#{d.name}" : "#{d.name}-#{d.source_version}") rescue "#{d.name}"
46
46
  dep_list[key_name] = d
47
47
  dep_list.merge!(flatten_dep_tree(d.dependencies))
48
48
  end
data/lib/inspec/dsl.rb CHANGED
@@ -95,7 +95,7 @@ module Inspec::DSL
95
95
  # 1. Fetching VERSION from a profile dependency name which is in a format NAME-VERSION.
96
96
  # 2. Matching original profile dependency name with profile name used with include or require control DSL.
97
97
  source_version = value.source_version
98
- unless source_version.nil? || source_version.empty?
98
+ unless source_version.blank?
99
99
  profile_id_key = key.split("-#{source_version}")[0]
100
100
  new_profile_id = key if profile_id_key == profile_id
101
101
  end
@@ -15,5 +15,6 @@ module Inspec
15
15
  "passed"
16
16
  end
17
17
  end
18
+
18
19
  end
19
20
  end
data/lib/inspec/errors.rb CHANGED
@@ -24,4 +24,9 @@ module Inspec
24
24
  end
25
25
 
26
26
  class InvalidProfileSignature < Error; end
27
+
28
+ class FeatureConfigMissingError < Error; end
29
+ class FeatureConfigTamperedError < Error; end
30
+
31
+ class ProfileSignatureRequired < Error; end
27
32
  end
@@ -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
@@ -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
@@ -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
@@ -16,6 +16,7 @@ require "inspec/utils/json_profile_summary"
16
16
  require "inspec/dependency_loader"
17
17
  require "inspec/dependency_installer"
18
18
  require "inspec/utils/profile_ast_helpers"
19
+ require "plugins/inspec-sign/lib/inspec-sign/base"
19
20
 
20
21
  module Inspec
21
22
  class Profile
@@ -82,7 +83,7 @@ module Inspec
82
83
  end
83
84
 
84
85
  attr_reader :source_reader, :backend, :runner_context, :check_mode
85
- attr_accessor :parent_profile, :profile_id, :profile_name
86
+ attr_accessor :parent_profile, :profile_id, :profile_name, :target
86
87
  def_delegator :@source_reader, :tests
87
88
  def_delegator :@source_reader, :libraries
88
89
  def_delegator :@source_reader, :metadata
@@ -181,6 +182,26 @@ module Inspec
181
182
  @state == :failed
182
183
  end
183
184
 
185
+ def verify_if_signed
186
+ if signed?
187
+ verify_signed_profile
188
+ true
189
+ else
190
+ false
191
+ end
192
+ end
193
+
194
+ def signed?
195
+ # Signed profiles have .iaf extension
196
+ (@source_reader&.target&.parent&.class == Inspec::IafProvider)
197
+ end
198
+
199
+ def verify_signed_profile
200
+ # Kitchen inspec send target profile in Hash format in some scenarios. For example: While using local profile with kitchen, {:path => "path/to/kitchen/lcoal-profile"}
201
+ target_profile = target.is_a?(Hash) ? target.values[0] : target
202
+ InspecPlugins::Sign::Base.profile_verify(target_profile, true) if target_profile
203
+ end
204
+
184
205
  #
185
206
  # Is this profile is supported on the current platform of the
186
207
  # backend machine and the current inspec version.
@@ -215,8 +236,30 @@ module Inspec
215
236
  @params ||= load_params
216
237
  end
217
238
 
239
+ def virtual_profile?
240
+ # A virtual profile is for virtual profile evaluation
241
+ # Used by shell & inspec detect command.
242
+ (name == "inspec-shell") && (files&.length == 1) && (files[0] == "inspec.yml")
243
+ end
244
+
218
245
  def collect_tests
219
246
  unless @tests_collected || failed?
247
+
248
+ # This is that one common place in InSpec engine which is used to collect tests of InSpec profile
249
+ # One common place used by most of the CLI commands using profile, like exec, export etc
250
+ # Checking for profile signature in parent profile only
251
+ # Child profiles of a signed profile are extracted to cache dir
252
+ # Hence they are not in .iaf format
253
+ # Only runs this block when preview flag CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING is set
254
+ Inspec.with_feature("inspec-mandatory-profile-signing") {
255
+ if !parent_profile && !virtual_profile?
256
+ cfg = Inspec::Config.cached
257
+ if cfg.is_a?(Inspec::Config) && !cfg.allow_unsigned_profiles?
258
+ raise Inspec::ProfileSignatureRequired, "Signature required for profile: #{name}. Please provide a signed profile. Or set CHEF_ALLOW_UNSIGNED_PROFILES in the environment. Or use `--allow-unsigned-profiles` flag with InSpec CLI." unless verify_if_signed
259
+ end
260
+ end
261
+ }
262
+
220
263
  return unless supports_platform?
221
264
 
222
265
  locked_dependencies.each(&:collect_tests)
@@ -248,7 +291,7 @@ module Inspec
248
291
  ## Find the waivers file
249
292
  # - TODO: cli_opts and instance_variable_get could be exposed
250
293
  waiver_paths = cfg.instance_variable_get(:@cli_opts)["waiver_file"]
251
- if waiver_paths.nil? || waiver_paths.empty?
294
+ if waiver_paths.blank?
252
295
  Inspec::Log.error "Must use --waiver-file with --filter-waived-controls"
253
296
  Inspec::UI.new.exit(:usage_error)
254
297
  end
@@ -276,7 +319,7 @@ module Inspec
276
319
  # be processed and rendered
277
320
  tests.each do |control_filename, source_code|
278
321
  cleared_tests = source_code.scan(/control\s+['"].+?['"].+?(?=(?:control\s+['"].+?['"])|\z)/m).collect do |element|
279
- next if element.nil? || element.empty?
322
+ next if element.blank?
280
323
 
281
324
  if element&.match?(waived_control_id_regex)
282
325
  splitlines = element.split("\n")
@@ -317,7 +317,7 @@ module Inspec::Reporters
317
317
  not_applicable = 0
318
318
 
319
319
  all_unique_controls.each do |control|
320
- next if control[:status].nil? || control[:status].empty?
320
+ next if control[:status].blank?
321
321
 
322
322
  if control[:status] == "failed"
323
323
  failed += 1
@@ -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
- config[:run_data] = run_data
13
- case name
14
- when "cli"
15
- reporter = Inspec::Reporters::CLI.new(config)
16
- when "json"
17
- reporter = Inspec::Reporters::Json.new(config)
18
- # This reporter is only used for Chef internal. We reserve the
19
- # right to introduce breaking changes to this reporter at any time.
20
- when "json-automate"
21
- reporter = Inspec::Reporters::JsonAutomate.new(config)
22
- when "automate"
23
- reporter = Inspec::Reporters::Automate.new(config)
24
- when "yaml"
25
- reporter = Inspec::Reporters::Yaml.new(config)
26
- else
27
- # If we made it here, it must be a plugin, and we know it exists (because we validated it in config.rb)
28
- activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
29
- activator.activate!
30
- reporter = activator.implementation_class.new(config)
31
- end
32
- reporter.enhanced_outcomes = enhanced_outcomes
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
- # optional send_report method on reporter
35
- return reporter.send_report if defined?(reporter.send_report)
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
- reporter.render
38
- output = reporter.rendered_output
43
+ # optional send_report method on reporter
44
+ return reporter.send_report if defined?(reporter.send_report)
39
45
 
40
- if config["file"]
41
- # create destination directory if it does not exist
42
- dirname = File.dirname(config["file"])
43
- FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
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
- File.write(config["file"], output)
46
- elsif config["stdout"] == true
47
- print output
48
- $stdout.flush
49
- end
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
- config[:run_data] = run_data
55
- case name
56
- when "json"
57
- reporter = Inspec::Reporters::Json.new(config)
58
- when "json-automate"
59
- reporter = Inspec::Reporters::JsonAutomate.new(config)
60
- when "yaml"
61
- reporter = Inspec::Reporters::Yaml.new(config)
62
- else
63
- # If we made it here, it might be a plugin
64
- begin
65
- activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
66
- activator.activate!
67
- reporter = activator.implementation_class.new(config)
68
- unless reporter.respond_to(:report?)
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
- reporter.report
89
+ reporter.report
90
+ }
78
91
  end
79
92
  end