inspec-core 4.1.4.preview → 4.2.0.preview

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4153e41e5d8fddfb696a73688b7d92b299aed682
4
- data.tar.gz: 9a9c379e82e175edacc705e63b299e091be4df1b
3
+ metadata.gz: 3cbd02d922c9f1c6cbe5d81b74598ac88ca10676
4
+ data.tar.gz: 31b971b6c1c65154a2952d4eb27ab6b1edb765ae
5
5
  SHA512:
6
- metadata.gz: abbbc79378e7f76da10089bea0030fe19c0ad470eba604caaa5ad909ceb8977919f9849346e558f3dceb213d290d2317ec12a282936100cb2152f412f034aac2
7
- data.tar.gz: 806520071a450b2a8c4fe097dbc580d3de4b6beed0b09f95f3ae3e4af2948ec653456ff59bdc815396149391b0a82d496f3abdaf608af1653ccd1ed82544009d
6
+ metadata.gz: 91dc0c86668e00121588268d2828521a73e315efa0480919b09d3ee7e82e2dd4a9d93e88204a47e960c96e31ed425ff32e3131a60f94590053c417c3da0a2344
7
+ data.tar.gz: 45628e2783b3ba28495fa7efe504956c21a9462355a77ef87aa164e9738858b186f187c6c27f18629083b66a5c02cf77835e9fde5cb7c200d8dc6aa8e997152f
@@ -3,12 +3,12 @@
3
3
  "unknown_group_action": "ignore",
4
4
  "groups": {
5
5
  "attrs_value_replaces_default": {
6
- "action": "ignore",
6
+ "action": "warn",
7
7
  "prefix": "The 'default' option for attributes is being replaced by 'value' - please use it instead."
8
8
  },
9
9
  "aws_resources_in_resource_pack": {
10
10
  "comment": "See #3822",
11
- "action": "ignore",
11
+ "action": "warn",
12
12
  "prefix": "AWS resources shipped with core InSpec are being to moved to a resource pack for faster iteration. Please update your profiles to depend on git@github.com:inspec/inspec-aws.git ."
13
13
  },
14
14
  "cli_option_json_config": {
@@ -17,11 +17,11 @@
17
17
  "comment": "See #3661"
18
18
  },
19
19
  "file_resource_be_mounted_matchers": {
20
- "action": "warn",
20
+ "action": "fail_control",
21
21
  "suffix": "This will not be supported in InSpec 4.0."
22
22
  },
23
23
  "host_resource_proto_usage": {
24
- "action": "warn",
24
+ "action": "fail_control",
25
25
  "suffix": "This will not be supported in InSpec 4.0."
26
26
  },
27
27
  "inspec_ui_methods": {
@@ -30,67 +30,67 @@
30
30
  "comment": "See #3715"
31
31
  },
32
32
  "mssql_session_pass_option": {
33
- "action": "warn",
33
+ "action": "exit",
34
34
  "suffix": "This will not be supported in InSpec 4.0."
35
35
  },
36
36
  "oracledb_session_pass_option": {
37
- "action": "warn",
38
- "suffix": "This will not be supported in InSpec 4.0."
37
+ "action": "exit",
38
+ "suffix": "This is not supported in InSpec 4.0."
39
39
  },
40
40
  "property_filesystem_size": {
41
- "action": "ignore",
41
+ "action": "warn",
42
42
  "comment": "See #3778"
43
43
  },
44
44
  "property_processes_list": {
45
- "action": "warn",
46
- "suffix": "This property will be removed in InSpec 4.0."
45
+ "action": "fail_control",
46
+ "suffix": "This property was removed in InSpec 4.0."
47
47
  },
48
48
  "properties_aws_iam_user": {
49
- "action": "warn",
50
- "suffix": "This property will be removed in InSpec 4.0."
49
+ "action": "fail_control",
50
+ "suffix": "This property was removed in InSpec 4.0."
51
51
  },
52
52
  "properties_shadow": {
53
- "action": "warn",
54
- "suffix": "This property will be removed in InSpec 4.0."
53
+ "action": "fail_control",
54
+ "suffix": "This property was removed in InSpec 4.0."
55
55
  },
56
56
  "rename_attributes_to_inputs": {
57
- "action": "ignore",
57
+ "action": "warn",
58
58
  "prefix": "InSpec Attributes are being renamed to InSpec Inputs to avoid confusion with Chef Attributes.",
59
59
  "comment": "See #3802"
60
60
  },
61
61
  "resource_apache": {
62
- "action": "warn",
63
- "suffix": "This resource will be removed in InSpec 4.0."
62
+ "action": "exit",
63
+ "suffix": "This resource was removed in InSpec 4.0."
64
64
  },
65
65
  "resource_azure_generic_resource": {
66
66
  "action": "warn",
67
67
  "prefix": "The azure_generic_resource is deprecated. Please use a specific resource. See: 'https://github.com/inspec/inspec/issues/3131'"
68
68
  },
69
69
  "resource_iis_website": {
70
- "action": "warn",
71
- "suffix": "This resource will be removed in InSpec 4.0.",
70
+ "action": "exit",
71
+ "suffix": "This resource was removed in InSpec 4.0.",
72
72
  "comment": "Needed for ServerSpec compatibility"
73
73
  },
74
74
  "resource_linux_kernel_parameter": {
75
- "action": "warn",
76
- "suffix": "This resource will be removed in InSpec 4.0.",
75
+ "action": "exit",
76
+ "suffix": "This resource was removed in InSpec 4.0.",
77
77
  "comment": "Needed for ServerSpec compatibility"
78
78
  },
79
79
  "resource_ppa": {
80
- "action": "warn",
81
- "suffix": "This resource will be removed in InSpec 4.0.",
80
+ "action": "exit",
81
+ "suffix": "This resource was removed in InSpec 4.0.",
82
82
  "comment": "Needed for ServerSpec compatibility"
83
83
  },
84
84
  "resource_script": {
85
- "action": "warn",
85
+ "action": "exit",
86
86
  "suffix": "This resource will be removed in InSpec 4.0"
87
87
  },
88
88
  "resource_user_serverspec_compat": {
89
- "action": "warn"
89
+ "action": "fail_control"
90
90
  },
91
91
  "resource_windows_registry_key": {
92
- "action": "warn",
93
- "suffix": "This resource will be removed in InSpec 4.0.",
92
+ "action": "exit",
93
+ "suffix": "This resource was removed in InSpec 4.0.",
94
94
  "comment": "Needed for ServerSpec compatibility"
95
95
  },
96
96
  "serverspec_compatibility": {
@@ -101,11 +101,11 @@
101
101
  "action": "warn"
102
102
  },
103
103
  "mount_parser_serverspec_compat": {
104
- "action": "warn"
104
+ "action": "fail_control"
105
105
  },
106
106
  "wmi_non_hash_usage": {
107
- "action": "warn",
108
- "suffix": "This property will be removed in InSpec 4.0."
107
+ "action": "fail_control",
108
+ "suffix": "This property was removed in InSpec 4.0."
109
109
  }
110
110
  }
