class Inspec::Input
NOTE: due to namespacing, this reopens and extends the existing Inspec::Input
. This should be under Inspec::Objects but that ship has sailed.
Constants
- DEFAULT_PRIORITY_FOR_DSL_ATTRIBUTES
TODO: this is not used anywhere? If you call `input` in a control file, the input will receive this priority. You can override that with a :priority option.
- DEFAULT_PRIORITY_FOR_UNKNOWN_CALLER
If you somehow manage to initialize an
Input
outside of theDSL
, AND you don't provide anInput::Event
, this is the priority you get.- DEFAULT_PRIORITY_FOR_VALUE_SET
If you directly call value=, this is the priority assigned. This is the highest priority within InSpec core; though plugins are free to go higher.
- VALID_TYPES
Validation types for input values
Attributes
description[R]
events[R]
identifier[R]
name[R]
pattern[R]
required[R]
sensitive[R]
title[R]
type[R]
Public Class Methods
infer_event(options)
click to toggle source
We can determine a value:
# File lib/inspec/input.rb, line 241 def self.infer_event(options) # Don't rely on this working; you really should be passing a proper Input::Event # with the context information you have. location = Input::Event.probe_stack event = Input::Event.new( action: :set, provider: options[:provider] || :unknown, priority: options[:priority] || Inspec::Input::DEFAULT_PRIORITY_FOR_UNKNOWN_CALLER, file: location.path, line: location.lineno ) if options.key?(:default) Inspec.deprecate(:attrs_value_replaces_default, "attribute name: '#{name}'") if options.key?(:value) Inspec::Log.warn "Input #{@name} created using both :default and :value options - ignoring :default" options.delete(:default) else options[:value] = options.delete(:default) end end event.value = options[:value] if options.key?(:value) options[:event] = event end
new(name, options = {})
click to toggle source
# File lib/inspec/input.rb, line 183 def initialize(name, options = {}) @name = name @opts = options if @opts.key?(:default) Inspec.deprecate(:attrs_value_replaces_default, "input name: '#{name}'") if @opts.key?(:value) Inspec::Log.warn "Input #{@name} created using both :default and :value options - ignoring :default" @opts.delete(:default) end end # Array of Input::Event objects. These compete with one another to determine # the value of the input when value() is called, as well as providing a # debugging record of when and how the value changed. @events = [] events.push make_creation_event(options) update(options) end
Public Instance Methods
diagnostic_string()
click to toggle source
# File lib/inspec/input.rb, line 207 def diagnostic_string "Input #{name}, with history:\n" + events.map(&:diagnostic_string).map { |line| " #{line}" }.join("\n") end
has_value?()
click to toggle source
# File lib/inspec/input.rb, line 329 def has_value? !current_value(false).is_a? NO_VALUE_SET end
ruby_var_identifier()
click to toggle source
# File lib/inspec/objects/input.rb, line 32 def ruby_var_identifier identifier || "attr_" + name.downcase.strip.gsub(/\s+/, "-").gsub(/[^\w-]/, "") end
set_events()
click to toggle source
TODO: is this here just for testing?
# File lib/inspec/input.rb, line 203 def set_events events.select { |e| e.action == :set } end
to_hash()
click to toggle source
# File lib/inspec/input.rb, line 333 def to_hash as_hash = { name: name, options: {} } %i{description title identifier type required value sensitive pattern}.each do |field| val = send(field) next if val.nil? as_hash[:options][field] = val end as_hash end
to_ruby()
click to toggle source
# File lib/inspec/objects/input.rb, line 36 def to_ruby res = ["#{ruby_var_identifier} = attribute('#{name}',{"] res.push " title: '#{title}'," unless title.to_s.empty? res.push " value: #{value.inspect}," unless value.to_s.empty? # to_ruby may generate code that is to be used by older versions of inspec. # Anything older than 3.4 will not recognize the value: option, so # send the default: option as well. See #3759 res.push " default: #{value.inspect}," unless value.to_s.empty? res.push " description: '#{description}'," unless description.to_s.empty? res.push "})" res.join("\n") end
to_s()
click to toggle source
# File lib/inspec/input.rb, line 348 def to_s "Input #{name} with value " + (sensitive ? "*** (senstive)" : "#{current_value}") end
update(options)
click to toggle source
# File lib/inspec/input.rb, line 216 def update(options) _update_set_metadata(options) normalize_type_restriction! normalize_pattern_restriction! # Values are set by passing events in; but we can also infer an event. if options.key?(:value) || options.key?(:default) if options.key?(:event) if options.key?(:value) || options.key?(:default) Inspec::Log.warn "Do not provide both an Event and a value as an option to attribute('#{name}') - using value from event" end else self.class.infer_event(options) # Sets options[:event] end end events << options[:event] if options.key? :event enforce_type_restriction! enforce_pattern_restriction! end
value()
click to toggle source
# File lib/inspec/input.rb, line 324 def value enforce_required_validation! current_value end
value=(new_value, priority = DEFAULT_PRIORITY_FOR_VALUE_SET)
click to toggle source
# File lib/inspec/input.rb, line 308 def value=(new_value, priority = DEFAULT_PRIORITY_FOR_VALUE_SET) # Inject a new Event with the new value. location = Event.probe_stack events << Event.new( action: :set, provider: :value_setter, priority: priority, value: new_value, file: location.path, line: location.lineno ) enforce_type_restriction! enforce_pattern_restriction! end
Private Instance Methods
_update_set_metadata(options)
click to toggle source
# File lib/inspec/input.rb, line 268 def _update_set_metadata(options) # Basic metadata @title = options[:title] if options.key?(:title) @description = options[:description] if options.key?(:description) @required = options[:required] if options.key?(:required) @identifier = options[:identifier] if options.key?(:identifier) # TODO: determine if this is ever used @type = options[:type] if options.key?(:type) @sensitive = options[:sensitive] if options.key?(:sensitive) @pattern = options[:pattern] if options.key?(:pattern) end
current_value(warn_on_missing = true)
click to toggle source
Determine the current winning value, but don't validate it
# File lib/inspec/input.rb, line 290 def current_value(warn_on_missing = true) # Examine the events to determine highest-priority value. Tie-break # by using the last one set. events_that_set_a_value = events.select(&:value_has_been_set?) winning_priority = events_that_set_a_value.map(&:priority).max winning_events = events_that_set_a_value.select { |e| e.priority == winning_priority } winning_event = winning_events.last # Last for tie-break if winning_event.nil? # No value has been set - return special no value object NO_VALUE_SET.new(name, warn_on_missing) else winning_event.value # May still be nil end end
enforce_pattern_restriction!()
click to toggle source
# File lib/inspec/input.rb, line 418 def enforce_pattern_restriction! return unless pattern return unless has_value? string_value = current_value(false).to_s valid_pattern = string_value.match?(pattern) unless valid_pattern error = Inspec::Input::ValidationError.new error.input_name = @name error.input_value = string_value error.input_pattern = pattern raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to pattern '#{error.input_pattern}'." end end
enforce_required_validation!()
click to toggle source
# File lib/inspec/input.rb, line 358 def enforce_required_validation! return unless required # skip if we are not doing an exec call (archive/vendor/check) return unless Inspec::BaseCLI.inspec_cli_command == :exec proposed_value = current_value(false) if proposed_value.nil? || proposed_value.is_a?(NO_VALUE_SET) error = Inspec::Input::RequiredError.new error.input_name = name raise error, "Input '#{error.input_name}' is required and does not have a value." end end
enforce_type_restriction!()
click to toggle source
# File lib/inspec/input.rb, line 371 def enforce_type_restriction! # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity return unless type return unless has_value? type_req = type return if type_req == "Any" proposed_value = current_value(false) invalid_type = false if type_req == "Regexp" invalid_type = true unless valid_regexp?(proposed_value) elsif type_req == "Numeric" invalid_type = true unless valid_numeric?(proposed_value) elsif type_req == "Boolean" invalid_type = true unless [true, false].include?(proposed_value) elsif proposed_value.is_a?(Module.const_get(type_req)) == false # TODO: why is this case here? invalid_type = true end if invalid_type == true error = Inspec::Input::ValidationError.new error.input_name = @name error.input_value = proposed_value error.input_type = type_req raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to type '#{error.input_type}'." end end
make_creation_event(options)
click to toggle source
# File lib/inspec/input.rb, line 279 def make_creation_event(options) loc = options[:location] || Event.probe_stack Input::Event.new( action: :create, provider: options[:provider], file: loc.path, line: loc.lineno ) end
normalize_pattern_restriction!()
click to toggle source
# File lib/inspec/input.rb, line 434 def normalize_pattern_restriction! return unless pattern unless valid_regexp?(pattern) error = Inspec::Input::PatternError.new error.input_pattern = pattern raise error, "Pattern '#{error.input_pattern}' is not a valid regex pattern." end @pattern = pattern end
normalize_type_restriction!()
click to toggle source
# File lib/inspec/input.rb, line 401 def normalize_type_restriction! return unless type type_req = type.capitalize abbreviations = { "Num" => "Numeric", "Regex" => "Regexp", } type_req = abbreviations[type_req] if abbreviations.key?(type_req) unless VALID_TYPES.include?(type_req) error = Inspec::Input::TypeError.new error.input_type = type_req raise error, "Type '#{error.input_type}' is not a valid input type." end @type = type_req end
valid_numeric?(value)
click to toggle source
# File lib/inspec/input.rb, line 445 def valid_numeric?(value) Float(value) true rescue false end
valid_regexp?(value)
click to toggle source
# File lib/inspec/input.rb, line 452 def valid_regexp?(value) # check for invalid regex syntax Regexp.new(value) true rescue false end