inspec-core 4.18.114 → 4.20.10

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/inspec-core.gemspec +1 -1
  4. data/lib/inspec/base_cli.rb +4 -1
  5. data/lib/inspec/cli.rb +13 -22
  6. data/lib/inspec/config.rb +21 -7
  7. data/lib/inspec/fetcher/git.rb +1 -1
  8. data/lib/inspec/fetcher/local.rb +1 -1
  9. data/lib/inspec/fetcher/url.rb +1 -1
  10. data/lib/inspec/input_registry.rb +33 -1
  11. data/lib/inspec/plugin/v2/plugin_types/reporter.rb +68 -0
  12. data/lib/inspec/profile.rb +16 -4
  13. data/lib/inspec/reporters.rb +16 -8
  14. data/lib/inspec/resources/x509_certificate.rb +1 -1
  15. data/lib/inspec/rule.rb +7 -3
  16. data/lib/inspec/run_data.rb +71 -0
  17. data/lib/inspec/run_data/control.rb +83 -0
  18. data/lib/inspec/run_data/profile.rb +109 -0
  19. data/lib/inspec/run_data/result.rb +40 -0
  20. data/lib/inspec/run_data/statistics.rb +36 -0
  21. data/lib/inspec/utils/deprecation/config_file.rb +21 -0
  22. data/lib/inspec/utils/json_profile_summary.rb +35 -0
  23. data/lib/inspec/utils/telemetry/run_context_probe.rb +48 -0
  24. data/lib/inspec/version.rb +1 -1
  25. data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +18 -1
  26. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +28 -6
  27. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.rb +18 -0
  28. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/reporter.rb +27 -0
  29. data/lib/plugins/inspec-reporter-json-min/README.md +10 -0
  30. data/lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min.rb +13 -0
  31. data/lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min/reporter.rb +50 -0
  32. data/lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min/version.rb +5 -0
  33. metadata +17 -5
  34. data/lib/inspec/reporters/json_min.rb +0 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bee754e41f53d26552da0d2972a28cf55127effcad8c93df678227e99061e676
4
- data.tar.gz: 60bd7f57517db8c4e1848ded6ac1fd580b61c29a9b2d24168480b3d62e37557f
3
+ metadata.gz: ef611d3b2bb1d1c8ddca64adb1ed3eab50fcbec55a45cb49334ed2084c45e7da
4
+ data.tar.gz: 96664cb6183b137db4f84cd26aed2b19496475045bbb458ea9d9004e92aad3f7
5
5
  SHA512:
6
- metadata.gz: 7c9b443a572925dd12dc846c094256aff3f9fabcc4508ee51ab813f0ec6221088c500ce9a1c77cca693258b48a94212043d2ffd4eb8c49052dbb15bc8d93cc00
7
- data.tar.gz: 55c2e45a2ef2e8c9da30a24eb4b09e213320e1d33d2c50e30856754038d75f59576dd76f084a59ee2028920baa37674c0683b426dce6bb3dc06bc5a8c879566b
6
+ metadata.gz: f1d44fc61e4663862a0628a0fcc4c4e3538bdb35d50cee94f1130c47fed5a982297da9c559128b0dddc2a7375658252924959020f24f518202b25939357340de
7
+ data.tar.gz: e33e28b5863ce15a54c8f2f76dd3a458762c24fb7d365ba145ae06b6b0be14134b90da5f2610d834ae71a170e5f6b180c78a8ac8763f484b3bbcfa808eec8565
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ gem "inspec", path: "."
9
9
  # in it in order to package the executable. Hence the odd backwards dependency.
10
10
  gem "inspec-bin", path: "./inspec-bin"
11
11
 
12
- gem "ffi", ">= 1.9.14"
12
+ gem "ffi", ">= 1.9.14", "!= 1.13.0"
13
13
 
14
14
  group :omnibus do
15
15
  gem "rb-readline"
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
39
39
  spec.add_dependency "faraday", ">= 0.9.0"
40
40
  spec.add_dependency "tty-table", "~> 0.10"
41
41
  spec.add_dependency "tty-prompt", "~> 0.17"
42
- spec.add_dependency "tomlrb", "~> 1.2"
42
+ spec.add_dependency "tomlrb", "~> 1.2.0"
43
43
  spec.add_dependency "addressable", "~> 2.4"
44
44
  spec.add_dependency "parslet", "~> 1.5"
45
45
  spec.add_dependency "semverse", "~> 3.0"