111
111
  }
data/lib/inspec/cli.rb CHANGED
@@ -392,7 +392,11 @@ require 'license_acceptance/acceptor'
392
392
  begin
393
393
  if (commands_exempt_from_license_check & ARGV.map(&:downcase)).empty? && # Did they use a non-exempt command?
394
394
  !ARGV.empty? # Did they supply at least one command?
395
- LicenseAcceptance::Acceptor.check_and_persist('inspec', Inspec::VERSION)
395
+ LicenseAcceptance::Acceptor.check_and_persist(
396
+ 'inspec',
397
+ Inspec::VERSION,
398
+ logger: Inspec::Log,
399
+ )
396
400
  end
397
401
  rescue LicenseAcceptance::LicenseNotAcceptedError
398
402
  Inspec::Log.error 'InSpec cannot execute without accepting the license'
@@ -26,8 +26,23 @@ module Inspec
26
26
  with_resource_dsl resources_dsl
27
27
 
28
28
  # allow attributes to be accessed within control blocks
29
- define_method :attribute do |name|
30
- Inspec::InputRegistry.find_input(name, profile_id).value
29
+ # TODO: deprecate name, use input()
30
+ define_method :attribute do |input_name, options = {}|
31
+ if options.empty?
32
+ # Simply an access, no event here
33
+ Inspec::InputRegistry.find_or_register_input(input_name, profile_id).value
34
+ else
35
+ options[:priority] = 20
36
+ options[:provider] = :inline_control_code
37
+ evt = Inspec::Input.infer_event(options)
38
+ Inspec::InputRegistry.find_or_register_input(input_name, profile_name, event: evt).value
39
+ end
40
+ end
41
+
42
+ # Find the Input object, but don't collapse to a value.
43
+ # Will return nil on a miss.
44
+ define_method :input_object do |input_name|
45
+ Inspec::InputRegistry.find_or_register_input(input_name, profile_id)
31
46
  end
32
47
 
33
48
  # Support for Control DSL plugins.
@@ -168,14 +183,25 @@ module Inspec
168
183
  end
169
184
 
170
185
  # method for inputs; import input handling
171
- define_method :attribute do |name, options = nil|
172
- if options.nil?
173
- Inspec::InputRegistry.find_input(name, profile_id).value
186
+ # TODO: deprecate name, use input()
187
+ define_method :attribute do |input_name, options = {}|
188
+ if options.empty?
189
+ # Simply an access, no event here
190
+ Inspec::InputRegistry.find_or_register_input(input_name, profile_id).value
174
191
  else
175
- profile_context_owner.register_input(name, options)
192
+ options[:priority] = 20
193
+ options[:provider] = :inline_control_code
194
+ evt = Inspec::Input.infer_event(options)
195
+ Inspec::InputRegistry.find_or_register_input(input_name, profile_name, event: evt).value
176
196
  end
177
197
  end
178
198
 
199
+ # Find the Input object, but don't collapse to a value.
200
+ # Will return nil on a miss.
201
+ define_method :input_object do |input_name|
202
+ Inspec::InputRegistry.find_or_register_input(input_name, profile_id)
203
+ end
204
+
179
205
  define_method :skip_control do |id|
180
206
  profile_context_owner.unregister_rule(id)
181
207
  end
@@ -118,6 +118,7 @@ module Inspec
118
118
  return @profile unless @profile.nil?
119
119
  opts = @opts.dup
120
120
  opts[:backend] = @backend
121
+ opts[:runner_conf] = Inspec::Config.cached
121
122
  if !@dependencies.nil? && !@dependencies.empty?
122
123
  opts[:dependencies] = Inspec::DependencySet.from_array(@dependencies, @cwd, @cache, @backend)
123
124
  end
@@ -23,6 +23,7 @@ module Inspec
23
23
  # implementation of the fetcher being used.
24
24
  #
25
25
  class Resolver
26
+ # Here deps is an Array of Hashes
26
27
  def self.resolve(dependencies, cache, working_dir, backend)
27
28
  reqs = dependencies.map do |dep|
28
29
  req = Inspec::Requirement.from_metadata(dep, cache, cwd: working_dir, backend: backend)
@@ -47,6 +48,7 @@ module Inspec
47
48
  end
48
49
  end
49
50
 
51
+ # Here deps is an Array of Inspec::Requirement
50
52
  def resolve(deps, top_level = true, seen_items = {}, path_string = '') # rubocop:disable Metrics/AbcSize
51
53
  graph = {}
52
54
  if top_level
data/lib/inspec/dsl.rb CHANGED
@@ -79,7 +79,7 @@ module Inspec::DSL
79
79
 
