inspec-core 5.17.4 → 5.21.29
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +20 -17
- data/etc/deprecations.json +4 -0
- data/inspec-core.gemspec +23 -23
- data/lib/inspec/base_cli.rb +7 -0
- data/lib/inspec/cli.rb +68 -11
- data/lib/inspec/dependencies/dependency_set.rb +6 -2
- data/lib/inspec/dsl.rb +24 -5
- data/lib/inspec/enhanced_outcomes.rb +19 -0
- data/lib/inspec/env_printer.rb +1 -1
- data/lib/inspec/errors.rb +2 -0
- data/lib/inspec/exceptions.rb +4 -0
- data/lib/inspec/fetcher/url.rb +1 -1
- data/lib/inspec/file_provider.rb +36 -0
- data/lib/inspec/formatters/base.rb +69 -16
- data/lib/inspec/iaf_file.rb +127 -0
- data/lib/inspec/plugin/v2/loader.rb +19 -8
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +1 -0
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +54 -0
- data/lib/inspec/profile.rb +17 -7
- data/lib/inspec/reporters/base.rb +1 -0
- data/lib/inspec/reporters/cli.rb +94 -3
- data/lib/inspec/reporters/json.rb +3 -1
- data/lib/inspec/reporters/yaml.rb +3 -1
- data/lib/inspec/reporters.rb +2 -1
- data/lib/inspec/resources/aide_conf.rb +4 -0
- data/lib/inspec/resources/apache.rb +4 -0
- data/lib/inspec/resources/apache_conf.rb +4 -0
- data/lib/inspec/resources/apt.rb +6 -1
- data/lib/inspec/resources/audit_policy.rb +5 -0
- data/lib/inspec/resources/auditd_conf.rb +4 -0
- data/lib/inspec/resources/bash.rb +4 -0
- data/lib/inspec/resources/bond.rb +4 -0
- data/lib/inspec/resources/bridge.rb +4 -0
- data/lib/inspec/resources/cassandradb_conf.rb +5 -0
- data/lib/inspec/resources/cassandradb_session.rb +8 -3
- data/lib/inspec/resources/chocolatey_package.rb +4 -0
- data/lib/inspec/resources/chrony_conf.rb +4 -0
- data/lib/inspec/resources/command.rb +5 -0
- data/lib/inspec/resources/cpan.rb +4 -0
- data/lib/inspec/resources/cran.rb +4 -0
- data/lib/inspec/resources/cron.rb +5 -0
- data/lib/inspec/resources/csv.rb +6 -1
- data/lib/inspec/resources/dh_params.rb +4 -0
- data/lib/inspec/resources/docker_container.rb +4 -0
- data/lib/inspec/resources/docker_image.rb +4 -0
- data/lib/inspec/resources/docker_plugin.rb +4 -0
- data/lib/inspec/resources/docker_service.rb +4 -0
- data/lib/inspec/resources/etc_group.rb +4 -0
- data/lib/inspec/resources/etc_hosts_allow_deny.rb +5 -0
- data/lib/inspec/resources/file.rb +7 -2
- data/lib/inspec/resources/filesystem.rb +4 -0
- data/lib/inspec/resources/gem.rb +4 -0
- data/lib/inspec/resources/groups.rb +4 -0
- data/lib/inspec/resources/grub_conf.rb +4 -0
- data/lib/inspec/resources/host.rb +4 -0
- data/lib/inspec/resources/http.rb +6 -2
- data/lib/inspec/resources/ibmdb2_conf.rb +8 -0
- data/lib/inspec/resources/ibmdb2_session.rb +12 -3
- data/lib/inspec/resources/iis_app.rb +4 -0
- data/lib/inspec/resources/iis_app_pool.rb +4 -0
- data/lib/inspec/resources/iis_site.rb +4 -0
- data/lib/inspec/resources/inetd_conf.rb +4 -0
- data/lib/inspec/resources/interface.rb +4 -0
- data/lib/inspec/resources/ip6tables.rb +4 -0
- data/lib/inspec/resources/ipfilter.rb +4 -0
- data/lib/inspec/resources/ipnat.rb +4 -0
- data/lib/inspec/resources/iptables.rb +4 -0
- data/lib/inspec/resources/json.rb +4 -0
- data/lib/inspec/resources/kernel_module.rb +4 -0
- data/lib/inspec/resources/kernel_parameter.rb +4 -0
- data/lib/inspec/resources/key_rsa.rb +4 -0
- data/lib/inspec/resources/ksh.rb +4 -0
- data/lib/inspec/resources/limits_conf.rb +4 -0
- data/lib/inspec/resources/login_defs.rb +4 -0
- data/lib/inspec/resources/lxc.rb +65 -9
- data/lib/inspec/resources/mongodb.rb +4 -0
- data/lib/inspec/resources/mongodb_conf.rb +5 -0
- data/lib/inspec/resources/mongodb_session.rb +6 -1
- data/lib/inspec/resources/mount.rb +4 -0
- data/lib/inspec/resources/mssql_session.rb +4 -0
- data/lib/inspec/resources/mssql_sys_conf.rb +7 -0
- data/lib/inspec/resources/mysql_conf.rb +4 -0
- data/lib/inspec/resources/mysql_session.rb +8 -1
- data/lib/inspec/resources/nginx.rb +6 -1
- data/lib/inspec/resources/nginx_conf.rb +4 -0
- data/lib/inspec/resources/noop.rb +4 -0
- data/lib/inspec/resources/npm.rb +4 -0
- data/lib/inspec/resources/ntp_conf.rb +4 -0
- data/lib/inspec/resources/oneget.rb +4 -0
- data/lib/inspec/resources/opa_api.rb +10 -0
- data/lib/inspec/resources/opa_cli.rb +14 -0
- data/lib/inspec/resources/oracledb_conf.rb +5 -0
- data/lib/inspec/resources/oracledb_listener_conf.rb +4 -0
- data/lib/inspec/resources/oracledb_session.rb +23 -4
- data/lib/inspec/resources/os.rb +4 -0
- data/lib/inspec/resources/os_env.rb +4 -0
- data/lib/inspec/resources/package.rb +4 -0
- data/lib/inspec/resources/parse_config.rb +10 -1
- data/lib/inspec/resources/pip.rb +4 -0
- data/lib/inspec/resources/platform.rb +4 -0
- data/lib/inspec/resources/podman.rb +353 -0
- data/lib/inspec/resources/podman_container.rb +84 -0
- data/lib/inspec/resources/podman_image.rb +108 -0
- data/lib/inspec/resources/podman_network.rb +81 -0
- data/lib/inspec/resources/podman_pod.rb +101 -0
- data/lib/inspec/resources/podman_volume.rb +87 -0
- data/lib/inspec/resources/postfix_conf.rb +4 -0
- data/lib/inspec/resources/postgres_conf.rb +4 -0
- data/lib/inspec/resources/postgres_session.rb +8 -4
- data/lib/inspec/resources/powershell.rb +4 -0
- data/lib/inspec/resources/processes.rb +6 -4
- data/lib/inspec/resources/rabbitmq_config.rb +4 -0
- data/lib/inspec/resources/registry_key.rb +4 -0
- data/lib/inspec/resources/security_identifier.rb +4 -0
- data/lib/inspec/resources/security_policy.rb +4 -0
- data/lib/inspec/resources/service.rb +5 -1
- data/lib/inspec/resources/ssh_config.rb +4 -0
- data/lib/inspec/resources/sybase_conf.rb +4 -0
- data/lib/inspec/resources/sybase_session.rb +4 -0
- data/lib/inspec/resources/sys_info.rb +4 -0
- data/lib/inspec/resources/timezone.rb +4 -0
- data/lib/inspec/resources/users.rb +4 -0
- data/lib/inspec/resources/vbscript.rb +5 -0
- data/lib/inspec/resources/virtualization.rb +4 -0
- data/lib/inspec/resources/windows_feature.rb +5 -1
- data/lib/inspec/resources/windows_firewall.rb +4 -0
- data/lib/inspec/resources/windows_firewall_rule.rb +4 -0
- data/lib/inspec/resources/windows_hotfix.rb +4 -0
- data/lib/inspec/resources/windows_task.rb +4 -0
- data/lib/inspec/resources/wmi.rb +4 -0
- data/lib/inspec/resources/x509_certificate.rb +59 -0
- data/lib/inspec/resources/yum.rb +4 -0
- data/lib/inspec/resources/zfs_dataset.rb +4 -0
- data/lib/inspec/resources/zfs_pool.rb +4 -0
- data/lib/inspec/rule.rb +55 -18
- data/lib/inspec/run_data/control.rb +6 -0
- data/lib/inspec/run_data/statistics.rb +8 -2
- data/lib/inspec/runner.rb +18 -8
- data/lib/inspec/runner_rspec.rb +3 -2
- data/lib/inspec/schema/exec_json.rb +78 -2
- data/lib/inspec/schema/output_schema.rb +4 -1
- data/lib/inspec/schema/profile_json.rb +46 -0
- data/lib/inspec/schema.rb +91 -0
- data/lib/inspec/secrets/yaml.rb +7 -1
- data/lib/inspec/ui.rb +1 -0
- data/lib/inspec/utils/convert.rb +8 -0
- data/lib/inspec/utils/podman.rb +24 -0
- data/lib/inspec/utils/waivers/csv_file_reader.rb +34 -0
- data/lib/inspec/utils/waivers/excel_file_reader.rb +39 -0
- data/lib/inspec/utils/waivers/json_file_reader.rb +15 -0
- data/lib/inspec/utils/yaml_profile_summary.rb +34 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec/waiver_file_reader.rb +61 -0
- data/lib/matchers/matchers.rb +7 -1
- data/lib/plugins/inspec-init/templates/profiles/alicloud/README.md +27 -0
- data/lib/plugins/inspec-init/templates/profiles/alicloud/controls/example.rb +10 -0
- data/lib/plugins/inspec-init/templates/profiles/alicloud/inputs.yml +1 -0
- data/lib/plugins/inspec-init/templates/profiles/alicloud/inspec.yml +14 -0
- data/lib/plugins/inspec-reporter-html2/README.md +1 -1
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +11 -5
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +11 -7
- data/lib/plugins/inspec-reporter-html2/templates/default.css +12 -0
- data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -1
- data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +7 -1
- data/lib/plugins/{inspec-artifact/inspec-artifact.gemspec → inspec-sign/inspec-sign.gemspec} +2 -2
- data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +164 -0
- data/lib/plugins/{inspec-artifact/lib/inspec-artifact → inspec-sign/lib/inspec-sign}/cli.rb +14 -23
- data/lib/plugins/inspec-sign/lib/inspec-sign.rb +12 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +39 -13
- data/lib/source_readers/inspec.rb +8 -2
- metadata +33 -15
- data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +0 -187
- data/lib/plugins/inspec-artifact/lib/inspec-artifact.rb +0 -12
data/lib/inspec/rule.rb
CHANGED
@@ -8,13 +8,15 @@ require "inspec/impact"
|
|
8
8
|
require "inspec/resource"
|
9
9
|
require "inspec/resources/os"
|
10
10
|
require "inspec/input_registry"
|
11
|
+
require "inspec/waiver_file_reader"
|
12
|
+
require "inspec/utils/convert"
|
11
13
|
|
12
14
|
module Inspec
|
13
15
|
class Rule
|
14
16
|
include ::RSpec::Matchers
|
15
17
|
|
16
18
|
attr_reader :__waiver_data
|
17
|
-
attr_accessor :resource_dsl
|
19
|
+
attr_accessor :resource_dsl, :na_impact_freeze
|
18
20
|
attr_reader :__profile_id
|
19
21
|
|
20
22
|
def initialize(id, profile_id, resource_dsl, opts, &block)
|
@@ -38,6 +40,7 @@ module Inspec
|
|
38
40
|
@__merge_count = 0
|
39
41
|
@__merge_changes = []
|
40
42
|
@__skip_only_if_eval = opts[:skip_only_if_eval]
|
43
|
+
@__na_rule = {}
|
41
44
|
|
42
45
|
# evaluate the given definition
|
43
46
|
return unless block_given?
|
@@ -73,10 +76,13 @@ module Inspec
|
|
73
76
|
end
|
74
77
|
|
75
78
|
def impact(v = nil)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
79
|
+
# N/A impact freeze is required when only_applicable_if block has reset impact value to zero"
|
80
|
+
unless na_impact_freeze
|
81
|
+
if v.is_a?(String)
|
82
|
+
@impact = Inspec::Impact.impact_from_string(v)
|
83
|
+
elsif !v.nil?
|
84
|
+
@impact = v
|
85
|
+
end
|
80
86
|
end
|
81
87
|
|
82
88
|
@impact
|
@@ -133,15 +139,28 @@ module Inspec
|
|
133
139
|
#
|
134
140
|
# @param [Type] &block returns true if tests are added, false otherwise
|
135
141
|
# @return [nil]
|
136
|
-
def only_if(message = nil)
|
142
|
+
def only_if(message = nil, impact: nil)
|
137
143
|
return unless block_given?
|
138
144
|
return if @__skip_only_if_eval == true
|
139
145
|
|
146
|
+
self.impact(impact) if impact && !yield
|
140
147
|
@__skip_rule[:result] ||= !yield
|
141
148
|
@__skip_rule[:type] = :only_if
|
142
149
|
@__skip_rule[:message] = message
|
143
150
|
end
|
144
151
|
|
152
|
+
def only_applicable_if(message = nil)
|
153
|
+
return unless block_given?
|
154
|
+
return if yield
|
155
|
+
|
156
|
+
impact(0.0)
|
157
|
+
self.na_impact_freeze = true # this flag prevents impact value to reset to any other value
|
158
|
+
|
159
|
+
@__na_rule[:result] ||= !yield
|
160
|
+
@__na_rule[:type] = :only_applicable_if
|
161
|
+
@__na_rule[:message] = message
|
162
|
+
end
|
163
|
+
|
145
164
|
# Describe will add one or more tests to this control. There is 2 ways
|
146
165
|
# of calling it:
|
147
166
|
#
|
@@ -252,6 +271,10 @@ module Inspec
|
|
252
271
|
rule.instance_variable_get(:@__skip_rule)
|
253
272
|
end
|
254
273
|
|
274
|
+
def self.na_status(rule)
|
275
|
+
rule.instance_variable_get(:@__na_rule)
|
276
|
+
end
|
277
|
+
|
255
278
|
def self.set_skip_rule(rule, value, message = nil, type = :only_if)
|
256
279
|
rule.instance_variable_set(:@__skip_rule,
|
257
280
|
{
|
@@ -273,16 +296,26 @@ module Inspec
|
|
273
296
|
# creates a dummay array of "checks" with a skip outcome
|
274
297
|
def self.prepare_checks(rule)
|
275
298
|
skip_check = skip_status(rule)
|
276
|
-
|
299
|
+
na_check = na_status(rule)
|
300
|
+
return checks(rule) unless skip_check[:result].eql?(true) || na_check[:result].eql?(true)
|
277
301
|
|
278
|
-
|
279
|
-
|
302
|
+
resource = rule.noop
|
303
|
+
if skip_check[:result].eql?(true)
|
304
|
+
if skip_check[:message]
|
305
|
+
msg = "Skipped control due to #{skip_check[:type]} condition: #{skip_check[:message]}"
|
306
|
+
else
|
307
|
+
msg = "Skipped control due to #{skip_check[:type]} condition."
|
308
|
+
end
|
309
|
+
resource.skip_resource(msg)
|
280
310
|
else
|
281
|
-
|
311
|
+
if na_check[:message]
|
312
|
+
msg = "N/A control due to #{na_check[:type]} condition: #{na_check[:message]}"
|
313
|
+
else
|
314
|
+
msg = "N/A control due to #{na_check[:type]} condition."
|
315
|
+
end
|
316
|
+
resource.fail_resource(msg)
|
282
317
|
end
|
283
318
|
|
284
|
-
resource = rule.noop
|
285
|
-
resource.skip_resource(msg)
|
286
319
|
[["describe", [resource], nil]]
|
287
320
|
end
|
288
321
|
|
@@ -337,17 +370,20 @@ module Inspec
|
|
337
370
|
# only_if mechanism)
|
338
371
|
# Double underscore: not intended to be called as part of the DSL
|
339
372
|
def __apply_waivers
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
373
|
+
control_id = @__rule_id # TODO: control ID slugging
|
374
|
+
waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options)
|
375
|
+
|
376
|
+
waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files) unless waiver_files.nil?
|
377
|
+
|
378
|
+
return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
|
344
379
|
|
345
380
|
# An InSpec Input is a datastructure that tracks a profile parameter
|
346
381
|
# over time. Its value can be set by many sources, and it keeps a
|
347
382
|
# log of each "set" event so that when it is collapsed to a value,
|
348
383
|
# it can determine the correct (highest priority) value.
|
349
384
|
# Store in an instance variable for.. later reading???
|
350
|
-
@__waiver_data =
|
385
|
+
@__waiver_data = waiver_data_by_profile[control_id]
|
386
|
+
|
351
387
|
__waiver_data["skipped_due_to_waiver"] = false
|
352
388
|
__waiver_data["message"] = ""
|
353
389
|
|
@@ -358,7 +394,7 @@ module Inspec
|
|
358
394
|
# YAML will automagically give us a Date or a Time.
|
359
395
|
# If transcoding YAML between languages (e.g. Go) the date might have also ended up as a String.
|
360
396
|
# A string that does not represent a valid time results in the date 0000-01-01.
|
361
|
-
if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.
|
397
|
+
if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.parse(expiry).year != 0)
|
362
398
|
expiry = expiry.to_time if expiry.is_a? Date
|
363
399
|
expiry = Time.parse(expiry) if expiry.is_a? String
|
364
400
|
if expiry < Time.now # If the waiver expired, return - no skip applied
|
@@ -376,6 +412,7 @@ module Inspec
|
|
376
412
|
# expiration_date. We only care here if it has a "run" key and it
|
377
413
|
# is false-like, since all non-skipped waiver operations are handled
|
378
414
|
# during reporting phase.
|
415
|
+
__waiver_data["run"] = Converter.to_boolean(__waiver_data["run"]) if __waiver_data.key?("run")
|
379
416
|
return unless __waiver_data.key?("run") && !__waiver_data["run"]
|
380
417
|
|
381
418
|
# OK, apply a skip.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "inspec/enhanced_outcomes"
|
2
|
+
|
1
3
|
module Inspec
|
2
4
|
class RunData
|
3
5
|
Control = Struct.new(
|
@@ -31,6 +33,10 @@ module Inspec
|
|
31
33
|
].each do |field|
|
32
34
|
self[field] = raw_ctl_data[field]
|
33
35
|
end
|
36
|
+
|
37
|
+
def status
|
38
|
+
Inspec::EnhancedOutcomes.determine_status(results, impact)
|
39
|
+
end
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
@@ -16,14 +16,20 @@ module Inspec
|
|
16
16
|
:total,
|
17
17
|
:passed,
|
18
18
|
:skipped,
|
19
|
-
:failed
|
19
|
+
:failed,
|
20
|
+
:not_reviewed,
|
21
|
+
:not_applicable,
|
22
|
+
:error
|
20
23
|
) do
|
21
24
|
include HashLikeStruct
|
22
25
|
def initialize(raw_stat_ctl_data)
|
23
26
|
self.total = raw_stat_ctl_data[:total]
|
24
27
|
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
28
|
self.failed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:failed][:total])
|
29
|
+
self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total]) if raw_stat_ctl_data[:skipped]
|
30
|
+
self.not_reviewed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_reviewed][:total]) if raw_stat_ctl_data[:not_reviewed]
|
31
|
+
self.not_applicable = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_applicable][:total]) if raw_stat_ctl_data[:not_applicable]
|
32
|
+
self.error = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:error][:total]) if raw_stat_ctl_data[:error]
|
27
33
|
end
|
28
34
|
end
|
29
35
|
class Controls
|
data/lib/inspec/runner.rb
CHANGED
@@ -60,9 +60,11 @@ module Inspec
|
|
60
60
|
end
|
61
61
|
|
62
62
|
if @conf[:waiver_file]
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
@conf[:waiver_file].each do |file|
|
64
|
+
unless File.file?(file)
|
65
|
+
raise Inspec::Exceptions::WaiversFileDoesNotExist, "Waiver file #{file} does not exist."
|
66
|
+
end
|
67
|
+
end
|
66
68
|
end
|
67
69
|
|
68
70
|
# About reading inputs:
|
@@ -133,12 +135,20 @@ module Inspec
|
|
133
135
|
all_controls.each do |rule|
|
134
136
|
unless rule.nil?
|
135
137
|
register_rule(rule)
|
136
|
-
|
137
|
-
|
138
|
+
total_checks = 0
|
139
|
+
control_describe_checks = ::Inspec::Rule.prepare_checks(rule)
|
140
|
+
|
141
|
+
examples = control_describe_checks.flat_map do |m, a, b|
|
142
|
+
get_check_example(m, a, b)
|
143
|
+
end.compact
|
144
|
+
|
145
|
+
examples.map { |example| total_checks += example.examples.count }
|
146
|
+
|
147
|
+
unless control_describe_checks.empty?
|
138
148
|
# controls with empty tests are avoided
|
139
149
|
# checks represent tests within control
|
140
|
-
controls_count += 1
|
141
|
-
control_checks_count_map[rule.to_s] =
|
150
|
+
controls_count += 1 if control_checks_count_map[rule.to_s].nil?
|
151
|
+
control_checks_count_map[rule.to_s] = control_checks_count_map[rule.to_s].to_i + total_checks
|
142
152
|
end
|
143
153
|
end
|
144
154
|
end
|
@@ -158,7 +168,7 @@ module Inspec
|
|
158
168
|
return if @conf["reporter"].nil?
|
159
169
|
|
160
170
|
@conf["reporter"].each do |reporter|
|
161
|
-
result = Inspec::Reporters.render(reporter, run_data)
|
171
|
+
result = Inspec::Reporters.render(reporter, run_data, @conf["enhanced_outcomes"])
|
162
172
|
raise Inspec::ReporterError, "Error generating reporter '#{reporter[0]}'" if result == false
|
163
173
|
end
|
164
174
|
end
|
data/lib/inspec/runner_rspec.rb
CHANGED
@@ -107,11 +107,11 @@ module Inspec
|
|
107
107
|
stats = @formatter.results[:statistics][:controls]
|
108
108
|
load_failures = @formatter.results[:profiles]&.select { |p| p[:status] == "failed" }&.any?
|
109
109
|
skipped = @formatter.results.dig(:profiles, 0, :status) == "skipped"
|
110
|
-
if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped && !load_failures
|
110
|
+
if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped && !load_failures && (stats[:error] && stats[:error][:total] == 0) # placed error count condition because of enhanced outcomes
|
111
111
|
0
|
112
112
|
elsif load_failures
|
113
113
|
@conf["distinct_exit"] ? 102 : 1
|
114
|
-
elsif stats[:failed][:total] > 0
|
114
|
+
elsif stats[:failed][:total] > 0 || (stats[:error] && stats[:error][:total] > 0)
|
115
115
|
@conf["distinct_exit"] ? 100 : 1
|
116
116
|
elsif stats[:skipped][:total] > 0 || skipped
|
117
117
|
@conf["distinct_exit"] ? 101 : 0
|
@@ -196,6 +196,7 @@ module Inspec
|
|
196
196
|
def configure_output
|
197
197
|
RSpec.configuration.output_stream = $stdout
|
198
198
|
@formatter = RSpec.configuration.add_formatter(Inspec::Formatters::Base)
|
199
|
+
@formatter.enhanced_outcomes = @conf.final_options["enhanced_outcomes"]
|
199
200
|
RSpec.configuration.add_formatter(Inspec::Formatters::ShowProgress, $stderr) if @conf[:show_progress]
|
200
201
|
set_optional_formatters
|
201
202
|
RSpec.configuration.color = @conf["color"]
|
@@ -19,8 +19,8 @@ module Inspec
|
|
19
19
|
# Lists the potential values for a control result
|
20
20
|
CONTROL_RESULT_STATUS = Primitives::SchemaType.new("Control Result Status", {
|
21
21
|
"type" => "string",
|
22
|
-
"enum" => %w{passed failed skipped
|
23
|
-
}, [], "The status of a control. Should be one of 'passed', 'failed',
|
22
|
+
"enum" => %w{passed failed skipped},
|
23
|
+
}, [], "The status of a control. Should be one of 'passed', 'failed', or 'skipped'.")
|
24
24
|
|
25
25
|
# Represents the statistics/result of a control"s execution
|
26
26
|
CONTROL_RESULT = Primitives::SchemaType.new("Control Result", {
|
@@ -75,6 +75,36 @@ module Inspec
|
|
75
75
|
},
|
76
76
|
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.")
|
77
77
|
|
78
|
+
# Represents a control produced with enhanced outcomes option
|
79
|
+
ENHANCED_OUTCOME_CONTROL = Primitives::SchemaType.new("Exec JSON Control", {
|
80
|
+
"type" => "object",
|
81
|
+
"additionalProperties" => true,
|
82
|
+
"required" => %w{id title desc impact refs tags code source_location results},
|
83
|
+
"properties" => {
|
84
|
+
"id" => Primitives.desc(Primitives::STRING, "The id."),
|
85
|
+
"title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."), # Nullable string
|
86
|
+
"desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."),
|
87
|
+
"descriptions" => Primitives.desc(Primitives.array(CONTROL_DESCRIPTION.ref), "A set of additional descriptions. Example: the 'fix' text."),
|
88
|
+
"impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."),
|
89
|
+
"status" => {
|
90
|
+
"enum" => %w{passed failed not_applicable not_reviewed error},
|
91
|
+
"description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"),
|
92
|
+
},
|
93
|
+
"refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."),
|
94
|
+
"tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."),
|
95
|
+
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code."),
|
96
|
+
"source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."),
|
97
|
+
"results" => Primitives.desc(Primitives.array(CONTROL_RESULT.ref), %q(
|
98
|
+
The set of all tests within the control and their results and findings. Example:
|
99
|
+
For Chef Inspec, if in the control's code we had the following:
|
100
|
+
describe sshd_config do
|
101
|
+
its('Port') { should cmp 22 }
|
102
|
+
end
|
103
|
+
The findings from this block would be appended to the results, as well as those of any other blocks within the control.
|
104
|
+
)),
|
105
|
+
},
|
106
|
+
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.")
|
107
|
+
|
78
108
|
# Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019
|
79
109
|
# However, concessions were made to the reality of current reporters, specifically
|
80
110
|
# with how description is omitted and version/inspec_version aren't as advertised online
|
@@ -112,6 +142,40 @@ module Inspec
|
|
112
142
|
},
|
113
143
|
}, [CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT], "Information on the set of controls assessed. Example: it can include the name of the Inspec profile and any findings.")
|
114
144
|
|
145
|
+
ENHANCED_OUTCOME_PROFILE = Primitives::SchemaType.new("Exec JSON Profile", {
|
146
|
+
"type" => "object",
|
147
|
+
"additionalProperties" => true,
|
148
|
+
"required" => %w{name sha256 supports attributes groups controls},
|
149
|
+
# Name is mandatory in inspec.yml.
|
150
|
+
# supports, controls, groups, and attributes are always present, even if empty
|
151
|
+
# sha256, status, status_message
|
152
|
+
"properties" => {
|
153
|
+
# These are provided in inspec.yml
|
154
|
+
"name" => Primitives.desc(Primitives::STRING, "The name - must be unique."),
|
155
|
+
"title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."),
|
156
|
+
"maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."),
|
157
|
+
"copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."),
|
158
|
+
"copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contact information of the copyright holder(s)."),
|
159
|
+
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."),
|
160
|
+
"parent_profile" => Primitives.desc(Primitives::STRING, "The name of the parent profile if the profile is a dependency of another."),
|
161
|
+
"license" => Primitives.desc(Primitives::STRING, "The copyright license. Example: the full text or the name, such as 'Apache License, Version 2.0'."),
|
162
|
+
"summary" => Primitives.desc(Primitives::STRING, "The summary. Example: the Security Technical Implementation Guide (STIG) header."),
|
163
|
+
"version" => Primitives.desc(Primitives::STRING, "The version of the profile."),
|
164
|
+
"supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."),
|
165
|
+
"description" => Primitives.desc(Primitives::STRING, "The description - should be more detailed than the summary."),
|
166
|
+
"inspec_version" => Primitives.desc(Primitives::STRING, "The version of Inspec."),
|
167
|
+
|
168
|
+
# These are generated at runtime, and all except status_message and skip_message are guaranteed
|
169
|
+
"sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
|
170
|
+
"status" => Primitives.desc(Primitives::STRING, "The status. Example: loaded."), # enum? loaded, failed, skipped
|
171
|
+
"status_message" => Primitives.desc(Primitives::STRING, "The reason for the status. Example: why it was skipped or failed to load."),
|
172
|
+
"skip_message" => Primitives.desc(Primitives::STRING, "The reason for skipping if it was skipped."), # Deprecated field - status_message should be used instead.
|
173
|
+
"controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls including any findings."),
|
174
|
+
"groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."),
|
175
|
+
"attributes" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used in the run."),
|
176
|
+
},
|
177
|
+
}, [ENHANCED_OUTCOME_CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT], "Information on the set of controls assessed. Example: it can include the name of the Inspec profile and any findings.")
|
178
|
+
|
115
179
|
# Result of exec json. Top level value
|
116
180
|
# TODO: Include the format of top level controls. This was omitted for lack of sufficient examples
|
117
181
|
OUTPUT = Primitives::SchemaType.new("Exec JSON Output", {
|
@@ -125,6 +189,18 @@ module Inspec
|
|
125
189
|
"version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."),
|
126
190
|
},
|
127
191
|
}, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.")
|
192
|
+
|
193
|
+
ENHANCED_OUTCOME_OUTPUT = Primitives::SchemaType.new("Exec JSON Output", {
|
194
|
+
"type" => "object",
|
195
|
+
"additionalProperties" => true,
|
196
|
+
"required" => %w{platform profiles statistics version},
|
197
|
+
"properties" => {
|
198
|
+
"platform" => Primitives.desc(Primitives::PLATFORM.ref, "Information on the platform the run from the tool that generated the findings was from. Example: the name of the operating system."),
|
199
|
+
"profiles" => Primitives.desc(Primitives.array(PROFILE.ref), "Information on the run(s) from the tool that generated the findings. Example: the findings."),
|
200
|
+
"statistics" => Primitives.desc(Primitives::STATISTICS.ref, "Statistics for the run(s) from the tool that generated the findings. Example: the runtime duration."),
|
201
|
+
"version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."),
|
202
|
+
},
|
203
|
+
}, [Primitives::PLATFORM, ENHANCED_OUTCOME_PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.")
|
128
204
|
end
|
129
205
|
end
|
130
206
|
end
|
@@ -30,6 +30,8 @@ module Inspec
|
|
30
30
|
"profile-json" => OutputSchema.finalize(Schema::ProfileJson::PROFILE),
|
31
31
|
"exec-json" => OutputSchema.finalize(Schema::ExecJson::OUTPUT),
|
32
32
|
"exec-jsonmin" => OutputSchema.finalize(Schema::ExecJsonMin::OUTPUT),
|
33
|
+
"profile-json-enhanced-outcomes" => OutputSchema.finalize(Schema::ProfileJson::ENHANCED_OUTCOME_PROFILE),
|
34
|
+
"exec-json-enhanced-outcomes" => OutputSchema.finalize(Schema::ExecJson::ENHANCED_OUTCOME_OUTPUT),
|
33
35
|
"platforms" => PLATFORMS,
|
34
36
|
}.freeze
|
35
37
|
|
@@ -37,7 +39,8 @@ module Inspec
|
|
37
39
|
LIST.keys
|
38
40
|
end
|
39
41
|
|
40
|
-
def self.json(name)
|
42
|
+
def self.json(name, opts)
|
43
|
+
name += "-enhanced-outcomes" if opts["enhanced_outcomes"]
|
41
44
|
if !LIST.key?(name)
|
42
45
|
raise("Cannot find schema #{name.inspect}.")
|
43
46
|
elsif LIST[name].is_a?(Proc)
|
@@ -31,6 +31,28 @@ module Inspec
|
|
31
31
|
},
|
32
32
|
}, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.")
|
33
33
|
|
34
|
+
# Represents a control with enhanced outcomes status information
|
35
|
+
ENHANCED_OUTCOME_CONTROL = Primitives::SchemaType.new("Profile JSON Control", {
|
36
|
+
"type" => "object",
|
37
|
+
"additionalProperties" => true,
|
38
|
+
"required" => %w{id title desc impact tags code},
|
39
|
+
"properties" => {
|
40
|
+
"id" => Primitives.desc(Primitives::STRING, "The id."),
|
41
|
+
"title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."),
|
42
|
+
"desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."),
|
43
|
+
"descriptions" => Primitives.desc(CONTROL_DESCRIPTIONS.ref, "A set of additional descriptions. Example: the 'fix' text."),
|
44
|
+
"impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."),
|
45
|
+
"status" => {
|
46
|
+
"enum" => %w{passed failed not_applicable not_reviewed error},
|
47
|
+
"description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"),
|
48
|
+
},
|
49
|
+
"refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."),
|
50
|
+
"tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."),
|
51
|
+
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code."),
|
52
|
+
"source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."),
|
53
|
+
},
|
54
|
+
}, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.")
|
55
|
+
|
34
56
|
# A profile that has not been run.
|
35
57
|
PROFILE = Primitives::SchemaType.new("Profile JSON Profile", {
|
36
58
|
"type" => "object",
|
@@ -55,6 +77,30 @@ module Inspec
|
|
55
77
|
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."), # Can have depends, but NOT a parentprofile
|
56
78
|
},
|
57
79
|
}, [Primitives::SUPPORT, CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.")
|
80
|
+
|
81
|
+
ENHANCED_OUTCOME_PROFILE = Primitives::SchemaType.new("Profile JSON Profile", {
|
82
|
+
"type" => "object",
|
83
|
+
"additionalProperties" => true, # Anything in the yaml will be put in here. LTTODO: Make this stricter!
|
84
|
+
"required" => %w{name supports controls groups sha256},
|
85
|
+
"properties" => {
|
86
|
+
"name" => Primitives.desc(Primitives::STRING, "The name - must be unique."),
|
87
|
+
"supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."),
|
88
|
+
"controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls - contains no findings as the assessment has not yet occurred."),
|
89
|
+
"groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."),
|
90
|
+
"inputs" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used to be in the run."),
|
91
|
+
"sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
|
92
|
+
"status" => Primitives.desc(Primitives::STRING, "The status. Example: skipped."),
|
93
|
+
"generator" => Primitives.desc(Primitives::GENERATOR.ref, "The tool that generated this file. Example: Chef Inspec."),
|
94
|
+
"version" => Primitives.desc(Primitives::STRING, "The version of the profile."),
|
95
|
+
|
96
|
+
# Other properties possible in inspec docs, but that aren"t guaranteed
|
97
|
+
"title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."),
|
98
|
+
"maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."),
|
99
|
+
"copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."),
|
100
|
+
"copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contract information of the copyright holder(s)."),
|
101
|
+
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."), # Can have depends, but NOT a parentprofile
|
102
|
+
},
|
103
|
+
}, [Primitives::SUPPORT, ENHANCED_OUTCOME_CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.")
|
58
104
|
end
|
59
105
|
end
|
60
106
|
end
|
data/lib/inspec/schema.rb
CHANGED
@@ -111,6 +111,43 @@ module Inspec
|
|
111
111
|
},
|
112
112
|
}.freeze
|
113
113
|
|
114
|
+
CONTROL_ENHANCED_OUTCOME = {
|
115
|
+
"type" => "object",
|
116
|
+
"additionalProperties" => false,
|
117
|
+
"properties" => {
|
118
|
+
"id" => { "type" => "string" },
|
119
|
+
"title" => { "type" => %w{string null} },
|
120
|
+
"desc" => { "type" => %w{string null} },
|
121
|
+
"descriptions" => { "type" => %w{array} },
|
122
|
+
"impact" => { "type" => "number" },
|
123
|
+
"status" => {
|
124
|
+
"enum" => %w{passed failed not_applicable not_reviewed error},
|
125
|
+
"description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"),
|
126
|
+
},
|
127
|
+
"refs" => REFS,
|
128
|
+
"tags" => TAGS,
|
129
|
+
"code" => { "type" => "string" },
|
130
|
+
"source_location" => {
|
131
|
+
"type" => "object",
|
132
|
+
"properties" => {
|
133
|
+
"ref" => { "type" => "string" },
|
134
|
+
"line" => { "type" => "number" },
|
135
|
+
},
|
136
|
+
},
|
137
|
+
"results" => { "type" => "array", "items" => RESULT },
|
138
|
+
"waiver_data" => {
|
139
|
+
"type" => "object",
|
140
|
+
"properties" => {
|
141
|
+
"skipped_due_to_waiver" => { "type" => "string" },
|
142
|
+
"run" => { "type" => "boolean" },
|
143
|
+
"message" => { "type" => "string" },
|
144
|
+
"expiration_date" => { "type" => "string" },
|
145
|
+
"justification" => { "type" => "string" },
|
146
|
+
},
|
147
|
+
},
|
148
|
+
},
|
149
|
+
}.freeze
|
150
|
+
|
114
151
|
SUPPORTS = {
|
115
152
|
"type" => "object",
|
116
153
|
"additionalProperties" => false,
|
@@ -173,6 +210,45 @@ module Inspec
|
|
173
210
|
},
|
174
211
|
}.freeze
|
175
212
|
|
213
|
+
PROFILE_ENHANCED_OUTCOME = {
|
214
|
+
"type" => "object",
|
215
|
+
"additionalProperties" => false,
|
216
|
+
"properties" => {
|
217
|
+
"name" => { "type" => "string" },
|
218
|
+
"version" => { "type" => "string", "optional" => true },
|
219
|
+
"sha256" => { "type" => "string", "optional" => false },
|
220
|
+
|
221
|
+
"title" => { "type" => "string", "optional" => true },
|
222
|
+
"maintainer" => { "type" => "string", "optional" => true },
|
223
|
+
"copyright" => { "type" => "string", "optional" => true },
|
224
|
+
"copyright_email" => { "type" => "string", "optional" => true },
|
225
|
+
"license" => { "type" => "string", "optional" => true },
|
226
|
+
"summary" => { "type" => "string", "optional" => true },
|
227
|
+
"status" => { "type" => "string", "optional" => false },
|
228
|
+
"status_message" => { "type" => "string", "optional" => true },
|
229
|
+
# skip_message is deprecated, status_message should be used to store the reason for skipping
|
230
|
+
"skip_message" => { "type" => "string", "optional" => true },
|
231
|
+
|
232
|
+
"supports" => {
|
233
|
+
"type" => "array",
|
234
|
+
"items" => SUPPORTS,
|
235
|
+
"optional" => true,
|
236
|
+
},
|
237
|
+
"controls" => {
|
238
|
+
"type" => "array",
|
239
|
+
"items" => CONTROL_ENHANCED_OUTCOME,
|
240
|
+
},
|
241
|
+
"groups" => {
|
242
|
+
"type" => "array",
|
243
|
+
"items" => CONTROL_GROUP,
|
244
|
+
},
|
245
|
+
"attributes" => { # TODO: rename to inputs, refs #3802
|
246
|
+
"type" => "array",
|
247
|
+
# TODO: more detailed specification needed
|
248
|
+
},
|
249
|
+
},
|
250
|
+
}.freeze
|
251
|
+
|
176
252
|
EXEC_JSON = {
|
177
253
|
"type" => "object",
|
178
254
|
"additionalProperties" => false,
|
@@ -187,6 +263,20 @@ module Inspec
|
|
187
263
|
},
|
188
264
|
}.freeze
|
189
265
|
|
266
|
+
EXEC_JSON_ENHANCED_OUTCOME = {
|
267
|
+
"type" => "object",
|
268
|
+
"additionalProperties" => false,
|
269
|
+
"properties" => {
|
270
|
+
"platform" => PLATFORM,
|
271
|
+
"profiles" => {
|
272
|
+
"type" => "array",
|
273
|
+
"items" => PROFILE_ENHANCED_OUTCOME,
|
274
|
+
},
|
275
|
+
"statistics" => STATISTICS,
|
276
|
+
"version" => { "type" => "string" },
|
277
|
+
},
|
278
|
+
}.freeze
|
279
|
+
|
190
280
|
MIN_CONTROL = {
|
191
281
|
"type" => "object",
|
192
282
|
"additionalProperties" => false,
|
@@ -228,6 +318,7 @@ module Inspec
|
|
228
318
|
LIST = {
|
229
319
|
"exec-json" => EXEC_JSON,
|
230
320
|
"exec-jsonmin" => EXEC_JSONMIN,
|
321
|
+
"exec-json-enhanced-outcome" => EXEC_JSON_ENHANCED_OUTCOME,
|
231
322
|
"platforms" => PLATFORMS,
|
232
323
|
}.freeze
|
233
324
|
|
data/lib/inspec/secrets/yaml.rb
CHANGED
@@ -16,7 +16,13 @@ module Secrets
|
|
16
16
|
|
17
17
|
# array of yaml file paths
|
18
18
|
def initialize(target)
|
19
|
-
|
19
|
+
# Ruby 3.1 treats YAML load as a dangerous operation by default, requiring us to declare date and time classes as permitted
|
20
|
+
# It's not a valid option in 3.0.x
|
21
|
+
if Gem.ruby_version >= Gem::Version.new("3.1.0")
|
22
|
+
@inputs = ::YAML.load_file(target, permitted_classes: [Date, Time])
|
23
|
+
else
|
24
|
+
@inputs = ::YAML.load_file(target)
|
25
|
+
end
|
20
26
|
|
21
27
|
if @inputs == false || !@inputs.is_a?(Hash)
|
22
28
|
Inspec::Log.warn("#{self.class} unable to parse #{target}: invalid YAML or contents is not a Hash")
|
data/lib/inspec/ui.rb
CHANGED
data/lib/inspec/utils/convert.rb
CHANGED
@@ -5,4 +5,12 @@ module Converter
|
|
5
5
|
val = val.to_i if val =~ /^\d+$/
|
6
6
|
val
|
7
7
|
end
|
8
|
+
|
9
|
+
def self.to_boolean(value)
|
10
|
+
if ["true", "True", "TRUE", true, "yes", "y", "YES", "Y"].include? value
|
11
|
+
true
|
12
|
+
elsif ["false", "False", "FALSE", false, "no", "n", "NO", "N"].include? value
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
8
16
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "inspec/resources/command"
|
2
|
+
|
3
|
+
module Inspec
|
4
|
+
module Utils
|
5
|
+
module Podman
|
6
|
+
def podman_running?
|
7
|
+
inspec.command("podman version").exit_status == 0
|
8
|
+
end
|
9
|
+
|
10
|
+
# Generates the template in this format using labels hash: "\"id\": {{json .ID}}, \"name\": {{json .Name}}",
|
11
|
+
def generate_go_template(labels)
|
12
|
+
(labels.map { |k, v| "\"#{k}\": {{json .#{v}}}" }).join(", ")
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_command_output(output)
|
16
|
+
require "json" unless defined?(JSON)
|
17
|
+
JSON.parse(output)
|
18
|
+
rescue JSON::ParserError => _e
|
19
|
+
warn "Could not parse the command output"
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|