inspec 2.2.61 → 2.2.64

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -15
  3. data/README.md +0 -1
  4. data/docs/dev/plugins.md +321 -0
  5. data/docs/profiles.md +20 -18
  6. data/lib/bundles/inspec-artifact/cli.rb +1 -0
  7. data/lib/bundles/inspec-compliance/cli.rb +1 -0
  8. data/lib/bundles/inspec-habitat/cli.rb +1 -0
  9. data/lib/bundles/inspec-init/cli.rb +1 -0
  10. data/lib/bundles/inspec-supermarket/cli.rb +1 -0
  11. data/lib/inspec.rb +4 -2
  12. data/lib/inspec/base_cli.rb +1 -0
  13. data/lib/inspec/cli.rb +35 -16
  14. data/lib/inspec/control_eval_context.rb +7 -6
  15. data/lib/inspec/dependencies/requirement.rb +0 -1
  16. data/lib/inspec/fetcher.rb +1 -2
  17. data/lib/inspec/library_eval_context.rb +1 -1
  18. data/lib/inspec/plugin/v1.rb +2 -0
  19. data/lib/inspec/{plugins → plugin/v1/plugin_types}/cli.rb +2 -0
  20. data/lib/inspec/{plugins → plugin/v1/plugin_types}/fetcher.rb +1 -1
  21. data/lib/inspec/{plugins → plugin/v1/plugin_types}/resource.rb +0 -0
  22. data/lib/inspec/{plugins → plugin/v1/plugin_types}/secret.rb +1 -1
  23. data/lib/inspec/{plugins → plugin/v1/plugin_types}/source_reader.rb +1 -1
  24. data/lib/inspec/{plugins.rb → plugin/v1/plugins.rb} +7 -5
  25. data/lib/{utils/plugin_registry.rb → inspec/plugin/v1/registry.rb} +0 -0
  26. data/lib/inspec/plugin/v2.rb +30 -0
  27. data/lib/inspec/plugin/v2/activator.rb +16 -0
  28. data/lib/inspec/plugin/v2/loader.rb +204 -0
  29. data/lib/inspec/plugin/v2/plugin_base.rb +98 -0
  30. data/lib/inspec/plugin/v2/plugin_types/cli.rb +27 -0
  31. data/lib/inspec/plugin/v2/plugin_types/mock.rb +12 -0
  32. data/lib/inspec/plugin/v2/registry.rb +76 -0
  33. data/lib/inspec/plugin/v2/status.rb +29 -0
  34. data/lib/inspec/reporters.rb +5 -1
  35. data/lib/inspec/reporters/automate.rb +1 -1
  36. data/lib/inspec/reporters/{json_merged.rb → json_automate.rb} +1 -1
  37. data/lib/inspec/resource.rb +1 -1
  38. data/lib/inspec/rule.rb +14 -8
  39. data/lib/inspec/secrets.rb +1 -2
  40. data/lib/inspec/source_reader.rb +1 -2
  41. data/lib/inspec/version.rb +1 -1
  42. data/lib/resources/apache_conf.rb +1 -1
  43. metadata +20 -10
@@ -6,6 +6,7 @@ require 'pathname'
6
6
  require 'set'
7
7
  require 'tempfile'
8
8
  require 'yaml'
9
+ require 'inspec/base_cli'
9
10
 
10
11
  # Notes:
11
12
  #
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'thor'
6
6
  require 'erb'
7
+ require 'inspec/base_cli'
7
8
 
8
9
  module Compliance
9
10
  class ComplianceCLI < Inspec::BaseCLI
@@ -2,6 +2,7 @@
2
2
  # author: Adam Leff
3
3
 
4
4
  require 'thor'
5
+ require 'inspec/base_cli'
5
6
 
6
7
  module Habitat
7
8
  class HabitatProfileCLI < Thor
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'pathname'
4
4
  require_relative 'renderer'
5
+ require 'inspec/base_cli'
5
6
 
6
7
  module Init
7
8
  class CLI < Inspec::BaseCLI
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # author: Christoph Hartmann
3
3
  # author: Dominik Richter
