inspec 4.16.0 → 4.17.7

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/lib/inspec.rb +0 -1
  3. data/lib/inspec/backend.rb +7 -0
  4. data/lib/inspec/base_cli.rb +2 -0
  5. data/lib/inspec/cli.rb +3 -10
  6. data/lib/inspec/config.rb +3 -4
  7. data/lib/inspec/control_eval_context.rb +5 -3
  8. data/lib/inspec/dsl.rb +24 -1
  9. data/lib/inspec/errors.rb +0 -26
  10. data/lib/inspec/file_provider.rb +33 -43
  11. data/lib/inspec/formatters/base.rb +1 -0
  12. data/lib/inspec/impact.rb +2 -0
  13. data/lib/inspec/input.rb +410 -0
  14. data/lib/inspec/input_registry.rb +10 -1
  15. data/lib/inspec/objects.rb +3 -1
  16. data/lib/inspec/objects/input.rb +5 -387
  17. data/lib/inspec/objects/tag.rb +1 -1
  18. data/lib/inspec/plugin/v1/plugin_types/resource.rb +16 -5
  19. data/lib/inspec/plugin/v2/activator.rb +4 -8
  20. data/lib/inspec/plugin/v2/loader.rb +19 -3
  21. data/lib/inspec/profile.rb +1 -1
  22. data/lib/inspec/profile_context.rb +1 -1
  23. data/lib/inspec/reporters/json.rb +70 -88
  24. data/lib/inspec/resource.rb +1 -0
  25. data/lib/inspec/resources.rb +9 -2
  26. data/lib/inspec/resources/aide_conf.rb +4 -0
  27. data/lib/inspec/resources/apt.rb +19 -19
  28. data/lib/inspec/resources/etc_fstab.rb +4 -0
  29. data/lib/inspec/resources/etc_hosts.rb +4 -0
  30. data/lib/inspec/resources/firewalld.rb +4 -0
  31. data/lib/inspec/resources/json.rb +10 -3
  32. data/lib/inspec/resources/mssql_session.rb +1 -1
  33. data/lib/inspec/resources/platform.rb +18 -13
  34. data/lib/inspec/resources/postfix_conf.rb +6 -2
  35. data/lib/inspec/resources/security_identifier.rb +4 -0
  36. data/lib/inspec/resources/sys_info.rb +65 -4
  37. data/lib/inspec/resources/user.rb +1 -0
  38. data/lib/inspec/rule.rb +68 -6
  39. data/lib/inspec/runner.rb +6 -1
  40. data/lib/inspec/runner_rspec.rb +1 -0
  41. data/lib/inspec/shell.rb +8 -1
  42. data/lib/inspec/utils/pkey_reader.rb +1 -1
  43. data/lib/inspec/version.rb +1 -1
  44. data/lib/matchers/matchers.rb +2 -0
  45. data/lib/plugins/inspec-plugin-manager-cli/test/functional/help_test.rb +23 -0
  46. data/lib/plugins/inspec-plugin-manager-cli/test/functional/helper.rb +62 -0
  47. data/lib/plugins/inspec-plugin-manager-cli/test/functional/install_test.rb +368 -0
  48. data/lib/plugins/inspec-plugin-manager-cli/test/functional/list_test.rb +101 -0
  49. data/lib/plugins/inspec-plugin-manager-cli/test/functional/search_test.rb +129 -0
  50. data/lib/plugins/inspec-plugin-manager-cli/test/functional/uninstall_test.rb +63 -0
  51. data/lib/plugins/inspec-plugin-manager-cli/test/functional/update_test.rb +84 -0
  52. metadata +11 -3
  53. data/lib/plugins/inspec-plugin-manager-cli/test/functional/inspec-plugin_test.rb +0 -845
@@ -57,6 +57,10 @@ module Inspec::Resources
57
57
  where { mount_point == "/home" }.entries[0].mount_options
58
58
  end
59
59
 
60
+ def to_s
61
+ "File System Table File (fstab)"
62
+ end
63
+
60
64
  private
61
65
 
62
66
  def read_content
@@ -37,6 +37,10 @@ module Inspec::Resources
37
37
  .register_column(:all_host_names, field: "all_host_names")
