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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/inspec-core.gemspec +2 -2
  4. data/lib/inspec/base_cli.rb +4 -0
  5. data/lib/inspec/cli.rb +7 -17
  6. data/lib/inspec/config.rb +28 -6
  7. data/lib/inspec/fetcher/git.rb +1 -1
  8. data/lib/inspec/fetcher/local.rb +1 -1
  9. data/lib/inspec/fetcher/url.rb +1 -1
  10. data/lib/inspec/input.rb +9 -9
  11. data/lib/inspec/plugin/v2/installer.rb +23 -2
  12. data/lib/inspec/plugin/v2/plugin_types/reporter.rb +68 -0
  13. data/lib/inspec/profile.rb +16 -4
  14. data/lib/inspec/reporters.rb +4 -1
  15. data/lib/inspec/reporters/base.rb +22 -0
  16. data/lib/inspec/resources/service.rb +2 -2
  17. data/lib/inspec/resources/virtualization.rb +86 -2
  18. data/lib/inspec/resources/x509_certificate.rb +1 -1
  19. data/lib/inspec/rule.rb +8 -4
  20. data/lib/inspec/run_data.rb +64 -0
  21. data/lib/inspec/run_data/control.rb +83 -0
  22. data/lib/inspec/run_data/profile.rb +109 -0
  23. data/lib/inspec/run_data/result.rb +40 -0
  24. data/lib/inspec/run_data/statistics.rb +36 -0
  25. data/lib/inspec/shell.rb +8 -3
  26. data/lib/inspec/utils/json_profile_summary.rb +35 -0
  27. data/lib/inspec/version.rb +1 -1
  28. data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +18 -1
  29. data/lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb +4 -0
  30. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +28 -6
  31. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.rb +18 -0
  32. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/reporter.rb +27 -0
  33. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +11 -8
  34. 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
- collect_data_linux
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
- # TODO: where is vmware?
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
@@ -59,7 +59,7 @@ module Inspec::Resources
59
59
  def fingerprint
60
60
  return if @cert.nil?
61
61
 
62
- OpenSSL::Digest::SHA1.new(@cert.to_der).to_s
62
+ OpenSSL::Digest.new("SHA1", @cert.to_der).to_s
63
63
  end
64
64
 
65
65
  def serial
@@ -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
- if expiry.is_a?(Date)
357
- # It appears that yaml.rb automagically parses dates for us
358
- if expiry < Date.today # If the waiver expired, return - no skip applied
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