80
80
  def self.filter_included_controls(context, profile, &block)
81
81
  mock = Inspec::Backend.create(Inspec::Config.mock)
82
- include_ctx = Inspec::ProfileContext.for_profile(profile, mock, {})
82
+ include_ctx = Inspec::ProfileContext.for_profile(profile, mock)
83
83
  include_ctx.load(block) if block_given?
84
84
  # remove all rules that were not registered
85
85
  context.all_rules.each do |r|
data/lib/inspec/impact.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  module Inspec::Impact
5
5
  IMPACT_SCORES = {
6
6
  'none' => 0.0,
7
- 'low' => 0.01,
7
+ 'low' => 0.1,
8
8
  'medium' => 0.4,
9
9
  'high' => 0.7,
10
10
  'critical' => 0.9,
@@ -1,83 +1,224 @@
1
1
  require 'forwardable'
2
2
  require 'singleton'
3
3
  require 'inspec/objects/input'
4
+ require 'inspec/secrets'
5
+ require 'inspec/exceptions'
4
6
 
5
7
  module Inspec
8
+ # The InputRegistry's responsibilities include:
9
+ # - maintaining a list of Input objects that are bound to profiles
10
+ # - assisting in the lookup and creation of Inputs
6
11
  class InputRegistry
7
12
  include Singleton
8
13
  extend Forwardable
9
14
 
10
- attr_reader :list
11
- def_delegator :list, :each
12
- def_delegator :list, :[]
13
- def_delegator :list, :key?, :profile_exist?
14
- def_delegator :list, :select
15
+ attr_reader :inputs_by_profile, :profile_aliases
16
+ def_delegator :inputs_by_profile, :each
17
+ def_delegator :inputs_by_profile, :[]
18
+ def_delegator :inputs_by_profile, :key?, :profile_known?
19
+ def_delegator :inputs_by_profile, :select
20
+ def_delegator :profile_aliases, :key?, :profile_alias?
15
21
 
16
- # These self methods are convenience methods so you dont always
17
- # have to specify instance when calling the registry
18
- def self.find_input(name, profile)
19
- instance.find_input(name, profile)
20
- end
22
+ def initialize
23
+ # Keyed on String profile_name => Hash of String input_name => Input object
24
+ @inputs_by_profile = {}
21
25
 
22
- def self.register_input(name, profile, options = {})
23
- instance.register_input(name, profile, options)
26
+ # this is a list of optional profile name overrides set in the inspec.yml
27
+ @profile_aliases = {}
24
28
  end
25
29
 
26
- def self.register_profile_alias(name, alias_name)
27
- instance.register_profile_alias(name, alias_name)
30
+ #-------------------------------------------------------------#
31
+ # Support for Profiles
32
+ #-------------------------------------------------------------#
33
+
34
+ def register_profile_alias(name, alias_name)
35
+ @profile_aliases[name] = alias_name
28
36
  end
29
37
 
30
- def self.list_inputs_for_profile(profile)
31
- instance.list_inputs_for_profile(profile)
38
+ def list_inputs_for_profile(profile)
39
+ inputs_by_profile[profile] = {} unless profile_known?(profile)
40
+ inputs_by_profile[profile]
32
41
  end
33
42
 
34
- def initialize
35
- # this is a collection of profiles which have a value of input objects
36
- @list = {}
43
+ #-------------------------------------------------------------#
44
+ # Support for Individual Inputs
45
+ #-------------------------------------------------------------#
37
46
 
38
- # this is a list of optional profile name overrides set in the inspec.yml
39
- @profile_aliases = {}
47
+ def find_or_register_input(input_name, profile_name, options = {})
48
+ if profile_alias?(profile_name)
49
+ alias_name = profile_name
50
+ profile_name = profile_aliases[profile_name]
51
+ handle_late_arriving_alias(alias_name, profile_name) if profile_known?(alias_name)
52
+ end
53
+
54
+ inputs_by_profile[profile_name] ||= {}
55
+ if inputs_by_profile[profile_name].key?(input_name)
56
+ inputs_by_profile[profile_name][input_name].update(options)
57
+ else
58
+ inputs_by_profile[profile_name][input_name] = Inspec::Input.new(input_name, options)
59
+ end
60
+
61
+ inputs_by_profile[profile_name][input_name]
40
62
  end
41
63
 
42
- def find_input(name, profile)
43
- profile = @profile_aliases[profile] if !profile_exist?(profile) && @profile_aliases[profile]
44
- unless profile_exist?(profile)
45
- error = Inspec::InputRegistry::ProfileLookupError.new
46
- error.profile_name = profile
47
- raise error, "Profile '#{error.profile_name}' does not have any inputs"
64
+ # It is possible for a wrapper profile to create an input in metadata,
65
+ # referring to the child profile by an alias that has not yet been registered.
66
+ # The registry will then store the inputs under the alias, as if the alias
67
+ # were a true profile.
68
+ # If that happens and the child profile also mentions the input, we will
69
+ # need to move some things - all inputs should be stored under the true
70
+ # profile name, and no inputs should be stored under the alias.
71
+ def handle_late_arriving_alias(alias_name, profile_name)
72
+ inputs_by_profile[profile_name] ||= {}
73
+ inputs_by_profile[alias_name].each do |input_name, input_from_alias|
74
+ # Move the inpuut, or if it exists, merge events
75
+ existing = inputs_by_profile[profile_name][input_name]
76
+ if existing
77
+ existing.events.concat(input_from_alias.events)
78
+ else
79
+ inputs_by_profile[profile_name][input_name] = input_from_alias
80
+ end
48
81
  end
82
+ # Finally, delete the (now copied-out) entry for the alias
83
+ inputs_by_profile.delete(alias_name)
84
+ end
85
+ #-------------------------------------------------------------#
86
+ # Support for Binding Inputs
87
+ #-------------------------------------------------------------#
88
+
89
+ # This method is called by the Profile as soon as it has
90
+ # enough context to allow binding inputs to it.
91
+ def bind_profile_inputs(profile_name, sources = {})
92
+ inputs_by_profile[profile_name] ||= {}
93
+
94
+ # In a more perfect world, we could let the core plugins choose
95
+ # self-determine what to do; but as-is, the APIs that call this
96
+ # are a bit over-constrained.
97
+ bind_inputs_from_metadata(profile_name, sources[:profile_metadata])
98
+ bind_inputs_from_input_files(profile_name, sources[:cli_input_files])
99
+ bind_inputs_from_runner_api(profile_name, sources[:runner_api])
100
+ end
101
+
102
+ private
49
103
 
50
- unless list[profile].key?(name)
51
- error = Inspec::InputRegistry::InputLookupError.new
52
- error.input_name = name
53
- error.profile_name = profile
54
- raise error, "Profile '#{error.profile_name}' does not have an input with name '#{error.input_name}'"
104
+ def bind_inputs_from_runner_api(profile_name, input_hash)
105
+ # TODO: move this into a core plugin
106
+
107
+ return if input_hash.nil?
108
+ return if input_hash.empty?
109
+
110
+ # These arrive as a bare hash - values are raw values, not options
111
+ input_hash.each do |input_name, input_value|
112
+ loc = Inspec::Input::Event.probe_stack # TODO: likely modify this to look for a kitchen.yml, if that is realistic
113
+ evt = Inspec::Input::Event.new(
114
+ value: input_value,
115
+ provider: :runner_api, # TODO: suss out if audit cookbook or kitchen-inspec or something unknown
116
+ priority: 40,
117
+ file: loc.path,
118
+ line: loc.lineno,
119
+ )
120
+ find_or_register_input(input_name, profile_name, event: evt)
55
121
  end
56
- list[profile][name]
57
122
  end
58
123
 
59
- def register_input(name, profile, options = {})
60
- # check for a profile override name
61
- if profile_exist?(profile) && list[profile][name] && options.empty?
62
- list[profile][name]
63
- else
64
- list[profile] = {} unless profile_exist?(profile)
65
- list[profile][name] = Inspec::Input.new(name, options)
124
+ def bind_inputs_from_input_files(profile_name, file_list)
125
+ # TODO: move this into a core plugin
126
+
127
+ return if file_list.nil?
128
+ return if file_list.empty?
129
+
130
+ file_list.each do |path|
131
+ validate_inputs_file_readability!(path)
132
+
133
+ # TODO: drop this SecretsBackend stuff, will be handled by plugin system
134
+ data = Inspec::SecretsBackend.resolve(path)
135
+ if data.nil?
136
+ raise Inspec::Exceptions::SecretsBackendNotFound,
137
+ "Cannot find parser for inputs file '#{path}'. " \
138
+ 'Check to make sure file has the appropriate extension.'
139
+ end
140
+
141
+ next if data.inputs.nil?
142
+ data.inputs.each do |input_name, input_value|
143
+ evt = Inspec::Input::Event.new(
144
+ value: input_value,
145
+ provider: :cli_files,
146
+ priority: 40,
147
+ file: path,
148
+ # TODO: any way we could get a line number?
149
+ )
150
+ find_or_register_input(input_name, profile_name, event: evt)
151
+ end
66
152
  end
67
153
  end
68
154
 
69
- def register_profile_alias(name, alias_name)
70
- @profile_aliases[name] = alias_name
155
+ def validate_inputs_file_readability!(path)
156
+ unless File.exist?(path)
157
+ raise Inspec::Exceptions::InputsFileDoesNotExist,
158
+ "Cannot find input file '#{path}'. " \
159
+ 'Check to make sure file exists.'
160
+ end
161
+
162
+ unless File.readable?(path)
163
+ raise Inspec::Exceptions::InputsFileNotReadable,
164
+ "Cannot read input file '#{path}'. " \
165
+ 'Check to make sure file is readable.'
166
+ end
167
+
168
+ true
71
169
  end
72
170
 
73
- def list_inputs_for_profile(profile)
74
- list[profile] = {} unless profile_exist?(profile)
75
- list[profile]
171
+ def bind_inputs_from_metadata(profile_name, profile_metadata_obj)
172
+ # TODO: move this into a core plugin
173
+ # TODO: add deprecation stuff
174
+ return if profile_metadata_obj.nil? # Metadata files are technically optional
175
+
176
+ if profile_metadata_obj.params.key?(:attributes) && profile_metadata_obj.params[:attributes].is_a?(Array)
177
+ profile_metadata_obj.params[:attributes].each do |input_orig|
178
+ input_options = input_orig.dup
179
+ input_name = input_options.delete(:name)
180
+ input_options.merge!({ priority: 30, provider: :profile_metadata, file: File.join(profile_name, 'inspec.yml') })
181
+ evt = Inspec::Input.infer_event(input_options)
182
+
183
+ # Profile metadata may set inputs in other profiles by naming them.
184
+ if input_options[:profile]
185
+ profile_name = input_options[:profile] || profile_name
186
+ # Override priority to force this to win. Allow user to set their own priority.
187
+ evt.priority = input_orig[:priority] || 35
188
+ end
189
+ find_or_register_input(input_name,
190
+ profile_name,
191
+ type: input_options[:type],
192
+ required: input_options[:required],
193
+ event: evt)
194
+ end
195
+ elsif profile_metadata_obj.params.key?(:attributes)
196
+ Inspec::Log.warn 'Inputs must be defined as an Array. Skipping current definition.'
197
+ end
76
198
  end
77
199
 
200
+ #-------------------------------------------------------------#
201
+ # Other Support
202
+ #-------------------------------------------------------------#
203
+ public
204
+
205
+ # Used in testing
78
206
  def __reset
79
- @list = {}
207
+ @inputs_by_profile = {}
80
208
  @profile_aliases = {}
81
209
  end
210
+
211
+ # These class methods are convenience methods so you don't always
212
+ # have to call #instance when calling the registry
213
+ [
214
+ :find_or_register_input,
215
+ :register_profile_alias,
216
+ :list_inputs_for_profile,
217
+ :bind_profile_inputs,
218
+ ].each do |meth|
219
+ define_singleton_method(meth) do |*args|
220
+ instance.send(meth, *args)
221
+ end
222
+ end
82
223
  end
83
224
  end
@@ -14,6 +14,73 @@ end
14
14
 
15
15
  module Inspec
16
16
  class Input
17
+ #===========================================================================#
18
+ # Class Input::Event
19
+ #===========================================================================#
20
+
21
+ # Information about how the input obtained its value.
22
+ # Each time it changes, an Input::Event is added to the #events array.
23
+ class Event
24
+ EVENT_PROPERTIES = [
25
+ :action, # :create, :set, :fetch
26
+ :provider, # Name of the plugin
27
+ :priority, # Priority of this plugin for resolving conflicts. 1-100, higher numbers win.
28
+ :value, # New value, if provided.
29
+ :file, # File containing the input-changing action, if known
30
+ :line, # Line in file containing the input-changing action, if known
31
+ :hit, # if action is :fetch, true if the remote source had the input
32
+ ].freeze
33
+
34
+ # Value has a special handler
35
+ EVENT_PROPERTIES.reject { |p| p == :value }.each do |prop|
36
+ attr_accessor prop
37
+ end
38
+
39
+ attr_reader :value
40
+
41
+ def initialize(properties = {})
42
+ @value_has_been_set = false
43
+
44
+ properties.each do |prop_name, prop_value|
45
+ if EVENT_PROPERTIES.include? prop_name
46
+ # OK, save the property
47
+ send((prop_name.to_s + '=').to_sym, prop_value)
48
+ else
49
+ raise "Unrecognized property to Input::Event: #{prop_name}"
50
+ end
51
+ end
52
+ end
53
+
54
+ def value=(the_val)
55
+ # Even if set to nil or false, it has indeed been set; note that fact.
56
+ @value_has_been_set = true
57
+ @value = the_val
58
+ end
59
+
60
+ def value_has_been_set?
61
+ @value_has_been_set
62
+ end
63
+
64
+ def diagnostic_string
65
+ to_h.reject { |_, val| val.nil? }.to_a.map { |pair| "#{pair[0]}: '#{pair[1]}'" }.join(', ')
66
+ end
67
+
68
+ def to_h
69
+ EVENT_PROPERTIES.each_with_object({}) do |prop, hash|
70
+ hash[prop] = send(prop)
71
+ end
72
+ end
73
+
74
+ def self.probe_stack
75
+ frames = caller_locations(2, 40)
76
+ frames.reject! { |f| f.path && f.path.include?('/lib/inspec/') }
77
+ frames.first
78
+ end
79
+ end
80
+
81
+ #===========================================================================#
82
+ # Class NO_VALUE_SET
83
+ #===========================================================================#
17
84
  # This special class is used to represent the value when an input has
18
85
  # not been assigned a value. This allows a user to explicitly assign nil
19
86
  # to an input.
@@ -62,8 +129,11 @@ module Inspec
62
129
  end
63
130
 
64
131
  class Input
65
- attr_accessor :name
132
+ #===========================================================================#
133
+ # Class Inspec::Input
134
+ #===========================================================================#
66
135
 
136
+ # Validation types for input values
67
137
  VALID_TYPES = %w{
68
138
  String
69
139
  Numeric
@@ -74,6 +144,21 @@ module Inspec
74
144
  Any
75
145
  }.freeze
76
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
+
160
+ attr_reader :description, :events, :identifier, :name, :required, :title, :type
161
+
77
162
  def initialize(name, options = {})
78
163
  @name = name
79
164
  @opts = options
@@ -82,49 +167,164 @@ module Inspec
82
167
  if @opts.key?(:value)
83
168
  Inspec::Log.warn "Input #{@name} created using both :default and :value options - ignoring :default"
84
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
85
205
  else
86
- @opts[:value] = @opts.delete(:default)
206
+ self.class.infer_event(options) # Sets options[:event]
87
207
  end
88
208
  end
89
- @value = @opts[:value]
90
- validate_value_type(@value) if @opts.key?(:type) && @opts.key?(:value)
209
+ events << options[:event] if options.key? :event
210
+
211
+ enforce_type_restriction!
91
212
  end
92
213
 
93
- def value=(new_value)
94
- validate_value_type(new_value) if @opts.key?(:type)
95
- @value = new_value
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
96
241
  end
97
242
 
98
- def value
99
- if @value.nil?
100
- validate_required(@value) if @opts[:required] == true
101
- @value = value_or_dummy
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)
102
276
  else