4
+ require 'inspec/base_cli'
4
5
 
5
6
  module Supermarket
6
7
  class SupermarketCLI < Inspec::BaseCLI
@@ -16,9 +16,11 @@ require 'inspec/shell'
16
16
  require 'inspec/formatters'
17
17
  require 'inspec/reporters'
18
18
 
19
- # all utils that may be required by plugins
19
+ require 'inspec/plugin/v2'
20
+ require 'inspec/plugin/v1'
21
+
22
+ # all utils that may be required by legacy plugins
20
23
  require 'inspec/base_cli'
21
24
  require 'inspec/fetcher'
22
25
  require 'inspec/source_reader'
23
26
  require 'inspec/resource'
24
- require 'inspec/plugins'
@@ -168,6 +168,7 @@ module Inspec
168
168
  'documentation',
169
169
  'html',
170
170
  'json',
171
+ 'json-automate',
171
172
  'json-min',
172
173
  'json-rspec',
173
174
  'junit',
@@ -10,7 +10,8 @@ require 'pp'
10
10
  require 'utils/json_log'
11
11
  require 'utils/latest_version'
12
12
  require 'inspec/base_cli'
13
- require 'inspec/plugins'
13
+ require 'inspec/plugin/v1'
14
+ require 'inspec/plugin/v2'
14
15
  require 'inspec/runner_mock'
15
16
  require 'inspec/env_printer'
16
17
  require 'inspec/schema'
@@ -20,7 +21,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
20
21
  desc: 'Set the log level: info (default), debug, warn, error'
21
22
 
22
23
  class_option :log_location, type: :string,
23
- desc: 'Location to send diagnostic log messages to. (default: STDOUT or STDERR)'
24
+ desc: 'Location to send diagnostic log messages to. (default: STDOUT or Inspec::Log.error)'
24
25
 
25
26
  class_option :diagnose, type: :boolean,
26
27
  desc: 'Show diagnostics (versions, configurations)'
@@ -282,17 +283,35 @@ class Inspec::InspecCLI < Inspec::BaseCLI
282
283
  end
283
284
  end
284
285
 
285
- # Load all plugins on startup
286
- ctl = Inspec::PluginCtl.new
287
- ctl.list.each { |x| ctl.load(x) }
288
-
289
- # load CLI plugins before the Inspec CLI has been started
290
- Inspec::Plugins::CLI.subcommands.each { |_subcommand, params|
291
- Inspec::InspecCLI.register(
292
- params[:klass],
293
- params[:subcommand_name],
294
- params[:usage],
295
- params[:description],
296
- params[:options],
297
- )
298
- }
286
+ begin
287
+ # Load v2 plugins
288
+ v2_loader = Inspec::Plugin::V2::Loader.new
289
+ v2_loader.load_all
290
+ v2_loader.exit_on_load_error
291
+ v2_loader.activate_mentioned_cli_plugins
292
+
293
+ # Load v1 plugins on startup
294
+ ctl = Inspec::PluginCtl.new
295
+ ctl.list.each { |x| ctl.load(x) }
296
+
297
+ # load v1 CLI plugins before the Inspec CLI has been started
298
+ Inspec::Plugins::CLI.subcommands.each { |_subcommand, params|
299
+ Inspec::InspecCLI.register(
300
+ params[:klass],
301
+ params[:subcommand_name],
302
+ params[:usage],
303
+ params[:description],
304
+ params[:options],
305
+ )
306
+ }
307
+ rescue Inspec::Plugin::V2::Exception => v2ex
308
+ Inspec::Log.error v2ex.message
309
+
310
+ if ARGV.include?('--debug')
311
+ Inspec::Log.error v2ex.class.name
312
+ Inspec::Log.error v2ex.backtrace.join("\n")
313
+ else
314
+ Inspec::Log.error 'Run again with --debug for a stacktrace.'
315
+ end
316
+ exit 2
317
+ end
@@ -52,6 +52,7 @@ module Inspec
52
52
  @conf = conf
53
53
  @dependencies = dependencies
54
54
  @require_loader = require_loader
55
+ @skip_file_message = nil
55
56
  @skip_file = false
