inspec-core 2.2.61 → 2.2.64

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,98 @@
1
+ module Inspec::Plugin::V2
2
+ # Base class for all plugins. Specific plugin types *may* inherit from this; but they must register with it.
3
+ class PluginBase
4
+ # rubocop: disable Style/ClassVars
5
+ @@plugin_type_classes = {}
6
+ # rubocop: enable Style/ClassVars
7
+
8
+ #=====================================================================#
9
+ # Management Methods
10
+ #=====================================================================#
11
+
12
+ # The global registry.
13
+ # @returns [Inspec::Plugin::V2::Registry] the singleton Plugin Registry object.
14
+ def self.registry
15
+ Inspec::Plugin::V2::Registry.instance
16
+ end
17
+
18
+ # Inform the plugin system of a new plgin type.
19
+ # This has the following effects:
20
+ # * enables Inspec.plugin(2, :your_type_here) to return the plugin
21
+ # type base class
22
+ # * defines the DSL method with the same name as the plugin type.
23
+ #
24
+ # @param [Symbol] plugin_type_name
25
+ # @param [Class] the plugin type class, defaults to assuming inheritance
26
+ def self.register_plugin_type(plugin_type_name, new_plugin_type_base_class = self)
27
+ new_dsl_method_name = plugin_type_name
28
+
29
+ # This lets the Inspec.plugin(2,:your_plugin_type) work
30
+ @@plugin_type_classes[plugin_type_name] = new_plugin_type_base_class
31
+
32
+ # This part defines the DSL command to register a concrete plugin's implementation of a plugin type
33
+ Inspec::Plugin::V2::PluginBase.define_singleton_method(new_dsl_method_name) do |hook_name, &hook_body|
34
+ plugin_concrete_class = self
35
+
36
+ # Verify class is registered (i.e. plugin_name has been called)
37
+ status = registry.find_status_by_class(plugin_concrete_class)
38
+ if status.nil?
39
+ raise Inspec::Plugin::V2::LoadError, "You must call 'plugin_name' prior to calling #{plugin_type_name} for plugin class #{plugin_concrete_class}"
40
+ end
41
+
42
+ # Construct the Activator record
43
+ activator = Inspec::Plugin::V2::Activator.new
44
+ activator.plugin_name = plugin_concrete_class.plugin_name
45
+ activator.plugin_type = plugin_type_name
46
+ activator.activator_name = hook_name.to_sym
47
+ activator.activation_proc = hook_body
48
+
49
+ status.activators << activator
50
+ end
51
+ end
52
+
53
+ # Determine the base class for a given plugin type
54
+ # @param [Symbol] plugin_type_name
55
+ # @returns [Class] the plugin type base class
56
+ def self.base_class_for_type(plugin_type_name)
57
+ @@plugin_type_classes[plugin_type_name]
58
+ end
59
+
60
+ #=====================================================================#
61
+ # DSL Methods
62
+ #=====================================================================#
63
+
64
+ # If no name provided, looks up a known plugin by class and returns the name.
65
+ #
66
+ # DSL method to declare a plugin. Once this has been called, the plugin will certainly be
67
+ # registered (known) with the Registry, and is eligible to be activated.
68
+ # This mainly handles status annotation.
69
+ #
70
+ # @param [Symbol] Name of the plugin. If a string is provided, it is converted to a Symbol.
71
+ # @returns [Symbol] Name of the plugin
72
+ def self.plugin_name(name = nil)
73
+ reg = Inspec::Plugin::V2::Registry.instance
74
+ return reg.find_status_by_class(self).name if name.nil?
75
+
76
+ name = name.to_sym
77
+
78
+ # Typically our status will already exist in the registry, from loading the
79
+ # plugin.json. If we're being loaded, presumably entry_point,
80
+ # installation_type, version
81
+ # are known.
82
+ unless reg.known_plugin?(name)
83
+ # Under some testing situations, we may not pre-exist.
84
+ status = Inspec::Plugin::V2::Status.new
85
+ reg.register(name, status)
86
+ status.entry_point = 'inline'
87
+ status.installation_type = :mock_inline
88
+ end
89
+
90
+ status = reg[name]
91
+ status.api_generation = 2
92
+ status.plugin_class = self
93
+ status.name = name
94
+
95
+ name
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,27 @@
1
+ require 'inspec/base_cli'
2
+
3
+ module Inspec::Plugin::V2::PluginType
4
+ class CliCommand < Inspec::BaseCLI
5
+ # This class MUST inherit from Thor, which makes it a bit awkward to register the plugin subtype
6
+ # Since we can't inherit from PluginBase, we use the two-arg form of register_plugin_type
7
+ Inspec::Plugin::V2::PluginBase.register_plugin_type(:cli_command, self)
8
+
9
+ # Provide a description for the command group.
10
+ def self.subcommand_desc(usage_msg, desc_msg)
11
+ @usage_msg = usage_msg
12
+ @desc_msg = desc_msg
13
+ end
14
+
15
+ # Register the command group with Thor. This must be called on the implementation class AFTER
16
+ # the the cli_command activator has been called
17
+ def self.register_with_thor
18
+ # Figure out my activator name (= subcommand group name)
19
+ subcommand_name = Inspec::Plugin::V2::Registry.instance \
20
+ .find_activators(plugin_type: :cli_command, implementation_class: self) \
21
+ .first.activator_name.to_s
22
+
23
+ # Register with Thor
24
+ Inspec::InspecCLI.register(self, subcommand_name, @usage_msg, @desc_msg, {})
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ module Inspec::Plugin::V2::PluginType
2
+ # Test plugin type
3
+ class Mock < Inspec::Plugin::V2::PluginBase
4
+ register_plugin_type(:mock_plugin_type)
5
+
6
+ # This is the API for the mock plugin type: when a mock plugin is
7
+ # activated, it is expected to be able to respond to this, and "do something"
8
+ def mock_hook
9
+ raise NotImplementedError, 'Mock plugins must implement mock_hook'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,76 @@
1
+ require 'forwardable'
2
+ require 'singleton'
3
+ require_relative 'status'
4
+ require_relative 'activator'
5
+
6
+ module Inspec::Plugin::V2
7
+ class Registry
8
+ include Singleton
9
+ extend Forwardable
10
+
11
+ attr_reader :registry
12
+ def_delegator :registry, :each
13
+ def_delegator :registry, :[]
14
+ def_delegator :registry, :key?, :known_plugin?
15
+ def_delegator :registry, :keys, :plugin_names
16
+ def_delegator :registry, :values, :plugin_statuses
17
+ def_delegator :registry, :select
18
+
19
+ def initialize
20
+ @registry = {}
21
+ end
22
+
23
+ def any_load_failures?
24
+ !plugin_statuses.select(&:load_exception).empty?
25
+ end
26
+
27
+ def loaded_plugin?(name)
28
+ registry.dig(name, :loaded)
29
+ end
30
+
31
+ def loaded_count
32
+ registry.values.select(&:loaded).count
33
+ end
34
+
35
+ def known_count
36
+ registry.values.count
37
+ end
38
+
39
+ def loaded_plugin_names
40
+ registry.values.select(&:loaded).map(&:name)
41
+ end
42
+
43
+ def find_status_by_class(klass)
44
+ registry.values.detect { |status| status.plugin_class == klass }
45
+ end
46
+
47
+ # Finds Activators matching criteria (all optional) you specify as a Hash.
48
+ # @param [Symbol] plugin_name Restricts the search to the given plugin
49
+ # @param [Symbol] plugin_type Restricts the search to the given plugin type
50
+ # @param [Symbol] activator_name Name of the activator
51
+ # @param [Class] implementation_class Implementation class returned by an already-actived plugin type
52
+ # @returns [Array] Possibly empty array of Activators
53
+ def find_activators(filters = {})
54
+ plugin_statuses.map(&:activators).flatten.select do |act|
55
+ [:plugin_name, :plugin_type, :activator_name, :implementation_class].all? do |criteria|
56
+ !filters.key?(criteria) || act[criteria] == filters[criteria]
57
+ end
58
+ end
59
+ end
60
+
61
+ def register(name, status)
62
+ if known_plugin? name
63
+ Inspec::Log.warn "PluginLoader: refusing to re-register plugin '#{name}': an existing plugin with that name was loaded via #{existing.installation_type}-loading from #{existing.entry_point}"
64
+ else
65
+ registry[name.to_sym] = status
66
+ end
67
+ end
68
+
69
+ alias []= register
70
+
71
+ # Provided for test support. Purges the registry.
72
+ def __reset
73
+ @registry.clear
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ module Inspec::Plugin::V2
2
+ # Track loading status of each plugin. These are the elements of the Registry.
3
+ #
4
+ # Lifecycle of an installed plugin:
5
+ # If present in the config file, bundled, or core, it is "known"
6
+ # All known plugins are loaded. v1 plugins auto-activate. All loaded plugins know their version.
7
+ # v2 plugins activate when they are used. All activated plugins know their implementation class.
8
+ Status = Struct.new(
9
+ :activators, # Array of Activators - where plugin_type info gets stored
10
+ :api_generation, # 0,1,2 # TODO: convert all bundled (v0) to v2
11
+ :plugin_class, # Plugin class
12
+ :entry_point, # a gem name or filesystem path
13
+ :installation_type, # :gem, :path, :core, bundle # TODO: combine core and bundle
14
+ :loaded, # true, false False could mean not attempted or failed
15
+ :load_exception, # Exception class if it failed to load
16
+ :name, # String name
17
+ :version, # three-digit version. Core / bundled plugins use InSpec version here.
18
+ ) do
19
+ def initialize(*)
20
+ super
21
+ self[:activators] = []
22
+ self[:loaded] = false
23
+ end
24
+
25
+ def plugin_types
26
+ activators.map(&:plugin_type).uniq.sort
27
+ end
28
+ end
29
+ end
@@ -1,7 +1,7 @@
1
1
  require 'inspec/reporters/base'
