inspec-core 4.18.104 → 4.19.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5866bdaba80db3f77d6a3a31af54fcdc6584cb0315093975a90e2a67616b95bd
4
- data.tar.gz: a7a84ee7bba5072fc042ea7bd9af8692808e3cc4ae328d182c9af05dd495affd
3
+ metadata.gz: d3f655ad7598f4a71bab9caff608eca6990e6c66242d1c11519d105c89168fcb
4
+ data.tar.gz: f6679028fc6283e7482143368d09edf70cb74d648bd45ede5d2bb8e4eb6ec165
5
5
  SHA512:
6
- metadata.gz: 438487cd4e20e19f95c767d5627b246f8db486b9686df766ec1cc17883b93aeffad42e86f7bcdc513ecb6b39fa85c2a1f8d4344f1675b8c3768877204abef000
7
- data.tar.gz: 32e554b6063e10342aa64d1f5697cc39b1c1b69f6e8b2af19367595b61fe5ef4c6f0c37d121db9195291430a62782bb52bb643c5275e07bda2f2a52054f8b8e5
6
+ metadata.gz: ea24f149c474dc40974f05d017d3544ce2f4e3b762f1474f7c9146d01cf3c074a8526406bc7bb1b325cd973251f0ffc5cad06a86d7686dda0e7bf6a226f834c0
7
+ data.tar.gz: cb74991d735738e58dbf04ed6c0dc5e32c923d83fe61cc7143a8af611050f001bb0d174949ed34a8ba7f97cd16735ca96e8d845e970abda579a12f44dec6fc22
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"] # 1.13 does not work on Windows: https://github.com/ffi/ffi/issues/784
13
13
 
14
14
  group :omnibus do
15
15
  gem "rb-readline"
data/README.md CHANGED
@@ -141,7 +141,7 @@ That requires [bundler](http://bundler.io/):
141
141
 
142
142
  ```bash
143
143
  bundle install
144
- bundle exec bin/inspec help
144
+ bundle exec inspec help
145
145
  ```
146
146
 
147
147
  To install it as a gem locally, run:
@@ -27,11 +27,11 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency "license-acceptance", ">= 0.2.13", "< 2.0"
28
28
  spec.add_dependency "thor", ">= 0.20", "< 2.0"
29
29
  spec.add_dependency "json_schemer", "~> 0.2.1"
30
- spec.add_dependency "method_source", "~> 0.8"
30
+ spec.add_dependency "method_source", ">= 0.8", "< 2.0"
31
31
  spec.add_dependency "rubyzip", "~> 1.2", ">= 1.2.2"
32
32
  spec.add_dependency "rspec", "~> 3.9"
33
33
  spec.add_dependency "rspec-its", "~> 1.2"
34
- spec.add_dependency "pry", "~> 0"
34
+ spec.add_dependency "pry", "~> 0.13"
35
35
  spec.add_dependency "hashie", "~> 3.4"
36
36
  spec.add_dependency "mixlib-log", "~> 3.0"
37
37
  spec.add_dependency "sslshake", "~> 1.2"
@@ -135,6 +135,10 @@ module Inspec
135
135
  option :reporter, type: :array,
136
136
  banner: "one two:/output/file/path",
137
137
  desc: "Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, junit, yaml"
138
+ option :reporter_message_truncation, type: :string,
139
+ desc: "Number of characters to truncate failure messages in report data to (default: no truncation)"
140
+ option :reporter_backtrace_inclusion, type: :boolean,
141
+ desc: "Include a code backtrace in report data (default: true)"
138
142
  option :input, type: :array, banner: "name1=value1 name2=value2",
139
143
  desc: "Specify one or more inputs directly on the command line, as --input NAME=VALUE"
140
144
  option :input_file, type: :array,
@@ -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
@@ -328,21 +328,36 @@ 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
347
  json-min
340
- json-rspec
341
348
  junit
342
- progress
343
349
  yaml
344
350
  }
345
351
 
352
+ # Additional reporters may be loaded via plugins. They will have already been detected at
353
+ # this point (see v2_loader.load_all in cli.rb) but they may not (and need not) be
354
+ # activated at this point. We only care about their existance and their name, for validation's sake.
355
+ plugin_reporters = Inspec::Plugin::V2::Registry.instance\
356
+ .find_activators(plugin_type: :reporter)\
357
+ .map(&:activator_name).map(&:to_s)
358
+
359
+ valid_types = rspec_built_in_formatters + inspec_reporters_that_are_not_yet_plugins + plugin_reporters
360
+
346
361
  reporters.each do |reporter_name, reporter_config|
347
362
  raise NotImplementedError, "'#{reporter_name}' is not a valid reporter type." unless valid_types.include?(reporter_name)
348
363
 
