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
@@ -1,6 +1,6 @@
1
1
  require "forwardable"
2
2
  require "singleton"
3
- require "inspec/objects/input"
3
+ require "inspec/input"
4
4
  require "inspec/secrets"
5
5
  require "inspec/exceptions"
6
6
  require "inspec/plugin/v2"
@@ -13,6 +13,15 @@ module Inspec
13
13
  include Singleton
14
14
  extend Forwardable
15
15
 
16
+ class Error < Inspec::Error; end
17
+ class ProfileLookupError < Error
18
+ attr_accessor :profile_name
19
+ end
20
+ class InputLookupError < Error
21
+ attr_accessor :profile_name
22
+ attr_accessor :input_name
23
+ end
24
+
16
25
  attr_reader :inputs_by_profile, :profile_aliases, :plugins
17
26
  def_delegator :inputs_by_profile, :each
18
27
  def_delegator :inputs_by_profile, :[]
@@ -1,5 +1,5 @@
1
1
  module Inspec
2
- autoload :Input, "inspec/objects/input"
2
+ # TODO: these should be namespaced in Objects
3
3
  autoload :Tag, "inspec/objects/tag"
4
4
  autoload :Control, "inspec/objects/control"
5
5
  autoload :Describe, "inspec/objects/describe"
@@ -10,3 +10,5 @@ module Inspec
10
10
  autoload :Test, "inspec/objects/test"
11
11
  autoload :Value, "inspec/objects/value"
12
12
  end
13
+
14
+ require "inspec/objects/input" # already defined so you can't autoload
@@ -1,307 +1,14 @@
1
- require "inspec/utils/deprecation"
1
+ require "inspec/input"
2
2
 
3
- # For backwards compatibility during the rename (see #3802),
4
- # maintain the Inspec::Attribute namespace for people checking for
5
- # Inspec::Attribute::DEFAULT_ATTRIBUTE
6
3
  module Inspec
7
- class Attribute
8
- # This only exists to create the Inspec::Attribute::DEFAULT_ATTRIBUTE symbol with a class
9
- class DEFAULT_ATTRIBUTE; end # rubocop: disable Naming/ClassAndModuleCamelCase
10
- end
11
- end
12
-
13
- module Inspec
14
- class Input
15
- #===========================================================================#
16
- # Class Input::Event
17
- #===========================================================================#
18
-
19
- # Information about how the input obtained its value.
20
- # Each time it changes, an Input::Event is added to the #events array.
21
- class Event
22
- EVENT_PROPERTIES = [
23
- :action, # :create, :set, :fetch
24
- :provider, # Name of the plugin
25
- :priority, # Priority of this plugin for resolving conflicts. 1-100, higher numbers win.
26
- :value, # New value, if provided.
27
- :file, # File containing the input-changing action, if known
28
- :line, # Line in file containing the input-changing action, if known
29
- :hit, # if action is :fetch, true if the remote source had the input
30
- ].freeze
31
-
32
- # Value has a special handler
33
- EVENT_PROPERTIES.reject { |p| p == :value }.each do |prop|
34
- attr_accessor prop
35
- end
36
-
37
- attr_reader :value
38
-
39
- def initialize(properties = {})
40
- @value_has_been_set = false
41
-
42
- properties.each do |prop_name, prop_value|
43
- if EVENT_PROPERTIES.include? prop_name
44
- # OK, save the property
45
- send((prop_name.to_s + "=").to_sym, prop_value)
46
- else
47
- raise "Unrecognized property to Input::Event: #{prop_name}"
48
- end
49
- end
50
- end
51
-
52
- def value=(the_val)
53
- # Even if set to nil or false, it has indeed been set; note that fact.
54
- @value_has_been_set = true
55
- @value = the_val
56
- end
57
-
58
- def value_has_been_set?
59
- @value_has_been_set
60
- end
61
-
62
- def diagnostic_string
63
- to_h.reject { |_, val| val.nil? }.to_a.map { |pair| "#{pair[0]}: '#{pair[1]}'" }.join(", ")
64
- end
65
-
66
- def to_h
67
- EVENT_PROPERTIES.each_with_object({}) do |prop, hash|
68
- hash[prop] = send(prop)
69
- end
70
- end
71
-
72
- def self.probe_stack
73
- frames = caller_locations(2, 40)
74
- frames.reject! { |f| f.path && f.path.include?("/lib/inspec/") }
75
- frames.first
76
- end
77
- end
78
-
79
- #===========================================================================#
80
- # Class NO_VALUE_SET
81
- #===========================================================================#
82
- # This special class is used to represent the value when an input has
83
- # not been assigned a value. This allows a user to explicitly assign nil
84
- # to an input.
85
- class NO_VALUE_SET # rubocop: disable Naming/ClassAndModuleCamelCase
86
- def initialize(name)
87
- @name = name
88
-
89
- # output warn message if we are in a exec call
90
- if Inspec::BaseCLI.inspec_cli_command == :exec
91
- Inspec::Log.warn(
92
- "Input '#{@name}' does not have a value. "\
93
- "Use --input-file to provide a value for '#{@name}' or specify a "\
94
- "value with `attribute('#{@name}', value: 'somevalue', ...)`."
95
- )
96
- end
97
- end
98
-
99
- def method_missing(*_)
100
- self
101
- end
102
-
103
- def respond_to_missing?(_, _)
104
- true
105
- end
106
-
107
- def to_s
108
- "Input '#{@name}' does not have a value. Skipping test."
109
- end
110
-
111
- def is_a?(klass)
112
- if klass == Inspec::Attribute::DEFAULT_ATTRIBUTE
113
- Inspec.deprecate(:rename_attributes_to_inputs, "Don't check for `is_a?(Inspec::Attribute::DEFAULT_ATTRIBUTE)`, check for `Inspec::Input::NO_VALUE_SET")
114
- true # lie for backward compatibility
115
- else
116
- super(klass)
117
- end
118
- end
119
4
 