2
2
  require 'inspec/reporters/cli'
3
3
  require 'inspec/reporters/json'
4
- require 'inspec/reporters/json_merged'
4
+ require 'inspec/reporters/json_automate'
5
5
  require 'inspec/reporters/json_min'
6
6
  require 'inspec/reporters/junit'
7
7
  require 'inspec/reporters/automate'
@@ -17,6 +17,10 @@ module Inspec::Reporters
17
17
  reporter = Inspec::Reporters::CLI.new(config)
18
18
  when 'json'
19
19
  reporter = Inspec::Reporters::Json.new(config)
20
+ # This reporter is only used for Chef internal. We reserve the
21
+ # right to introduce breaking changes to this reporter at any time.
22
+ when 'json-automate'
23
+ reporter = Inspec::Reporters::JsonAutomate.new(config)
20
24
  when 'json-min'
21
25
  reporter = Inspec::Reporters::JsonMin.new(config)
22
26
  when 'junit'
@@ -4,7 +4,7 @@ require 'json'
4
4
  require 'net/http'
5
5
 
6
6
  module Inspec::Reporters
7
- class Automate < JsonMerged
7
+ class Automate < JsonAutomate
8
8
  def initialize(config)
9
9
  super(config)
10
10
 
@@ -3,7 +3,7 @@
3
3
  require 'json'
