inspec-core 4.18.114 → 4.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/inspec/cli.rb +7 -17
- data/lib/inspec/config.rb +21 -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/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/resources/x509_certificate.rb +1 -1
- data/lib/inspec/rule.rb +7 -3
- 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/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: 0c6037004db1c4ef1ba337ea6ef710938a2ce1d02c45e56b0bdfe7349b146a92
|
4
|
+
data.tar.gz: 926e31bc929c5e5c76e3a5e89ed686218000a2ad5374c6b7901ac006ee2536a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36c0d1cf39247500f0bc33a633279c08bdaeab0c2dc848fc14f54405cb8c3b3fb1d86ae0ef0afc0ce1394c73b6de978fc61e4ff523b2dfc56d58536920caae47
|
7
|
+
data.tar.gz: c385ff9ee77aca4689dc4a83c8bef4984a3b509e6a7f459655e44a5f1a1944792c2ac1f78b0626cde72697c2b6fee1d4c2eb05ae566d928596e447d5d08f2e34
|
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
|
|
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)
|
@@ -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
|
data/lib/inspec/rule.rb
CHANGED
@@ -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
|
@@ -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.19.0
|
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-05-
|
11
|
+
date: 2020-05-27 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
|