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.
- checksums.yaml +4 -4
- data/lib/inspec.rb +0 -1
- data/lib/inspec/backend.rb +7 -0
- data/lib/inspec/base_cli.rb +2 -0
- data/lib/inspec/cli.rb +3 -10
- data/lib/inspec/config.rb +3 -4
- data/lib/inspec/control_eval_context.rb +5 -3
- data/lib/inspec/dsl.rb +24 -1
- data/lib/inspec/errors.rb +0 -26
- data/lib/inspec/file_provider.rb +33 -43
- data/lib/inspec/formatters/base.rb +1 -0
- data/lib/inspec/impact.rb +2 -0
- data/lib/inspec/input.rb +410 -0
- data/lib/inspec/input_registry.rb +10 -1
- data/lib/inspec/objects.rb +3 -1
- data/lib/inspec/objects/input.rb +5 -387
- data/lib/inspec/objects/tag.rb +1 -1
- data/lib/inspec/plugin/v1/plugin_types/resource.rb +16 -5
- data/lib/inspec/plugin/v2/activator.rb +4 -8
- data/lib/inspec/plugin/v2/loader.rb +19 -3
- data/lib/inspec/profile.rb +1 -1
- data/lib/inspec/profile_context.rb +1 -1
- data/lib/inspec/reporters/json.rb +70 -88
- data/lib/inspec/resource.rb +1 -0
- data/lib/inspec/resources.rb +9 -2
- data/lib/inspec/resources/aide_conf.rb +4 -0
- data/lib/inspec/resources/apt.rb +19 -19
- data/lib/inspec/resources/etc_fstab.rb +4 -0
- data/lib/inspec/resources/etc_hosts.rb +4 -0
- data/lib/inspec/resources/firewalld.rb +4 -0
- data/lib/inspec/resources/json.rb +10 -3
- data/lib/inspec/resources/mssql_session.rb +1 -1
- data/lib/inspec/resources/platform.rb +18 -13
- data/lib/inspec/resources/postfix_conf.rb +6 -2
- data/lib/inspec/resources/security_identifier.rb +4 -0
- data/lib/inspec/resources/sys_info.rb +65 -4
- data/lib/inspec/resources/user.rb +1 -0
- data/lib/inspec/rule.rb +68 -6
- data/lib/inspec/runner.rb +6 -1
- data/lib/inspec/runner_rspec.rb +1 -0
- data/lib/inspec/shell.rb +8 -1
- data/lib/inspec/utils/pkey_reader.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +2 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/help_test.rb +23 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/helper.rb +62 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/install_test.rb +368 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/list_test.rb +101 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/search_test.rb +129 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/uninstall_test.rb +63 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/update_test.rb +84 -0
- metadata +11 -3
- 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/
|
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, :[]
|
data/lib/inspec/objects.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Inspec
|
2
|
-
|
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
|
data/lib/inspec/objects/input.rb
CHANGED
@@ -1,307 +1,14 @@
|
|
1
|
-
require "inspec/
|
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
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
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
|