4
4
 
5
5
  module Inspec::Reporters
6
- class JsonMerged < Json
6
+ class JsonAutomate < Json
7
7
  def initialize(config)
8
8
  super(config)
9
9
  @profiles = []
@@ -2,7 +2,7 @@
2
2
  # copyright: 2015, Vulcano Security GmbH
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
- require 'inspec/plugins'
5
+ require 'inspec/plugin/v1'
6
6
 
7
7
  module Inspec
8
8
  class ProfileNotFound < StandardError; end
data/lib/inspec/rule.rb CHANGED
@@ -42,7 +42,7 @@ module Inspec
42
42
  @__rule_id = id
43
43
  @__profile_id = profile_id
44
44
  @__checks = []
45
- @__skip_rule = nil
45
+ @__skip_rule = {}
46
46
  @__merge_count = 0
47
47
  @__merge_changes = []
48
48
  @__skip_only_if_eval = opts[:skip_only_if_eval]
@@ -118,11 +118,12 @@ module Inspec
118
118
  #
119
119
  # @param [Type] &block returns true if tests are added, false otherwise
120
120
  # @return [nil]
121
- def only_if
121
+ def only_if(message = nil)
122
122
  return unless block_given?
123
123
  return if @__skip_only_if_eval == true
124
124
 
125
- @__skip_rule ||= !yield
125
+ @__skip_rule[:result] ||= !yield
126
+ @__skip_rule[:message] = message
126
127
  end
127
128
 
128
129
  # Describe will add one or more tests to this control. There is 2 ways
@@ -174,8 +175,9 @@ module Inspec
174
175
  rule.instance_variable_get(:@__skip_rule)
175
176
  end
176
177
 
177
- def self.set_skip_rule(rule, value)
178
- rule.instance_variable_set(:@__skip_rule, value)
178
+ def self.set_skip_rule(rule, value, message = nil)
179
+ rule.instance_variable_set(:@__skip_rule,
180
+ { result: value, message: message })
179
181
  end
180
182
 
181
183
  def self.merge_count(rule)
@@ -187,9 +189,13 @@ module Inspec
187
189
  end
188
190
 
189
191
  def self.prepare_checks(rule)