@@ -140,7 +140,7 @@ module Inspec
140
140
  option :reporter_backtrace_inclusion, type: :boolean,
141
141
  desc: "Include a code backtrace in report data (default: true)"
142
142
  option :input, type: :array, banner: "name1=value1 name2=value2",
143
- desc: "Specify one or more inputs directly on the command line, as --input NAME=VALUE"
143
+ desc: "Specify one or more inputs directly on the command line, as --input NAME=VALUE. Accepts single-quoted YAML and JSON structures."
144
144
  option :input_file, type: :array,
145
145
  desc: "Load one or more input files, a YAML file with values for the profile to use"
146
146
  option :waiver_file, type: :array,
@@ -155,6 +155,9 @@ module Inspec
155
155
  desc: "Show progress while executing tests."
156
156
  option :distinct_exit, type: :boolean, default: true,
157
157
  desc: "Exit with code 101 if any tests fail, and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures."
158
+ option :silence_deprecations, type: :array,
159
+ banner: "[all]|[GROUP GROUP...]",
160
+ desc: "Suppress deprecation warnings. See install_dir/etc/deprecations.json for list of GROUPs or use 'all'."
158
161
  end
159
162
 
160
163
  def self.format_platform_info(params: {}, indent: 0, color: 39)
@@ -4,6 +4,7 @@ require "inspec/utils/deprecation/deprecator"
4
4
  require "inspec/dist"
5
5
  require "inspec/backend"
6
6
  require "inspec/dependencies/cache"
7
+ require "inspec/utils/json_profile_summary"
7
8
 
8
9
  module Inspec # TODO: move this somewhere "better"?
9
10
  autoload :BaseCLI, "inspec/base_cli"
@@ -77,24 +78,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
77
78
  o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
78
79
 
79
80
  profile = Inspec::Profile.for_target(target, o)
80
- info = profile.info
81
- # add in inspec version
82
- info[:generator] = {
83
- name: "inspec",
84
- version: Inspec::VERSION,
85
- }
86
81
  dst = o[:output].to_s
87
- if dst.empty?
88
- puts JSON.dump(info)
89
- else
90
- if File.exist? dst
91
- puts "----> updating #{dst}"
92
- else
93
- puts "----> creating #{dst}"
94
- end
95
- fdst = File.expand_path(dst)
96
- File.write(fdst, JSON.dump(info))
97
- end
82
+
83
+ # Write JSON
84
+ Inspec::Utils::JsonProfileSummary.produce_json(
85
+ info: profile.info,
86
+ write_path: dst
87
+ )
98
88
  rescue StandardError => e
99
89
  pretty_handle_exception(e)
100
90
  end
@@ -385,6 +375,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
385
375
  puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}"
386
376
  end
387
377
 
378
+ desc "run_context", "used to test run-context detection", hide: true
379
+ def run_context
380
+ require "inspec/utils/telemetry/run_context_probe"
381
+ puts Inspec::Telemetry::RunContextProbe.guess_run_context
382
+ end
383
+
388
384
  desc "version", "prints the version of this tool"
389
385
  option :format, type: :string
390
386
  def version
@@ -397,11 +393,6 @@ class Inspec::InspecCLI < Inspec::BaseCLI
397
393
  end
398
394
  map %w{-v --version} => :version
399
395
 
400
- desc "nothing", "does nothing"
401
- def nothing
402
- puts "you did nothing"
403
- end
404
-
405
396
  private
406
397
 
407
398
  def run_command(opts)
@@ -328,21 +328,35 @@ module Inspec
328
328
  def validate_reporters!(reporters)
329
329
  return if reporters.nil?
330
330
 