@@ -360,6 +375,13 @@ module Inspec
360
375
  end
361
376
 
362
377
  raise ArgumentError, "The option --reporter can only have a single report outputting to stdout." if stdout_reporters > 1
378
+
379
+ # reporter_message_truncation needs to either be the string "ALL", an Integer, or a string representing an integer
380
+ if (truncation = @merged_options["reporter_message_truncation"])
381
+ unless truncation == "ALL" || truncation.is_a?(Integer) || truncation.to_i.to_s == truncation
382
+ raise ArgumentError, "reporter_message_truncation is set to #{truncation}. It must be set to an integer value or ALL to indicate no truncation."
383
+ end
384
+ end
363
385
  end
364
386
 
365
387
  def validate_plugins!
@@ -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)
@@ -98,15 +98,15 @@ module Inspec
98
98
  # not been assigned a value. This allows a user to explicitly assign nil
99
99
  # to an input.
100
100
  class NO_VALUE_SET # rubocop: disable Naming/ClassAndModuleCamelCase
101
- def initialize(name)
101
+ def initialize(name, warn_on_create = true)
102
102
  @name = name
103
103
 
104
104
  # output warn message if we are in a exec call
105
- if Inspec::BaseCLI.inspec_cli_command == :exec
105
+ if warn_on_create && Inspec::BaseCLI.inspec_cli_command == :exec
106
106
  Inspec::Log.warn(
107
107
  "Input '#{@name}' does not have a value. "\
108
- "Use --input-file to provide a value for '#{@name}' or specify a "\
109
- "value with `attribute('#{@name}', value: 'somevalue', ...)`."
108
+ "Use --input-file or --input to provide a value for '#{@name}' or specify a "\
109
+ "value with `input('#{@name}', value: 'somevalue', ...)`."
110
110
  )
111
111
  end
112
112
  end
@@ -277,7 +277,7 @@ module Inspec
277
277
  end
278
278
 
279
279
  # Determine the current winning value, but don't validate it
280
- def current_value
280
+ def current_value(warn_on_missing = true)
281
281
  # Examine the events to determine highest-priority value. Tie-break
282
282
  # by using the last one set.
283
283
  events_that_set_a_value = events.select(&:value_has_been_set?)
@@ -287,7 +287,7 @@ module Inspec
287
287
 
288
288
  if winning_event.nil?
289
289
  # No value has been set - return special no value object
290
- NO_VALUE_SET.new(name)
290
+ NO_VALUE_SET.new(name, warn_on_missing)
291
291
  else
292
292
  winning_event.value # May still be nil
293
293
  end
@@ -315,7 +315,7 @@ module Inspec
315
315
  end
316
316
 
317
317
  def has_value?
318
- !current_value.is_a? NO_VALUE_SET
318
+ !current_value(false).is_a? NO_VALUE_SET
319
319
  end
320
320
 
321
321
  def to_hash
@@ -348,7 +348,7 @@ module Inspec
348
348
  # skip if we are not doing an exec call (archive/vendor/check)
349
349
  return unless Inspec::BaseCLI.inspec_cli_command == :exec
350
350
 
351
- proposed_value = current_value
351
+ proposed_value = current_value(false)
352
352
  if proposed_value.nil? || proposed_value.is_a?(NO_VALUE_SET)
353
353
  error = Inspec::Input::RequiredError.new
354
354
  error.input_name = name
@@ -363,7 +363,7 @@ module Inspec
363
363
  type_req = type
364
364
  return if type_req == "Any"
365
365
 
366
- proposed_value = current_value
366
+ proposed_value = current_value(false)
367
367
 
368
368
  invalid_type = false
369
369
  if type_req == "Regexp"
@@ -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]] +
@@ -30,7 +30,10 @@ module Inspec::Reporters
30
30
  when "yaml"
31
31
  reporter = Inspec::Reporters::Yaml.new(config)
32
32
  else
33
- raise NotImplementedError, "'#{name}' is not a valid reporter type."
33
+ # If we made it here, it must be a plugin, and we know it exists (because we validated it in config.rb)
34
+ activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
35
+ activator.activate!
36
+ reporter = activator.implementation_class.new(config)
34
37
  end
35
38
 
36
39
  # optional send_report method on reporter
@@ -5,9 +5,31 @@ module Inspec::Reporters
5
5
  def initialize(config)
6
6
  @config = config
7
7
  @run_data = config[:run_data]
8
+ apply_report_resize_options unless @run_data.nil?
8
9
  @output = ""
9
10
  end
10
11
 
