inspec-core 5.22.29 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Chef-EULA +9 -0
  3. data/Gemfile +10 -1
  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 +300 -230
  11. data/lib/inspec/config.rb +24 -2
  12. data/lib/inspec/dependencies/cache.rb +33 -0
  13. data/lib/inspec/enhanced_outcomes.rb +1 -0
  14. data/lib/inspec/errors.rb +5 -0
  15. data/lib/inspec/exceptions.rb +2 -0
  16. data/lib/inspec/feature/config.rb +75 -0
  17. data/lib/inspec/feature/runner.rb +26 -0
  18. data/lib/inspec/feature.rb +34 -0
  19. data/lib/inspec/fetcher/git.rb +5 -0
  20. data/lib/inspec/globals.rb +6 -0
  21. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +7 -0
  22. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +30 -2
  23. data/lib/inspec/profile.rb +373 -12
  24. data/lib/inspec/reporters/cli.rb +1 -1
  25. data/lib/inspec/reporters.rb +67 -54
  26. data/lib/inspec/resources/security_policy.rb +7 -2
  27. data/lib/inspec/run_data.rb +7 -5
  28. data/lib/inspec/runner.rb +34 -5
  29. data/lib/inspec/runner_rspec.rb +12 -9
  30. data/lib/inspec/secrets/yaml.rb +9 -3
  31. data/lib/inspec/shell.rb +10 -0
  32. data/lib/inspec/ui.rb +4 -0
  33. data/lib/inspec/utils/licensing_config.rb +9 -0
  34. data/lib/inspec/utils/profile_ast_helpers.rb +372 -0
  35. data/lib/inspec/version.rb +1 -1
  36. data/lib/inspec/waiver_file_reader.rb +68 -27
  37. data/lib/inspec.rb +2 -1
  38. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -168
  39. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
  40. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  41. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
  42. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
  43. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
  44. data/lib/plugins/inspec-license/README.md +16 -0
  45. data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
  46. data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
  47. data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
  48. data/lib/plugins/inspec-parallel/README.md +27 -0
  49. data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
  50. data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
  51. data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
  52. data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
  53. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
  54. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
  55. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
  56. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
  57. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
  58. data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
  59. data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
  60. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +7 -6
  61. data/lib/plugins/inspec-reporter-html2/templates/default.js +6 -6
  62. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +6 -2
  63. data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
  64. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
  65. 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
- 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
 
@@ -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
@@ -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