103
- @value
277
+ winning_event.value # May still be nil
104
278
  end
105
279
  end
106
280
 
107
- def title
108
- @opts[:title]
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
+
296
+ new_value
109
297
  end
110
298
 
111
- def description
112
- @opts[:description]
299
+ def value
300
+ enforce_required_validation!
301
+ current_value
113
302
  end
114
303
 
115
- def ruby_var_identifier
116
- @opts[:identifier] || 'attr_' + @name.downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
304
+ def has_value?
305
+ !current_value.is_a? NO_VALUE_SET
117
306
  end
118
307
 
308
+ #--------------------------------------------------------------------------#
309
+ # Marshalling
310
+ #--------------------------------------------------------------------------#
311
+
119
312
  def to_hash
120
- {
121
- name: @name,
122
- options: @opts,
123
- }
313
+ as_hash = { name: name, options: {} }
314
+ [:description, :title, :identifier, :type, :required, :value].each do |field|
315
+ val = send(field)
316
+ next if val.nil?
317
+ as_hash[:options][field] = val
318
+ end
319
+ as_hash
320
+ end
321
+
322
+ def ruby_var_identifier
323
+ identifier || 'attr_' + name.downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
124
324
  end
125
325
 
126
326
  def to_ruby
127
- res = ["#{ruby_var_identifier} = attribute('#{@name}',{"]
327
+ res = ["#{ruby_var_identifier} = attribute('#{name}',{"]
128
328
  res.push " title: '#{title}'," unless title.to_s.empty?