12
+ # Apply options such as message truncation and removal of backtraces
13
+ def apply_report_resize_options
14
+ runtime_config = Inspec::Config.cached.respond_to?(:final_options) ? Inspec::Config.cached.final_options : {}
15
+
16
+ message_truncation = runtime_config[:reporter_message_truncation] || "ALL"
17
+ trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
18
+ include_backtrace = runtime_config[:reporter_backtrace_inclusion].nil? ? true : runtime_config[:reporter_backtrace_inclusion]
19
+
20
+ @run_data[:profiles]&.each do |p|
21
+ p[:controls].each do |c|
22
+ c[:results]&.map! do |r|
23
+ r.delete(:backtrace) unless include_backtrace
24
+ if r.key?(:message) && r[:message] != "" && trunc > -1
25
+ r[:message] = r[:message][0...trunc] + "[Truncated to #{trunc} characters]"
26
+ end
27
+ r
28
+ end
29
+ end
30
+ end
31
+ end
32
+
11
33
  def output(str, newline = true)
12
34
  @output << str
13
35
  @output << "\n" if newline
@@ -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
@@ -332,7 +332,7 @@ module Inspec
332
332
  input_name = @__rule_id # TODO: control ID slugging
333
333
  registry = Inspec::InputRegistry.instance
334
334
  input = registry.inputs_by_profile.dig(__profile_id, input_name)
335
- return unless input
335
+ return unless input && input.has_value? && input.value.is_a?(Hash)
336
336
 
337
337
  # An InSpec Input is a datastructure that tracks a profile parameter
338
338
  # over time. Its value can be set by many sources, and it keeps a
