inspec-core 4.22.22 → 4.24.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +20 -28
- data/inspec-core.gemspec +3 -4
- data/lib/inspec/base_cli.rb +10 -0
- data/lib/inspec/cli.rb +4 -2
- data/lib/inspec/config.rb +19 -0
- data/lib/inspec/formatters/base.rb +16 -0
- data/lib/inspec/globals.rb +8 -2
- data/lib/inspec/input.rb +4 -3
- data/lib/inspec/input_registry.rb +7 -1
- data/lib/inspec/plugin/v2/loader.rb +5 -0
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +4 -31
- data/lib/inspec/reporters/base.rb +7 -29
- data/lib/inspec/reporters/json.rb +2 -0
- data/lib/inspec/resource.rb +2 -0
- data/lib/inspec/resources/apt.rb +5 -5
- data/lib/inspec/resources/grub_conf.rb +2 -1
- data/lib/inspec/resources/package.rb +1 -1
- data/lib/inspec/resources/platform.rb +11 -1
- data/lib/inspec/resources/postgres_session.rb +5 -3
- data/lib/inspec/resources/processes.rb +1 -1
- data/lib/inspec/resources/wmi.rb +15 -7
- data/lib/inspec/rule.rb +8 -8
- data/lib/inspec/run_data.rb +1 -1
- data/lib/inspec/run_data/profile.rb +3 -2
- data/lib/inspec/run_data/result.rb +2 -0
- data/lib/inspec/schema.rb +2 -0
- data/lib/inspec/schema/exec_json.rb +1 -1
- data/lib/inspec/shell.rb +3 -3
- data/lib/inspec/utils/run_data_filters.rb +104 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-init/templates/profiles/aws/README.md +1 -1
- data/lib/plugins/inspec-reporter-html2/README.md +1 -1
- data/lib/plugins/inspec-reporter-junit/README.md +9 -7
- data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit.rb +10 -1
- data/lib/plugins/inspec-reporter-junit/lib/inspec-reporter-junit/reporter.rb +93 -11
- data/lib/plugins/shared/core_plugin_test_helper.rb +0 -16
- metadata +13 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40b226d810bf523c7b914a00d3e3e0556d23eb78b944f12fe7af7fac35a279f4
|
4
|
+
data.tar.gz: ff6cafe82a9e7234814575a3723098ccb1238fb88754cb4891288547e9c3a47b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c663121d4e9dba5764ba321d391b4779f3b9236b827fc7dca58aa2a6a3b9d76a5749205635c38ca6a28f614364f19fef1c7c790321067fef2ae1e1eb9a1dbd74
|
7
|
+
data.tar.gz: da255c1b0637e48aa1f83ea42607829767450bd03fd5f970e527afa81898ba215b7ac4ec66c508d23ef2de74ebb9e3dc26893427c76ab4176e67ffaf5df42c54
|
data/Gemfile
CHANGED
@@ -11,6 +11,11 @@ gem "inspec-bin", path: "./inspec-bin"
|
|
11
11
|
|
12
12
|
gem "ffi", ">= 1.9.14", "!= 1.13.0"
|
13
13
|
|
14
|
+
if Gem.ruby_version.to_s.start_with?("2.5")
|
15
|
+
# 16.7.23 required ruby 2.6+
|
16
|
+
gem "chef-utils", "< 16.7.23" # TODO: remove when we drop ruby 2.5
|
17
|
+
end
|
18
|
+
|
14
19
|
group :omnibus do
|
15
20
|
gem "rb-readline"
|
16
21
|
gem "appbundler"
|
@@ -20,44 +25,31 @@ end
|
|
20
25
|
|
21
26
|
group :test do
|
22
27
|
gem "chefstyle", "~> 1.2.1"
|
23
|
-
gem "minitest", "~> 5.5"
|
24
|
-
gem "minitest-sprint", "~> 1.0"
|
25
|
-
gem "rake", ">= 10"
|
26
|
-
gem "simplecov", ["~> 0.10", "<=0.18.2"]
|
27
28
|
gem "concurrent-ruby", "~> 1.0"
|
29
|
+
gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows
|
30
|
+
gem "json_schemer", ">= 0.2.1", "< 0.2.12"
|
31
|
+
gem "m"
|
32
|
+
gem "minitest-sprint", "~> 1.0"
|
33
|
+
gem "minitest", "~> 5.5"
|
28
34
|
gem "mocha", "~> 1.1"
|
35
|
+
gem "nokogiri", "~> 1.9"
|
36
|
+
gem "pry-byebug"
|
37
|
+
gem "pry", "~> 0.10"
|
38
|
+
gem "rake", ">= 10"
|
29
39
|
gem "ruby-progressbar", "~> 1.8"
|
40
|
+
gem "simplecov", ["~> 0.10", "<=0.18.2"]
|
30
41
|
gem "webmock", "~> 3.0"
|
31
|
-
gem "m"
|
32
|
-
gem "pry", "~> 0.10"
|
33
|
-
gem "pry-byebug"
|
34
|
-
gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows
|
35
42
|
end
|
36
43
|
|
37
44
|
group :integration do
|
38
45
|
gem "berkshelf"
|
39
|
-
gem "test-kitchen"
|
40
|
-
gem "kitchen-vagrant"
|
41
|
-
gem "
|
42
|
-
gem "
|
43
|
-
gem "kitchen-inspec"
|
44
|
-
gem "kitchen-ec2"
|
45
|
-
gem "kitchen-dokken"
|
46
|
+
gem "test-kitchen", ">= 2.8"
|
47
|
+
gem "kitchen-vagrant", ">= 1.7"
|
48
|
+
gem "kitchen-inspec", ">= 2.0"
|
49
|
+
gem "kitchen-dokken", ">= 2.11"
|
46
50
|
gem "git"
|
47
51
|
end
|
48
52
|
|
49
|
-
# gems for Maintainers.md generation
|
50
|
-
group :maintenance do
|
51
|
-
gem "tomlrb"
|
52
|
-
|
53
|
-
# To sync maintainers with github
|
54
|
-
gem "octokit"
|
55
|
-
gem "netrc"
|
56
|
-
end
|
57
|
-
|
58
53
|
group :deploy do
|
59
54
|
gem "inquirer"
|
60
|
-
end
|
61
|
-
|
62
|
-
# add these additional dependencies into Gemfile.local
|
63
|
-
eval_gemfile(__FILE__ + ".local") if File.exist?(__FILE__ + ".local")
|
55
|
+
end
|
data/inspec-core.gemspec
CHANGED
@@ -24,19 +24,18 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
# Implementation dependencies
|
26
26
|
spec.add_dependency "chef-telemetry", "~> 1.0"
|
27
|
-
spec.add_dependency "license-acceptance", ">= 0.2.13", "<
|
27
|
+
spec.add_dependency "license-acceptance", ">= 0.2.13", "< 3.0"
|
28
28
|
spec.add_dependency "thor", ">= 0.20", "< 2.0"
|
29
|
-
spec.add_dependency "json_schemer", ">= 0.2.1", "< 0.2.12"
|
30
29
|
spec.add_dependency "method_source", ">= 0.8", "< 2.0"
|
31
30
|
spec.add_dependency "rubyzip", "~> 1.2", ">= 1.2.2"
|
32
|
-
spec.add_dependency "rspec", "~> 3.9"
|
31
|
+
spec.add_dependency "rspec", "~> 3.9.0"
|
33
32
|
spec.add_dependency "rspec-its", "~> 1.2"
|
34
33
|
spec.add_dependency "pry", "~> 0.13"
|
35
34
|
spec.add_dependency "hashie", "~> 3.4"
|
36
35
|
spec.add_dependency "mixlib-log", "~> 3.0"
|
37
36
|
spec.add_dependency "sslshake", "~> 1.2"
|
38
37
|
spec.add_dependency "parallel", "~> 1.9"
|
39
|
-
spec.add_dependency "faraday", ">= 0.9.0"
|
38
|
+
spec.add_dependency "faraday", ">= 0.9.0", "< 1.1"
|
40
39
|
spec.add_dependency "tty-table", "~> 0.10"
|
41
40
|
spec.add_dependency "tty-prompt", "~> 0.17"
|
42
41
|
spec.add_dependency "tomlrb", "~> 1.2.0"
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -158,6 +158,16 @@ module Inspec
|
|
158
158
|
option :silence_deprecations, type: :array,
|
159
159
|
banner: "[all]|[GROUP GROUP...]",
|
160
160
|
desc: "Suppress deprecation warnings. See install_dir/etc/deprecations.json for list of GROUPs or use 'all'."
|
161
|
+
option :diff, type: :boolean, default: true,
|
162
|
+
desc: "Use --no-diff to suppress 'diff' output of failed textual test results."
|
163
|
+
option :sort_results_by, type: :string, default: "file", banner: "--sort-results-by=none|control|file|random",
|
164
|
+
desc: "After normal execution order, results are sorted by control ID, or by file (default), or randomly. None uses legacy unsorted mode."
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.help(*args)
|
168
|
+
super(*args)
|
169
|
+
puts "\nAbout #{Inspec::Dist::PRODUCT_NAME}:"
|
170
|
+
puts " Patents: chef.io/patents\n\n"
|
161
171
|
end
|
162
172
|
|
163
173
|
def self.format_platform_info(params: {}, indent: 0, color: 39)
|
data/lib/inspec/cli.rb
CHANGED
@@ -48,7 +48,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
48
48
|
desc: "Allow or disable user interaction"
|
49
49
|
|
50
50
|
class_option :disable_core_plugins, type: :string, banner: "", # Actually a boolean, but this suppresses the creation of a --no-disable...
|
51
|
-
desc: "Disable loading all plugins that are shipped in the lib/plugins directory of InSpec. Useful in development."
|
51
|
+
desc: "Disable loading all plugins that are shipped in the lib/plugins directory of InSpec. Useful in development.",
|
52
|
+
hide: true
|
52
53
|
|
53
54
|
class_option :disable_user_plugins, type: :string, banner: "",
|
54
55
|
desc: "Disable loading all plugins that the user installed."
|
@@ -194,7 +195,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
194
195
|
pretty_handle_exception(e)
|
195
196
|
end
|
196
197
|
|
197
|
-
desc "exec LOCATIONS",
|
198
|
+
desc "exec LOCATIONS", "Run all tests at LOCATIONS."
|
199
|
+
long_desc <<~EOT
|
198
200
|
Run all test files at the specified LOCATIONS.
|
199
201
|
|
200
202
|
Loads the given profile(s) and fetches their dependencies if needed. Then
|
data/lib/inspec/config.rb
CHANGED
@@ -405,6 +405,18 @@ module Inspec
|
|
405
405
|
@plugin_cfg = data
|
406
406
|
end
|
407
407
|
|
408
|
+
def validate_sort_results_by!(option_value)
|
409
|
+
expected = %w{
|
410
|
+
none
|
411
|
+
control
|
412
|
+
file
|
413
|
+
random
|
414
|
+
}
|
415
|
+
return if expected.include? option_value
|
416
|
+
|
417
|
+
raise Inspec::ConfigError::Invalid, "--sort-results-by must be one of #{expected.join(", ")}"
|
418
|
+
end
|
419
|
+
|
408
420
|
#-----------------------------------------------------------------------#
|
409
421
|
# Merging Options
|
410
422
|
#-----------------------------------------------------------------------#
|
@@ -435,6 +447,7 @@ module Inspec
|
|
435
447
|
finalize_parse_reporters(options)
|
436
448
|
finalize_handle_sudo(options)
|
437
449
|
finalize_compliance_login(options)
|
450
|
+
finalize_sort_results(options)
|
438
451
|
|
439
452
|
Thor::CoreExt::HashWithIndifferentAccess.new(options)
|
440
453
|
end
|
@@ -509,6 +522,12 @@ module Inspec
|
|
509
522
|
end
|
510
523
|
end
|
511
524
|
|
525
|
+
def finalize_sort_results(options)
|
526
|
+
if options.key?("sort_results_by")
|
527
|
+
validate_sort_results_by!(options["sort_results_by"])
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
512
531
|
class Defaults
|
513
532
|
DEFAULTS = {
|
514
533
|
exec: {
|
@@ -159,6 +159,14 @@ module Inspec::Formatters
|
|
159
159
|
resource_title: example.metadata[:described_class] || example.metadata[:example_group][:description],
|
160
160
|
expectation_message: format_expectation_message(example),
|
161
161
|
waiver_data: example.metadata[:waiver_data],
|
162
|
+
# This enforces the resource name as expected based off of the class
|
163
|
+
# name. However, if we wanted the `name` attribute against the class
|
164
|
+
# to be canonical for this case (consider edge cases!) we would use
|
165
|
+
# example.metadata[:described_class].instance_variable_get(:@__resource_name__)&.to_s
|
166
|
+
resource_class: example.metadata[:described_class].class.superclass.name,
|
167
|
+
# This is a raw grep of the text passed to the resource in any format,
|
168
|
+
# and is used to enforce near-uniqueness against the resource.
|
169
|
+
resource_params: find_resource_params(example.metadata[:described_class]),
|
162
170
|
}
|
163
171
|
|
164
172
|
unless (pid = example.metadata[:profile_id]).nil?
|
@@ -174,6 +182,14 @@ module Inspec::Formatters
|
|
174
182
|
res
|
175
183
|
end
|
176
184
|
|
185
|
+
def find_resource_params(example)
|
186
|
+
if example.class.ancestors.include?(Inspec::Resource)
|
187
|
+
example.instance_variable_get(:@resource_params)
|
188
|
+
else
|
189
|
+
[]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
177
193
|
def format_expectation_message(example)
|
178
194
|
if (example.metadata[:example_group][:description_args].first == example.metadata[:example_group][:described_class]) ||
|
179
195
|
example.metadata[:example_group][:described_class].nil?
|
data/lib/inspec/globals.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
require_relative "utils/install_context"
|
2
2
|
|
3
3
|
module Inspec
|
4
|
-
|
5
4
|
extend Inspec::InstallContextHelpers
|
6
5
|
|
7
6
|
def self.config_dir
|
8
|
-
ENV["INSPEC_CONFIG_DIR"]
|
7
|
+
ENV["INSPEC_CONFIG_DIR"] || File.join(home_path, ".inspec")
|
9
8
|
end
|
10
9
|
|
11
10
|
def self.src_root
|
12
11
|
@src_root ||= File.expand_path(File.join(__FILE__, "../../.."))
|
13
12
|
end
|
14
13
|
|
14
|
+
def self.home_path
|
15
|
+
Dir.home
|
16
|
+
rescue ArgumentError, NoMethodError
|
17
|
+
# If ENV['HOME'] is not set, Dir.home will fail due to expanding the ~. Fallback to Etc.
|
18
|
+
require "etc" unless defined?(Etc)
|
19
|
+
Etc.getpwuid.dir
|
20
|
+
end
|
15
21
|
end
|
data/lib/inspec/input.rb
CHANGED
@@ -171,7 +171,7 @@ module Inspec
|
|
171
171
|
# are free to go higher.
|
172
172
|
DEFAULT_PRIORITY_FOR_VALUE_SET = 60
|
173
173
|
|
174
|
-
attr_reader :description, :events, :identifier, :name, :required, :title, :type
|
174
|
+
attr_reader :description, :events, :identifier, :name, :required, :sensitive, :title, :type
|
175
175
|
|
176
176
|
def initialize(name, options = {})
|
177
177
|
@name = name
|
@@ -264,6 +264,7 @@ module Inspec
|
|
264
264
|
@required = options[:required] if options.key?(:required)
|
265
265
|
@identifier = options[:identifier] if options.key?(:identifier) # TODO: determine if this is ever used
|
266
266
|
@type = options[:type] if options.key?(:type)
|
267
|
+
@sensitive = options[:sensitive] if options.key?(:sensitive)
|
267
268
|
end
|
268
269
|
|
269
270
|
def make_creation_event(options)
|
@@ -320,7 +321,7 @@ module Inspec
|
|
320
321
|
|
321
322
|
def to_hash
|
322
323
|
as_hash = { name: name, options: {} }
|
323
|
-
%i{description title identifier type required value}.each do |field|
|
324
|
+
%i{description title identifier type required value sensitive}.each do |field|
|
324
325
|
val = send(field)
|
325
326
|
next if val.nil?
|
326
327
|
|
@@ -334,7 +335,7 @@ module Inspec
|
|
334
335
|
#--------------------------------------------------------------------------#
|
335
336
|
|
336
337
|
def to_s
|
337
|
-
"Input #{name} with #{current_value}"
|
338
|
+
"Input #{name} with value " + (sensitive ? "*** (senstive)" : "#{current_value}")
|
338
339
|
end
|
339
340
|
|
340
341
|
#--------------------------------------------------------------------------#
|
@@ -29,6 +29,8 @@ module Inspec
|
|
29
29
|
def_delegator :inputs_by_profile, :select
|
30
30
|
def_delegator :profile_aliases, :key?, :profile_alias?
|
31
31
|
|
32
|
+
attr_accessor :cache_inputs
|
33
|
+
|
32
34
|
def initialize
|
33
35
|
# Keyed on String profile_name => Hash of String input_name => Input object
|
34
36
|
@inputs_by_profile = {}
|
@@ -43,6 +45,9 @@ module Inspec
|
|
43
45
|
activator.activate!
|
44
46
|
activator.implementation_class.new
|
45
47
|
end
|
48
|
+
|
49
|
+
# Activate caching for inputs by default
|
50
|
+
@cache_inputs = true
|
46
51
|
end
|
47
52
|
|
48
53
|
#-------------------------------------------------------------#
|
@@ -84,7 +89,7 @@ module Inspec
|
|
84
89
|
|
85
90
|
# Find or create the input
|
86
91
|
inputs_by_profile[profile_name] ||= {}
|
87
|
-
if inputs_by_profile[profile_name].key?(input_name)
|
92
|
+
if inputs_by_profile[profile_name].key?(input_name) && cache_inputs
|
88
93
|
inputs_by_profile[profile_name][input_name].update(options)
|
89
94
|
else
|
90
95
|
inputs_by_profile[profile_name][input_name] = Inspec::Input.new(input_name, options)
|
@@ -316,6 +321,7 @@ module Inspec
|
|
316
321
|
profile_name,
|
317
322
|
type: input_options[:type],
|
318
323
|
required: input_options[:required],
|
324
|
+
sensitive: input_options[:sensitive],
|
319
325
|
event: evt
|
320
326
|
)
|
321
327
|
end
|
@@ -50,6 +50,11 @@ module Inspec::Plugin::V2
|
|
50
50
|
# we want to allow "sidecar loading", in which case a plugin may add an entry to the registry.
|
51
51
|
registry.plugin_names.dup.each do |plugin_name|
|
52
52
|
plugin_details = registry[plugin_name]
|
53
|
+
|
54
|
+
# Under some conditions (kitchen-inspec with multiple test suites, for example), this may be
|
55
|
+
# called multple times. Don't reload anything.
|
56
|
+
next if plugin_details.loaded
|
57
|
+
|
53
58
|
# We want to capture literally any possible exception here, since we are storing them.
|
54
59
|
# rubocop: disable Lint/RescueException
|
55
60
|
begin
|
@@ -1,18 +1,20 @@
|
|
1
1
|
require_relative "../../../run_data"
|
2
|
+
require_relative "../../../utils/run_data_filters"
|
2
3
|
|
3
4
|
module Inspec::Plugin::V2::PluginType
|
4
5
|
class Reporter < Inspec::Plugin::V2::PluginBase
|
5
6
|
register_plugin_type(:reporter)
|
7
|
+
include Inspec::Utils::RunDataFilters
|
6
8
|
|
7
9
|
attr_reader :run_data
|
8
10
|
|
9
11
|
def initialize(config)
|
10
12
|
@config = config
|
11
13
|
|
12
|
-
#
|
14
|
+
# Filter the run_data while still a Hash; if it is huge, this
|
13
15
|
# saves on conversion time
|
14
16
|
@run_data = config[:run_data] || {}
|
15
|
-
|
17
|
+
apply_run_data_filters_to_hash
|
16
18
|
|
17
19
|
unless Inspec::RunData.compatible_schema?(self.class.run_data_schema_constraints)
|
18
20
|
# Best we can do is warn here, the InSpec run has finished
|
@@ -24,26 +26,6 @@ module Inspec::Plugin::V2::PluginType
|
|
24
26
|
@output = ""
|
25
27
|
end
|
26
28
|
|
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
|
-
process_message_truncation(r)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
29
|
def output(str, newline = true)
|
48
30
|
@output << str
|
49
31
|
@output << "\n" if newline
|
@@ -61,14 +43,5 @@ module Inspec::Plugin::V2::PluginType
|
|
61
43
|
def self.run_data_schema_constraints
|
62
44
|
raise NotImplementedError, "#{self.class} must implement a `run_data_schema_constraints` class method to declare its compatibiltity with the RunData API."
|
63
45
|
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def process_message_truncation(result)
|
68
|
-
if result.key?(:message) && result[:message] != "" && @trunc > -1 && result[:message].length > @trunc
|
69
|
-
result[:message] = result[:message][0...@trunc] + "[Truncated to #{@trunc} characters]"
|
70
|
-
end
|
71
|
-
result
|
72
|
-
end
|
73
46
|
end
|
74
47
|
end
|
@@ -1,30 +1,17 @@
|
|
1
|
+
require_relative "../utils/run_data_filters"
|
2
|
+
|
1
3
|
module Inspec::Reporters
|
2
4
|
class Base
|
5
|
+
include Inspec::Utils::RunDataFilters
|
6
|
+
|
3
7
|
attr_reader :run_data
|
4
8
|
|
5
9
|
def initialize(config)
|
6
10
|
@config = config
|
7
|
-
@run_data = config[:run_data]
|
8
|
-
|
9
|
-
@output = ""
|
10
|
-
end
|
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 : {}
|
11
|
+
@run_data = config[:run_data] || {}
|
12
|
+
apply_run_data_filters_to_hash
|
15
13
|
|
16
|
-
|
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
|
-
process_message_truncation(r)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
14
|
+
@output = ""
|
28
15
|
end
|
29
16
|
|
30
17
|
def output(str, newline = true)
|
@@ -40,14 +27,5 @@ module Inspec::Reporters
|
|
40
27
|
def render
|
41
28
|
raise NotImplementedError, "#{self.class} must implement a `#render` method to format its output."
|
42
29
|
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def process_message_truncation(result)
|
47
|
-
if result.key?(:message) && result[:message] != "" && @trunc > -1 && result[:message].length > @trunc
|
48
|
-
result[:message] = result[:message][0...@trunc] + "[Truncated to #{@trunc} characters]"
|
49
|
-
end
|
50
|
-
result
|
51
|
-
end
|
52
30
|
end
|
53
31
|
end
|
data/lib/inspec/resource.rb
CHANGED
@@ -108,6 +108,7 @@ module Inspec
|
|
108
108
|
# Infrastructure / Bookkeeping
|
109
109
|
|
110
110
|
def self.__register(name, resource_klass)
|
111
|
+
# This has bitten us and should be a great candidate to remove in InSpec5
|
111
112
|
cl = Class.new(resource_klass) do # TODO: remove
|
112
113
|
# As best I can figure out, this anonymous class only exists
|
113
114
|
# because we're trying to avoid having resources with
|
@@ -116,6 +117,7 @@ module Inspec
|
|
116
117
|
# documentation.
|
117
118
|
def initialize(backend, name, *args)
|
118
119
|
supersuper_initialize(backend, name) do
|
120
|
+
@resource_params = args
|
119
121
|
super(*args)
|
120
122
|
end
|
121
123
|
end
|
data/lib/inspec/resources/apt.rb
CHANGED
@@ -87,13 +87,13 @@ module Inspec::Resources
|
|
87
87
|
active = raw_line == line
|
88
88
|
|
89
89
|
# formats:
|
90
|
-
# deb
|
91
|
-
# deb
|
92
|
-
# deb [trusted=yes]
|
90
|
+
# deb "http://archive.ubuntu.com/ubuntu/" wily main restricted ...
|
91
|
+
# deb http://archive.ubuntu.com/ubuntu/ wily main restricted ...
|
92
|
+
# deb [trusted=yes] http://archive.ubuntu.com/ubuntu/ wily main restricted ...
|
93
|
+
# deb [arch=amd64 trusted=yes] http://archive.ubuntu.com/ubuntu/ wily main restricted ...
|
93
94
|
# deb cdrom:[Ubuntu 15.10 _Wily Werewolf_ - Release amd64 (20151021)]/ wily main restricted ...
|
94
95
|
|
95
|
-
words = line.split
|
96
|
-
words.delete_at 1 if words[1] && words[1].start_with?("[")
|
96
|
+
words = line.sub(/^(deb|deb-src)\s+\[.+?\]/, '\1').split
|
97
97
|
type, url, distro, *components = words
|
98
98
|
url = url.delete('"') if url
|
99
99
|
|
@@ -29,7 +29,7 @@ module Inspec::Resources
|
|
29
29
|
@content = read_file(@conf_path)
|
30
30
|
@kernel = kernel || "default"
|
31
31
|
rescue UnknownGrubConfig
|
32
|
-
skip_resource "The `
|
32
|
+
skip_resource "The `grub_conf` resource is not yet supported on the target OS #{inspec.os[:name]}."
|
33
33
|
end
|
34
34
|
|
35
35
|
def config_for_platform(path)
|
@@ -77,6 +77,7 @@ module Inspec::Resources
|
|
77
77
|
|
78
78
|
def grub2_parse_kernel_lines(content, conf)
|
79
79
|
menu_entries = extract_menu_entries(content)
|
80
|
+
return {} if menu_entries.empty?
|
80
81
|
|
81
82
|
if @kernel == "default"
|
82
83
|
default_menu_entry(menu_entries, conf["GRUB_DEFAULT"])
|
@@ -314,7 +314,7 @@ module Inspec::Resources
|
|
314
314
|
# Find the package
|
315
315
|
cmd = inspec.command <<-EOF.gsub(/^\s*/, "")
|
316
316
|
Get-ItemProperty (@("#{search_paths.join('", "')}") | Where-Object { Test-Path $_ }) |
|
317
|
-
Where-Object { $_.DisplayName -
|
317
|
+
Where-Object { $_.DisplayName -like "#{package_name}" -or $_.PSChildName -like "#{package_name}" } |
|
318
318
|
Select-Object -Property DisplayName,DisplayVersion | ConvertTo-Json
|
319
319
|
EOF
|
320
320
|
|
@@ -81,7 +81,7 @@ module Inspec::Resources
|
|
81
81
|
when :os, :platform then
|
82
82
|
platform?(v)
|
83
83
|
when :os_name, :platform_name then
|
84
|
-
|
84
|
+
check_name(v)
|
85
85
|
when :release then
|
86
86
|
check_release(v)
|
87
87
|
end
|
@@ -99,6 +99,16 @@ module Inspec::Resources
|
|
99
99
|
|
100
100
|
private
|
101
101
|
|
102
|
+
def check_name(value)
|
103
|
+
# allow wild card matching
|
104
|
+
if value.include?("*")
|
105
|
+
cleaned = Regexp.escape(value).gsub('\*', ".*?")
|
106
|
+
name =~ /#{cleaned}/
|
107
|
+
else
|
108
|
+
name == value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
102
112
|
def check_release(value)
|
103
113
|
# allow wild card matching
|
104
114
|
if value.include?("*")
|
@@ -26,12 +26,13 @@ module Inspec::Resources
|
|
26
26
|
supports platform: "windows"
|
27
27
|
desc "Use the postgres_session InSpec audit resource to test SQL commands run against a PostgreSQL database."
|
28
28
|
example <<~EXAMPLE
|
29
|
-
sql = postgres_session('username', 'password', 'host')
|
29
|
+
sql = postgres_session('username', 'password', 'host', 'port')
|
30
30
|
query('sql_query', ['database_name'])` contains the query and (optional) database to execute
|
31
31
|
|
32
32
|
# default values:
|
33
33
|
# username: 'postgres'
|
34
34
|
# host: 'localhost'
|
35
|
+
# port: 5432
|
35
36
|
# db: databse == db_user running the sql query
|
36
37
|
|
37
38
|
describe sql.query('SELECT * FROM pg_shadow WHERE passwd IS NULL;') do
|
@@ -39,10 +40,11 @@ module Inspec::Resources
|
|
39
40
|
end
|
40
41
|
EXAMPLE
|
41
42
|
|
42
|
-
def initialize(user, pass, host = nil)
|
43
|
+
def initialize(user, pass, host = nil, port = nil)
|
43
44
|
@user = user || "postgres"
|
44
45
|
@pass = pass
|
45
46
|
@host = host || "localhost"
|
47
|
+
@port = port || 5432
|
46
48
|
end
|
47
49
|
|
48
50
|
def query(query, db = [])
|
@@ -64,7 +66,7 @@ module Inspec::Resources
|
|
64
66
|
|
65
67
|
def create_psql_cmd(query, db = [])
|
66
68
|
dbs = db.map { |x| "-d #{x}" }.join(" ")
|
67
|
-
"PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h #{@host} -A -t -c #{escaped_query(query)}"
|
69
|
+
"PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -h #{@host} -p #{@port} -A -t -c #{escaped_query(query)}"
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
@@ -138,7 +138,7 @@ module Inspec::Resources
|
|
138
138
|
command: 8,
|
139
139
|
}
|
140
140
|
else
|
141
|
-
command = "ps
|
141
|
+
command = "ps wwaxo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command"
|
142
142
|
regex = /^(.+?)\s+(\d+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
143
143
|
field_map = {
|
144
144
|
label: 1,
|
data/lib/inspec/resources/wmi.rb
CHANGED
@@ -16,7 +16,10 @@ module Inspec::Resources
|
|
16
16
|
namespace: 'root\\rsop\\computer',
|
17
17
|
filter: 'KeyName = \'MinimumPasswordAge\' And precedence=1'
|
18
18
|
}) do
|
19
|
-
its('Setting') { should
|
19
|
+
its('Setting') { should cmp true }
|
20
|
+
end
|
21
|
+
describe wmi({namespace: "root\\cimv2", query: "SELECT installstate FROM win32_optionalfeature"}) do
|
22
|
+
its("installstate") { should include 2 }
|
20
23
|
end
|
21
24
|
EXAMPLE
|
22
25
|
|
@@ -66,13 +69,18 @@ module Inspec::Resources
|
|
66
69
|
|
67
70
|
# run wmi command and filter empty wmi
|
68
71
|
script = <<-EOH
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
$
|
73
|
-
|
72
|
+
Function Aggregate {
|
73
|
+
$propsHash = @{}
|
74
|
+
ForEach ($wmiObj in $Input) {
|
75
|
+
ForEach ($wmiProp in $wmiObj.properties) {
|
76
|
+
If($propsHash.ContainsKey($wmiProp.name)) {
|
77
|
+
$propsHash[$wmiProp.name].add($wmiProp.value) | Out-Null
|
78
|
+
} Else {
|
79
|
+
$propsHash[$wmiProp.name] = [System.Collections.ArrayList]@($wmiProp.value)
|
80
|
+
}
|
74
81
|
}
|
75
|
-
|
82
|
+
}
|
83
|
+
$propsHash
|
76
84
|
}
|
77
85
|
Get-WmiObject #{params} | Aggregate | ConvertTo-Json
|
78
86
|
EOH
|
data/lib/inspec/rule.rb
CHANGED
@@ -343,14 +343,8 @@ module Inspec
|
|
343
343
|
__waiver_data["skipped_due_to_waiver"] = false
|
344
344
|
__waiver_data["message"] = ""
|
345
345
|
|
346
|
-
#
|
347
|
-
#
|
348
|
-
# is false-like, since all non-skipped waiver operations are handled
|
349
|
-
# during reporting phase.
|
350
|
-
return unless __waiver_data.key?("run") && !__waiver_data["run"]
|
351
|
-
|
352
|
-
# OK, the intent is to skip. Does it have an expiration date, and
|
353
|
-
# if so, is it in the future?
|
346
|
+
# Does it have an expiration date, and if so, is it in the future?
|
347
|
+
# This sets a waiver message before checking `run: true`
|
354
348
|
expiry = __waiver_data["expiration_date"]
|
355
349
|
if expiry
|
356
350
|
# YAML will automagically give us a Date or a Time.
|
@@ -370,6 +364,12 @@ module Inspec
|
|
370
364
|
end
|
371
365
|
end
|
372
366
|
|
367
|
+
# Waivers should have a hash value with keys possibly including "run" and
|
368
|
+
# expiration_date. We only care here if it has a "run" key and it
|
369
|
+
# is false-like, since all non-skipped waiver operations are handled
|
370
|
+
# during reporting phase.
|
371
|
+
return unless __waiver_data.key?("run") && !__waiver_data["run"]
|
372
|
+
|
373
373
|
# OK, apply a skip.
|
374
374
|
@__skip_rule[:result] = true
|
375
375
|
@__skip_rule[:type] = :waiver
|
data/lib/inspec/run_data.rb
CHANGED
@@ -47,7 +47,7 @@ module Inspec
|
|
47
47
|
# core reporters have been migrated to plugins. It is probable that new data elements
|
48
48
|
# and new Hash compatibility behavior will be added during the core reporter plugin
|
49
49
|
# conversion process.
|
50
|
-
SCHEMA_VERSION = "0.
|
50
|
+
SCHEMA_VERSION = "0.3.0".freeze
|
51
51
|
|
52
52
|
def self.compatible_schema?(constraints)
|
53
53
|
reqs = Gem::Requirement.create(constraints)
|
@@ -96,11 +96,12 @@ module Inspec
|
|
96
96
|
# There are probably others
|
97
97
|
:value,
|
98
98
|
:type,
|
99
|
-
:required
|
99
|
+
:required,
|
100
|
+
:sensitive
|
100
101
|
) do
|
101
102
|
include HashLikeStruct
|
102
103
|
def initialize(raw_opts_data)
|
103
|
-
%i{value type required}.each { |f| self[f] = raw_opts_data[f] }
|
104
|
+
%i{value type required sensitive}.each { |f| self[f] = raw_opts_data[f] }
|
104
105
|
end
|
105
106
|
end
|
106
107
|
end
|
@@ -8,6 +8,7 @@ module Inspec
|
|
8
8
|
:run_time, # Float seconds execution time
|
9
9
|
:skip_message, # String
|
10
10
|
:start_time, # DateTime
|
11
|
+
:resource_params, # What is passed to the resource as a raw grep
|
11
12
|
:status, # String
|
12
13
|
:resource_title, # Ugly internals
|
13
14
|
# :waiver_data, # Undocumented tramp data / not exposed in this API
|
@@ -34,6 +35,7 @@ module Inspec
|
|
34
35
|
end
|
35
36
|
|
36
37
|
self.resource_name = raw_res_data[:resource_title].instance_variable_get(:@__resource_name__)&.to_s
|
38
|
+
self.resource_params = raw_res_data[:resource_title].instance_variable_get(:@grep)&.to_s
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
data/lib/inspec/schema.rb
CHANGED
@@ -56,6 +56,7 @@ module Inspec
|
|
56
56
|
"code_desc" => { "type" => "string" },
|
57
57
|
"run_time" => { "type" => "number" },
|
58
58
|
"start_time" => { "type" => "string" },
|
59
|
+
"resource_class" => { "type" => "string", "optional" => true },
|
59
60
|
"skip_message" => { "type" => "string", "optional" => true },
|
60
61
|
"resource" => { "type" => "string", "optional" => true },
|
61
62
|
"message" => { "type" => "string", "optional" => true },
|
@@ -194,6 +195,7 @@ module Inspec
|
|
194
195
|
"profile_sha256" => { "type" => "string" },
|
195
196
|
"status" => { "type" => "string" },
|
196
197
|
"code_desc" => { "type" => "string" },
|
198
|
+
"resource_class" => { "type" => "string", "optional" => true },
|
197
199
|
"skip_message" => { "type" => "string", "optional" => true },
|
198
200
|
"resource" => { "type" => "string", "optional" => true },
|
199
201
|
"message" => { "type" => "string", "optional" => true },
|
@@ -74,7 +74,7 @@ module Inspec
|
|
74
74
|
},
|
75
75
|
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT])
|
76
76
|
|
77
|
-
# Based loosely on https://
|
77
|
+
# Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019
|
78
78
|
# However, concessions were made to the reality of current reporters, specifically
|
79
79
|
# with how description is omitted and version/inspec_version aren't as advertised online
|
80
80
|
PROFILE = Primitives::SchemaType.new("Exec JSON Profile", {
|
data/lib/inspec/shell.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
autoload :Pry, "pry"
|
2
2
|
|
3
3
|
module Inspec
|
4
4
|
# A pry based shell for inspec. Given a runner (with a configured backend and
|
@@ -137,7 +137,7 @@ module Inspec
|
|
137
137
|
end
|
138
138
|
|
139
139
|
info += "#{mark "Web Reference:"}\n\n"
|
140
|
-
info += "https://
|
140
|
+
info += "https://docs.chef.io/inspec/resources/#{topic}\n\n"
|
141
141
|
puts info
|
142
142
|
else
|
143
143
|
begin
|
@@ -208,7 +208,7 @@ module Inspec
|
|
208
208
|
|
209
209
|
its('content') { should_not match /^MyKey:\\s+some value/ }
|
210
210
|
|
211
|
-
For more examples, see: https://
|
211
|
+
For more examples, see: https://docs.chef.io/inspec/matchers/
|
212
212
|
|
213
213
|
EOL
|
214
214
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Inspec
|
2
|
+
module Utils
|
3
|
+
# RunDataFilters is a mixin for core Reporters and plugin reporters.
|
4
|
+
# The methods operate on the run_data Hash (prior to any conversion to a
|
5
|
+
# full RunData object).
|
6
|
+
# All methods here operate using the run_data accessor and modify
|
7
|
+
# its contents in place (if needed).
|
8
|
+
module RunDataFilters
|
9
|
+
|
10
|
+
# Long name, but we want to be clear this operates on the Hash
|
11
|
+
# This is the only method that client libraries need to call; any future
|
12
|
+
# feature growth should be handled internally here.
|
13
|
+
def apply_run_data_filters_to_hash
|
14
|
+
@config[:runtime_config] = Inspec::Config.cached || {}
|
15
|
+
apply_report_resize_options
|
16
|
+
redact_sensitive_inputs
|
17
|
+
suppress_diff_output
|
18
|
+
sort_controls
|
19
|
+
end
|
20
|
+
|
21
|
+
# Apply options such as message truncation and removal of backtraces
|
22
|
+
def apply_report_resize_options
|
23
|
+
runtime_config = @config[:runtime_config]
|
24
|
+
|
25
|
+
message_truncation = runtime_config[:reporter_message_truncation] || "ALL"
|
26
|
+
@trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
|
27
|
+
include_backtrace = runtime_config[:reporter_backtrace_inclusion].nil? ? true : runtime_config[:reporter_backtrace_inclusion]
|
28
|
+
|
29
|
+
@run_data[:profiles]&.each do |p|
|
30
|
+
p[:controls].each do |c|
|
31
|
+
c[:results]&.map! do |r|
|
32
|
+
r.delete(:backtrace) unless include_backtrace
|
33
|
+
process_message_truncation(r)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Find any inputs with :sensitive = true and replace their values with "***"
|
40
|
+
def redact_sensitive_inputs
|
41
|
+
@run_data[:profiles]&.each do |p|
|
42
|
+
p[:inputs]&.each do |i|
|
43
|
+
next unless i[:options][:sensitive]
|
44
|
+
|
45
|
+
i[:options][:value] = "***"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Optionally suppress diff output in the message field
|
51
|
+
def suppress_diff_output
|
52
|
+
return if @config[:runtime_config][:diff]
|
53
|
+
|
54
|
+
@run_data[:profiles]&.each do |p|
|
55
|
+
p[:controls]&.each do |c|
|
56
|
+
c[:results]&.each do |r|
|
57
|
+
next unless r[:message] # :message only set on failure
|
58
|
+
|
59
|
+
pos = r[:message].index("\n\nDiff:")
|
60
|
+
next unless pos # Only textual tests get Diffs
|
61
|
+
|
62
|
+
r[:message] = r[:message].slice(0, pos)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Optionally sort controls within each profile in report
|
69
|
+
def sort_controls
|
70
|
+
sort_type = @config[:runtime_config][:sort_results_by]
|
71
|
+
return unless sort_type
|
72
|
+
return if sort_type == "none"
|
73
|
+
|
74
|
+
@run_data[:profiles]&.each do |p|
|
75
|
+
p[:controls] ||= []
|
76
|
+
p[:groups] ||= []
|
77
|
+
|
78
|
+
case sort_type
|
79
|
+
when "control"
|
80
|
+
p[:controls].sort_by! { |c| c[:id] }
|
81
|
+
when "random"
|
82
|
+
p[:controls].shuffle!
|
83
|
+
when "file"
|
84
|
+
# Sort the controls by file, but preserve order within the file.
|
85
|
+
# Files are called "groups" in the run_data, and the filename is in the id.
|
86
|
+
sorted_control_ids = p[:groups].sort_by { |g| g[:id] }.map { |g| g[:controls] }.flatten
|
87
|
+
controls_by_id = {}
|
88
|
+
p[:controls].each { |c| controls_by_id[c[:id]] = c }
|
89
|
+
p[:controls] = sorted_control_ids.map { |cid| controls_by_id[cid] }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def process_message_truncation(result)
|
97
|
+
if result.key?(:message) && result[:message] != "" && @trunc > -1 && result[:message].length > @trunc
|
98
|
+
result[:message] = result[:message][0...@trunc] + "[Truncated to #{@trunc} characters]"
|
99
|
+
end
|
100
|
+
result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/inspec/version.rb
CHANGED
@@ -26,7 +26,7 @@ Creating new profile at /Users/spaterson/my-profile
|
|
26
26
|
aws_vpc_id: 'custom-vpc-id'
|
27
27
|
```
|
28
28
|
|
29
|
-
The related control will simply be skipped if this is not provided. See the [InSpec DSL documentation](https://
|
29
|
+
The related control will simply be skipped if this is not provided. See the [InSpec DSL documentation](https://docs.chef.io/inspec/dsl_inspec/) for more details on conditional execution using `only_if`.
|
30
30
|
|
31
31
|
## Run the tests
|
32
32
|
|
@@ -24,7 +24,7 @@ Note the `2` in the reporter name. If you omit it and run `--reporter html` inst
|
|
24
24
|
|
25
25
|
## Configuring the Plugin
|
26
26
|
|
27
|
-
The `html2` reporter requires no configuration to function. However, two options--`alternate_css_file` and `alternate_js_file`--are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. For details, see [our configuration file documentation](https://
|
27
|
+
The `html2` reporter requires no configuration to function. However, two options--`alternate_css_file` and `alternate_js_file`--are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. For details, see [our configuration file documentation](https://docs.chef.io/inspec/config/).
|
28
28
|
|
29
29
|
For example:
|
30
30
|
|
@@ -1,15 +1,17 @@
|
|
1
|
-
# junit
|
1
|
+
# junit and junit2 reporters
|
2
2
|
|
3
|
-
This is the implementation of the junit XML
|
3
|
+
This is the implementation of the junit and junit2 XML reporters.
|
4
4
|
|
5
|
-
##
|
5
|
+
## Installation
|
6
6
|
|
7
|
-
This plugin
|
7
|
+
This plugin ships with Chef InSpec and requires no additional installation.
|
8
8
|
|
9
|
-
## What
|
9
|
+
## What These Plugins Do
|
10
10
|
|
11
|
-
|
11
|
+
`junit` is the legacy Chef InSpec JUnit reporter, which is retained for backwards compatibility. It generates an XML report in Apache Ant JUnit format. The output format is considered nonstandard in several ways. New users are advised to use `junit2`.
|
12
|
+
|
13
|
+
`junit2` is an updated reporter that provides JUnit output according to the schema published by [Windy Road](https://github.com/windyroad/JUnit-Schema).
|
12
14
|
|
13
15
|
## Implementation Note
|
14
16
|
|
15
|
-
This reporter uses the REXML XML generator, but
|
17
|
+
This reporter uses the REXML XML generator at runtime, but uses Nokogiri, a more heavyweight XML library, for testing. This design keeps packaging requirements lightweight and free of compiled dependencies.
|
@@ -3,10 +3,19 @@ module InspecPlugins
|
|
3
3
|
module JUnitReporter
|
4
4
|
class Plugin < ::Inspec.plugin(2)
|
5
5
|
plugin_name :'inspec-reporter-junit'
|
6
|
+
|
7
|
+
# Legacy JUnit reporter, which generates subtly incorrect XML.
|
6
8
|
reporter :junit do
|
7
9
|
require_relative "inspec-reporter-junit/reporter"
|
8
|
-
InspecPlugins::JUnitReporter::
|
10
|
+
InspecPlugins::JUnitReporter::ReporterV1
|
9
11
|
end
|
12
|
+
|
13
|
+
# v2 reporter, which generates valid JUnit XML.
|
14
|
+
reporter :junit2 do
|
15
|
+
require_relative "inspec-reporter-junit/reporter"
|
16
|
+
InspecPlugins::JUnitReporter::ReporterV2
|
17
|
+
end
|
18
|
+
|
10
19
|
end
|
11
20
|
end
|
12
21
|
end
|
@@ -12,8 +12,8 @@ module InspecPlugins::JUnitReporter
|
|
12
12
|
testsuites = REXML::Element.new("testsuites")
|
13
13
|
xml_output.add(testsuites)
|
14
14
|
|
15
|
-
run_data.profiles.
|
16
|
-
testsuites.add(build_profile_xml(profile))
|
15
|
+
run_data.profiles.each_with_index do |profile, idx|
|
16
|
+
testsuites.add(build_profile_xml(profile, idx))
|
17
17
|
end
|
18
18
|
|
19
19
|
formatter = REXML::Formatters::Pretty.new
|
@@ -22,7 +22,42 @@ module InspecPlugins::JUnitReporter
|
|
22
22
|
output(formatter.write(xml_output.root, ""))
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
25
|
+
def count_profile_tests(profile)
|
26
|
+
profile.controls.reduce(0) do |acc, elem|
|
27
|
+
acc + elem.results.count
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def count_profile_failed_tests(profile)
|
32
|
+
profile.controls.reduce(0) do |acc, elem|
|
33
|
+
acc + elem.results.reduce(0) do |fail_test_total, test_case|
|
34
|
+
test_case.status == "failed" ? fail_test_total + 1 : fail_test_total
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def count_profile_skipped_tests(profile)
|
40
|
+
profile.controls.reduce(0) do |acc, elem|
|
41
|
+
acc + elem.results.reduce(0) do |skip_test_total, test_case|
|
42
|
+
test_case.status == "skipped" ? skip_test_total + 1 : skip_test_total
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def count_profile_errored_tests(profile)
|
48
|
+
profile.controls.reduce(0) do |acc, elem|
|
49
|
+
acc + elem.results.reduce(0) do |err_test_total, test_case|
|
50
|
+
test_case.backtrace.nil? ? err_test_total : err_test_total + 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# This is the "Legacy" JUnit reporter. It produces XML which is not
|
57
|
+
# correct according to the JUnit standard. It is retained for backwards
|
58
|
+
# compatibility.
|
59
|
+
class ReporterV1 < Reporter
|
60
|
+
def build_profile_xml(profile, _idx)
|
26
61
|
profile_xml = REXML::Element.new("testsuite")
|
27
62
|
profile_xml.add_attribute("name", profile.name)
|
28
63
|
profile_xml.add_attribute("tests", count_profile_tests(profile))
|
@@ -55,19 +90,66 @@ module InspecPlugins::JUnitReporter
|
|
55
90
|
|
56
91
|
result_xml
|
57
92
|
end
|
93
|
+
end
|
58
94
|
|
59
|
-
|
60
|
-
|
61
|
-
|
95
|
+
# This is the "Corrected" JUnit reporter. It produces XML which is intended
|
96
|
+
# to be valid. It should be used whenever possible.
|
97
|
+
class ReporterV2 < Reporter
|
98
|
+
def build_profile_xml(profile, idx)
|
99
|
+
profile_xml = REXML::Element.new("testsuite")
|
100
|
+
profile_xml.add_attribute("name", profile.name)
|
101
|
+
profile_xml.add_attribute("tests", count_profile_tests(profile))
|
102
|
+
profile_xml.add_attribute("id", idx + 1)
|
103
|
+
|
104
|
+
# junit2 counts failures and errors separately
|
105
|
+
errors = count_profile_errored_tests(profile)
|
106
|
+
profile_xml.add_attribute("errors", errors)
|
107
|
+
profile_xml.add_attribute("failures", count_profile_failed_tests(profile) - errors)
|
108
|
+
profile_xml.add_attribute("skipped", count_profile_skipped_tests(profile))
|
109
|
+
|
110
|
+
profile_xml.add_attribute("hostname", run_data.platform.target.nil? ? "" : run_data.platform.target.to_s)
|
111
|
+
# Author of the schema specified 8601, then went on to add
|
112
|
+
# a regex that requires no TZ
|
113
|
+
profile_xml.add_attribute("timestamp", Time.now.iso8601.slice(0, 19))
|
114
|
+
|
115
|
+
# These are empty but are just here to satisfy the schema
|
116
|
+
profile_xml.add_attribute("package", "")
|
117
|
+
profile_xml.add(REXML::Element.new("properties"))
|
118
|
+
|
119
|
+
profile_time = 0.0
|
120
|
+
profile.controls.each do |control|
|
121
|
+
control.results.each do |result|
|
122
|
+
profile_time += result.run_time
|
123
|
+
profile_xml.add(build_result_xml(profile.name, control, result))
|
124
|
+
end
|
62
125
|
end
|
126
|
+
profile_xml.add_attribute("time", "%.6f" % profile_time)
|
127
|
+
|
128
|
+
profile_xml.add(REXML::Element.new("system-out"))
|
129
|
+
profile_xml.add(REXML::Element.new("system-err"))
|
130
|
+
|
131
|
+
profile_xml
|
63
132
|
end
|
64
133
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
134
|
+
def build_result_xml(profile_name, control, result)
|
135
|
+
result_xml = REXML::Element.new("testcase")
|
136
|
+
result_xml.add_attribute("name", result.code_desc)
|
137
|
+
result_xml.add_attribute("classname", control.title.nil? ? "#{profile_name}.Anonymous" : "#{profile_name}.#{control.id}")
|
138
|
+
|
139
|
+
# <Nokogiri::XML::SyntaxError: 20:0: ERROR: Element 'testcase', attribute 'time': '4.9e-05' is not a valid value of the atomic type 'xs:decimal'.
|
140
|
+
# So, we format it.
|
141
|
+
result_xml.add_attribute("time", "%.6f" % result.run_time)
|
142
|
+
|
143
|
+
if result.status == "failed"
|
144
|
+
failure_element = REXML::Element.new("failure")
|
145
|
+
failure_element.add_attribute("message", result.message)
|
146
|
+
failure_element.add_attribute("type", result.resource_title&.to_s || "")
|
147
|
+
result_xml.add(failure_element)
|
148
|
+
elsif result.status == "skipped"
|
149
|
+
result_xml.add_element("skipped")
|
70
150
|
end
|
151
|
+
|
152
|
+
result_xml
|
71
153
|
end
|
72
154
|
end
|
73
155
|
end
|
@@ -50,22 +50,6 @@ module CorePluginFunctionalHelper
|
|
50
50
|
include CorePluginBaseHelper
|
51
51
|
include FunctionalHelper
|
52
52
|
|
53
|
-
# TODO: so much duplication! Remove everything we can!
|
54
|
-
require "train"
|
55
|
-
TRAIN_CONNECTION = Train.create("local", command_runner: :generic).connection
|
56
|
-
|
57
|
-
# TODO: remove me! it's in test/functional/helper.rb
|
58
|
-
def run_inspec_process(command_line, opts = {})
|
59
|
-
prefix = ""
|
60
|
-
if opts.key?(:prefix)
|
61
|
-
prefix = opts[:prefix]
|
62
|
-
elsif opts.key?(:env)
|
63
|
-
prefix = assemble_env_prefix opts[:env]
|
64
|
-
end
|
65
|
-
|
66
|
-
TRAIN_CONNECTION.run_command("#{prefix} #{exec_inspec} #{command_line}")
|
67
|
-
end
|
68
|
-
|
69
53
|
# This helper does some fancy footwork to make InSpec think a plugin
|
70
54
|
# under development is temporarily installed.
|
71
55
|
# @param String command_line Invocation, without the word 'inspec'
|
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.24.8
|
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-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef-telemetry
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
version: 0.2.13
|
34
34
|
- - "<"
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: '
|
36
|
+
version: '3.0'
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: 0.2.13
|
44
44
|
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
46
|
+
version: '3.0'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: thor
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,26 +64,6 @@ dependencies:
|
|
64
64
|
- - "<"
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: '2.0'
|
67
|
-
- !ruby/object:Gem::Dependency
|
68
|
-
name: json_schemer
|
69
|
-
requirement: !ruby/object:Gem::Requirement
|
70
|
-
requirements:
|
71
|
-
- - ">="
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
version: 0.2.1
|
74
|
-
- - "<"
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: 0.2.12
|
77
|
-
type: :runtime
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - ">="
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: 0.2.1
|
84
|
-
- - "<"
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
version: 0.2.12
|
87
67
|
- !ruby/object:Gem::Dependency
|
88
68
|
name: method_source
|
89
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,14 +110,14 @@ dependencies:
|
|
130
110
|
requirements:
|
131
111
|
- - "~>"
|
132
112
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
113
|
+
version: 3.9.0
|
134
114
|
type: :runtime
|
135
115
|
prerelease: false
|
136
116
|
version_requirements: !ruby/object:Gem::Requirement
|
137
117
|
requirements:
|
138
118
|
- - "~>"
|
139
119
|
- !ruby/object:Gem::Version
|
140
|
-
version:
|
120
|
+
version: 3.9.0
|
141
121
|
- !ruby/object:Gem::Dependency
|
142
122
|
name: rspec-its
|
143
123
|
requirement: !ruby/object:Gem::Requirement
|
@@ -229,6 +209,9 @@ dependencies:
|
|
229
209
|
- - ">="
|
230
210
|
- !ruby/object:Gem::Version
|
231
211
|
version: 0.9.0
|
212
|
+
- - "<"
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '1.1'
|
232
215
|
type: :runtime
|
233
216
|
prerelease: false
|
234
217
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -236,6 +219,9 @@ dependencies:
|
|
236
219
|
- - ">="
|
237
220
|
- !ruby/object:Gem::Version
|
238
221
|
version: 0.9.0
|
222
|
+
- - "<"
|
223
|
+
- !ruby/object:Gem::Version
|
224
|
+
version: '1.1'
|
239
225
|
- !ruby/object:Gem::Dependency
|
240
226
|
name: tty-table
|
241
227
|
requirement: !ruby/object:Gem::Requirement
|
@@ -638,6 +624,7 @@ files:
|
|
638
624
|
- lib/inspec/utils/object_traversal.rb
|
639
625
|
- lib/inspec/utils/parser.rb
|
640
626
|
- lib/inspec/utils/pkey_reader.rb
|
627
|
+
- lib/inspec/utils/run_data_filters.rb
|
641
628
|
- lib/inspec/utils/simpleconfig.rb
|
642
629
|
- lib/inspec/utils/spdx.rb
|
643
630
|
- lib/inspec/utils/spdx.txt
|