120
- def kind_of?(klass)
121
- if klass == Inspec::Attribute::DEFAULT_ATTRIBUTE
122
- Inspec.deprecate(:rename_attributes_to_inputs, "Don't check for `kind_of?(Inspec::Attribute::DEFAULT_ATTRIBUTE)`, check for `Inspec::Input::NO_VALUE_SET")
123
- true # lie for backward compatibility
124
- else
125
- super(klass)
126
- end
127
- end
128
- end
129
- end
5
+ # NOTE: due to namespacing, this reopens and extends the existing
6
+ # Inspec::Input. This should be under Inspec::Objects but that ship
7
+ # has sailed.
130
8
 
131
9
  class Input
132
- #===========================================================================#
133
- # Class Inspec::Input
134
- #===========================================================================#
135
-
136
- # Validation types for input values
137
- VALID_TYPES = %w{
138
- String
139
- Numeric
140
- Regexp
141
- Array
142
- Hash
143
- Boolean
144
- Any
145
- }.freeze
146
-
147
- # If you call `input` in a control file, the input will receive this priority.
148
- # You can override that with a :priority option.
149
- DEFAULT_PRIORITY_FOR_DSL_ATTRIBUTES = 20
150
-
151
- # If you somehow manage to initialize an Input outside of the DSL,
152
- # AND you don't provide an Input::Event, this is the priority you get.
153
- DEFAULT_PRIORITY_FOR_UNKNOWN_CALLER = 10
154
-
155
- # If you directly call value=, this is the priority assigned.
156
- # This is the highest priority within InSpec core; though plugins
157
- # are free to go higher.
158
- DEFAULT_PRIORITY_FOR_VALUE_SET = 60
159
10
 