@@ -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
@@ -0,0 +1,64 @@
1
+
2
+ module Inspec
3
+ module HashLikeStruct
4
+ def keys
5
+ members
6
+ end
7
+
8
+ def key?(item)
9
+ members.include?(item)
10
+ end
11
+ end
12
+
13
+ RunData = Struct.new(
14
+ :controls, # Array of Inspec::RunData::Control (flattened)
15
+ :other_checks,
16
+ :profiles, # Array of Inspec::RunData::Profile
17
+ :platform, # Inspec::RunData::Platform
18
+ :statistics, # Inspec::RunData::Statistics
19
+ :version # String
20
+ ) do
21
+ include HashLikeStruct
22
+ def initialize(raw_run_data)
23
+ self.controls = raw_run_data[:controls].map { |c| Inspec::RunData::Control.new(c) }
24
+ self.profiles = raw_run_data[:profiles].map { |p| Inspec::RunData::Profile.new(p) }
25
+ self.statistics = Inspec::RunData::Statistics.new(raw_run_data[:statistics])
26
+ self.platform = Inspec::RunData::Platform.new(raw_run_data[:platform])
27
+ self.version = raw_run_data[:version]
28
+ end
29
+ end
30
+
31
+ class RunData
32
+
33
+ # This is the data layout version of RunData.
34
+ # We plan to follow a data-oriented version of semver:
35
+ # patch: fixing a bug in the provenance or description of a data element, no key changes
36
+ # minor: adding new data elements
37
+ # major: deleting or renaming data elements
38
+ # Less than major version 1.0.0, the API is considered unstable.
39
+ # The current plan is to bump the major version to 1.0.0 when all of the existing
40
+ # core reporters have been migrated to plugins. It is probable that new data elements
41
+ # and new Hash compatibility behavior will be added during the core reporter plugin
42
+ # conversion process.
43
+ SCHEMA_VERSION = "0.1.0".freeze
44
+
45
+ def self.compatible_schema?(constraints)
46
+ reqs = Gem::Requirement.create(constraints)
47
+ reqs.satisfied_by?(Gem::Version.new(SCHEMA_VERSION))
48
+ end
49
+
50
+ Platform = Struct.new(
51
+ :name, :release, :target
52
+ ) do
53
+ include HashLikeStruct
54
+ def initialize(raw_plat_data)
55
+ %i{name release target}.each { |f| self[f] = raw_plat_data[f] || "" }
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ require_relative "run_data/result"
62
+ require_relative "run_data/control"
63
+ require_relative "run_data/profile"
64
+ require_relative "run_data/statistics"
@@ -0,0 +1,83 @@
1
+ module Inspec
2
+ class RunData
3
+ Control = Struct.new(
4
+ :code, # String
5
+ :desc, # String
6
+ :descriptions, # Hash with custom keys
7
+ :id, # String
8
+ :impact, # Float
9
+ :refs, # Complex local
10
+ :results, # complex standalone
11
+ :source_location, # Complex local
12
+ :tags, # Hash with custom keys
13
+ :title, # String
14
+ :waiver_data # Complex local
15
+ ) do
16
+ include HashLikeStruct
17
+ def initialize(raw_ctl_data)
18
+ self.refs = (raw_ctl_data[:refs] || []).map { |r| Inspec::RunData::Control::Ref.new(r) }
19
+ self.results = (raw_ctl_data[:results] || []).map { |r| Inspec::RunData::Result.new(r) }
20
+ self.source_location = Inspec::RunData::Control::SourceLocation.new(raw_ctl_data[:source_location] || {})
21
+ self.waiver_data = Inspec::RunData::Control::WaiverData.new(raw_ctl_data[:waiver_data] || {})
22
+
23
+ [
24
+ :code, # String
25
+ :desc, # String
26
+ :descriptions, # Hash with custom keys
27
+ :id, # String
28
+ :impact, # Float
29
+ :tags, # Hash with custom keys
30
+ :title, # String
31
+ ].each do |field|
32
+ self[field] = raw_ctl_data[field]
33
+ end
34
+ end
35
+ end
36
+
37
+ class Control
38
+ Ref = Struct.new(
39
+ :url, :ref
40
+ ) do
41
+ include HashLikeStruct
42
+ def initialize(raw_ref_data)
43
+ %i{url ref}.each { |f| self[f] = raw_ref_data[f] }
44
+ end
45
+ end
46
+
47
+ SourceLocation = Struct.new(
48
+ :line, :ref
49
+ ) do
50
+ include HashLikeStruct
51
+ def initialize(raw_sl_data)
52
+ %i{line ref}.each { |f| self[f] = raw_sl_data[f] }
53
+ end
54
+ end
55
+
56
+ # {
57
+ # "expiration_date"=>#<Date: 2077-06-01 ((2479821j,0s,0n),+0s,2299161j)>,
58
+ # "justification"=>"Lack of imagination",
59
+ # "run"=>false,
60
+ # "skipped_due_to_waiver"=>true,
61
+ # "message"=>""}
62
+ WaiverData = Struct.new(
63
+ :expiration_date,
64
+ :justification,
65
+ :run,
66
+ :skipped_due_to_waiver,
67
+ :message
68
+ ) do
69
+ include HashLikeStruct
70
+ def initialize(raw_wv_data)
71
+ # These have string keys in the raw data!
72
+ %i{
73
+ expiration_date
74
+ justification
75
+ run
76
+ skipped_due_to_waiver
77
+ message
78
+ }.each { |f| self[f] = raw_wv_data[f.to_s] }
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,109 @@
1
+ module Inspec
2
+ class RunData
3
+ Profile = Struct.new(
4
+ :controls, # complex standalone
5
+ :copyright,
6
+ :copyright_email,
7
+ :depends, # complex local
8
+ :groups, # complex local
9
+ :inputs, # complex local
10
+ :license,
11
+ :maintainer,
12
+ :name,
13
+ :sha256,
14
+ :status,
15
+ :summary,
16
+ :supports, # complex local
17
+ :parent_profile,
18
+ :skip_message,
19
+ :waiver_data, # Undocumented but used in JSON reporter - should not be?
20
+ :title,
21
+ :version
22
+ ) do
23
+ include HashLikeStruct
24
+ def initialize(raw_prof_data)
25
+ self.controls = (raw_prof_data[:controls] || []).map { |c| Inspec::RunData::Control.new(c) }
26
+ self.depends = (raw_prof_data[:depends] || []).map { |d| Inspec::RunData::Profile::Dependency.new(d) }
27
+ self.groups = (raw_prof_data[:groups] || []).map { |g| Inspec::RunData::Profile::Group.new(g) }
28
+ self.inputs = (raw_prof_data[:inputs] || []).map { |i| Inspec::RunData::Profile::Input.new(i) }
29
+ self.supports = (raw_prof_data[:supports] || []).map { |s| Inspec::RunData::Profile::Support.new(s) }
30
+
31
+ %i{
32
+ copyright
33
+ copyright_email
34
+ license
35
+ maintainer
36
+ name
37
+ sha256
38
+ status
39
+ summary
40
+ title
41
+ version
42
+ parent_profile
43
+ skip_message
44
+ waiver_data
45
+ }.each do |field|
46
+ self[field] = raw_prof_data[field]
47
+ end
48
+ end
49
+ end
50
+
51
+ class Profile
52
+ # Good candidate for keyword_init, but that is not in 2.4
53
+ Dependency = Struct.new(
54
+ :name, :path, :status, :skip_message, :git, :url, :compliance, :supermarket, :branch, :tag, :commit, :version, :relative_path
55
+ ) do
56
+ include HashLikeStruct
57
+ def initialize(raw_dep_data)
58
+ %i{name path status skip_message git url supermarket compliance branch tag commit version relative_path}.each { |f| self[f] = raw_dep_data[f] }
59
+ end
60
+ end
61
+
62
+ Support = Struct.new(
63
+ # snake case
64
+ :platform_family, :platform_name, :release, :platform
65
+ ) do
66
+ include HashLikeStruct
67
+ def initialize(raw_sup_data)
68
+ %i{release platform}.each { |f| self[f] = raw_sup_data[f] }
69
+ self.platform_family = raw_sup_data[:"platform-family"]
70
+ self.platform_name = raw_sup_data[:"platform-name"]
71
+ end
72
+ end
73
+
74
+ # Good candidate for keyword_init, but that is not in 2.4
75
+ Group = Struct.new(
76
+ :title, :controls, :id
77
+ ) do
78
+ include HashLikeStruct
79
+ def initialize(raw_grp_data)
80
+ %i{title id}.each { |f| self[f] = raw_grp_data[f] }
81
+ [:controls].each { |f| self[f] = raw_grp_data[f] || [] }
82
+ end
83
+ end
84
+
85
+ Input = Struct.new(
86
+ :name, :options
87
+ ) do
88
+ include HashLikeStruct
89
+ def initialize(raw_input_data)
90
+ self.name = raw_input_data[:name]
91
+ self.options = Inspec::RunData::Profile::Input::Options.new(raw_input_data[:options])
92
+ end
93
+ end
94
+ class Input
95
+ Options = Struct.new(
96
+ # There are probably others
97
+ :value,
98
+ :type,
99
+ :required
100
+ ) do
101
+ include HashLikeStruct
102
+ def initialize(raw_opts_data)
103
+ %i{value type required}.each { |f| self[f] = raw_opts_data[f] }
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,40 @@
1
+ module Inspec
2
+ class RunData
3
+ Result = Struct.new(
4
+ :message, # Human-friendly test failure message
5
+ :code_desc, # Generated test description
6
+ :expectation_message, # a substring of code_desc
7
+ :resource_name, # We try to determine this
8
+ :run_time, # Float seconds execution time
9
+ :skip_message, # String
10
+ :start_time, # DateTime
11
+ :status, # String
12
+ :resource_title, # Ugly internals
13
+ # :waiver_data, # Undocumented tramp data / not exposed in this API
14
+ :resource, # Undocumented, what is this
15
+ :exception,
16
+ :backtrace
17
+ ) do
18
+ include HashLikeStruct
19
+ def initialize(raw_res_data)
20
+ [
21
+ :status, # String
22
+ :code_desc, # Generated test description
23
+ :expectation_message, # a substring of code_desc
24
+ :skip_message, # String
25
+ :run_time,
26
+ :start_time,
27
+ :resource_title,
28
+ :resource,
29
+ :exception,
30
+ :backtrace,
31
+ :message,
32
+ ].each do |field|
33
+ self[field] = raw_res_data[field]
34
+ end
35
+
36
+ self.resource_name = raw_res_data[:resource_title].instance_variable_get(:@__resource_name__)&.to_s
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ module Inspec
2
+ class RunData
3
+ # {:duration=>0.018407, :controls=>{:total=>3, :passed=>{:total=>3}, :skipped=>{:total=>0}, :failed=>{:total=>0}}}
4
+ Statistics = Struct.new(
5
+ :duration,
6
+ :controls
7
+ ) do
8
+ include HashLikeStruct
9
+ def initialize(raw_stat_data)
10
+ self.controls = Inspec::RunData::Statistics::Controls.new(raw_stat_data[:controls])
11
+ self.duration = raw_stat_data[:duration]
12
+ end
13
+ end
14
+ class Statistics
15
+ Controls = Struct.new(
16
+ :total,
17
+ :passed,
18
+ :skipped,
19
+ :failed
20
+ ) do
21
+ include HashLikeStruct
22
+ def initialize(raw_stat_ctl_data)
23
+ self.total = raw_stat_ctl_data[:total]
24
+ self.passed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:passed][:total])
25
+ self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total])
26
+ self.failed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:failed][:total])
27
+ end
28
+ end
29
+ class Controls
30
+ Total = Struct.new(:total) do
31
+ include HashLikeStruct
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -36,8 +36,13 @@ module Inspec
36
36
  end
