inspec-core 3.0.52 → 3.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 792ec0ed85e8c7f56b0f1756fe68ee6930cdec48752f160c4dd65e25886ca6be
4
- data.tar.gz: f93bbe45badce127b6b32cdf701f894bcc6ddb213f1eb900295271417e4cd095
3
+ metadata.gz: d789972e56d9edede66194f64a2539d91ceb4d5df71067d22d0f3a8a3a772d95
4
+ data.tar.gz: b0d96149a8d311b9e6926286f7369da1b944eb47d915a33a06fc0dad2aa046ac
5
5
  SHA512:
6
- metadata.gz: 7bec79ae8ede29a52874c4cfbeba2b3a181c4823130a226542ee5070c2733ffd9b24c8167331c48233250f0f5ce6aa40f4b18139979acc7806e9571f980b312c
7
- data.tar.gz: 7d30ee1df7bd9b78406541d1adae0ae2e19f746cf4dbc788f26c912346aab9f8417da54fb37bfbc6b2605be45675e83112e991fd8f0f05be20bcd2cf3bee8585
6
+ metadata.gz: aa9bc42927433d0ba2d7e30f03bce48bf2b79031706e8ab72659d509d78ac7342402d59c3782596e01b86b0ef71c4d227856b85b52d06025ea3295eb829677e6
7
+ data.tar.gz: d66ac71b3d480127c66ae14999c6e66f5b680a5b87e0e8beda105f1c08edb198df987a827553676c30dd3d2289517aa20d92499fed599993920a28349e434d42
data/CHANGELOG.md CHANGED
@@ -1,27 +1,47 @@
1
1
  # Change Log
2
2
  <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
3
- <!-- latest_release 3.0.52 -->
4
- ## [v3.0.52](https://github.com/inspec/inspec/tree/v3.0.52) (2018-11-15)
3
+ <!-- latest_release 3.0.61 -->
4
+ ## [v3.0.61](https://github.com/inspec/inspec/tree/v3.0.61) (2018-11-29)
5
5
 