190
- msg = skip_status(rule)
191
- return checks(rule) unless msg
192
- msg = 'Skipped control due to only_if condition.' if msg == true
192
+ skip_check = skip_status(rule)
193
+ return checks(rule) unless skip_check[:result].eql?(true)
194
+ if skip_check[:message]
195
+ msg = "Skipped control due to only_if condition: #{skip_check[:message]}"
196
+ else
197
+ msg = 'Skipped control due to only_if condition.'
198
+ end
193
199
 
194
200
  # TODO: we use os as the carrier here, but should consider
195
201
  # a separate resource to do skipping
@@ -2,8 +2,7 @@
2
2
  # author: Christoph Hartmann
3
3
  # author: Dominik Richter
4
4
 
5
- require 'inspec/plugins'
6
- require 'utils/plugin_registry'
5
+ require 'inspec/plugin/v1'
7
6
 
8
7
  module Inspec
9
8
  SecretsBackend = PluginRegistry.new
@@ -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
  # Pre-checking of target resolution. Make sure that SourceReader plugins
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '2.2.61'
7
+ VERSION = '2.2.64'
8
8
  end
@@ -125,7 +125,7 @@ module Inspec::Resources
125
125
  end
126
126
 
127
127
  def read_file(path)
128
- @files_contents[path] ||= read_file_content(path)
128
+ @files_contents[path] ||= read_file_content(path, true)
129
129
  end
130
130
 
131
131
  def conf_dir
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: 2.2.61
4
+ version: 2.2.64
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-09 00:00:00.000000000 Z
11
+ date: 2018-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train-core
@@ -294,6 +294,7 @@ files:
294
294
  - docs/dev/control-eval.md
295
295
  - docs/dev/filtertable-internals.md
296
296
  - docs/dev/filtertable-usage.md
297
+ - docs/dev/plugins.md
297
298
  - docs/dsl_inspec.md
298
299
  - docs/dsl_resource.md
299
300
  - docs/glossary.md
@@ -548,12 +549,22 @@ files:
548
549
  - lib/inspec/objects/tag.rb
549
550
  - lib/inspec/objects/test.rb
550
551
  - lib/inspec/objects/value.rb
551
- - lib/inspec/plugins.rb
552
- - lib/inspec/plugins/cli.rb
553
- - lib/inspec/plugins/fetcher.rb
554
- - lib/inspec/plugins/resource.rb
555
- - lib/inspec/plugins/secret.rb
556
- - lib/inspec/plugins/source_reader.rb
552
+ - lib/inspec/plugin/v1.rb
553
+ - lib/inspec/plugin/v1/plugin_types/cli.rb
554
+ - lib/inspec/plugin/v1/plugin_types/fetcher.rb
555
+ - lib/inspec/plugin/v1/plugin_types/resource.rb
556
+ - lib/inspec/plugin/v1/plugin_types/secret.rb
557
+ - lib/inspec/plugin/v1/plugin_types/source_reader.rb
558
+ - lib/inspec/plugin/v1/plugins.rb
559
+ - lib/inspec/plugin/v1/registry.rb
560
+ - lib/inspec/plugin/v2.rb
561
+ - lib/inspec/plugin/v2/activator.rb
562
+ - lib/inspec/plugin/v2/loader.rb
563
+ - lib/inspec/plugin/v2/plugin_base.rb
564
+ - lib/inspec/plugin/v2/plugin_types/cli.rb
565
+ - lib/inspec/plugin/v2/plugin_types/mock.rb
566
+ - lib/inspec/plugin/v2/registry.rb
567
+ - lib/inspec/plugin/v2/status.rb
557
568
  - lib/inspec/polyfill.rb
558
569
  - lib/inspec/profile.rb
559
570
  - lib/inspec/profile_context.rb
@@ -563,7 +574,7 @@ files:
563
574
  - lib/inspec/reporters/base.rb
564
575
  - lib/inspec/reporters/cli.rb
565
576
  - lib/inspec/reporters/json.rb
566
- - lib/inspec/reporters/json_merged.rb
577
+ - lib/inspec/reporters/json_automate.rb
567
578
  - lib/inspec/reporters/json_min.rb
568
579
  - lib/inspec/reporters/junit.rb
569
580
  - lib/inspec/reporters/yaml.rb
@@ -701,7 +712,6 @@ files:
701
712
  - lib/utils/object_traversal.rb
702
713
  - lib/utils/parser.rb
703
714
  - lib/utils/pkey_reader.rb
704
- - lib/utils/plugin_registry.rb
705
715
  - lib/utils/simpleconfig.rb
706
716
  - lib/utils/spdx.rb
707
717
  - lib/utils/spdx.txt