inspec-core 4.18.100 → 4.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/inspec-core.gemspec +2 -2
- data/lib/inspec/base_cli.rb +4 -0
- data/lib/inspec/cli.rb +7 -17
- data/lib/inspec/config.rb +28 -6
- data/lib/inspec/fetcher/git.rb +1 -1
- data/lib/inspec/fetcher/local.rb +1 -1
- data/lib/inspec/fetcher/url.rb +1 -1
- data/lib/inspec/input.rb +9 -9
- data/lib/inspec/plugin/v2/installer.rb +23 -2
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +68 -0
- data/lib/inspec/profile.rb +16 -4
- data/lib/inspec/reporters.rb +4 -1
- data/lib/inspec/reporters/base.rb +22 -0
- data/lib/inspec/resources/service.rb +2 -2
- data/lib/inspec/resources/virtualization.rb +86 -2
- data/lib/inspec/resources/x509_certificate.rb +1 -1
- data/lib/inspec/rule.rb +8 -4
- data/lib/inspec/run_data.rb +64 -0
- data/lib/inspec/run_data/control.rb +83 -0
- data/lib/inspec/run_data/profile.rb +109 -0
- data/lib/inspec/run_data/result.rb +40 -0
- data/lib/inspec/run_data/statistics.rb +36 -0
- data/lib/inspec/shell.rb +8 -3
- 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-compliance/lib/inspec-compliance/api.rb +4 -0
- 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
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +11 -8
- metadata +20 -6
@@ -104,6 +104,8 @@ module Inspec::Resources
|
|
104
104
|
os = inspec.os
|
105
105
|
platform = os[:name]
|
106
106
|
|
107
|
+
return WindowsSrv.new(inspec) if os.windows?
|
108
|
+
|
107
109
|
# Ubuntu
|
108
110
|
# @see: https://wiki.ubuntu.com/SystemdForUpstartUsers
|
109
111
|
# Ubuntu 15.04 : Systemd
|
@@ -154,8 +156,6 @@ module Inspec::Resources
|
|
154
156
|
SysV.new(inspec, service_ctl)
|
155
157
|
when "mac_os_x"
|
156
158
|
LaunchCtl.new(inspec, service_ctl)
|
157
|
-
when "windows"
|
158
|
-
WindowsSrv.new(inspec)
|
159
159
|
when "freebsd"
|
160
160
|
BSDInit.new(inspec, service_ctl)
|
161
161
|
when "arch"
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require "hashie/mash"
|
2
|
+
require "inspec/resources/powershell"
|
2
3
|
|
3
4
|
module Inspec::Resources
|
4
5
|
class Virtualization < Inspec.resource(1)
|
5
6
|
name "virtualization"
|
6
7
|
supports platform: "unix"
|
8
|
+
supports platform: "windows"
|
7
9
|
desc "Use the virtualization InSpec audit resource to test various virtualization platforms."
|
8
10
|
example <<~EXAMPLE
|
9
11
|
describe virtualization do
|
@@ -25,7 +27,15 @@ module Inspec::Resources
|
|
25
27
|
def initialize
|
26
28
|
# TODO: no need for hashie here... in fact, no reason for a hash at all
|
27
29
|
@virtualization_data = Hashie::Mash.new
|
28
|
-
|
30
|
+
|
31
|
+
if inspec.os.linux?
|
32
|
+
collect_data_linux
|
33
|
+
elsif inspec.os.windows?
|
34
|
+
collect_data_windows
|
35
|
+
end
|
36
|
+
|
37
|
+
# Allows checking for non-virtualized systems as well
|
38
|
+
@virtualization_data[:physical] = @virtualization_data.empty?
|
29
39
|
end
|
30
40
|
|
31
41
|
# add helper methods for easy access of properties
|
@@ -36,6 +46,14 @@ module Inspec::Resources
|
|
36
46
|
end
|
37
47
|
end
|
38
48
|
|
49
|
+
def virtual_system?
|
50
|
+
@virtualization_data[:role] == "guest"
|
51
|
+
end
|
52
|
+
|
53
|
+
def physical_system?
|
54
|
+
@virtualization_data[:physical]
|
55
|
+
end
|
56
|
+
|
39
57
|
def params
|
40
58
|
# TODO: this is broken. cannot return anything but nil
|
41
59
|
collect_data_linux
|
@@ -236,6 +254,39 @@ module Inspec::Resources
|
|
236
254
|
true
|
237
255
|
end
|
238
256
|
|
257
|
+
# Detect VMware
|
258
|
+
def detect_vmware
|
259
|
+
return false unless inspec.file("/sys/devices/virtual/dmi/id/product_name").exist?
|
260
|
+
|
261
|
+
product_name = inspec.file("/sys/devices/virtual/dmi/id/product_name").content
|
262
|
+
|
263
|
+
if product_name =~ /^VMware/
|
264
|
+
@virtualization_data[:system] = "vmware"
|
265
|
+
@virtualization_data[:role] = "guest"
|
266
|
+
else
|
267
|
+
return false
|
268
|
+
end
|
269
|
+
|
270
|
+
true
|
271
|
+
end
|
272
|
+
|
273
|
+
# Detect Hyper-V
|
274
|
+
# @see https://gallery.technet.microsoft.com/scriptcenter/Get-MachineType-VM-or-ff43f3a9
|
275
|
+
def detect_hyperv
|
276
|
+
return false unless inspec.file("/sys/devices/virtual/dmi/id/product_name").exist?
|
277
|
+
|
278
|
+
product_name = inspec.file("/sys/devices/virtual/dmi/id/product_name").content
|
279
|
+
|
280
|
+
if product_name.rstrip == "Virtual Machine"
|
281
|
+
@virtualization_data[:system] = "hyper-v"
|
282
|
+
@virtualization_data[:role] = "guest"
|
283
|
+
else
|
284
|
+
return false
|
285
|
+
end
|
286
|
+
|
287
|
+
true
|
288
|
+
end
|
289
|
+
|
239
290
|
def collect_data_linux
|
240
291
|
# This avoids doing multiple detections in a single test
|
241
292
|
return unless @virtualization_data.empty?
|
@@ -253,7 +304,40 @@ module Inspec::Resources
|
|
253
304
|
return if detect_openvz
|
254
305
|
return if detect_openstack
|
255
306
|
return if detect_parallels
|
256
|
-
|
307
|
+
return if detect_vmware
|
308
|
+
return if detect_hyperv
|
309
|
+
end
|
310
|
+
|
311
|
+
def windows_computer_system
|
312
|
+
script = "Get-WmiObject -Class Win32_ComputerSystem -ErrorAction Stop | ConvertTo-Json"
|
313
|
+
cmd = inspec.powershell(script)
|
314
|
+
|
315
|
+
JSON.parse(cmd.stdout)
|
316
|
+
rescue JSON::ParserError => _e
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
|
320
|
+
def collect_data_windows
|
321
|
+
# This avoids doing multiple detections in a single test
|
322
|
+
return unless @virtualization_data.empty?
|
323
|
+
|
324
|
+
case windows_computer_system["Model"]
|
325
|
+
when "Virtual Machine"
|
326
|
+
@virtualization_data[:system] = "hyper-v"
|
327
|
+
when /^VMware/
|
328
|
+
@virtualization_data[:system] = "vmware"
|
329
|
+
when "VirtualBox"
|
330
|
+
@virtualization_data[:system] = "virtualbox"
|
331
|
+
end
|
332
|
+
|
333
|
+
case windows_computer_system[:manufacturer]
|
334
|
+
when "Xen"
|
335
|
+
@virtualization_data[:system] = "xen"
|
336
|
+
when "QEMU"
|
337
|
+
@virtualization_data[:system] = "kvm"
|
338
|
+
end
|
339
|
+
|
340
|
+
@virtualization_data[:role] = "guest" if @virtualization_data[:system]
|
257
341
|
end
|
258
342
|
end
|
259
343
|
end
|
data/lib/inspec/rule.rb
CHANGED
@@ -332,7 +332,7 @@ module Inspec
|
|
332
332
|
input_name = @__rule_id # TODO: control ID slugging
|
333
333
|
registry = Inspec::InputRegistry.instance
|
334
334
|
input = registry.inputs_by_profile.dig(__profile_id, input_name)
|
335
|
-
return unless input
|
335
|
+
return unless input && input.has_value? && input.value.is_a?(Hash)
|
336
336
|
|
337
337
|
# An InSpec Input is a datastructure that tracks a profile parameter
|
338
338
|
# over time. Its value can be set by many sources, and it keeps a
|
@@ -353,9 +353,13 @@ module Inspec
|
|
353
353
|
# if so, is it in the future?
|
354
354
|
expiry = __waiver_data["expiration_date"]
|
355
355
|
if expiry
|
356
|
-
|
357
|
-
|
358
|
-
|
356
|
+
# YAML will automagically give us a Date or a Time.
|
357
|
+
# If transcoding YAML between languages (e.g. Go) the date might have also ended up as a String.
|
358
|
+
# A string that does not represent a valid time results in the date 0000-01-01.
|
359
|
+
if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.new(expiry).year != 0)
|
360
|
+
expiry = expiry.to_time if expiry.is_a? Date
|
361
|
+
expiry = Time.new(expiry) if expiry.is_a? String
|
362
|
+
if expiry < Time.now # If the waiver expired, return - no skip applied
|
359
363
|
__waiver_data["message"] = "Waiver expired on #{expiry}, evaluating control normally"
|
360
364
|
return
|
361
365
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
module Inspec
|
3
|
+
module HashLikeStruct
|
4
|
+
def keys
|
5
|
+
members
|
6
|
+
end
|
7
|
+
|
8
|
+
def key?(item)
|
9
|
+
members.include?(item)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RunData = Struct.new(
|
14
|
+
:controls, # Array of Inspec::RunData::Control (flattened)
|
15
|
+
:other_checks,
|
16
|
+
:profiles, # Array of Inspec::RunData::Profile
|
17
|
+
:platform, # Inspec::RunData::Platform
|
18
|
+
:statistics, # Inspec::RunData::Statistics
|
19
|
+
:version # String
|
20
|
+
) do
|
21
|
+
include HashLikeStruct
|
22
|
+
def initialize(raw_run_data)
|
23
|
+
self.controls = raw_run_data[:controls].map { |c| Inspec::RunData::Control.new(c) }
|
24
|
+
self.profiles = raw_run_data[:profiles].map { |p| Inspec::RunData::Profile.new(p) }
|
25
|
+
self.statistics = Inspec::RunData::Statistics.new(raw_run_data[:statistics])
|
26
|
+
self.platform = Inspec::RunData::Platform.new(raw_run_data[:platform])
|
27
|
+
self.version = raw_run_data[:version]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class RunData
|
32
|
+
|
33
|
+
# This is the data layout version of RunData.
|
34
|
+
# We plan to follow a data-oriented version of semver:
|
35
|
+
# patch: fixing a bug in the provenance or description of a data element, no key changes
|
36
|
+
# minor: adding new data elements
|
37
|
+
# major: deleting or renaming data elements
|
38
|
+
# Less than major version 1.0.0, the API is considered unstable.
|
39
|
+
# The current plan is to bump the major version to 1.0.0 when all of the existing
|
40
|
+
# core reporters have been migrated to plugins. It is probable that new data elements
|
41
|
+
# and new Hash compatibility behavior will be added during the core reporter plugin
|
42
|
+
# conversion process.
|
43
|
+
SCHEMA_VERSION = "0.1.0".freeze
|
44
|
+
|
45
|
+
def self.compatible_schema?(constraints)
|
46
|
+
reqs = Gem::Requirement.create(constraints)
|
47
|
+
reqs.satisfied_by?(Gem::Version.new(SCHEMA_VERSION))
|
48
|
+
end
|
49
|
+
|
50
|
+
Platform = Struct.new(
|
51
|
+
:name, :release, :target
|
52
|
+
) do
|
53
|
+
include HashLikeStruct
|
54
|
+
def initialize(raw_plat_data)
|
55
|
+
%i{name release target}.each { |f| self[f] = raw_plat_data[f] || "" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
require_relative "run_data/result"
|
62
|
+
require_relative "run_data/control"
|
63
|
+
require_relative "run_data/profile"
|
64
|
+
require_relative "run_data/statistics"
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Inspec
|
2
|
+
class RunData
|
3
|
+
Control = Struct.new(
|
4
|
+
:code, # String
|
5
|
+
:desc, # String
|
6
|
+
:descriptions, # Hash with custom keys
|
7
|
+
:id, # String
|
8
|
+
:impact, # Float
|
9
|
+
:refs, # Complex local
|
10
|
+
:results, # complex standalone
|
11
|
+
:source_location, # Complex local
|
12
|
+
:tags, # Hash with custom keys
|
13
|
+
:title, # String
|
14
|
+
:waiver_data # Complex local
|
15
|
+
) do
|
16
|
+
include HashLikeStruct
|
17
|
+
def initialize(raw_ctl_data)
|
18
|
+
self.refs = (raw_ctl_data[:refs] || []).map { |r| Inspec::RunData::Control::Ref.new(r) }
|
19
|
+
self.results = (raw_ctl_data[:results] || []).map { |r| Inspec::RunData::Result.new(r) }
|
20
|
+
self.source_location = Inspec::RunData::Control::SourceLocation.new(raw_ctl_data[:source_location] || {})
|
21
|
+
self.waiver_data = Inspec::RunData::Control::WaiverData.new(raw_ctl_data[:waiver_data] || {})
|
22
|
+
|
23
|
+
[
|
24
|
+
:code, # String
|
25
|
+
:desc, # String
|
26
|
+
:descriptions, # Hash with custom keys
|
27
|
+
:id, # String
|
28
|
+
:impact, # Float
|
29
|
+
:tags, # Hash with custom keys
|
30
|
+
:title, # String
|
31
|
+
].each do |field|
|
32
|
+
self[field] = raw_ctl_data[field]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Control
|
38
|
+
Ref = Struct.new(
|
39
|
+
:url, :ref
|
40
|
+
) do
|
41
|
+
include HashLikeStruct
|
42
|
+
def initialize(raw_ref_data)
|
43
|
+
%i{url ref}.each { |f| self[f] = raw_ref_data[f] }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
SourceLocation = Struct.new(
|
48
|
+
:line, :ref
|
49
|
+
) do
|
50
|
+
include HashLikeStruct
|
51
|
+
def initialize(raw_sl_data)
|
52
|
+
%i{line ref}.each { |f| self[f] = raw_sl_data[f] }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# {
|
57
|
+
# "expiration_date"=>#<Date: 2077-06-01 ((2479821j,0s,0n),+0s,2299161j)>,
|
58
|
+
# "justification"=>"Lack of imagination",
|
59
|
+
# "run"=>false,
|
60
|
+
# "skipped_due_to_waiver"=>true,
|
61
|
+
# "message"=>""}
|
62
|
+
WaiverData = Struct.new(
|
63
|
+
:expiration_date,
|
64
|
+
:justification,
|
65
|
+
:run,
|
66
|
+
:skipped_due_to_waiver,
|
67
|
+
:message
|
68
|
+
) do
|
69
|
+
include HashLikeStruct
|
70
|
+
def initialize(raw_wv_data)
|
71
|
+
# These have string keys in the raw data!
|
72
|
+
%i{
|
73
|
+
expiration_date
|
74
|
+
justification
|
75
|
+
run
|
76
|
+
skipped_due_to_waiver
|
77
|
+
message
|
78
|
+
}.each { |f| self[f] = raw_wv_data[f.to_s] }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Inspec
|
2
|
+
class RunData
|
3
|
+
Profile = Struct.new(
|
4
|
+
:controls, # complex standalone
|
5
|
+
:copyright,
|
6
|
+
:copyright_email,
|
7
|
+
:depends, # complex local
|
8
|
+
:groups, # complex local
|
9
|
+
:inputs, # complex local
|
10
|
+
:license,
|
11
|
+
:maintainer,
|
12
|
+
:name,
|
13
|
+
:sha256,
|
14
|
+
:status,
|
15
|
+
:summary,
|
16
|
+
:supports, # complex local
|
17
|
+
:parent_profile,
|
18
|
+
:skip_message,
|
19
|
+
:waiver_data, # Undocumented but used in JSON reporter - should not be?
|
20
|
+
:title,
|
21
|
+
:version
|
22
|
+
) do
|
23
|
+
include HashLikeStruct
|
24
|
+
def initialize(raw_prof_data)
|
25
|
+
self.controls = (raw_prof_data[:controls] || []).map { |c| Inspec::RunData::Control.new(c) }
|
26
|
+
self.depends = (raw_prof_data[:depends] || []).map { |d| Inspec::RunData::Profile::Dependency.new(d) }
|
27
|
+
self.groups = (raw_prof_data[:groups] || []).map { |g| Inspec::RunData::Profile::Group.new(g) }
|
28
|
+
self.inputs = (raw_prof_data[:inputs] || []).map { |i| Inspec::RunData::Profile::Input.new(i) }
|
29
|
+
self.supports = (raw_prof_data[:supports] || []).map { |s| Inspec::RunData::Profile::Support.new(s) }
|
30
|
+
|
31
|
+
%i{
|
32
|
+
copyright
|
33
|
+
copyright_email
|
34
|
+
license
|
35
|
+
maintainer
|
36
|
+
name
|
37
|
+
sha256
|
38
|
+
status
|
39
|
+
summary
|
40
|
+
title
|
41
|
+
version
|
42
|
+
parent_profile
|
43
|
+
skip_message
|
44
|
+
waiver_data
|
45
|
+
}.each do |field|
|
46
|
+
self[field] = raw_prof_data[field]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Profile
|
52
|
+
# Good candidate for keyword_init, but that is not in 2.4
|
53
|
+
Dependency = Struct.new(
|
54
|
+
:name, :path, :status, :skip_message, :git, :url, :compliance, :supermarket, :branch, :tag, :commit, :version, :relative_path
|
55
|
+
) do
|
56
|
+
include HashLikeStruct
|
57
|
+
def initialize(raw_dep_data)
|
58
|
+
%i{name path status skip_message git url supermarket compliance branch tag commit version relative_path}.each { |f| self[f] = raw_dep_data[f] }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Support = Struct.new(
|
63
|
+
# snake case
|
64
|
+
:platform_family, :platform_name, :release, :platform
|
65
|
+
) do
|
66
|
+
include HashLikeStruct
|
67
|
+
def initialize(raw_sup_data)
|
68
|
+
%i{release platform}.each { |f| self[f] = raw_sup_data[f] }
|
69
|
+
self.platform_family = raw_sup_data[:"platform-family"]
|
70
|
+
self.platform_name = raw_sup_data[:"platform-name"]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Good candidate for keyword_init, but that is not in 2.4
|
75
|
+
Group = Struct.new(
|
76
|
+
:title, :controls, :id
|
77
|
+
) do
|
78
|
+
include HashLikeStruct
|
79
|
+
def initialize(raw_grp_data)
|
80
|
+
%i{title id}.each { |f| self[f] = raw_grp_data[f] }
|
81
|
+
[:controls].each { |f| self[f] = raw_grp_data[f] || [] }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Input = Struct.new(
|
86
|
+
:name, :options
|
87
|
+
) do
|
88
|
+
include HashLikeStruct
|
89
|
+
def initialize(raw_input_data)
|
90
|
+
self.name = raw_input_data[:name]
|
91
|
+
self.options = Inspec::RunData::Profile::Input::Options.new(raw_input_data[:options])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
class Input
|
95
|
+
Options = Struct.new(
|
96
|
+
# There are probably others
|
97
|
+
:value,
|
98
|
+
:type,
|
99
|
+
:required
|
100
|
+
) do
|
101
|
+
include HashLikeStruct
|
102
|
+
def initialize(raw_opts_data)
|
103
|
+
%i{value type required}.each { |f| self[f] = raw_opts_data[f] }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Inspec
|
2
|
+
class RunData
|
3
|
+
Result = Struct.new(
|
4
|
+
:message, # Human-friendly test failure message
|
5
|
+
:code_desc, # Generated test description
|
6
|
+
:expectation_message, # a substring of code_desc
|
7
|
+
:resource_name, # We try to determine this
|
8
|
+
:run_time, # Float seconds execution time
|
9
|
+
:skip_message, # String
|
10
|
+
:start_time, # DateTime
|
11
|
+
:status, # String
|
12
|
+
:resource_title, # Ugly internals
|
13
|
+
# :waiver_data, # Undocumented tramp data / not exposed in this API
|
14
|
+
:resource, # Undocumented, what is this
|
15
|
+
:exception,
|
16
|
+
:backtrace
|
17
|
+
) do
|
18
|
+
include HashLikeStruct
|
19
|
+
def initialize(raw_res_data)
|
20
|
+
[
|
21
|
+
:status, # String
|
22
|
+
:code_desc, # Generated test description
|
23
|
+
:expectation_message, # a substring of code_desc
|
24
|
+
:skip_message, # String
|
25
|
+
:run_time,
|
26
|
+
:start_time,
|
27
|
+
:resource_title,
|
28
|
+
:resource,
|
29
|
+
:exception,
|
30
|
+
:backtrace,
|
31
|
+
:message,
|
32
|
+
].each do |field|
|
33
|
+
self[field] = raw_res_data[field]
|
34
|
+
end
|
35
|
+
|
36
|
+
self.resource_name = raw_res_data[:resource_title].instance_variable_get(:@__resource_name__)&.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|