331
- # TODO: move this into a reporter plugin type system
332
- valid_types = %w{
333
- automate
334
- cli
331
+ # These "reporters" are actually RSpec Formatters.
332
+ # json-rspec is our alias for RSpec's json formatter.
333
+ rspec_built_in_formatters = %w{
335
334
  documentation
336
335
  html
336
+ json-rspec
337
+ progress
338
+ }
339
+
340
+ # These are true reporters, but have not been migrated to be plugins yet.
341
+ # Tracked on https://github.com/inspec/inspec/issues/3667
342
+ inspec_reporters_that_are_not_yet_plugins = %w{
343
+ automate
344
+ cli
337
345
  json
338
346
  json-automate
339
- json-min
340
- json-rspec
341
347
  junit
342
- progress
343
348
  yaml
344
349
  }
345
350
 
351
+ # Additional reporters may be loaded via plugins. They will have already been detected at
352
+ # this point (see v2_loader.load_all in cli.rb) but they may not (and need not) be
353
+ # activated at this point. We only care about their existance and their name, for validation's sake.
354
+ plugin_reporters = Inspec::Plugin::V2::Registry.instance\
355
+ .find_activators(plugin_type: :reporter)\
356
+ .map(&:activator_name).map(&:to_s)
357
+
358
+ valid_types = rspec_built_in_formatters + inspec_reporters_that_are_not_yet_plugins + plugin_reporters
359
+
346
360
  reporters.each do |reporter_name, reporter_config|
347
361
  raise NotImplementedError, "'#{reporter_name}' is not a valid reporter type." unless valid_types.include?(reporter_name)
348
362
 
@@ -99,7 +99,7 @@ module Inspec::Fetcher
99
99
  def cache_key
100
100
  return resolved_ref unless @relative_path
101
101
 
102
- OpenSSL::Digest::SHA256.hexdigest(resolved_ref + @relative_path)
102
+ OpenSSL::Digest.hexdigest("SHA256", resolved_ref + @relative_path)
103
103
  end
104
104
 
105
105
  def archive_path
@@ -104,7 +104,7 @@ module Inspec::Fetcher
104
104
  return @archive_shasum if @archive_shasum
105
105
  raise(Inspec::FetcherFailure, "Profile dependency local path '#{target}' does not exist") unless File.exist?(target)
106
106
 
107
- @archive_shasum = OpenSSL::Digest::SHA256.digest(File.read(target)).unpack("H*")[0]
107
+ @archive_shasum = OpenSSL::Digest.digest("SHA256", File.read(target)).unpack("H*")[0]
108
108
  end
109
109
 
110
110
  def resolved_source
@@ -127,7 +127,7 @@ module Inspec::Fetcher
127
127
  end
128
128
 
129
129
  def sha256
130
- @archive_shasum ||= OpenSSL::Digest::SHA256.digest(File.read(@archive_path || temp_archive_path)).unpack("H*")[0]
130
+ @archive_shasum ||= OpenSSL::Digest.digest("SHA256", File.read(@archive_path || temp_archive_path)).unpack("H*")[0]
131
131
  end
132
132
 
133
133
  def file_type_from_remote(remote)
@@ -166,8 +166,9 @@ module Inspec
166
166
  end
167
167
  end
168
168
  input_name, input_value = pair.split("=")
169
+ input_value = parse_cli_input_value(input_name, input_value)
169
170
  evt = Inspec::Input::Event.new(
170
- value: input_value.chomp(","), # Trim trailing comma if any
171
+ value: input_value,
171
172
  provider: :cli,
172
173
  priority: 50
173
174
  )
@@ -175,6 +176,37 @@ module Inspec
175
176
  end
176
177
  end
177
178
 
179
+ # Remove trailing commas, resolve type.
180
+ def parse_cli_input_value(input_name, given_value)
181
+ value = given_value.chomp(",") # Trim trailing comma if any
182
+ case value
183
+ when /^true|false$/i
184
+ value = !!(value =~ /true/i)
185
+ when /^-?\d+$/
186
+ value = value.to_i
187
+ when /^-?\d+\.\d+$/
188
+ value = value.to_f
189
+ when /^(\[|\{).*(\]|\})$/
190
+ # Look for complex values and try to parse them.
191
+ require "yaml"
192
+ begin
193
+ value = YAML.load(value)
194
+ rescue Psych::SyntaxError => yaml_error
195
+ # It could be that we just tried to run JSON through the YAML parser.
196
+ require "json"
197
+ begin
198
+ value = JSON.parse(value)
199
+ rescue JSON::ParserError => json_error
200
+ msg = "Unparseable value '#{value}' for --input #{input_name}.\n"
201
+ msg += "When treated as YAML, error: #{yaml_error.message}\n"
202
+ msg += "When treated as JSON, error: #{json_error.message}"
203
+ Inspec::Log.warn msg
204
+ end
205
+ end
206
+ end
207
+ value
208
+ end
209
+
178
210
  def bind_inputs_from_runner_api(profile_name, input_hash)
179
211
  # TODO: move this into a core plugin
180
212
 
@@ -0,0 +1,68 @@
1
+ require_relative "../../../run_data"
2
+
3
+ module Inspec::Plugin::V2::PluginType
4
+ class Reporter < Inspec::Plugin::V2::PluginBase
5
+ register_plugin_type(:reporter)
6
+
7
+ attr_reader :run_data
8
+
9
+ def initialize(config)
10
+ @config = config
11
+
12
+ # Trim the run_data while still a Hash; if it is huge, this
13
+ # saves on conversion time
14
+ @run_data = config[:run_data] || {}
15
+ apply_report_resize_options
16
+
17
+ unless Inspec::RunData.compatible_schema?(self.class.run_data_schema_constraints)
18
+ # Best we can do is warn here, the InSpec run has finished
19
+ # TODO: one day, perhaps switch RunData implementations to try to satisfy constraints?
20
+ Inspec::Log.warn "Reporter does not support RunData API (#{Inspec::RunData::SCHEMA_VERSION}), Reporter constraints: '#{self.class.run_data_schema_constraints}'"
21
+ end
22
+ # Convert to RunData object for consumption by Reporter
23
+ @run_data = Inspec::RunData.new(@run_data)
24
+ @output = ""
25
+ end
26
+
27
+ # This is a temporary duplication of code from lib/inspec/reporters/base.rb
28
+ # To be DRY'd up once the core reporters become plugins...
29
+ # Apply options such as message truncation and removal of backtraces
30
+ def apply_report_resize_options
31
+ runtime_config = Inspec::Config.cached.respond_to?(:final_options) ? Inspec::Config.cached.final_options : {}
32
+
33
+ message_truncation = runtime_config[:reporter_message_truncation] || "ALL"
34
+ trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
35
+ include_backtrace = runtime_config[:reporter_backtrace_inclusion].nil? ? true : runtime_config[:reporter_backtrace_inclusion]
36
+
37
+ @run_data[:profiles]&.each do |p|
38
+ p[:controls].each do |c|
39
+ c[:results]&.map! do |r|
40
+ r.delete(:backtrace) unless include_backtrace
41
+ if r.key?(:message) && r[:message] != "" && trunc > -1
42
+ r[:message] = r[:message][0...trunc] + "[Truncated to #{trunc} characters]"
43
+ end
44
+ r
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def output(str, newline = true)
51
+ @output << str
52
+ @output << "\n" if newline
53
+ end
54
+
55
+ def rendered_output
56
+ @output
57
+ end
58
+
59
+ # each reporter must implement #render
60
+ def render
61
+ raise NotImplementedError, "#{self.class} must implement a `#render` method to format its output."
62
+ end
63
+
64
+ def self.run_data_schema_constraints
65
+ raise NotImplementedError, "#{self.class} must implement a `run_data_schema_constraints` class method to declare its compatibiltity with the RunData API."
66
+ end
67
+ end
68
+ end
@@ -12,6 +12,7 @@ require "inspec/method_source"
12
12
  require "inspec/dependencies/cache"
13
13
  require "inspec/dependencies/lockfile"
14
14
  require "inspec/dependencies/dependency_set"
15
+ require "inspec/utils/json_profile_summary"
15
16
 
16
17
  module Inspec
17
18
  class Profile
@@ -465,28 +466,39 @@ module Inspec
465
466
  end
466
467
 
467
468
  # remove existing archive
468
- File.delete(dst) if dst.exist?
469
+ FileUtils.rm_f(dst) if dst.exist?
469
470
  @logger.info "Generate archive #{dst}."
470
471
 
471
472
  # filter files that should not be part of the profile
472
473
  # TODO ignore all .files, but add the files to debug output
473
474
 
475
+ # Generate temporary inspec.json for archive
476
+ Inspec::Utils::JsonProfileSummary.produce_json(
477
+ info: info,
478
+ write_path: "#{root_path}inspec.json",
479
+ suppress_output: true
480
+ )
481
+
474
482
  # display all files that will be part of the archive
475
483
  @logger.debug "Add the following files to archive:"
476
484
  files.each { |f| @logger.debug " " + f }
485
+ @logger.debug " inspec.json"
477
486
 
478
487
  if opts[:zip]
479
488
  # generate zip archive
480
489
  require "inspec/archive/zip"
481
490
  zag = Inspec::Archive::ZipArchiveGenerator.new
482
- zag.archive(root_path, files, dst)
491
+ zag.archive(root_path, files.push("inspec.json"), dst)
483
492
  else
484
493
  # generate tar archive
485
494
  require "inspec/archive/tar"
486
495
  tag = Inspec::Archive::TarArchiveGenerator.new
487
- tag.archive(root_path, files, dst)
496
+ tag.archive(root_path, files.push("inspec.json"), dst)
488
497
  end
489
498
 
499
+ # Cleanup
500
+ FileUtils.rm_f("#{root_path}inspec.json")
501
+
490
502
  @logger.info "Finished archive generation."
491
503
  true
492
504
  end
@@ -559,7 +571,7 @@ module Inspec
559
571
  # get all dependency checksums
560
572
  deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }]
