inspec-core 3.0.52 → 3.0.61
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -12
- data/lib/inspec/control_eval_context.rb +26 -1
- data/lib/inspec/dsl.rb +23 -0
- data/lib/inspec/plugin/v1/plugin_types/resource.rb +28 -0
- data/lib/inspec/plugin/v2.rb +1 -0
- data/lib/inspec/plugin/v2/config_file.rb +148 -0
- data/lib/inspec/plugin/v2/installer.rb +17 -37
- data/lib/inspec/plugin/v2/loader.rb +11 -85
- data/lib/inspec/plugin/v2/plugin_types/dsl.rb +11 -0
- data/lib/inspec/plugin/v2/registry.rb +15 -0
- data/lib/inspec/rspec_extensions.rb +71 -7
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-init/test/functional/inspec_init_test.rb +13 -1
- data/lib/resources/filesystem.rb +81 -8
- data/lib/resources/package.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d789972e56d9edede66194f64a2539d91ceb4d5df71067d22d0f3a8a3a772d95
|
4
|
+
data.tar.gz: b0d96149a8d311b9e6926286f7369da1b944eb47d915a33a06fc0dad2aa046ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
4
|
-
## [v3.0.
|
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
|
-
####
|
7
|
-
-
|
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.
|
11
|
-
### Changes since 3.0.
|
10
|
+
<!-- release_rollup since=3.0.52 -->
|
11
|
+
### Changes since 3.0.52 release
|
12
12
|
|
13
|
-
####
|
14
|
-
-
|
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
|
-
-
|
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
|
-
-
|
21
|
-
|
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 'should' 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
|
data/lib/inspec/plugin/v2.rb
CHANGED
@@ -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
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
275
|
-
|
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 =
|
252
|
+
status.name = plugin_entry[:name]
|
295
253
|
status.loaded = false
|
296
|
-
status.installation_type = (
|
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 =
|
258
|
+
status.version = plugin_entry[:version]
|
301
259
|
when :path
|
302
|
-
status.entry_point =
|
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 '
|
2
|
+
require 'inspec/plugin/v2'
|
3
3
|
require 'rspec/core/example_group'
|
4
4
|
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
data/lib/inspec/version.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/resources/filesystem.rb
CHANGED
@@ -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
|
-
|
19
|
-
|
20
|
-
|
46
|
+
info = @fsman.info(@partition)
|
47
|
+
info[:size]
|
48
|
+
end
|
21
49
|
|
22
|
-
|
23
|
-
|
24
|
-
|
50
|
+
def type
|
51
|
+
info = @fsman.info(@partition)
|
52
|
+
info[:type]
|
25
53
|
end
|
26
54
|
|
27
|
-
def
|
28
|
-
|
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
|
data/lib/resources/package.rb
CHANGED
@@ -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 "
|
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.
|
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-
|
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
|