56
57
  @skip_only_if_eval = skip_only_if_eval
57
58
  end
@@ -118,18 +119,18 @@ module Inspec
118
119
 
119
120
  define_method :register_control do |control, &block|
120
121
  if @skip_file
121
- ::Inspec::Rule.set_skip_rule(control, true)
122
+ ::Inspec::Rule.set_skip_rule(control, true, @skip_file_message)
122
123
  end
123
124
 
124
125
  unless profile_context_owner.profile_supports_platform?
125
126
  platform = inspec.platform
126
127
  msg = "Profile #{profile_context_owner.profile_id} is not supported on platform #{platform.name}/#{platform.release}."
127
- ::Inspec::Rule.set_skip_rule(control, msg)
128
+ ::Inspec::Rule.set_skip_rule(control, true, msg)
128
129
  end
129
130
 
130
131
  unless profile_context_owner.profile_supports_inspec_version?
131
132
  msg = "Profile #{profile_context_owner.profile_id} is not supported on InSpec version (#{Inspec::VERSION})."
132
- ::Inspec::Rule.set_skip_rule(control, msg)
133
+ ::Inspec::Rule.set_skip_rule(control, true, msg)
133
134
  end
134
135
 
135
136
  profile_context_owner.register_rule(control, &block) unless control.nil?
@@ -144,19 +145,19 @@ module Inspec
144
145
  profile_context_owner.unregister_rule(id)
145
146
  end
146
147
 
147
- define_method :only_if do |&block|
148
+ define_method :only_if do |message = nil, &block|
148
149
  return unless block
149
150
  return if @skip_file == true
150
151
  return if @skip_only_if_eval == true
151
152
 
152
153
  return if block.yield == true
153
-
154
154
  # Apply `set_skip_rule` for other rules in the same file
155
155
  profile_context_owner.rules.values.each do |r|
156
156
  sources_match = r.source_file == block.source_location[0]
157
- Inspec::Rule.set_skip_rule(r, true) if sources_match
157
+ Inspec::Rule.set_skip_rule(r, true, message) if sources_match
158
158
  end
159
159
 
160
+ @skip_file_message = message
160
161
  @skip_file = true
161
162
  end
162
163
 
@@ -1,6 +1,5 @@
1
1
  # encoding: utf-8
2
2
  require 'inspec/cached_fetcher'
3
- require 'inspec/dependencies/dependency_set'
4
3
  require 'semverse'
5
4
 
6
5
  module Inspec
@@ -2,8 +2,7 @@
2
2
  # author: Dominik Richter
3
3
  # author: Christoph Hartmann
4
4
 
5
- require 'inspec/plugins'
6
- require 'utils/plugin_registry'
5
+ require 'inspec/plugin/v1'
7
6
 
8
7
  module Inspec
9
8
  class FetcherRegistry < PluginRegistry
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # author: Steven Danna
3
3
  # author: Victoria Jeffrey
4
- require 'inspec/plugins/resource'
4
+ require 'inspec/plugin/v1/plugin_types/resource'
5
5
  require 'inspec/dsl_shared'
6
6
 
7
7
  module Inspec
@@ -0,0 +1,2 @@
1
+ require 'inspec/plugin/v1/plugins'
2
+ require 'inspec/plugin/v1/registry'
@@ -2,6 +2,8 @@
2
2
  # author: Christoph Hartmann
3
3
  # author: Dominik Richter
4
4
 
5
+ require 'inspec/plugin/v1/registry'
6
+
5
7
  module Inspec
6
8
  module Plugins
7
9
  # stores all CLI plugin, we expect those to the `Thor` subclasses
@@ -1,8 +1,8 @@
1
1
  # encoding: utf-8
2
2
  # author: Dominik Richter
3
3
  # author: Christoph Hartmann
4
- require 'utils/plugin_registry'
5
4
  require 'inspec/file_provider'
5
+ require 'inspec/plugin/v1/registry'
6
6
 
7
7
  module Inspec
8
8
  module Plugins
@@ -2,7 +2,7 @@
2
2
  # author: Dominik Richter
