inspec-core 4.18.108 → 4.20.2
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/lib/inspec/base_cli.rb +8 -1
- data/lib/inspec/cli.rb +7 -17
- data/lib/inspec/config.rb +28 -6
- data/lib/inspec/fetcher/git.rb +1 -1
- data/lib/inspec/fetcher/local.rb +1 -1
- data/lib/inspec/fetcher/url.rb +1 -1
- data/lib/inspec/input.rb +9 -9
- data/lib/inspec/input_registry.rb +33 -1
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +68 -0
- data/lib/inspec/profile.rb +16 -4
- data/lib/inspec/reporters.rb +4 -1
- data/lib/inspec/reporters/base.rb +22 -0
- data/lib/inspec/resources/x509_certificate.rb +1 -1
- data/lib/inspec/rule.rb +8 -4
- data/lib/inspec/run_data.rb +64 -0
- data/lib/inspec/run_data/control.rb +83 -0
- data/lib/inspec/run_data/profile.rb +109 -0
- data/lib/inspec/run_data/result.rb +40 -0
- data/lib/inspec/run_data/statistics.rb +36 -0
- data/lib/inspec/utils/deprecation/config_file.rb +21 -0
- data/lib/inspec/utils/json_profile_summary.rb +35 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +18 -1
- data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +28 -6
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.rb +18 -0
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/reporter.rb +27 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2395ce22d38edca9eaac2fad00c1fc61f3fb95a154618540d23bc2a1e6ce0fcd
|
4
|
+
data.tar.gz: 4ebdbe5526025408e729b52e0614b43fe89f70b0faae5eeecc8c23db8f0f1ff9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2b21bfbc96b4830edf20f1d48493257845bfbeb52137141269371066c47359092d3ed280cc64e9358b3587d0ddb0c54a643cafd730d57ad2d7ba2331d35b33a
|
7
|
+
data.tar.gz: 84bc11b59621836d20c045efcdee955cc97c605e5902fafaa79c8756dd44f66334731256c6ba4273484d645484f374b1ec2daf471a6dc56805ea819ab2685f38
|
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
data/lib/inspec/base_cli.rb
CHANGED
@@ -135,8 +135,12 @@ 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
|
-
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."
|
140
144
|
option :input_file, type: :array,
|
141
145
|
desc: "Load one or more input files, a YAML file with values for the profile to use"
|
142
146
|
option :waiver_file, type: :array,
|
@@ -151,6 +155,9 @@ module Inspec
|
|
151
155
|
desc: "Show progress while executing tests."
|
152
156
|
option :distinct_exit, type: :boolean, default: true,
|
153
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'."
|
154
161
|
end
|
155
162
|
|
156
163
|
def self.format_platform_info(params: {}, indent: 0, color: 39)
|
data/lib/inspec/cli.rb
CHANGED
@@ -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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
data/lib/inspec/config.rb
CHANGED
@@ -328,21 +328,36 @@ module Inspec
|
|
328
328
|
def validate_reporters!(reporters)
|
329
329
|
return if reporters.nil?
|
330
330
|
|
331
|
-
#
|
332
|
-
|
333
|
-
|
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!
|
data/lib/inspec/fetcher/git.rb
CHANGED
@@ -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
|
102
|
+
OpenSSL::Digest.hexdigest("SHA256", resolved_ref + @relative_path)
|
103
103
|
end
|
104
104
|
|
105
105
|
def archive_path
|
data/lib/inspec/fetcher/local.rb
CHANGED
@@ -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
|
107
|
+
@archive_shasum = OpenSSL::Digest.digest("SHA256", File.read(target)).unpack("H*")[0]
|
108
108
|
end
|
109
109
|
|
110
110
|
def resolved_source
|
data/lib/inspec/fetcher/url.rb
CHANGED
@@ -127,7 +127,7 @@ module Inspec::Fetcher
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def sha256
|
130
|
-
@archive_shasum ||= OpenSSL::Digest
|
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)
|
data/lib/inspec/input.rb
CHANGED
@@ -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 `
|
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"
|
@@ -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
|
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
|
data/lib/inspec/profile.rb
CHANGED
@@ -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
|
-
|
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
|
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]] +
|
data/lib/inspec/reporters.rb
CHANGED
@@ -30,7 +30,10 @@ module Inspec::Reporters
|
|
30
30
|
when "yaml"
|
31
31
|
reporter = Inspec::Reporters::Yaml.new(config)
|
32
32
|
else
|
33
|
-
|
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
|
data/lib/inspec/rule.rb
CHANGED
@@ -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
|
-
|
357
|
-
|
358
|
-
|
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
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require "stringio"
|
2
2
|
require "json"
|
3
3
|
require "inspec/globals"
|
4
|
+
require "inspec/config"
|
4
5
|
|
5
6
|
module Inspec
|
6
7
|
module Deprecation
|
@@ -32,6 +33,7 @@ module Inspec
|
|
32
33
|
@groups = {}
|
33
34
|
@unknown_group_action = :warn
|
34
35
|
validate!
|
36
|
+
silence_deprecations_from_cli
|
35
37
|
end
|
36
38
|
|
37
39
|
private
|
@@ -45,6 +47,25 @@ module Inspec
|
|
45
47
|
File.open(default_path)
|
46
48
|
end
|
47
49
|
|
50
|
+
def silence_deprecations_from_cli
|
51
|
+
# Read --silence-deprecations CLI option
|
52
|
+
cfg = Inspec::Config.cached
|
53
|
+
return unless cfg[:silence_deprecations]
|
54
|
+
|
55
|
+
groups_to_silence = cfg[:silence_deprecations]
|
56
|
+
silence_all = groups_to_silence.include?("all")
|
57
|
+
|
58
|
+
groups.each do |group_name, group|
|
59
|
+
# Only silence things that warn. Don't silence things that exit;
|
60
|
+
# those harsher measures are usually protecting removed code and ignoring
|
61
|
+
# and continuing regardless would be perilous and lead to errors.
|
62
|
+
if %i{warn fail_control}.include?(group.action) &&
|
63
|
+
(silence_all || groups_to_silence.include?(group_name.to_s))
|
64
|
+
group.action = :ignore
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
48
69
|
#====================================================================================================#
|
49
70
|
# Validation
|
50
71
|
#====================================================================================================#
|
@@ -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
|
data/lib/inspec/version.rb
CHANGED
@@ -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
|
@@ -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.
|
4
|
+
version: 4.20.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-
|
11
|
+
date: 2020-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef-telemetry
|
@@ -471,6 +471,7 @@ files:
|
|
471
471
|
- lib/inspec/plugin/v2/plugin_types/dsl.rb
|
472
472
|
- lib/inspec/plugin/v2/plugin_types/input.rb
|
473
473
|
- lib/inspec/plugin/v2/plugin_types/mock.rb
|
474
|
+
- lib/inspec/plugin/v2/plugin_types/reporter.rb
|
474
475
|
- lib/inspec/plugin/v2/registry.rb
|
475
476
|
- lib/inspec/plugin/v2/status.rb
|
476
477
|
- lib/inspec/profile.rb
|
@@ -613,6 +614,11 @@ files:
|
|
613
614
|
- lib/inspec/resources/zfs_pool.rb
|
614
615
|
- lib/inspec/rspec_extensions.rb
|
615
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
|
616
622
|
- lib/inspec/runner.rb
|
617
623
|
- lib/inspec/runner_mock.rb
|
618
624
|
- lib/inspec/runner_rspec.rb
|
@@ -648,6 +654,7 @@ files:
|
|
648
654
|
- lib/inspec/utils/hash.rb
|
649
655
|
- lib/inspec/utils/install_context.rb
|
650
656
|
- lib/inspec/utils/json_log.rb
|
657
|
+
- lib/inspec/utils/json_profile_summary.rb
|
651
658
|
- lib/inspec/utils/modulator.rb
|
652
659
|
- lib/inspec/utils/nginx_parser.rb
|
653
660
|
- lib/inspec/utils/object_traversal.rb
|
@@ -697,6 +704,7 @@ files:
|
|
697
704
|
- lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template.rb
|
698
705
|
- lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/cli_command.rb
|
699
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
|
700
708
|
- lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/version.rb
|
701
709
|
- lib/plugins/inspec-init/templates/profiles/aws/README.md
|
702
710
|
- lib/plugins/inspec-init/templates/profiles/aws/attributes.yml
|