561
573
 
562
- res = OpenSSL::Digest::SHA256.new
574
+ res = OpenSSL::Digest.new("SHA256")
563
575
  files = source_reader.tests.to_a + source_reader.libraries.to_a +
564
576
  source_reader.data_files.to_a +
565
577
  [["inspec.yml", source_reader.metadata.content]] +
@@ -2,7 +2,6 @@ require "inspec/reporters/base"
2
2
  require "inspec/reporters/cli"
3
3
  require "inspec/reporters/json"
4
4
  require "inspec/reporters/json_automate"
5
- require "inspec/reporters/json_min"
6
5
  require "inspec/reporters/junit"
7
6
  require "inspec/reporters/automate"
8
7
  require "inspec/reporters/yaml"
@@ -21,8 +20,6 @@ module Inspec::Reporters
21
20
  # right to introduce breaking changes to this reporter at any time.
22
21
  when "json-automate"
23
22
  reporter = Inspec::Reporters::JsonAutomate.new(config)
24
- when "json-min"
25
- reporter = Inspec::Reporters::JsonMin.new(config)
26
23
  when "junit"
27
24
  reporter = Inspec::Reporters::Junit.new(config)
28
25
  when "automate"
@@ -30,7 +27,10 @@ module Inspec::Reporters
30
27
  when "yaml"
