inspec-core 4.18.114 → 4.20.10

Sign up to get free protection for your applications and to get access to all the features.
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