38
38
  .install_filter_methods_on_resource(self, :params)
39
39
 
40
+ def to_s
41
+ "Hosts File"
42
+ end
43
+
40
44
  private
41
45
 
42
46
  def default_hosts_file_path
@@ -88,6 +88,10 @@ module Inspec::Resources
88
88
  firewalld_command("--zone=#{query_zone} --query-rich-rule='#{rule}'") == "yes"
89
89
  end
90
90
 
91
+ def to_s
92
+ "Firewall Rules"
93
+ end
94
+
91
95
  private
92
96
 
93
97
  def active_zones
@@ -93,10 +93,17 @@ module Inspec::Resources
93
93
  end
94
94
 
95
95
  def load_raw_from_command(command)
96
- command_output = inspec.command(command).stdout
97
- raise Inspec::Exceptions::ResourceSkipped, "No output from command: #{command}" if command_output.nil? || command_output.empty?
96
+ result = inspec.command(command)
98
97
 
99
- command_output
98
+ return result.stdout unless result.stdout.empty?
99
+
100
+ msg = if result.stderr.empty?
101
+ "No JSON output, STDERR was empty"
102
+ else
103
+ "No JSON output, STDERR:\n #{result.stderr}"
104
+ end
105
+
106
+ raise Inspec::Exceptions::ResourceFailed, msg
100
107
  end
101
108
 
102
109
  # for resources the subclass JsonConfig, this allows specification of the resource
@@ -53,7 +53,7 @@ module Inspec::Resources
53
53
  end
54
54
 
55
55
  def query(q) # rubocop:disable Metrics/PerceivedComplexity
56
- escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"').gsub(/\$/, '\\$')
56
+ escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '""').gsub(/\$/, '\\$')
57
57
  # surpress 'x rows affected' in SQLCMD with 'set nocount on;'
58
58
  cmd_string = "sqlcmd -Q \"set nocount on; #{escaped_query}\" -W -w 1024 -s ','"
59
59
  cmd_string += " -U '#{@user}' -P '#{@password}'" unless @user.nil? || @password.nil?
@@ -1,3 +1,5 @@
1
+ require "inspec/resource"
2
+
1
3
  module Inspec::Resources
2
4
  class PlatformResource < Inspec.resource(1)
3
5
  name "platform"
@@ -67,19 +69,22 @@ module Inspec::Resources
67
69
  return true if supports.nil? || supports.empty?
68
70
 
69
71
  status = true
70
- supports.each do |s|
71
- s.each do |k, v|
72
- if %i{os_family os-family platform_family platform-family}.include?(k)
73
- status = in_family?(v)
74
- elsif %i{os platform}.include?(k)
75
- status = platform?(v)
76
- elsif %i{os_name os-name platform_name platform-name}.include?(k)
77
- status = name == v
78
- elsif k == :release
79
- status = check_release(v)
80
- else
81
- status = false
82
- end
72
+ supports.each do |support|
73
+ support.each do |k, v|
74
+ status =
75
+ case k
76
+ when :os_family, :"os-family", :platform_family, :"platform-family" then
77
+ in_family?(v)
78
+ when :os, :platform then
79
+ platform?(v)
80
+ when :os_name, :"os-name", :platform_name, :"platform-name" then
81
+ name == v
82
+ when :release then
83
+ check_release(v)
84
+ else
85
+ false
86
+ end
87
+
83
88
  break if status == false
84
89
  end
85
90
  return true if status == true
@@ -11,7 +11,7 @@ module Inspec::Resources
11
11
  def initialize(*opts)
12
12
  @params = {}
13
13
  if opts.length == 1
14
- @raw_content = load_raw_content(opts)
14
+ @raw_content = load_raw_content(opts[0])
15
15
  else
16
16
  @raw_content = load_raw_content("/etc/postfix/main.cf")
17
17
  end
@@ -22,10 +22,14 @@ module Inspec::Resources
22
22
  SimpleConfig.new(content).params
23
23
  end
24
24
 
25
+ def to_s
26
+ "Postfix Mail Transfer Agent"
27
+ end
28
+
25
29
  private
26
30
 
27
31
  def resource_base_name