160
- attr_reader :description, :events, :identifier, :name, :required, :title, :type
161
-
162
- def initialize(name, options = {})
163
- @name = name
164
- @opts = options
165
- if @opts.key?(:default)
166
- Inspec.deprecate(:attrs_value_replaces_default, "input name: '#{name}'")
167
- if @opts.key?(:value)
168
- Inspec::Log.warn "Input #{@name} created using both :default and :value options - ignoring :default"
169
- @opts.delete(:default)
170
- end
171
- end
172
-
173
- # Array of Input::Event objects. These compete with one another to determine
174
- # the value of the input when value() is called, as well as providing a
175
- # debugging record of when and how the value changed.
176
- @events = []
177
- events.push make_creation_event(options)
178
-
179
- update(options)
180
- end
181
-
182
- def set_events
183
- events.select { |e| e.action == :set }
184
- end
185
-
186
- def diagnostic_string
187
- "Input #{name}, with history:\n" +
188
- events.map(&:diagnostic_string).map { |line| " #{line}" }.join("\n")
189
- end
190
-
191
- #--------------------------------------------------------------------------#
192
- # Managing Value
193
- #--------------------------------------------------------------------------#
194
-
195
- def update(options)
196
- _update_set_metadata(options)
197
- normalize_type_restriction!
198
-
199
- # Values are set by passing events in; but we can also infer an event.
200
- if options.key?(:value) || options.key?(:default)
201
- if options.key?(:event)
202
- if options.key?(:value) || options.key?(:default)
203
- Inspec::Log.warn "Do not provide both an Event and a value as an option to attribute('#{name}') - using value from event"
204
- end
205
- else
206
- self.class.infer_event(options) # Sets options[:event]
207
- end
208
- end
209
- events << options[:event] if options.key? :event
210
-
211
- enforce_type_restriction!
212
- end
213
-
214
- # We can determine a value:
215
- # 1. By event.value (preferred)
216
- # 2. By options[:value]
217
- # 3. By options[:default] (deprecated)
218
- def self.infer_event(options)
219
- # Don't rely on this working; you really should be passing a proper Input::Event
220
- # with the context information you have.
221
- location = Input::Event.probe_stack
222
- event = Input::Event.new(
223
- action: :set,
224
- provider: options[:provider] || :unknown,
225
- priority: options[:priority] || Inspec::Input::DEFAULT_PRIORITY_FOR_UNKNOWN_CALLER,
226
- file: location.path,
227
- line: location.lineno
228
- )
229
-
230
- if options.key?(:default)
231
- Inspec.deprecate(:attrs_value_replaces_default, "attribute name: '#{name}'")
232
- if options.key?(:value)
233
- Inspec::Log.warn "Input #{@name} created using both :default and :value options - ignoring :default"
234
- options.delete(:default)
235
- else
236
- options[:value] = options.delete(:default)
237
- end
238
- end
239
- event.value = options[:value] if options.key?(:value)
240
- options[:event] = event
241
- end
242
-
243
- private
244
-
245
- def _update_set_metadata(options)
246
- # Basic metadata
247
- @title = options[:title] if options.key?(:title)
248
- @description = options[:description] if options.key?(:description)
249
- @required = options[:required] if options.key?(:required)
250
- @identifier = options[:identifier] if options.key?(:identifier) # TODO: determine if this is ever used
251
- @type = options[:type] if options.key?(:type)
252
- end
253
-
254
- def make_creation_event(options)
255
- loc = options[:location] || Event.probe_stack
256
- Input::Event.new(
257
- action: :create,
258
- provider: options[:provider],
259
- file: loc.path,
260
- line: loc.lineno
261
- )
262
- end
263
-
264
- # Determine the current winning value, but don't validate it
265
- def current_value
266
- # Examine the events to determine highest-priority value. Tie-break
267
- # by using the last one set.
268
- events_that_set_a_value = events.select(&:value_has_been_set?)
269
- winning_priority = events_that_set_a_value.map(&:priority).max
270
- winning_events = events_that_set_a_value.select { |e| e.priority == winning_priority }
271
- winning_event = winning_events.last # Last for tie-break
272
-
273
- if winning_event.nil?
274
- # No value has been set - return special no value object
275
- NO_VALUE_SET.new(name)
276
- else
277
- winning_event.value # May still be nil
278
- end
279
- end
280
-
281
- public
282
-
283
- def value=(new_value, priority = DEFAULT_PRIORITY_FOR_VALUE_SET)
284
- # Inject a new Event with the new value.
285
- location = Event.probe_stack
286
- events << Event.new(
287
- action: :set,
288
- provider: :value_setter,
289
- priority: priority,
290
- value: new_value,
291
- file: location.path,
292
- line: location.lineno
293
- )
294
- enforce_type_restriction!
295
- end
296
-
297
- def value
298
- enforce_required_validation!
299
- current_value
300
- end
301
-
302
- def has_value?
303
- !current_value.is_a? NO_VALUE_SET
304
- end
11
+ # NOTE: No initialize method or accessors for the reasons listed above
305
12
 