129
329
  res.push " value: #{value.inspect}," unless value.to_s.empty?
130
330
  # to_ruby may generate code that is to be used by older versions of inspec.
@@ -136,37 +336,78 @@ module Inspec
136
336
  res.join("\n")
137
337
  end
138
338
 
339
+ #--------------------------------------------------------------------------#
340
+ # Value Type Coercion
341
+ #--------------------------------------------------------------------------#
342
+
139
343
  def to_s
140
- "Input #{@name} with #{@value}"
344
+ "Input #{name} with #{current_value}"
141
345
  end
142
346
 
347
+ #--------------------------------------------------------------------------#
348
+ # Validation
349
+ #--------------------------------------------------------------------------#
350
+
143
351
  private
144
352
 
145
- def validate_required(value)
353
+ def enforce_required_validation!
354
+ return unless required
146
355
  # skip if we are not doing an exec call (archive/vendor/check)
147
356
  return unless Inspec::BaseCLI.inspec_cli_command == :exec
148
357
 
149
- # value will be set already if a secrets file was passed in
150
- if (!@opts.key?(:default) && value.nil?) || (@opts[:default].nil? && value.nil?)
358
+ proposed_value = current_value
359
+ if proposed_value.nil? || proposed_value.is_a?(NO_VALUE_SET)
151
360
  error = Inspec::Input::RequiredError.new