28
- "POSTFIX_CONF"
32
+ "Postfix Config"
29
33
  end
30
34
  end
31
35
  end
@@ -47,6 +47,10 @@ module Inspec::Resources
47
47
  @sids.key?(@name)
48
48
  end
49
49
 
50
+ def to_s
51
+ "Security Identifier"
52
+ end
53
+
50
54
  private
51
55
 
52
56
  def fetch_sids
@@ -13,20 +13,77 @@ module Inspec::Resources
13
13
  describe sys_info do
14
14
  its('hostname') { should eq 'example.com' }
15
15
  end
16
+
17
+ describe sys_info do
18
+ its('fqdn') { should eq 'user.example.com' }
19
+ end
20
+
16
21
  EXAMPLE
17
22
 
23
+ %w{ domain fqdn ip_address short }.each do |opt|
24
+ define_method(opt.to_sym) do
25
+ hostname(opt)
26
+ end
27
+ end
28
+
18
29
  # returns the hostname of the local system
19
- def hostname
30
+ def hostname(opt = nil)
20
31
  os = inspec.os
21
- if os.linux? || os.darwin?
22
- inspec.command("hostname").stdout.chomp
32
+ if os.linux?
33
+ linux_hostname(opt)
34
+ elsif os.darwin?
35
+ mac_hostname(opt)
23
36
  elsif os.windows?
24
- inspec.powershell("$env:computername").stdout.chomp
37
+ if !opt.nil?
38
+ skip_resource "The `sys_info.hostname` resource is not supported with that option on your OS."
39
+ else
40
+ inspec.powershell("$env:computername").stdout.chomp
41
+ end
25
42
  else
26
43
  skip_resource "The `sys_info.hostname` resource is not supported on your OS yet."
27
44
  end
28
45
  end
29
46
 
47
+ def linux_hostname(opt = nil)
48
+ if !opt.nil?
49
+ opt = case opt
50
+ when "f", "long", "fqdn", "full"
51
+ " -f"
52
+ when "d", "domain"
53
+ " -d"
54
+ when "i", "ip_address"
55
+ " -I"
56
+ when "s", "short"
57
+ " -s"
58
+ else
59
+ "ERROR"
60
+ end
61
+ end
62
+ if opt == "ERROR"
63
+ skip_resource "The `sys_info.hostname` resource is not supported with that option on your OS."
64
+ else
65
+ inspec.command("hostname#{opt}").stdout.chomp
66
+ end
67
+ end
68
+
69
+ def mac_hostname(opt = nil)
70
+ if !opt.nil?
71
+ opt = case opt
72
+ when "f", "long", "fqdn", "full"
73
+ " -f"
74
+ when "s", "short"
75
+ " -s"
76
+ else
77
+ "ERROR"
78
+ end
79
+ end
80
+ if opt == "ERROR"
81
+ skip_resource "The `sys_info.hostname` resource is not supported with that option on your OS."
82
+ else
83
+ inspec.command("hostname#{opt}").stdout.chomp
84
+ end
85
+ end
86
+
30
87
  # returns the Manufacturer of the local system
31
88
  def manufacturer
32
89
  os = inspec.os
@@ -54,5 +111,9 @@ module Inspec::Resources
54
111
  skip_resource "The `sys_info.model` resource is not supported on your OS yet."
55
112
  end
56
113
  end
114
+
115
+ def to_s
116
+ "System Information"
117
+ end
57
118
  end
58
119
  end
@@ -0,0 +1 @@
1
+ require "inspec/resources/users"
@@ -1,10 +1,12 @@
1
1
  # copyright: 2015, Dominik Richter
2
2
 
3
3
  require "method_source"
4
+ require "date"
4
5
  require "inspec/describe"
5
6
  require "inspec/expect"
6
7
  require "inspec/resource"
7
8
  require "inspec/resources/os"
9
+ require "inspec/input_registry"
8
10
 
9
11
  module Inspec
10
12
  class Rule
@@ -28,6 +30,7 @@ module Inspec
28
30
  @resource_dsl
29
31
  end
30
32
 
33
+ attr_reader :__waiver_data
31
34
  def initialize(id, profile_id, opts, &block)