37
37
 
38
38
  # configure pry shell prompt
39
- Pry.config.prompt_name = "inspec"
40
- Pry.prompt = [proc { "#{readline_ignore("\e[1m\e[32m")}#{Pry.config.prompt_name}> #{readline_ignore("\e[0m")}" }]
39
+ Pry::Prompt.add(
40
+ :inspec,
41
+ "inspec custom prompt"
42
+ ) do |_context, _nesting, _pry_instance, _sep|
43
+ "#{readline_ignore("\e[1m\e[32m")}inspec> #{readline_ignore("\e[0m")}"
44
+ end
45
+ Pry.config.prompt = Pry::Prompt[:inspec]
41
46
 
42
47
  # Add a help menu as the default intro
43
48
  Pry.hooks.add_hook(:before_session, "inspec_intro") do
@@ -64,7 +69,7 @@ module Inspec
64
69
 
65
70
  pry.pager.open do |pager|
66
71
  pager.print pry.config.output_prefix
67
- Pry::ColorPrinter.pp(value, pager, Pry::Terminal.width! - 1)
72
+ Pry::ColorPrinter.pp(value, pager, Pry::Output.new(pry).width - 1)
68
73
  end
69
74
  end
70
75
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Inspec
4
+ module Utils
5
+ #
6
+ # Inspec::Utils::JsonProfileSummary takes in certain information to identify a
7
+ # profile and then produces a JSON-formatted summary of that profile. It can
8
+ # return the results to STDOUT or a file. It is currently used in several
9
+ # places in the CLI such as `json`, `archive` and `artifact`.
10
+ #
11
+ #
12
+ module JsonProfileSummary
13
+ def self.produce_json(info:, write_path: "", suppress_output: false)
14
+ # add in inspec version
15
+ info[:generator] = {
16
+ name: "inspec",
17
+ version: Inspec::VERSION,
18
+ }
19
+ if write_path.empty?
20
+ puts JSON.dump(info)
21
+ else
22
+ unless suppress_output
23
+ if File.exist? write_path
24
+ Inspec::Log.info "----> updating #{write_path}"
25
+ else
26
+ Inspec::Log.info "----> creating #{write_path}"
27
+ end
28
+ end
29
+ full_write_path = File.expand_path(write_path)
30
+ File.write(full_write_path, JSON.dump(info))
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = "4.18.104".freeze
2
+ VERSION = "4.19.2".freeze
3
3
  end
@@ -5,6 +5,7 @@ require "set"
5
5
  require "tempfile"
6
6
  require "yaml"
7
7
  require "inspec/dist"
8
+ require "inspec/utils/json_profile_summary"
8
9
 
9
10
  module InspecPlugins
10
11
  module Artifact
@@ -40,9 +41,13 @@ module InspecPlugins
40
41
 
41
42
  def self.profile_sign(options)
42
43
  artifact = new
44
+ path_to_profile = options["profile"]
45
+
46
+ # Write inspec.json file within artifact
47
+ write_inspec_json(path_to_profile, options)
48
+
43
49
  Dir.mktmpdir do |workdir|
44
50
  puts "Signing #{options["profile"]} with key #{options["keyname"]}"
45
- path_to_profile = options["profile"]
46
51
  profile_md = artifact.read_profile_metadata(path_to_profile)
47
52
  artifact_filename = "#{profile_md["name"]}-#{profile_md["version"]}.#{SIGNED_PROFILE_SUFFIX}"
48
53
  tarfile = artifact.profile_compress(path_to_profile, profile_md, workdir)
@@ -63,6 +68,9 @@ module InspecPlugins
63
68
  end
64
69
  puts "Successfully generated #{artifact_filename}"
65
70
  end
71
+
72
+ # Cleanup
73
+ File.delete("#{path_to_profile}/inspec.json")
66
74
  end
67
75
 
68
76
  def self.profile_verify(options)
@@ -165,6 +173,15 @@ module InspecPlugins
165
173
  raise "Artifact is invalid"
166
174
  end
167
175
  end
176
+
177
+ def self.write_inspec_json(root_path, opts)
178
+ profile = Inspec::Profile.for_path(root_path, opts)
179
+ Inspec::Utils::JsonProfileSummary.produce_json(
180
+ info: profile.info,
181
+ write_path: "#{root_path}/inspec.json",
182
+ suppress_output: true
183
+ )
184
+ end
168
185
  end
169
186
  end
170
187
  end
@@ -302,6 +302,10 @@ module InspecPlugins
302
302
  )
303
303
  true
304
304
  else
305
+ Inspec::Log.debug(
306
+ "Received #{response.code} from #{url}#{automate_endpoint} - " \
307
+ "assuming target is not an #{AUTOMATE_PRODUCT_NAME}2 instance"
308
+ )
305
309
  false
306
310
  end
307
311
  end
@@ -41,8 +41,9 @@ module InspecPlugins
41
41
  templates_path: TEMPLATES_PATH,
42
42
  overwrite: options[:overwrite],
43
43
  file_rename_map: make_rename_map(plugin_type, plugin_name, snake_case),
44
- skip_files: make_skip_list,
44
+ skip_files: make_skip_list(template_vars["hooks"].keys),
45
45
  }
46
+
46
47
  renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
47
48
 
48
49
  renderer.render_with_values(template_path, plugin_type + " plugin", template_vars)
@@ -72,6 +73,7 @@ module InspecPlugins
72
73
  File.join("lib", "inspec-plugin-template") => File.join("lib", plugin_name),
73
74
  File.join("lib", "inspec-plugin-template.rb") => File.join("lib", plugin_name + ".rb"),
74
75
  File.join("lib", "inspec-plugin-template", "cli_command.rb") => File.join("lib", plugin_name, "cli_command.rb"),
76
+ File.join("lib", "inspec-plugin-template", "reporter.rb") => File.join("lib", plugin_name, "reporter.rb"),
75
77
  File.join("lib", "inspec-plugin-template", "plugin.rb") => File.join("lib", plugin_name, "plugin.rb"),
76
78
  File.join("lib", "inspec-plugin-template", "version.rb") => File.join("lib", plugin_name, "version.rb"),
77
79
  File.join("test", "functional", "inspec_plugin_template_test.rb") => File.join("test", "functional", snake_case + "_test.rb"),
@@ -168,6 +170,9 @@ module InspecPlugins
168
170
  if hooks_by_type.key?(:cli_command)
169
171
  vars[:command_name_dashes] = hooks_by_type[:cli_command].tr("_", "-")
170
172
  vars[:command_name_snake] = hooks_by_type[:cli_command].tr("-", "_")
173
+ elsif hooks_by_type.key?(:reporter)
174
+ vars[:reporter_name_dashes] = hooks_by_type[:reporter].tr("_", "-")
175
+ vars[:reporter_name_snake] = hooks_by_type[:reporter].tr("-", "_")
171
176
  end
172
177
  vars
173
178
  end
@@ -205,19 +210,20 @@ module InspecPlugins
205
210
  end
206
211
  end
207
212
 
208
- def make_skip_list
213
+ def make_skip_list(requested_hooks)
214
+ skips = []
209
215
  case options[:detail]
210
- when "full"
211
- []
216
+ when "full" # rubocop: disable Lint/EmptyWhen
217
+ # Do nothing but allow this case for validation
212
218
  when "core"
213
- [
219
+ skips += [
214
220
  "Gemfile",
215
221
  "inspec-plugin-template.gemspec",
216
222
  "LICENSE",
217
223
  "Rakefile",
218
224
  ]
219
225
  when "test-fixture"
220
- [
226
+ skips += [
221
227
  "Gemfile",
222
228
  "inspec-plugin-template.gemspec",
223
229
  "LICENSE",
@@ -237,6 +243,22 @@ module InspecPlugins
237
243
  ui.error "Unrecognized value for 'detail': #{options[:detail]} - expected one of full, core, test-fixture"
238
244
  ui.exit(:usage_error)
239
245
  end
246
+
247
+ # Remove hook-specific files
248
+ unless requested_hooks.include?(:cli_command)
249
+ skips += [
250
+ File.join("lib", "inspec-plugin-template", "cli_command.rb"),
251
+ File.join("test", "unit", "cli_args_test.rb"),
252
+ File.join("test", "functional", "inspec_plugin_template_test.rb"),
253
+ ]
254
+ end
255
+ unless requested_hooks.include?(:reporter)
256
+ skips += [
257
+ File.join("lib", "inspec-plugin-template", "reporter.rb"),
258
+ ]
259
+ end
260
+
261
+ skips.uniq
240
262
  end
241
263
  end
242
264
  end
@@ -28,6 +28,7 @@ module InspecPlugins
28
28
  # Internal machine name of the plugin. InSpec will use this in errors, etc.
29
29
  plugin_name :'<%= plugin_name %>'
30
30
 
31
+ <% if hooks[:cli_command] %>
31
32
  # Define a new CLI subcommand.
32
33
  # The argument here will be used to match against the command line args,
33
34
  # and if the user said `inspec list-resources`, this hook will get called.
@@ -48,6 +49,23 @@ module InspecPlugins
48
49
  # CLI engine tap into it.
49
50
  InspecPlugins::<%= module_name %>::CliCommand
50
51
  end
52
+ <% end %>
53
+
54
+ <% if hooks[:reporter] %>
55
+ # Define a new Reporter.
56
+ # The argument here will be used to match against the CLI --reporter option.
57
+ # `--reporter <%= reporter_name_snake %>` will load your reporter and call its renderer.
58
+ reporter :<%= reporter_name_snake %> do
59
+ # Calling this hook doesn't mean the reporter is being executed - just
60
+ # that we should be ready to do so. So, load the file that defines the
61
+ # functionality.
62
+ require '<%= plugin_name %>/reporter'
63
+
64
+ # Having loaded our functionality, return a class that will let the
65
+ # reporting engine tap into it.
66
+ InspecPlugins::<%= module_name %>::Reporter
67
+ end
68
+ <% end %>
51
69
  end
52
70
  end
53
71
  end
@@ -0,0 +1,27 @@
1
+ module InspecPlugins::<%= module_name %>
2
+ # This class will provide the actual Reporter implementation.
3
+ # Its superclass is provided by another call to Inspec.plugin,
4
+ # this time with two args. The first arg specifies we are requesting
5
+ # version 2 of the Plugins API. The second says we are making a
6
+ # Reporter plugin component, so please make available any DSL needed
7
+ # for that.
8
+
9
+ class Reporter < Inspec.plugin(2, :reporter)
10
+
11
+ # All a Reporter *must* do is define a render() method that calls
12
+ # output(). You should access the run_data accessor to read off the
13
+ # results of the run.
14
+ def render
15
+ # There is much more to explore in the run_data structure!
16
+ run_data[:profiles].each do |profile|
17
+ output(profile[:title])
18
+ profile[:controls].each do |control|
19
+ output(control[:title])
20
+ control[:results].each do |test|
21
+ output(test[:status])
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.18.104
4
+ version: 4.19.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef InSpec Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-07 00:00:00.000000000 Z
11
+ date: 2020-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-telemetry
@@ -82,16 +82,22 @@ dependencies:
82
82
  name: method_source
83
83
  requirement: !ruby/object:Gem::Requirement
84
84
  requirements:
85
- - - "~>"
85
+ - - ">="
86
86
  - !ruby/object:Gem::Version
87
87
  version: '0.8'
88
+ - - "<"
89
+ - !ruby/object:Gem::Version
90
+ version: '2.0'
88
91
  type: :runtime
89
92
  prerelease: false
90
93
  version_requirements: !ruby/object:Gem::Requirement
91
94
  requirements:
92
- - - "~>"
95
+ - - ">="
93
96
  - !ruby/object:Gem::Version
94
97
  version: '0.8'
98
+ - - "<"
99
+ - !ruby/object:Gem::Version
100
+ version: '2.0'
95
101
  - !ruby/object:Gem::Dependency
96
102
  name: rubyzip
97
103
  requirement: !ruby/object:Gem::Requirement
@@ -146,14 +152,14 @@ dependencies:
146
152
  requirements:
147
153
  - - "~>"
148
154
  - !ruby/object:Gem::Version
149
- version: '0'
155
+ version: '0.13'
150
156
  type: :runtime
151
157
  prerelease: false
152
158
  version_requirements: !ruby/object:Gem::Requirement
153
159
  requirements:
154
160
  - - "~>"
155
161
  - !ruby/object:Gem::Version
156
- version: '0'
162
+ version: '0.13'
157
163
  - !ruby/object:Gem::Dependency
158
164
  name: hashie
159
165
  requirement: !ruby/object:Gem::Requirement
@@ -465,6 +471,7 @@ files:
465
471
  - lib/inspec/plugin/v2/plugin_types/dsl.rb
466
472
  - lib/inspec/plugin/v2/plugin_types/input.rb
467
473
  - lib/inspec/plugin/v2/plugin_types/mock.rb
474
+ - lib/inspec/plugin/v2/plugin_types/reporter.rb
468
475
  - lib/inspec/plugin/v2/registry.rb
469
476
  - lib/inspec/plugin/v2/status.rb
470
477
  - lib/inspec/profile.rb
@@ -607,6 +614,11 @@ files:
607
614
  - lib/inspec/resources/zfs_pool.rb
608
615
  - lib/inspec/rspec_extensions.rb
609
616
  - lib/inspec/rule.rb
617
+ - lib/inspec/run_data.rb
618
+ - lib/inspec/run_data/control.rb
619
+ - lib/inspec/run_data/profile.rb
620
+ - lib/inspec/run_data/result.rb
621
+ - lib/inspec/run_data/statistics.rb
610
622
  - lib/inspec/runner.rb
611
623
  - lib/inspec/runner_mock.rb
612
624
  - lib/inspec/runner_rspec.rb
@@ -642,6 +654,7 @@ files:
642
654
  - lib/inspec/utils/hash.rb
643
655
  - lib/inspec/utils/install_context.rb
644
656
  - lib/inspec/utils/json_log.rb
657
+ - lib/inspec/utils/json_profile_summary.rb
645
658
  - lib/inspec/utils/modulator.rb
646
659
  - lib/inspec/utils/nginx_parser.rb
647
660
  - lib/inspec/utils/object_traversal.rb
@@ -691,6 +704,7 @@ files:
691
704
  - lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template.rb
692
705
  - lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/cli_command.rb
693
706
  - lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.rb
707
+ - lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/reporter.rb
694
708
  - lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/version.rb
695
709
  - lib/plugins/inspec-init/templates/profiles/aws/README.md
696
710
  - lib/plugins/inspec-init/templates/profiles/aws/attributes.yml