306
13
  #--------------------------------------------------------------------------#
307
14
  # Marshalling
@@ -334,94 +41,5 @@ module Inspec
334
41
  res.push "})"
335
42
  res.join("\n")
336
43
  end
337
-
338
- #--------------------------------------------------------------------------#
339
- # Value Type Coercion
340
- #--------------------------------------------------------------------------#
341
-
342
- def to_s
343
- "Input #{name} with #{current_value}"
344
- end
345
-
346
- #--------------------------------------------------------------------------#
347
- # Validation
348
- #--------------------------------------------------------------------------#
349
-
350
- private
351
-
352
- def enforce_required_validation!
353
- return unless required
354
- # skip if we are not doing an exec call (archive/vendor/check)
355
- return unless Inspec::BaseCLI.inspec_cli_command == :exec
356
-
357
- proposed_value = current_value
358
- if proposed_value.nil? || proposed_value.is_a?(NO_VALUE_SET)
359
- error = Inspec::Input::RequiredError.new
360
- error.input_name = name
361
- raise error, "Input '#{error.input_name}' is required and does not have a value."
362
- end
363
- end
364
-
365
- def enforce_type_restriction! # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
366
- return unless type
367
- return unless has_value?
368
-
369
- type_req = type
370
- return if type_req == "Any"
371
-
372
- proposed_value = current_value
373
-
374
- invalid_type = false
375
- if type_req == "Regexp"
376
- invalid_type = true unless valid_regexp?(proposed_value)
377
- elsif type_req == "Numeric"
378
- invalid_type = true unless valid_numeric?(proposed_value)
379
- elsif type_req == "Boolean"
380
- invalid_type = true unless [true, false].include?(proposed_value)
381
- elsif proposed_value.is_a?(Module.const_get(type_req)) == false
382
- # TODO: why is this case here?
383
- invalid_type = true
384
- end
385
-
386
- if invalid_type == true
387
- error = Inspec::Input::ValidationError.new
388
- error.input_name = @name
389
- error.input_value = proposed_value
390
- error.input_type = type_req
391
- raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to type '#{error.input_type}'."
392
- end
393
- end
394
-
395
- def normalize_type_restriction!
396
- return unless type
397
-
398
- type_req = type.capitalize
399
- abbreviations = {
400
- "Num" => "Numeric",
401
- "Regex" => "Regexp",
402
- }
403
- type_req = abbreviations[type_req] if abbreviations.key?(type_req)
404
- unless VALID_TYPES.include?(type_req)
405
- error = Inspec::Input::TypeError.new
406
- error.input_type = type_req
407
- raise error, "Type '#{error.input_type}' is not a valid input type."
408
- end
409
- @type = type_req
410
- end
411
-
412
- def valid_numeric?(value)
413
- Float(value)
414
- true
415
- rescue
416
- false
417
- end
418
-
419
- def valid_regexp?(value)
420
- # check for invalid regex syntax
421
- Regexp.new(value)
422
- true
423
- rescue
424
- false
425
- end
426
44
  end
427
45
  end
@@ -15,7 +15,7 @@ module Inspec
15
15
  end
16
16
 
17
17
  def to_ruby
18
- "tag #{key.inspect}: #{value.inspect}"
18
+ "tag #{key}: #{value.inspect}"
19
19
  end
20
20
 
21
21
  def to_s