32
35
  @impact = nil
33
36
  @title = nil
@@ -42,7 +45,7 @@ module Inspec
42
45
  @__rule_id = id
43
46
  @__profile_id = profile_id
44
47
  @__checks = []
45
- @__skip_rule = {}
48
+ @__skip_rule = {} # { result: true, message: "Why", type: [:only_if, :waiver] }
46
49
  @__merge_count = 0
47
50
  @__merge_changes = []
48
51
  @__skip_only_if_eval = opts[:skip_only_if_eval]
@@ -52,6 +55,11 @@ module Inspec
52
55
 
53
56
  begin
54
57
  instance_eval(&block)
58
+
59
+ # By applying waivers *after* the instance eval, we assure that
60
+ # waivers have higher precedence than only_if.
61
+ __apply_waivers
62
+
55
63
  rescue StandardError => e
56
64
  # We've encountered an exception while trying to eval the code inside the
57
65
  # control block. We need to prevent the exception from bubbling up, and
@@ -141,6 +149,7 @@ module Inspec
141
149
  return if @__skip_only_if_eval == true
142
150
 
143
151
  @__skip_rule[:result] ||= !yield
152
+ @__skip_rule[:type] = :only_if
144
153
  @__skip_rule[:message] = message
145
154
  end
146
155
 
@@ -193,9 +202,9 @@ module Inspec
193
202
  rule.instance_variable_get(:@__skip_rule)
194
203
  end
195
204
 
196
- def self.set_skip_rule(rule, value, message = nil)
205
+ def self.set_skip_rule(rule, value, message = nil, type = :only_if)
197
206
  rule.instance_variable_set(:@__skip_rule,
198
- { result: value, message: message })
207
+ { result: value, message: message, type: type })
199
208
  end
200
209
 
201
210
  def self.merge_count(rule)
@@ -206,14 +215,16 @@ module Inspec
206
215
  rule.instance_variable_get(:@__merge_changes)
207
216
  end
208
217
 
218
+ # If a rule is marked to be skipped, this
219
+ # creates a dummay array of "checks" with a skip outcome
209
220
  def self.prepare_checks(rule)
210
221
  skip_check = skip_status(rule)
211
222
  return checks(rule) unless skip_check[:result].eql?(true)
212
223
 
213
224
  if skip_check[:message]
214
- msg = "Skipped control due to only_if condition: #{skip_check[:message]}"
225
+ msg = "Skipped control due to #{skip_check[:type]} condition: #{skip_check[:message]}"
215
226
  else
216
- msg = "Skipped control due to only_if condition."
227
+ msg = "Skipped control due to #{skip_check[:type]} condition."
217
228
  end
218
229
 
219
230
  # TODO: we use os as the carrier here, but should consider
@@ -251,7 +262,8 @@ module Inspec
251
262
  skip_check = skip_status(src)
252
263
  sr = skip_check[:result]
253
264
  msg = skip_check[:message]
254
- set_skip_rule(dst, sr, msg) unless sr.nil?
265
+ skip_type = skip_check[:type]
266
+ set_skip_rule(dst, sr, msg, skip_type) unless sr.nil?
255
267
 
256
268
  # Save merge history
257
269
  dst.instance_variable_set(:@__merge_count, merge_count(dst) + 1)
@@ -267,6 +279,56 @@ module Inspec
267
279
  @__checks.push([describe_or_expect, values, block])
268
280
  end
269
281
 