6
- #### Merged Pull Requests
7
- - Load the compliance plugin when the fetcher is needed [#3609](https://github.com/inspec/inspec/pull/3609) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
6
+ #### New Features
7
+ - Plugin Type: DSLs [#3557](https://github.com/inspec/inspec/pull/3557) ([clintoncwolfe](https://github.com/clintoncwolfe))
8
8
  <!-- latest_release -->
9
9
 
10
- <!-- release_rollup since=3.0.46 -->
11
- ### Changes since 3.0.46 release
10
+ <!-- release_rollup since=3.0.52 -->
11
+ ### Changes since 3.0.52 release
12
12
 
13
- #### Merged Pull Requests
14
- - Load the compliance plugin when the fetcher is needed [#3609](https://github.com/inspec/inspec/pull/3609) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 3.0.52 -->
13
+ #### New Features
14
+ - Plugin Type: DSLs [#3557](https://github.com/inspec/inspec/pull/3557) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 3.0.61 -->
15
15
 
16
16
  #### Bug Fixes
17
- - Adds protection against zipslip vulnerability [#3604](https://github.com/inspec/inspec/pull/3604) ([hdost](https://github.com/hdost)) <!-- 3.0.51 -->
17
+ - package: fix package detection on windows [#3607](https://github.com/inspec/inspec/pull/3607) ([mhackethal](https://github.com/mhackethal)) <!-- 3.0.58 -->
18
+ - www: remove jquery sticky on the sidebar [#3623](https://github.com/inspec/inspec/pull/3623) ([arlimus](https://github.com/arlimus)) <!-- 3.0.57 -->
18
19
 
19
20
  #### Enhancements
20
- - Adding --no-pager to service checks [#3592](https://github.com/inspec/inspec/pull/3592) ([fernandoalex](https://github.com/fernandoalex)) <!-- 3.0.50 -->
21
- - aws_security_group: Query against other security group ids in allow_* matchers [#3576](https://github.com/inspec/inspec/pull/3576) ([j00p34](https://github.com/j00p34)) <!-- 3.0.49 -->
21
+ - filesystem: improve Windows support [#3606](https://github.com/inspec/inspec/pull/3606) ([mhackethal](https://github.com/mhackethal)) <!-- 3.0.56 -->
22
+
23
+ #### Merged Pull Requests
24
+ - Add SQLcl to Oracledb_session Doc [#3632](https://github.com/inspec/inspec/pull/3632) ([ibsavage](https://github.com/ibsavage)) <!-- 3.0.60 -->
25
+ - lc/add-aws-platform-template [#3622](https://github.com/inspec/inspec/pull/3622) ([Caprowni](https://github.com/Caprowni)) <!-- 3.0.59 -->
26
+ - Revert setting RSpec expectation syntax to &#39;should&#39; mode [#3620](https://github.com/inspec/inspec/pull/3620) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 3.0.55 -->
27
+ - Improvements to the functional helper run_inspec_process [#3603](https://github.com/inspec/inspec/pull/3603) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 3.0.54 -->
28
+ - Create a class to handle the plugins.json file [#3575](https://github.com/inspec/inspec/pull/3575) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 3.0.53 -->
22
29
  <!-- release_rollup -->
23
30
 
24
31
  <!-- latest_stable_release -->
32
+ ## [v3.0.52](https://github.com/inspec/inspec/tree/v3.0.52) (2018-11-15)
33
+
34
+ #### Enhancements
35
+ - aws_security_group: Query against other security group ids in allow_* matchers [#3576](https://github.com/inspec/inspec/pull/3576) ([j00p34](https://github.com/j00p34))
36
+ - Adding --no-pager to service checks [#3592](https://github.com/inspec/inspec/pull/3592) ([fernandoalex](https://github.com/fernandoalex))
37
+
38
+ #### Bug Fixes
39
+ - Adds protection against zipslip vulnerability [#3604](https://github.com/inspec/inspec/pull/3604) ([hdost](https://github.com/hdost))
40
+
41
+ #### Merged Pull Requests
42
+ - Load the compliance plugin when the fetcher is needed [#3609](https://github.com/inspec/inspec/pull/3609) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
43
+ <!-- latest_stable_release -->
44
+
25
45
  ## [v3.0.46](https://github.com/inspec/inspec/tree/v3.0.46) (2018-11-08)
26
46
 
27
47
  #### New Features
@@ -42,7 +62,6 @@
42
62
  - Fix functional tests issues with vendoring [#3572](https://github.com/inspec/inspec/pull/3572) ([jquick](https://github.com/jquick))
43
63
  - Fixes (some) ruby warnings related to functional tests [#3561](https://github.com/inspec/inspec/pull/3561) ([TheLonelyGhost](https://github.com/TheLonelyGhost))
44
64
  - Fixes broken link in documentation [#3588](https://github.com/inspec/inspec/pull/3588) ([dmccown](https://github.com/dmccown))
45
- <!-- latest_stable_release -->
46
65
 
47
66
  ## [v3.0.25](https://github.com/inspec/inspec/tree/v3.0.25) (2018-11-01)
48
67
 
@@ -29,6 +29,32 @@ module Inspec
29
29
  define_method :attribute do |name|
30
30
  Inspec::AttributeRegistry.find_attribute(name, profile_id).value
31
31
  end
32
+
33
+ # Support for Control DSL plugins.
34
+ # This is called when an unknown method is encountered
35
+ # within a control block.
36
+ def method_missing(method_name, *arguments, &block)
37
+ # Check to see if there is a control_dsl plugin activator hook with the method name
38
+ registry = Inspec::Plugin::V2::Registry.instance
39
+ hook = registry.find_activators(plugin_type: :control_dsl, activator_name: method_name).first
40
+ if hook
41
+ # OK, load the hook if it hasn't been already. We'll then know a module,
42
+ # which we can then inject into the context
43
+ registry.activate(:control_dsl, method_name) unless hook.activated?
44
+ # Inject the module's methods into the context.
45
+ # implementation_class is the field name, but this is actually a module.
46
+ self.class.include(hook.implementation_class)
47
+ # Now that the module is loaded, it defined one or more methods
48
+ # (presumably the one we were looking for.)
49
+ # We still haven't called it, so do so now.
50
+ send(method_name, *arguments, &block)
51
+ else
52
+ # If we couldn't find a plugin to match, maybe something up above has it,
53
+ # or maybe it is just a unknown method error.
54
+ super
55
+ end
56
+ end
57
+
32
58
  end
33
59
  end
34
60
 
@@ -44,7 +70,6 @@ module Inspec
44
70
  profile_context_owner = profile_context
45
71
  profile_id = profile_context.profile_id
46
72
  rule_class = rule_context(resources_dsl, profile_id)
47
-
48
73
  Class.new do # rubocop:disable Metrics/BlockLength
49
74
  include Inspec::DSL
50
75
  include Inspec::DSL::RequireOverride
data/lib/inspec/dsl.rb CHANGED
@@ -27,6 +27,29 @@ module Inspec::DSL
27
27
  add_resource(target_name, res)
28
28
  end
29
29
 
30
+ # Support for Outer Profile DSL plugins
31
+ # This is called when an unknown method is encountered
32
+ # "bare" in a control file - outside of a control or describe block.
33
+ def method_missing(method_name, *arguments, &block)
34
+ # Check to see if there is a outer_profile_dsl plugin activator hook with the method name
35
+ registry = Inspec::Plugin::V2::Registry.instance
36
+ hook = registry.find_activators(plugin_type: :outer_profile_dsl, activator_name: method_name).first
37
+ if hook
38
+ # OK, load the hook if it hasn't been already. We'll then know a module,
39
+ # which we can then inject into the context
40
+ registry.activate(:outer_profile_dsl, method_name) unless hook.activated?
41
+ # Inject the module's methods into the context
42
+ # implementation_class is the field name, but this is actually a module.
43
+ self.class.include(hook.implementation_class)
44
+ # Now that the module is loaded, it defined one or more methods
45
+ # (presumably the one we were looking for.)
46
+ # We still haven't called it, so do so now.
47
+ send(method_name, *arguments, &block)
48
+ else
49
+ super
50
+ end
51
+ end
52
+
30
53
  def self.load_spec_files_for_profile(bind_context, opts, &block)
31
54
  dependencies = opts[:dependencies]
32
55
  profile_id = opts[:profile_id]
@@ -39,6 +39,34 @@ module Inspec
39
39
  __resource_registry[@name].example(example)
40
40
  end
41
41
 
42
+ # Support for Resource DSL plugins.
43
+ # This is called when an unknown method is encountered
44
+ # within a resource class definition.
45
+ # Even tho this is defined as an instance method, it gets added to
46
+ # Inspec::Plugins::Resource via `extend`, so this is actually a class defintion.
47
+ def method_missing(method_name, *arguments, &block)
48
+ require 'inspec/plugin/v2'
49
+ # Check to see if there is a resource_dsl plugin activator hook with the method name
50
+ registry = Inspec::Plugin::V2::Registry.instance
51
+ hook = registry.find_activators(plugin_type: :resource_dsl, activator_name: method_name).first
52
+ if hook
53
+ # OK, load the hook if it hasn't been already. We'll then know a module,
54
+ # which we can then inject into the resource
55
+ registry.activate(:resource_dsl, method_name) unless hook.activated?
56
+ # Inject the module's methods into the resource as class methods.
57
+ # implementation_class is the field name, but this is actually a module.
58
+ extend(hook.implementation_class)
59
+ # Now that the module is loaded, it defined one or more methods
60
+ # (presumably the one we were looking for.)
61
+ # We still haven't called it, so do so now.
62
+ send(method_name, *arguments, &block)
63
+ else
64
+ # If we couldn't find a plugin to match, maybe something up above has it,
65
+ # or maybe it is just a unknown method error.
66
+ super
67
+ end
68
+ end
69
+
42
70
  def __resource_registry
43
71
  Inspec::Resource.registry
44
72
  end
@@ -24,6 +24,7 @@ module Inspec
24
24
  end
25
25
 
26
26
  require 'inspec/globals'
27
+ require 'inspec/plugin/v2/config_file'
27
28
  require 'inspec/plugin/v2/registry'
28
29
  require 'inspec/plugin/v2/loader'
29
30
  require 'inspec/plugin/v2/plugin_base'
@@ -0,0 +1,148 @@
1
+ require 'json'
2
+
3
+ module Inspec::Plugin::V2
4
+ # Represents the plugin config file on disk.
5
+ class ConfigFile
6
+ include Enumerable
7
+
8
+ attr_reader :path
9
+
10
+ def initialize(path = nil)
11
+ @path = path || self.class.default_path
12
+ @data = blank_structure
13
+
14
+ read_and_validate_file if File.exist?(@path)
15
+ end
16
+
17
+ # Returns the defaut path for a config file.
18
+ # This respects ENV['INSPEC_CONFIG_DIR'].
19
+ def self.default_path
20
+ File.join(Inspec.config_dir, 'plugins.json')
21
+ end
22
+
23
+ # Implement Enumerable. All Enumerable methds act
24
+ # on the plugins list, and yield Hashes that represent
25
+ # an entry.
26
+ def each(&block)
27
+ @data[:plugins].each(&block)
28
+ end
29
+
30
+ # Look for a plugin by name.
31
+ def plugin_by_name(name)
32
+ detect { |entry| entry[:name] == name.to_sym }
33
+ end
34
+
35
+ # Check for a plugin
36
+ def existing_entry?(name)
37
+ !plugin_by_name(name).nil?
38
+ end
39
+
40
+ # Add an entry with full validation.
41
+ def add_entry(proposed_entry)
42
+ unless proposed_entry.keys.all? { |field| field.is_a? Symbol }
43
+ raise Inspec::Plugin::V2::ConfigError, 'All keys to ConfigFile#add_entry must be symbols'
44
+ end
45
+
46
+ validate_entry(proposed_entry)
47
+
48
+ if existing_entry?(proposed_entry[:name])
49
+ raise Inspec::Plugin::V2::ConfigError, "Duplicate plugin name in call to ConfigFile#add_entry: '#{proposed_entry[:name]}'"
50
+ end
51
+
52
+ @data[:plugins] << proposed_entry
53
+ end
54
+
55
+ # Removes an entry specified by plugin name.
56
+ def remove_entry(name)
57
+ unless existing_entry?(name)
58
+ raise Inspec::Plugin::V2::ConfigError, "No such entry with plugin name '#{name}'"
59
+ end
60
+ @data[:plugins].delete_if { |entry| entry[:name] == name.to_sym }
61
+ end
62
+
63
+ # Save the file to disk as a JSON structure at the path.
64
+ def save
65
+ dir = File.dirname(path)
66
+ FileUtils.mkdir_p(dir)
67
+ File.write(path, JSON.pretty_generate(@data))
68
+ end
69
+
70
+ private
71
+
72
+ def blank_structure
73
+ {
74
+ plugins_config_version: '1.0.0',
75
+ plugins: [],
76
+ }
77
+ end
78
+
79
+ def read_and_validate_file
80
+ @data = JSON.parse(File.read(path), symbolize_names: true)
81
+ validate_file
82
+ rescue JSON::ParserError => e
83
+ raise Inspec::Plugin::V2::ConfigError, "Failed to load plugins JSON configuration from #{path}:\n#{e}"
84
+ end
85
+
86
+ def validate_file # rubocop: disable Metrics/AbcSize
87
+ unless @data[:plugins_config_version]
88
+ raise Inspec::Plugin::V2::ConfigError, "Missing 'plugins_config_version' entry at #{path} - currently support versions: 1.0.0"
89
+ end
90
+
91
+ unless @data[:plugins_config_version] == '1.0.0'
92
+ raise Inspec::Plugin::V2::ConfigError, "Unsupported plugins.json file version #{@data[:plugins_config_version]} at #{path} - currently support versions: 1.0.0"
93
+ end
94
+
95
+ plugin_entries = @data[:plugins]
96
+ if plugin_entries.nil?
97
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file at #{path} - missing top-level key named 'plugins', whose value should be an array"
98
+ end
99
+
100
+ unless plugin_entries.is_a?(Array)
101
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file at #{path} - top-level key named 'plugins' should be an array"
102
+ end
103
+
104
+ plugin_entries.each_with_index do |plugin_entry, idx|
105
+ begin
106
+ validate_entry(plugin_entry)
107
+ rescue Inspec::Plugin::V2::ConfigError => ex
108
+ # append some context to the message
109
+ raise Inspec::Plugin::V2::ConfigError, 'Malformed plugins.json file - ' + ex.message + " at index #{idx}"
110
+ end
111
+
112
+ # Check for duplicates
113
+ plugin_entries.each_with_index do |other_entry, other_idx|
114
+ next if idx == other_idx
115
+ next unless other_entry.is_a? Hash # We'll catch that invalid entry later
116
+ next if plugin_entry[:name] != other_entry[:name]
117
+ indices = [idx, other_idx].sort
118
+ raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - duplicate plugin entry '#{plugin_entry[:name]}' detected at index #{indices[0]} and #{indices[1]}"
119
+ end
120
+ end
121
+ end
122
+
123
+ def validate_entry(plugin_entry)
124
+ unless plugin_entry.is_a? Hash
125
+ raise Inspec::Plugin::V2::ConfigError, "each 'plugins' entry should be a Hash / JSON object"
126
+ end
127
+
128
+ unless plugin_entry.key? :name
129
+ raise Inspec::Plugin::V2::ConfigError, "'plugins' entry missing 'name' field"
130
+ end
131
+
132
+ # Symbolize the name.
133
+ plugin_entry[:name] = plugin_entry[:name].to_sym
134
+
135
+ if plugin_entry.key? :installation_type
136
+ seen_type = plugin_entry[:installation_type]
137
+ unless [:gem, :path].include? seen_type.to_sym
138
+ raise Inspec::Plugin::V2::ConfigError, "'plugins' entry with unrecognized installation_type (must be one of 'gem' or 'path')"
139
+ end
140
+ plugin_entry[:installation_type] = seen_type.to_sym
141
+
142
+ if plugin_entry[:installation_type] == :path && !plugin_entry.key?(:installation_path)
143
+ raise Inspec::Plugin::V2::ConfigError, "'plugins' entry with a 'path' installation_type missing installation path"
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -24,7 +24,7 @@ module Inspec::Plugin::V2
24
24
 
25
25
  Gem.configuration['verbose'] = false
26
26
 
27
- attr_reader :loader, :registry
27
+ attr_reader :conf_file, :loader, :registry
28
28
  def_delegator :loader, :plugin_gem_path, :gem_path
29
29
  def_delegator :loader, :plugin_conf_file_path
30
30
  def_delegator :loader, :list_managed_gems
@@ -459,45 +459,25 @@ module Inspec::Plugin::V2
459
459
  #===================================================================#
460
460
  # plugins.json Maintenance Methods #
461
461
  #===================================================================#
462
-
463
- # TODO: refactor the plugin.json file to have its own class, which Installer consumes
464
462
  def update_plugin_config_file(plugin_name, opts)
465
- config = update_plugin_config_data(plugin_name, opts)
466
- FileUtils.mkdir_p(Inspec.config_dir)
467
- File.write(plugin_conf_file_path, JSON.pretty_generate(config))
468
- end
469
-
470
- # TODO: refactor the plugin.json file to have its own class, which Installer consumes
471
- def update_plugin_config_data(plugin_name, opts)
472
- config = read_or_init_config_data
473
- config['plugins'].delete_if { |entry| entry['name'] == plugin_name }
474
- return config if opts[:action] == :uninstall
475
-
476
- entry = { 'name' => plugin_name }
477
-
478
- # Parsing by Requirement handles lot of awkward formattoes
479
- entry['version'] = Gem::Requirement.new(opts[:version]).to_s if opts.key?(:version)
480
-
481
- if opts.key?(:path)
482
- entry['installation_type'] = 'path'
483
- entry['installation_path'] = opts[:path]
463
+ # Be careful no to initialize this until just before we write.
464
+ # Under testing, ENV['INSPEC_CONFIG_DIR'] may have changed.
465
+ @conf_file = Inspec::Plugin::V2::ConfigFile.new
466
+
467
+ # Remove, then optionally rebuild, the entry for the plugin being modified.
468
+ conf_file.remove_entry(plugin_name) if conf_file.existing_entry?(plugin_name)
469
+ unless opts[:action] == :uninstall
470
+ entry = { name: plugin_name }
471
+ # Parsing by Requirement handles lot of awkward formattoes
472
+ entry[:version] = Gem::Requirement.new(opts[:version]).to_s if opts.key?(:version)
473
+ if opts.key?(:path)
474
+ entry[:installation_type] = :path
475
+ entry[:installation_path] = opts[:path]
476
+ end
477
+ conf_file.add_entry(entry)
484
478
  end
485
479
 
486
- config['plugins'] << entry
487
- config
488
- end
489
-
490
- # TODO: check for validity
491
- # TODO: refactor the plugin.json file to have its own class, which Installer consumes
492
- def read_or_init_config_data
493
- if File.exist?(plugin_conf_file_path)
494
- JSON.parse(File.read(plugin_conf_file_path))
495
- else
496
- {
497
- 'plugins_config_version' => '1.0.0',
498
- 'plugins' => [],
499
- }
500
- end
480
+ conf_file.save
501
481
  end
502
482
  end
503
483
  end
@@ -1,5 +1,5 @@
1
- require 'json'
2
1
  require 'inspec/log'
2
+ require 'inspec/plugin/v2/config_file'
3
3
 
4
4
  # Add the current directory of the process to the load path
5
5
  $LOAD_PATH.unshift('.') unless $LOAD_PATH.include?('.')
@@ -9,13 +9,13 @@ $LOAD_PATH.unshift(folder) unless $LOAD_PATH.include?('folder')
9
9
 
10
10
  module Inspec::Plugin::V2
11
11
  class Loader
12
- attr_reader :registry, :options
12
+ attr_reader :conf_file, :registry, :options
13
13
 
14
14
  def initialize(options = {})
15
15
  @options = options
16
16
  @registry = Inspec::Plugin::V2::Registry.instance
17
- read_conf_file
18
- unpack_conf_file
17
+ @conf_file = Inspec::Plugin::V2::ConfigFile.new
18
+ read_conf_file_into_registry
19
19
 
20
20
  # Old-style (v0, v1) co-distributed plugins were called 'bundles'
21
21
  # and were located in lib/bundles
@@ -104,27 +104,12 @@ module Inspec::Plugin::V2
104
104
 
105
105
  # OK, activate.
106
106
  if activate_me
107
- activate(:cli_command, act.activator_name)
107
+ registry.activate(:cli_command, act.activator_name)
108
108
  act.implementation_class.register_with_thor
109
109
  end
110
110
  end
111
111
  end
112
112
 
113
- def activate(plugin_type, hook_name)
114
- activator = registry.find_activators(plugin_type: plugin_type, activator_name: hook_name).first
115
- # We want to capture literally any possible exception here, since we are storing them.
116
- # rubocop: disable Lint/RescueException
117
- begin
118
- impl_class = activator.activation_proc.call
119
- activator.activated?(true)
120
- activator.implementation_class = impl_class
121
- rescue Exception => ex
122
- activator.exception = ex
123
- Inspec::Log.error "Could not activate #{activator.plugin_type} hook named '#{activator.activator_name}' for plugin #{plugin_name}"
124
- end
125
- # rubocop: enable Lint/RescueException
126
- end
127
-
128
113
  def plugin_gem_path
129
114
  self.class.plugin_gem_path
130
115
  end
@@ -157,16 +142,6 @@ module Inspec::Plugin::V2
157
142
  self.class.list_managed_gems
158
143
  end
159
144
 
160
- # TODO: refactor the plugin.json file to have its own class, which Loader consumes
161
- def plugin_conf_file_path
162
- self.class.plugin_conf_file_path
163
- end
164
-
165
- # TODO: refactor the plugin.json file to have its own class, which Loader consumes
166
- def self.plugin_conf_file_path
167
- File.join(Inspec.config_dir, 'plugins.json')
168
- end
169
-
170
145
  private
171
146
 
172
147
  # 'Activating' a gem adds it to the load path, so 'require "gemname"' will work.
@@ -271,71 +246,22 @@ module Inspec::Plugin::V2
271
246
  end
272
247
  end
273
248
 
274
- # TODO: DRY up re: Installer read_or_init_config_file
275
- # TODO: refactor the plugin.json file to have its own class, which Loader consumes
276
- def read_conf_file
277
- if File.exist?(plugin_conf_file_path)
278
- @plugin_file_contents = JSON.parse(File.read(plugin_conf_file_path))
279
- else
280
- @plugin_file_contents = {
281
- 'plugins_config_version' => '1.0.0',
282
- 'plugins' => [],
283
- }
284
- end
285
- rescue JSON::ParserError => e
286
- raise Inspec::Plugin::V2::ConfigError, "Failed to load plugins JSON configuration from #{plugin_conf_file_path}:\n#{e}"
287
- end
288
-
289
- # TODO: refactor the plugin.json file to have its own class, which Loader consumes
290
- def unpack_conf_file
291
- validate_conf_file
292
- @plugin_file_contents['plugins'].each do |plugin_json|
249
+ def read_conf_file_into_registry
250
+ conf_file.each do |plugin_entry|
293
251
  status = Inspec::Plugin::V2::Status.new
294
- status.name = plugin_json['name'].to_sym
252
+ status.name = plugin_entry[:name]
295
253
  status.loaded = false
296
- status.installation_type = (plugin_json['installation_type'] || :gem).to_sym
254
+ status.installation_type = (plugin_entry[:installation_type] || :gem)
297
255
  case status.installation_type
298
256
  when :gem
299
257
  status.entry_point = status.name.to_s
300
- status.version = plugin_json['version']
258
+ status.version = plugin_entry[:version]
301
259
  when :path
302
- status.entry_point = plugin_json['installation_path']
260
+ status.entry_point = plugin_entry[:installation_path]
303
261
  end
304
262
 
305
263
  registry[status.name] = status
306
264
  end
307
265
  end
308
-
309
- # TODO: refactor the plugin.json file to have its own class, which Loader consumes
310
- def validate_conf_file
311
- unless @plugin_file_contents['plugins_config_version'] == '1.0.0'
312
- 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"
313
- end
314
-
315
- plugin_entries = @plugin_file_contents['plugins']
316
- unless plugin_entries.is_a?(Array)
317
- raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - should have a top-level key named 'plugins', whose value is an array"
318
- end
319
-
320
- plugin_entries.each do |plugin_entry|
321
- unless plugin_entry.is_a? Hash
322
- raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'plugins' entry should be a Hash / JSON object"
323
- end
324
-
325
- unless plugin_entry.key? 'name'
326
- raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'plugins' entry must have a 'name' field"
327
- end
328
-
329
- next unless plugin_entry.key?('installation_type')
330
- unless %w{gem path}.include? plugin_entry['installation_type']
331
- raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'installation_type' must be one of 'gem' or 'path'"
332
- end
333
-
334
- next unless plugin_entry['installation_type'] == 'path'
335
- unless plugin_entry.key?('installation_path')
336
- raise Inspec::Plugin::V2::ConfigError, "Malformed plugins.json file - each 'plugins' entry with a 'path' installation_type must provide an 'installation_path' field"
337
- end
338
- end
339
- end
340
266
  end
341
267
  end
@@ -0,0 +1,11 @@
1
+ # All DSL plugin types are defined here.
2
+
3
+ module Inspec::Plugin::V2::PluginType
4
+ class Dsl < Inspec::Plugin::V2::PluginBase
5
+ register_plugin_type(:outer_profile_dsl)
6
+ register_plugin_type(:control_dsl)
7
+ register_plugin_type(:describe_dsl)
8
+ register_plugin_type(:test_dsl)
9
+ register_plugin_type(:resource_dsl)
10
+ end
11
+ end
@@ -67,6 +67,21 @@ module Inspec::Plugin::V2
67
67
  end
68
68
  end
69
69
 
70
+ def activate(plugin_type, hook_name)
71
+ activator = find_activators(plugin_type: plugin_type, activator_name: hook_name).first
72
+ # We want to capture literally any possible exception here, since we are storing them.
73
+ # rubocop: disable Lint/RescueException
74
+ begin
75
+ impl_class = activator.activation_proc.call
76
+ activator.activated?(true)
77
+ activator.implementation_class = impl_class
78
+ rescue Exception => ex
79
+ activator.exception = ex
80
+ Inspec::Log.error "Could not activate #{activator.plugin_type} hook named '#{activator.activator_name}' for plugin #{plugin_name}"
81
+ end
82
+ # rubocop: enable Lint/RescueException
83
+ end
84
+
70
85
  def register(name, status)
71
86
  if known_plugin? name
72
87
  Inspec::Log.debug "PluginLoader: refusing to re-register plugin '#{name}': an existing plugin with that name was loaded via #{registry[name].installation_type}-loading from #{registry[name].entry_point}"
@@ -1,20 +1,84 @@
1
1
  require 'inspec/attribute_registry'
2
- require 'rspec/core'
2
+ require 'inspec/plugin/v2'
3
3
  require 'rspec/core/example_group'
4
4
 
5
- # Setup RSpec to allow use of `should` syntax without warnings
6
- RSpec.configure do |config|
7
- config.expect_with(:rspec) do |rspec_expectations_config|
8
- rspec_expectations_config.syntax = :should
5
+ # Any additions to RSpec::Core::ExampleGroup (the RSpec class behind describe blocks) should go here.
6
+
7
+ module Inspec
8
+ # This module exists to intercept the method_missing *class* method on RSpec::Core::ExampleGroup
9
+ # and is part of support for DSL plugintypes
10
+ module DescribeDslLazyLoader
11
+ # Support for Describe DSL plugins
12
+ def method_missing(method_name, *arguments, &block)
13
+ # Check to see if there is a describe_dsl plugin activator hook with the method name
14
+ registry = Inspec::Plugin::V2::Registry.instance
15
+ hook = registry.find_activators(plugin_type: :describe_dsl, activator_name: method_name).first
16
+
17
+ if hook
18
+ # OK, load the hook if it hasn't been already. We'll then know a module,
19
+ # which we can then inject into the context
20
+ registry.activate(:describe_dsl, method_name) unless hook.activated?
21
+
22
+ # Inject the module's methods into the example group contexts.
23
+ # implementation_class is the field name, but this is actually a module.
24
+ # RSpec works by having these helper methods defined as class methods
25
+ # (see the definition of `let` as an example)
26
+ # So, we use extend to inject the new DSL methods.
27
+ RSpec::Core::ExampleGroup.extend(hook.implementation_class)
28
+
29
+ # We still haven't called the method we were looking for, so do so now.
30
+ send(method_name, *arguments, &block)
31
+ else
32
+ super
33
+ end
34
+ end
35
+ end
36
+
37
+ # This module exists to intercept the method_missing *instance* method on RSpec::Core::ExampleGroup
38
+ # and is part of support for DSL plugintypes
39
+ module TestDslLazyLoader
40
+ # Support for test DSL plugins
41
+ def method_missing(method_name, *arguments, &block)
42
+ # Check to see if there is a test_dsl plugin activator hook with the method name
43
+ registry = Inspec::Plugin::V2::Registry.instance
44
+ hook = registry.find_activators(plugin_type: :test_dsl, activator_name: method_name).first
45
+
46
+ if hook
47
+ # OK, load the hook if it hasn't been already. We'll then know a module,
48
+ # which we can then inject into the context
49
+ registry.activate(:test_dsl, method_name) unless hook.activated?
50
+
51
+ # Inject the module's methods into the example group contexts.
52
+ # implementation_class is the field name, but this is actually a module.
53
+ # RSpec works by having these helper methods defined as instance methods.
54
+ # So, we use include to inject the new DSL methods.
55
+ RSpec::Core::ExampleGroup.include(hook.implementation_class)
56
+
57
+ # We still haven't called the method we were looking for, so do so now.
58
+ send(method_name, *arguments, &block)
59
+ else
60
+ super
61
+ end
62
+ end
9
63
  end
10
64
  end
11
65
 
12
- # This file allows you to add ExampleGroups to be used in rspec tests
13
- #
14
66
  class RSpec::Core::ExampleGroup
15
67
  # This DSL method allows us to access the values of attributes within InSpec tests
16
68
  def attribute(name)
17
69
  Inspec::AttributeRegistry.find_attribute(name, self.class.metadata[:profile_id]).value
18
70
  end
19
71
  define_example_method :attribute
72
+
73
+ # Here, we have to ensure our method_missing gets called prior
74
+ # to RSpec::Core::ExampleGroup.method_missing (the class method).
75
+ # So, we use prepend.
76
+ # Because it is a class method we're attempting to prepend, we must
77
+ # prepend against the singleton class.
78
+ singleton_class.prepend Inspec::DescribeDslLazyLoader
79
+
80
+ # Here, we have to ensure our method_missing gets called prior
81
+ # to RSpec::Core::ExampleGroup#method_missing (the instance method).
82
+ # So, we use prepend.
83
+ prepend Inspec::TestDslLazyLoader
20
84
  end
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '3.0.52'
7
+ VERSION = '3.0.61'
8
8
  end
@@ -61,4 +61,16 @@ class InitCli < MiniTest::Test
61
61
  assert_includes Dir.entries(profile).join, 'README.md'
62
62
  end
63
63
  end
64
- end
64
+
65
+ def test_generating_inspec_profile_aws
66
+ Dir.mktmpdir do |dir|
67
+ profile = File.join(dir,'test-aws-profile')
68
+ out = run_inspec_process("init profile --platform aws test-aws-profile", prefix: "cd #{dir} &&")
69
+ assert_equal 0, out.exit_status
70
+ assert_includes out.stdout, 'Create new profile at'
71
+ assert_includes out.stdout, profile
72
+ assert_includes Dir.entries(profile).join, 'inspec.yml'
73
+ assert_includes Dir.entries(profile).join, 'README.md'
74
+ end
75
+ end
76
+ end
@@ -2,30 +2,103 @@ module Inspec::Resources
2
2
  class FileSystemResource < Inspec.resource(1)
3
3
  name 'filesystem'
4
4
  supports platform: 'linux'
5
+ supports platform: 'windows'
5
6
  desc 'Use the filesystem InSpec resource to test file system'
6
7
  example "
7
8
  describe filesystem('/') do
8
9
  its('size') { should be >= 32000 }
10
+ its('type') { should eq false }
11
+ end
12
+ describe filesystem('c:') do
13
+ its('size') { should be >= 90 }
14
+ its('type') { should eq 'NTFS' }
9
15
  end
10
16
  "
11
17
  attr_reader :partition
12
18
 
13
19
  def initialize(partition)
14
20
  @partition = partition
21
+ @cache = nil
22
+ # select file system manager
23
+ @fsman = nil
24
+
25
+ os = inspec.os
26
+ if os.linux?
27
+ @fsman = LinuxFileSystemResource.new(inspec)
28
+ elsif os.windows?
29
+ @fsman = WindowsFileSystemResource.new(inspec)
30
+ else
31
+ raise Inspec::Exceptions::ResourceSkipped, 'The `filesystem` resource is not supported on your OS yet.'
32
+ end
33
+ end
34
+
35
+ def info
36
+ return @cache if !@cache.nil?
37
+ return {} if @fsman.nil?
38
+ @cache = @fsman.info(@partition)
39
+ end
40
+
41
+ def to_s
42
+ "FileSystem #{@partition}"
15
43
  end
16
44
 
17
45
  def size
18
- @size ||= begin
19
- cmd = inspec.command("df #{partition} --output=size")
20
- raise Inspec::Exceptions::ResourceFailed, "Unable to get available space for partition #{partition}" if cmd.stdout.nil? || cmd.stdout.empty? || !cmd.exit_status.zero?
46
+ info = @fsman.info(@partition)
47
+ info[:size]
48
+ end
21
49
 
22
- value = cmd.stdout.gsub(/\dK-blocks[\r\n]/, '').strip
23
- value.to_i
24
- end
50
+ def type
51
+ info = @fsman.info(@partition)
52
+ info[:type]
25
53
  end
26
54
 
27
- def to_s
28
- "Filesystem #{partition}"
55
+ def name
56
+ info = @fsman.info(@partition)
57
+ info[:name]
58
+ end
59
+ end
60
+
61
+ class FsManagement
62
+ attr_reader :inspec
63
+ def initialize(inspec)
64
+ @inspec = inspec
65
+ end
66
+ end
67
+
68
+ class LinuxFileSystemResource < FsManagement
69
+ def info(partition)
70
+ cmd = inspec.command("df #{partition} --output=size")
71
+ raise Inspec::Exceptions::ResourceFailed, "Unable to get available space for partition #{partition}" if cmd.stdout.nil? || cmd.stdout.empty? || !cmd.exit_status.zero?
72
+ value = cmd.stdout.gsub(/\dK-blocks[\r\n]/, '').strip
73
+ {
74
+ name: partition,
75
+ size: value.to_i,
76
+ type: false,
77
+ }
78
+ end
79
+ end
80
+
81
+ class WindowsFileSystemResource < FsManagement
82
+ def info(partition)
83
+ cmd = inspec.command <<-EOF.gsub(/^\s*/, '')
84
+ $disk = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='#{partition}'"
85
+ $disk.Size = $disk.Size / 1GB
86
+ $disk | select -property DeviceID,Size,FileSystem | ConvertTo-Json
87
+ EOF
88
+
89
+ raise Inspec::Exceptions::ResourceSkipped, "Unable to get available space for partition #{partition}" if cmd.stdout == '' || cmd.exit_status.to_i != 0
90
+ begin
91
+ fs = JSON.parse(cmd.stdout)
92
+ rescue JSON::ParserError => e
93
+ raise Inspec::Exceptions::ResourceFailed,
94
+ 'Failed to parse JSON from Powershell. ' \
95
+ "Error: #{e}"
96
+ end
97
+ {
98
+ name: fs['DeviceID'],
99
+ size: fs['Size'].to_i,
100
+ type: fs['FileSystem'],
101
+ }
29
102
  end
30
103
  end
31
104
  end
@@ -288,7 +288,7 @@ module Inspec::Resources
288
288
  # Find the package
289
289
  cmd = inspec.command <<-EOF.gsub(/^\s*/, '')
290
290
  Get-ItemProperty (@("#{search_paths.join('", "')}") | Where-Object { Test-Path $_ }) |
291
- Where-Object { $_.DisplayName -match "^\\s*#{package_name.shellescape}\\s*$" -or $_.PSChildName -match "^\\s*#{package_name.shellescape}\\s*$" } |
291
+ Where-Object { $_.DisplayName -match "^\s*#{package_name.shellescape}\.*" -or $_.PSChildName -match "^\s*#{package_name.shellescape}\.*" } |
292
292
  Select-Object -Property DisplayName,DisplayVersion | ConvertTo-Json
293
293
  EOF
294
294
 
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: 3.0.52
4
+ version: 3.0.61
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-11-15 00:00:00.000000000 Z
11
+ date: 2018-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train-core
@@ -377,11 +377,13 @@ files:
377
377
  - lib/inspec/plugin/v1/registry.rb
378
378
  - lib/inspec/plugin/v2.rb
379
379
  - lib/inspec/plugin/v2/activator.rb
380
+ - lib/inspec/plugin/v2/config_file.rb
380
381
  - lib/inspec/plugin/v2/filter.rb
381
382
  - lib/inspec/plugin/v2/installer.rb
382
383
  - lib/inspec/plugin/v2/loader.rb
383
384
  - lib/inspec/plugin/v2/plugin_base.rb
384
385
  - lib/inspec/plugin/v2/plugin_types/cli.rb
386
+ - lib/inspec/plugin/v2/plugin_types/dsl.rb
385
387
  - lib/inspec/plugin/v2/plugin_types/mock.rb
386
388
  - lib/inspec/plugin/v2/registry.rb
387
389
  - lib/inspec/plugin/v2/status.rb