152
- error.input_name = @name
361
+ error.input_name = name
153
362
  raise error, "Input '#{error.input_name}' is required and does not have a value."
154
363
  end
155
364
  end
156
365
 
157
- def validate_type(type)
158
- type = type.capitalize
366
+ def enforce_type_restriction! # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
367
+ return unless type
368
+ return unless has_value?
369
+
370
+ type_req = type
371
+ return if type_req == 'Any'
372
+
373
+ proposed_value = current_value
374
+
375
+ invalid_type = false
376
+ if type_req == 'Regexp'
377
+ invalid_type = true if !valid_regexp?(proposed_value)
378
+ elsif type_req == 'Numeric'
379
+ invalid_type = true if !valid_numeric?(proposed_value)
380
+ elsif type_req == 'Boolean'
381
+ invalid_type = true if ![true, false].include?(proposed_value)
382
+ elsif proposed_value.is_a?(Module.const_get(type_req)) == false
383
+ # TODO: why is this case here?
384
+ invalid_type = true
385
+ end
386
+
387
+ if invalid_type == true
388
+ error = Inspec::Input::ValidationError.new
389
+ error.input_name = @name
390
+ error.input_value = proposed_value
391
+ error.input_type = type_req
392
+ raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to type '#{error.input_type}'."
393
+ end
394
+ end
395
+
396
+ def normalize_type_restriction!
397
+ return unless type
398
+
399
+ type_req = type.capitalize
159
400
  abbreviations = {
160
401
  'Num' => 'Numeric',
161
402
  'Regex' => 'Regexp',
162
403
  }
163
- type = abbreviations[type] if abbreviations.key?(type)
164
- if !VALID_TYPES.include?(type)
404
+ type_req = abbreviations[type_req] if abbreviations.key?(type_req)
405
+ if !VALID_TYPES.include?(type_req)
165
406
  error = Inspec::Input::TypeError.new
166
- error.input_type = type
407
+ error.input_type = type_req
167
408
  raise error, "Type '#{error.input_type}' is not a valid input type."
168
409
  end
169
- type
410
+ @type = type_req
170
411
  end
171
412
 
172
413
  def valid_numeric?(value)
@@ -177,41 +418,11 @@ module Inspec
177
418
  end
178
419
 
179
420
  def valid_regexp?(value)
180
- # check for invalid regex syntex
421
+ # check for invalid regex syntax
181
422
  Regexp.new(value)
182
423
  true
183
424
  rescue
184
425
  false
185
426
  end
186
-
187
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
188
- def validate_value_type(value)
189
- type = validate_type(@opts[:type])
190
- return if type == 'Any'
191
-
192
- invalid_type = false
193
- if type == 'Regexp'
194
- invalid_type = true if !value.is_a?(String) || !valid_regexp?(value)
195
- elsif type == 'Numeric'
196
- invalid_type = true if !valid_numeric?(value)
197
- elsif type == 'Boolean'
198
- invalid_type = true if ![true, false].include?(value)
199
- elsif value.is_a?(Module.const_get(type)) == false
200
- invalid_type = true
201
- end
202
-
203
- if invalid_type == true
204
- error = Inspec::Input::ValidationError.new
205
- error.input_name = @name
206
- error.input_value = value
207
- error.input_type = type
208
- raise error, "Input '#{error.input_name}' with value '#{error.input_value}' does not validate to type '#{error.input_type}'."
209
- end
210
- end
211
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
212
-
213
- def value_or_dummy
214
- @opts.key?(:value) ? @opts[:value] : Inspec::Input::NO_VALUE_SET.new(@name)
215
- end
216
427
  end
217
428
  end
@@ -81,7 +81,7 @@ module Inspec
81
81
  end
82
82
 
83
83
  attr_reader :source_reader, :backend, :runner_context, :check_mode
84
- attr_accessor :parent_profile, :profile_name
84
+ attr_accessor :parent_profile, :profile_id, :profile_name
85
85
  def_delegator :@source_reader, :tests