282
+ # Look for an input with a matching ID, and if found, apply waiver
283
+ # skipping logic. Basically, if we have a current waiver, and it says
284
+ # to skip, we'll replace all the checks with a dummy check (same as
285
+ # only_if mechanism)
286
+ # Double underscore: not intended to be called as part of the DSL
287
+ def __apply_waivers
288
+ input_name = @__rule_id # TODO: control ID slugging
289
+ registry = Inspec::InputRegistry.instance
290
+ input = registry.inputs_by_profile.dig(@__profile_id, input_name)
291
+ return unless input
292
+
293
+ # An InSpec Input is a datastructure that tracks a profile parameter
294
+ # over time. Its value can be set by many sources, and it keeps a
295
+ # log of each "set" event so that when it is collapsed to a value,
296
+ # it can determine the correct (highest priority) value.
297
+ # Store in an instance variable for.. later reading???
298
+ @__waiver_data = input.value
299
+ __waiver_data["skipped_due_to_waiver"] = false
300
+ __waiver_data["message"] = ""
301
+
302
+ # Waivers should have a hash value with keys possibly including skip and
303
+ # expiration_date. We only care here if it has a skip key and it
304
+ # is yes-like, since all non-skipped waiver operations are handled
305
+ # during reporting phase.
306
+ return unless __waiver_data.key?("skip") && __waiver_data["skip"]
307
+
308
+ # OK, the intent is to skip. Does it have an expiration date, and
309
+ # if so, is it in the future?
310
+ expiry = __waiver_data["expiration_date"]
311
+ if expiry
312
+ if expiry.is_a?(Date)
313
+ # It appears that yaml.rb automagically parses dates for us
314
+ if expiry < Date.today # If the waiver expired, return - no skip applied
315
+ __waiver_data["message"] = "Waiver expired on #{expiry}, evaluating control normally"
316
+ return
317
+ end
318
+ else
319
+ ui = Inspec::UI.new
320
+ ui.error("Unable to parse waiver expiration date '#{expiry}' for control #{@__rule_id}")
321
+ ui.exit(:usage_error)
322
+ end
323
+ end
324
+
325
+ # OK, apply a skip.
326
+ @__skip_rule[:result] = true
327
+ @__skip_rule[:type] = :waiver
328
+ @__skip_rule[:message] = __waiver_data["justification"]
329
+ __waiver_data["skipped_due_to_waiver"] = true
330
+ end
331
+
270
332
  #
271
333
  # Takes a block and returns a block that will run the given block
272
334
  # with access to the resource_dsl of the current class. This is to
@@ -9,7 +9,6 @@ require "inspec/metadata"
9
9
  require "inspec/config"
10
10
  require "inspec/dependencies/cache"
11
11
  require "inspec/dist"
12
- require "inspec/resources"
13
12
  require "inspec/reporters"
14
13
  require "inspec/runner_rspec"
15
14
  # spec requirements
@@ -57,6 +56,12 @@ module Inspec
57
56
  RunnerRspec.new(@conf)
58
57
  end
59
58
 
59
+ if @conf[:waiver_file]
60
+ waivers = @conf.delete(:waiver_file)
61
+ @conf[:input_file] ||= []
62
+ @conf[:input_file].concat waivers
63
+ end
64
+
60
65
  # About reading inputs:
61
66
  # @conf gets passed around a lot, eventually to
62
67
  # Inspec::InputRegistry.register_external_inputs.
@@ -171,6 +171,7 @@ module Inspec
171
171
  metadata[:descriptions] = rule.descriptions
172
172
  metadata[:code] = rule.instance_variable_get(:@__code)
173
173
  metadata[:source_location] = rule.instance_variable_get(:@__source_location)
174
+ metadata[:waiver_data] = rule.__waiver_data
174
175
  end
175
176
  end
176
177
  end
@@ -112,6 +112,7 @@ module Inspec
112
112
  #{print_target_info}
113
113
  EOF
114
114
  elsif topic == "resources"
115
+ require "inspec/resources"
115
116
  resources.sort.each do |resource|
116
117
  puts " - #{resource}"
117
118
  end
@@ -134,7 +135,13 @@ module Inspec
134
135
  info += "https://www.inspec.io/docs/reference/resources/#{topic}\n\n"
135
136
  puts info
136
137
  else
137
- puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources"
138
+ begin
139
+ require "inspec/resources/#{topic}"
140
+ help topic
141
+ rescue LoadError
142
+ # TODO: stderr!
143
+ puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources"
144
+ end
138
145
  end
139
146
  end
140
147
 
@@ -1,4 +1,4 @@
1
- require "inspec/objects/input"
1
+ require "inspec/input"
2
2
 
3
3
  module PkeyReader
4
4
  def read_pkey(filecontent, passphrase)
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = "4.16.0".freeze
2
+ VERSION = "4.17.7".freeze
3
3
  end