31
28
  reporter = Inspec::Reporters::Yaml.new(config)
32
29
  else
33
- raise NotImplementedError, "'#{name}' is not a valid reporter type."
30
+ # If we made it here, it must be a plugin, and we know it exists (because we validated it in config.rb)
31
+ activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
32
+ activator.activate!
33
+ reporter = activator.implementation_class.new(config)
34
34
  end
35
35
 
36
36
  # optional send_report method on reporter
@@ -57,15 +57,23 @@ module Inspec::Reporters
57
57
  case name
58
58
  when "json"
59
59
  reporter = Inspec::Reporters::Json.new(config)
60
- when "json-min"
61
- reporter = Inspec::Reporters::JsonMin.new(config)
62
60
  when "json-automate"
63
61
  reporter = Inspec::Reporters::JsonAutomate.new(config)
64
62
  when "yaml"
65
63
  reporter = Inspec::Reporters::Yaml.new(config)
66
64
  else
67
- # use base run_data hash for any other report
68
- return run_data
65
+ # If we made it here, it might be a plugin
66
+ begin
67
+ activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
68
+ activator.activate!
69
+ reporter = activator.implementation_class.new(config)
70
+ unless reporter.respond_to(:report?)
71
+ return run_data
72
+ end
73
+ rescue Inspec::Plugin::V2::LoadError
74
+ # Must not have been a plugin - just return the run_data
75
+ return run_data
76
+ end
69
77
  end
70
78
 
71
79
  reporter.report
@@ -59,7 +59,7 @@ module Inspec::Resources
59
59
  def fingerprint
60
60
  return if @cert.nil?
61
61
 
62
- OpenSSL::Digest::SHA1.new(@cert.to_der).to_s
62
+ OpenSSL::Digest.new("SHA1", @cert.to_der).to_s
63
63
  end
64
64
 
65
65
  def serial
@@ -353,9 +353,13 @@ module Inspec
353
353
  # if so, is it in the future?
354
354
  expiry = __waiver_data["expiration_date"]
355
355
  if expiry
356
- if expiry.is_a?(Date)
357
- # It appears that yaml.rb automagically parses dates for us
358
- if expiry < Date.today # If the waiver expired, return - no skip applied
356
+ # YAML will automagically give us a Date or a Time.
357
+ # If transcoding YAML between languages (e.g. Go) the date might have also ended up as a String.
358
+ # A string that does not represent a valid time results in the date 0000-01-01.
359
+ if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.new(expiry).year != 0)
360
+ expiry = expiry.to_time if expiry.is_a? Date
361
+ expiry = Time.new(expiry) if expiry.is_a? String
362
+ if expiry < Time.now # If the waiver expired, return - no skip applied
359
363
  __waiver_data["message"] = "Waiver expired on #{expiry}, evaluating control normally"
360
364
  return
361
365
  end