86
86
  def_delegator :@source_reader, :libraries
87
87
  def_delegator :@source_reader, :metadata
@@ -118,25 +118,32 @@ module Inspec
118
118
  @runtime_profile = RuntimeProfile.new(self)
119
119
  @backend.profile = @runtime_profile
120
120
 
121
+ # The AttributeRegistry is in charge of keeping track of inputs;
122
+ # it is the single source of truth. Now that we have a profile object,
123
+ # we can create any inputs that were provided by various mechanisms.
124
+ options[:runner_conf] ||= Inspec::Config.cached
125
+
126
+ if options[:runner_conf].key?(:attrs)
127
+ Inspec.deprecate(:rename_attributes_to_inputs, 'Use --input-file on the command line instead of --attrs.')
128
+ options[:runner_conf][:input_file] = options[:runner_conf].delete(:attrs)
129
+ end
130
+
131
+ Inspec::InputRegistry.bind_profile_inputs(
132
+ # Every input only exists in the context of a profile
133
+ metadata.params[:name], # TODO: test this with profile aliasing
134
+ # Remaining args are possible sources of inputs
135
+ cli_input_files: options[:runner_conf][:input_file], # From CLI --input-file
136
+ profile_metadata: metadata,
137
+ # TODO: deprecation checks here
138
+ runner_api: options[:runner_conf][:attributes], # This is the route the audit_cookbook and kitchen-inspec take
139
+ )
140
+
121
141
  @runner_context =
122
142
  options[:profile_context] ||
123
- Inspec::ProfileContext.for_profile(self, @backend, @input_values)
143
+ Inspec::ProfileContext.for_profile(self, @backend)
124
144
 
125
145
  @supports_platform = metadata.supports_platform?(@backend)
126
146
  @supports_runtime = metadata.supports_runtime?
127
- register_metadata_inputs
128
- end
129
-
130
- def register_metadata_inputs # TODO: deprecate
131
- if metadata.params.key?(:attributes) && metadata.params[:attributes].is_a?(Array)
132
- metadata.params[:attributes].each do |attribute|
133
- attr_dup = attribute.dup
134
- name = attr_dup.delete(:name)
135
- @runner_context.register_input(name, attr_dup)
136
- end
137
- elsif metadata.params.key?(:attributes)
138
- Inspec::Log.warn 'Inputs must be defined as an Array. Skipping current definition.'
139
- end
140
147
  end
141
148
 
142
149
  def name
@@ -595,7 +602,7 @@ module Inspec
595
602
  f = load_rule_filepath(prefix, rule)
596
603
  load_rule(rule, f, controls, groups)
597
604
  end
598
- params[:inputs] = @runner_context.inputs
605
+ params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
599
606
  params
600
607
  end
601
608
 
@@ -12,13 +12,11 @@ require 'inspec/objects/input'
12
12
 
13
13
  module Inspec
14
14
  class ProfileContext
15
- def self.for_profile(profile, backend, inputs)
16
- new(profile.name, backend, { 'profile' => profile,
17
- 'inputs' => inputs,
18
- 'check_mode' => profile.check_mode })
15
+ def self.for_profile(profile, backend)
16
+ new(profile.name, backend, { 'profile' => profile, 'check_mode' => profile.check_mode })
19
17
  end
20
18
 
21
- attr_reader :inputs, :backend, :profile_name, :profile_id, :resource_registry
19
+ attr_reader :backend, :profile_name, :profile_id, :resource_registry
22
20
  attr_accessor :rules
23
21
  def initialize(profile_id, backend, conf)
24
22
  if backend.nil?
@@ -35,7 +33,8 @@ module Inspec
35
33
  @lib_subcontexts = []
36
34
  @require_loader = ::Inspec::RequireLoader.new
37
35
  Inspec::InputRegistry.register_profile_alias(@profile_id, @profile_name) if @profile_id != @profile_name
38
- @inputs = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
36
+ # TODO: consider polling input source plugins; this is a bulk fetch opportunity
37
+
39
38
  # A local resource registry that only contains resources defined
40
39
  # in the transitive dependency tree of the loaded profile.
41
40
  @resource_registry = Inspec::Resource.new_registry
@@ -43,6 +42,10 @@ module Inspec
43
42
  @current_load = nil
44
43
  end
45
44
 
45
+ def attributes
46
+ Inspec::AttributeRegistry.list_attributes_for_profile(@profile_id)
47
+ end
48
+
46
49
  def dependencies
47
50
  if @conf['profile'].nil?
48
51
  {}
@@ -187,13 +190,6 @@ module Inspec
187
190
  end
188
191
  end
189
192
 
190
- def register_input(name, options = {})
191
- # we need to return an input object, to allow dermination of values
192
- input = Inspec::InputRegistry.register_input(name, @profile_id, options)
193
- input.value = @conf['inputs'][name] unless @conf['inputs'].nil? || @conf['inputs'][name].nil?
194
- input.value
195
- end
196
-
197
193
  def set_header(field, val)
198
194
  @current_load[field] = val
199
195
  end
@@ -66,9 +66,13 @@ end
66
66
  class RSpec::Core::ExampleGroup
67
67
  # This DSL method allows us to access the values of inputs within InSpec tests
68
68
  def attribute(name)
69
- Inspec::InputRegistry.find_input(name, self.class.metadata[:profile_id]).value
69
+ Inspec::InputRegistry.find_or_register_input(name, self.class.metadata[:profile_id]).value
70
70
  end
71
71
  define_example_method :attribute
72
+ def input_obj(name)
73
+ Inspec::InputRegistry.find_or_register_input(name, self.class.metadata[:profile_id])
74
+ end
75
+ define_example_method :input_obj
72
76
 
73
77
  # Here, we have to ensure our method_missing gets called prior
74
78
  # to RSpec::Core::ExampleGroup.method_missing (the class method).