3
3
  # author: Christoph Hartmann
4
4
 
5
- require 'utils/plugin_registry'
5
+ require 'inspec/plugin/v1/registry'
6
6
 
7
7
  module Inspec
8
8
  module Plugins
@@ -2,7 +2,7 @@
2
2
  # author: Dominik Richter
3
3
  # author: Christoph Hartmann
4
4
 
5
- require 'utils/plugin_registry'
5
+ require 'inspec/plugin/v1/registry'
6
6
 
7
7
  module Inspec
8
8
  module Plugins
@@ -6,12 +6,14 @@ require 'forwardable'
6
6
 
7
7
  module Inspec
8
8
  # Resource Plugins
9
+ # NOTE: the autoloading here is rendered moot by the fact that
10
+ # all core plugins are `require`'d by the base inspec.rb
9
11
  module Plugins
10
- autoload :Resource, 'inspec/plugins/resource'
11
- autoload :CLI, 'inspec/plugins/cli'
12
- autoload :Fetcher, 'inspec/plugins/fetcher'
13
- autoload :SourceReader, 'inspec/plugins/source_reader'
14
- autoload :Secret, 'inspec/plugins/secret'
12
+ autoload :Resource, 'inspec/plugin/v1/plugin_types/resource'
13
+ autoload :CLI, 'inspec/plugin/v1/plugin_types/cli'
14
+ autoload :Fetcher, 'inspec/plugin/v1/plugin_types/fetcher'
15
+ autoload :SourceReader, 'inspec/plugin/v1/plugin_types/source_reader'
16
+ autoload :Secret, 'inspec/plugin/v1/plugin_types/secret'
15
17
  end
16
18
 
17
19
  # PLEASE NOTE: The Plugin system is an internal mechanism for connecting