data/lib/inspec/runner.rb CHANGED
@@ -9,7 +9,6 @@ require 'inspec/backend'
9
9
  require 'inspec/profile_context'
10
10
  require 'inspec/profile'
11
11
  require 'inspec/metadata'
12
- require 'inspec/secrets'
13
12
  require 'inspec/config'
14
13
  require 'inspec/dependencies/cache'
15
14
  # spec requirements
@@ -32,7 +31,7 @@ module Inspec
32
31
  class Runner
33
32
  extend Forwardable
34
33
 
35
- attr_reader :backend, :rules, :inputs
34
+ attr_reader :backend, :rules
36
35
 
37
36
  def attributes
38
37
  Inspec.deprecate(:rename_attributes_to_inputs, "Don't call runner.attributes, call runner.inputs")
@@ -57,10 +56,17 @@ module Inspec
57
56
  RunnerRspec.new(@conf)
58
57
  end
59
58
 
60
- # list of profile inputs
61
- @inputs = {}
59
+ # About reading inputs:
60
+ # @conf gets passed around a lot, eventually to
61
+ # Inspec::InputRegistry.register_external_inputs.
62
+ #
63
+ # @conf may contain the key :attributes or :inputs, which is to be a Hash
64
+ # of values passed in from the Runner API.
65
+ # This is how kitchen-inspec and the audit_cookbook pass in inputs.
66
+ #
67
+ # @conf may contain the key :attrs or :input_file, which is to be an Array
68
+ # of file paths, each a YAML file. This how --input-file works.
62
69
 
63
- load_inputs(@conf)
64
70
  configure_transport
65
71
  end
66
72
 
@@ -101,7 +107,6 @@ module Inspec
101
107
  @test_collector.add_profile(requirement.profile)
102
108
  end
103
109
 
104
- @inputs = profile.runner_context.inputs if @inputs.empty?
105
110
  tests = profile.collect_tests
106
111
  all_controls += tests unless tests.nil?
107
112
  end
@@ -149,35 +154,6 @@ module Inspec
149
154
  @test_collector.exit_code
150
155
  end
151
156
 
152
- # determine all inputs before the execution, fetch data from secrets backend
153
- def load_inputs(options)
154
- # TODO: - rename :attributes - it is user-visible
155
- options[:attributes] ||= {}
156
-
157
- if options.key?(:attrs)
158
- Inspec.deprecate(:rename_attributes_to_inputs, 'Use --input-file on the command line instead of --attrs.')
159
- options[:input_file] = options.delete(:attrs)
160
- end
161
- secrets_targets = options[:input_file]
162
- return options[:attributes] if secrets_targets.nil?
163
-
164
- secrets_targets.each do |target|
165
- validate_inputs_file_readability!(target)
166
-
167
- secrets = Inspec::SecretsBackend.resolve(target)
168
- if secrets.nil?
169
- raise Inspec::Exceptions::SecretsBackendNotFound,
170
- "Cannot find parser for inputs file '#{target}'. " \
171
- 'Check to make sure file has the appropriate extension.'
172
- end
173
-
174
- next if secrets.inputs.nil?
175
- options[:attributes].merge!(secrets.inputs)
176
- end
177
-
178
- options[:attributes]
179
- end
180
-
181
157
  #
182
158
  # add_target allows the user to add a target whose tests will be
183
159
  # run when the user calls the run method.
@@ -209,7 +185,7 @@ module Inspec
209
185
  vendor_cache: @cache,
210
186
  backend: @backend,
211
187
  controls: @controls,
212
- inputs: @conf[:attributes]) # TODO: read form :inputs here (user visible)
188
+ runner_conf: @conf)
213
189
  raise "Could not resolve #{target} to valid input." if profile.nil?
214
190
  @target_profiles << profile if supports_profile?(profile)
215
191
  end
@@ -300,22 +276,6 @@ module Inspec
300
276
  examples.each { |e| @test_collector.add_test(e, rule) }
301
277
  end
302
278
 
303
- def validate_inputs_file_readability!(target)
304
- unless File.exist?(target)
305
- raise Inspec::Exceptions::InputsFileDoesNotExist,
306
- "Cannot find input file '#{target}'. " \
307
- 'Check to make sure file exists.'
308
- end
309
-
310
- unless File.readable?(target)
311
- raise Inspec::Exceptions::InputsFileNotReadable,
312
- "Cannot read input file '#{target}'. " \
313
- 'Check to make sure file is readable.'
314
- end
315
-
316
- true
317
- end
318
-
319
279
  def rspec_skipped_block(arg, opts, message)
320
280
  @test_collector.example_group(*arg, opts) do
321
281
  # Send custom `it` block to RSpec
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = '4.1.4.preview'.freeze
2
+ VERSION = '4.2.0.preview'.freeze
3
3
  end
@@ -108,7 +108,7 @@ module Inspec::Resources
108
108
  results = table.map { |row|
109
109
  res = {}
110
110
  headers.each { |header|
111
- res[header.downcase] = row[header]
111
+ res[header.downcase] = row[header] if header
112
112
  }
113
113
  Hashie::Mash.new(res)
114
114
  }
@@ -569,6 +569,10 @@ module Inspec::Resources
569
569
  # example: ::ffff:10.0.2.15:9200
570
570
  host.delete!('::ffff:') if host.start_with?('::ffff:')
571
571
 
572
+ # To remove brackets that might surround the IPv6 address
573
+ # example: [::] and [fe80::dc11:b9b6:514b:134]%eth0:123
574
+ host = host.tr('[]', '')
575
+
572
576
  # if there's an interface name in the local address, which is common for
573
577
  # IPv6 listeners, strip that out too.
574
578
  # example: fe80::a00:27ff:fe32:ed09%enp0s3
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.4.preview
4
+ version: 4.2.0.preview
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-22 00:00:00.000000000 Z
11
+ date: 2019-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train-core