@@ -0,0 +1,30 @@
1
+ require 'inspec/errors'
2
+
3
+ module Inspec
4
+ module Plugin
5
+ module V2
6
+ class Exception < Inspec::Error; end
7
+ class ConfigError < Inspec::Plugin::V2::Exception; end
8
+ class LoadError < Inspec::Plugin::V2::Exception; end
9
+ end
10
+ end
11
+ end
12
+
13
+ require_relative 'v2/registry'
14
+ require_relative 'v2/loader'
15
+ require_relative 'v2/plugin_base'
16
+
17
+ # Load all plugin type base classes
18
+ Dir.glob(File.join(__dir__, 'v2', 'plugin_types', '*.rb')).each { |file| require file }
19
+
20
+ module Inspec
21
+ # Provides the base class that plugin implementors should use.
22
+ def self.plugin(version, plugin_type = nil)
23
+ unless version == 2
24
+ raise 'Only plugins version 2 is supported!'
25
+ end
26
+
27
+ return Inspec::Plugin::V2::PluginBase if plugin_type.nil?
28
+ Inspec::Plugin::V2::PluginBase.base_class_for_type(plugin_type)
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ module Inspec::Plugin::V2
2
+ Activator = Struct.new(
3
+ :plugin_name,
4
+ :plugin_type,
5
+ :activator_name,
6
+ :activated,
7
+ :exception,
8
+ :activation_proc,
9
+ :implementation_class,
10
+ ) do
11
+ def initialize(*)
12
+ super
13
+ self[:activated] = false
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,204 @@
1
+ require 'json'
2
+ require 'inspec/log'
3
+
4
+ # Add the current directory of the process to the load path
5
+ $LOAD_PATH.unshift('.') unless $LOAD_PATH.include?('.')
6
+ # Add the InSpec source root directory to the load path
7
+ folder = File.expand_path(File.join('..', '..', '..', '..'), __dir__)
8
+ $LOAD_PATH.unshift(folder) unless $LOAD_PATH.include?('folder')
9
+
10
+ module Inspec::Plugin::V2
11
+ class Loader
12
+ attr_reader :registry, :options
13
+
14
+ def initialize(options = {})
15
+ @options = options
16
+ @registry = Inspec::Plugin::V2::Registry.instance
17
+ determine_plugin_conf_file
18
+ read_conf_file
19
+ unpack_conf_file
20
+ detect_bundled_plugins unless options[:omit_bundles]
21
+ end
22
+
23
+ def load_all
24
+ registry.each do |plugin_name, plugin_details|
25
+ # We want to capture literally any possible exception here, since we are storing them.
26
+ # rubocop: disable Lint/RescueException
27
+ begin
28
+ # We could use require, but under testing, we need to repeatedly reload the same
29
+ # plugin.
30
+ if plugin_details.entry_point.include?('test/unit/mock/plugins')
31
+ load plugin_details.entry_point + '.rb'
32
+ else
33
+ require plugin_details.entry_point
34
+ end
35
+ plugin_details.loaded = true
36
+ annotate_status_after_loading(plugin_name)
37
+ rescue ::Exception => ex
38
+ plugin_details.load_exception = ex
39
+ Inspec::Log.error "Could not load plugin #{plugin_name}"
40
+ end
41
+ # rubocop: enable Lint/RescueException
42
+ end
43
+ end
44
+
45
+ # This should possibly be in either lib/inspec/cli.rb or Registry
46
+ def exit_on_load_error
47
+ if registry.any_load_failures?
48
+ Inspec::Log.error 'Errors were encountered while loading plugins...'
49
+ registry.plugin_statuses.select(&:load_exception).each do |plugin_status|
50
+ Inspec::Log.error 'Plugin name: ' + plugin_status.name.to_s
51
+ Inspec::Log.error 'Error: ' + plugin_status.load_exception.message
52
+ if ARGV.include?('--debug')
53
+ Inspec::Log.error 'Exception: ' + plugin_status.load_exception.class.name
54
+ Inspec::Log.error 'Trace: ' + plugin_status.load_exception.backtrace.join("\n")
55
+ end
56
+ end
57
+ Inspec::Log.error('Run again with --debug for a stacktrace.') unless ARGV.include?('--debug')
58
+ exit 2
59
+ end
60
+ end
61
+
62
+ def activate(plugin_type, hook_name)
63
+ activator = registry.find_activators(plugin_type: plugin_type, activation_name: hook_name).first
64
+ # We want to capture literally any possible exception here, since we are storing them.
65
+ # rubocop: disable Lint/RescueException
66
+ begin
67
+ impl_class = activator.activation_proc.call
68
+ activator.activated = true
69
+ activator.implementation_class = impl_class
70
+ rescue Exception => ex
71
+ activator.exception = ex
72
+ Inspec::Log.error "Could not activate #{activator.plugin_type} hook named '#{activator.activator_name}' for plugin #{plugin_name}"
73
+ end
74
+ # rubocop: enable Lint/RescueException
75
+ end
76
+
77
+ def activate_mentioned_cli_plugins(cli_args = ARGV)
78
+ # Get a list of CLI plugin activation hooks
79
+ registry.find_activators(plugin_type: :cli_command).each do |act|
80
+ next if act.activated
81
+ # If there is anything in the CLI args with the same name, activate it
82
+ # If the word 'help' appears in the first position, load all CLI plugins
83
+ if cli_args.include?(act.activator_name.to_s) || cli_args[0] == 'help'
84
+ activate(:cli_command, act.activator_name)
85
+ act.implementation_class.register_with_thor
86
+ end
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def annotate_status_after_loading(plugin_name)
93
+ status = registry[plugin_name]
94
+ return if status.api_generation == 2 # Gen2 have self-annotating superclasses
95
+ case status.installation_type
96
+ when :bundle
97
+ annotate_bundle_plugin_status_after_load(plugin_name)
98
+ else
99
+ # TODO: are there any other cases? can this whole thing be eliminated?
100
+ raise "I only know how to annotate :bundle plugins when trying to load plugin #{plugin_name}" unless status.installation_type == :bundle
101
+ end
102
+ end
103
+
104
+ def annotate_bundle_plugin_status_after_load(plugin_name)
105
+ # HACK: we're relying on the fact that all bundles are gen0 and cli type
106
+ status = registry[plugin_name]
107
+ status.api_generation = 0
108
+ act = Activator.new
109
+ act.activated = true
110
+ act.plugin_type = :cli_command
111
+ act.plugin_name = plugin_name
112
+ act.activator_name = :default
113
+ status.activators = [act]
114
+
115
+ v0_subcommand_name = plugin_name.to_s.gsub('inspec-', '')
116
+ status.plugin_class = Inspec::Plugins::CLI.subcommands[v0_subcommand_name][:klass]
117
+ end
118
+
119
+ def detect_bundled_plugins
120
+ bundle_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'bundles'))
121
+ globs = [
122
+ File.join(bundle_dir, 'inspec-*.rb'),
123
+ File.join(bundle_dir, 'train-*.rb'),
124
+ ]
125
+ Dir.glob(globs).each do |loader_file|
126
+ name = File.basename(loader_file, '.rb').gsub(/^(inspec|train)-/, '')
127
+ status = Inspec::Plugin::V2::Status.new
128
+ status.name = name
129
+ status.entry_point = loader_file
130
+ status.installation_type = :bundle
131
+ status.loaded = false
132
+ registry[name] = status
133
+ end
134
+ end
135
+
136
+ def determine_plugin_conf_file
137
+ @plugin_conf_file_path = ENV['INSPEC_CONFIG_DIR'] ? ENV['INSPEC_CONFIG_DIR'] : File.join(Dir.home, '.inspec')
138
+ @plugin_conf_file_path = File.join(@plugin_conf_file_path, 'plugins.json')
139
+ end
140
+
141
+ def read_conf_file
142
+ if File.exist?(@plugin_conf_file_path)
143
+ @plugin_file_contents = JSON.parse(File.read(@plugin_conf_file_path))
144
+ else
145
+ @plugin_file_contents = {
146
+ 'plugins_config_version' => '1.0.0',
147
+ 'plugins' => [],
148
+ }
149
+ end
150
+ rescue JSON::ParserError => e
151
+ raise Inspec::Plugin::V2::ConfigError, "Failed to load plugins JSON configuration from #{@plugin_conf_file_path}:\n#{e}"
152
+ end
153
+
154
+ def unpack_conf_file
155
+ validate_conf_file
156
+ @plugin_file_contents['plugins'].each do |plugin_json|
157
+ status = Inspec::Plugin::V2::Status.new
158
+ status.name = plugin_json['name'].to_sym
159
+ status.loaded = false
160
+ status.installation_type = plugin_json['installation_type'].to_sym || :gem
161
+ case status.installation_type
162
+ when :gem
163
+ status.entry_point = status.name
164
+ status.version = plugin_json['version']
165
+ when :path
166
+ status.entry_point = plugin_json['installation_path']
167
+ end
168
+
169
+ registry[status.name] = status
170
+ end
171
+ end
172
+
173
+ def validate_conf_file
174
+ unless @plugin_file_contents['plugins_config_version'] == '1.0.0'
175
+ raise Inspec::Plugin::V2::ConfigError, "Unsupported plugins.json file version #{@plugin_file_contents['plugins_config_version']} at #{@plugin_conf_file_path} - currently support versions: 1.0.0"
176
+ end
177
+
178
+ plugin_entries = @plugin_file_contents['plugins']
179
+ unless plugin_entries.is_a?(Array)
180
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - should have a top-level key named 'plugins', whose value is an array"
181
+ end
182
+
183
+ plugin_entries.each do |plugin_entry|
184
+ unless plugin_entry.is_a? Hash
185
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'plugins' entry should be a Hash / JSON object"
186
+ end
187
+
188
+ unless plugin_entry.key? 'name'
189
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'plugins' entry must have a 'name' field"
190
+ end
191
+
192
+ next unless plugin_entry.key?('installation_type')
193
+ unless %w{gem path}.include? plugin_entry['installation_type']
194
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'installation_type' must be one of 'gem' or 'path'"
195
+ end
196
+
197
+ next unless plugin_entry['installation_type'] == 'path'
198
+ unless plugin_entry.key?('installation_path')
199
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'plugins' entry with a 'path' installation_type must provide an 'installation_path' field"
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end