inspec 2.2.112 → 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -2
- data/CHANGELOG.md +42 -19
- data/README.md +1 -1
- data/Rakefile +16 -3
- data/docs/dev/integration-testing.md +31 -0
- data/docs/dev/plugins.md +4 -2
- data/docs/dsl_inspec.md +104 -4
- data/docs/plugins.md +57 -0
- data/docs/resources/aws_ebs_volume.md.erb +76 -0
- data/docs/resources/aws_ebs_volumes.md.erb +86 -0
- data/docs/style.md +178 -0
- data/examples/plugins/inspec-resource-lister/Gemfile +12 -0
- data/examples/plugins/inspec-resource-lister/LICENSE +13 -0
- data/examples/plugins/inspec-resource-lister/README.md +62 -0
- data/examples/plugins/inspec-resource-lister/Rakefile +40 -0
- data/examples/plugins/inspec-resource-lister/inspec-resource-lister.gemspec +45 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister.rb +16 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/cli_command.rb +70 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/plugin.rb +55 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/version.rb +10 -0
- data/examples/plugins/inspec-resource-lister/test/fixtures/README.md +24 -0
- data/examples/plugins/inspec-resource-lister/test/functional/README.md +18 -0
- data/examples/plugins/inspec-resource-lister/test/functional/inspec_resource_lister_test.rb +110 -0
- data/examples/plugins/inspec-resource-lister/test/helper.rb +26 -0
- data/examples/plugins/inspec-resource-lister/test/unit/README.md +17 -0
- data/examples/plugins/inspec-resource-lister/test/unit/cli_args_test.rb +64 -0
- data/examples/plugins/inspec-resource-lister/test/unit/plugin_def_test.rb +51 -0
- data/examples/profile/controls/example.rb +9 -8
- data/inspec.gemspec +2 -1
- data/lib/inspec/attribute_registry.rb +1 -1
- data/lib/inspec/globals.rb +4 -0
- data/lib/inspec/objects/control.rb +18 -3
- data/lib/inspec/plugin/v2.rb +14 -3
- data/lib/inspec/plugin/v2/activator.rb +7 -2
- data/lib/inspec/plugin/v2/installer.rb +426 -0
- data/lib/inspec/plugin/v2/loader.rb +137 -30
- data/lib/inspec/plugin/v2/registry.rb +13 -4
- data/lib/inspec/profile.rb +2 -1
- data/lib/inspec/reporters/json.rb +11 -1
- data/lib/inspec/resource.rb +6 -15
- data/lib/inspec/rule.rb +18 -9
- data/lib/inspec/runner_rspec.rb +1 -1
- data/lib/inspec/schema.rb +1 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-plugin-manager-cli/README.md +6 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli.rb +18 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +420 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/plugin.rb +12 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/config_dirs/empty/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette.rb +2 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-wrong-structure/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name.rb +1 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/inspec-plugin_test.rb +651 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/unit/cli_args_test.rb +71 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/unit/plugin_def_test.rb +20 -0
- data/lib/plugins/shared/core_plugin_test_helper.rb +101 -2
- data/lib/plugins/things-for-train-integration.rb +14 -0
- data/lib/resource_support/aws.rb +2 -0
- data/lib/resources/aws/aws_ebs_volume.rb +122 -0
- data/lib/resources/aws/aws_ebs_volumes.rb +63 -0
- data/lib/resources/port.rb +10 -6
- metadata +56 -11
- data/docs/ruby_usage.md +0 -204
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative '../../../shared/core_plugin_test_helper.rb'
|
2
|
+
|
3
|
+
#-----------------------------------------------------------------------#
|
4
|
+
# Thor option defs
|
5
|
+
#-----------------------------------------------------------------------#
|
6
|
+
class PluginManagerCliOptions < MiniTest::Test
|
7
|
+
include CorePluginUnitHelper
|
8
|
+
let(:cli_class) { InspecPlugins::PluginManager::CliCommand }
|
9
|
+
|
10
|
+
def setup
|
11
|
+
require_relative '../../lib/inspec-plugin-manager-cli/cli_command'
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_list_args
|
15
|
+
arg_config = cli_class.all_commands['list'].options
|
16
|
+
assert_equal 1, arg_config.count, 'The list command should have 1 option'
|
17
|
+
|
18
|
+
assert_includes arg_config.keys, :all, 'The list command should have an --all option'
|
19
|
+
assert_equal :boolean, arg_config[:all].type, 'The --all option should be boolean'
|
20
|
+
assert_equal :a, arg_config[:all].aliases.first, 'The --all option should be aliased as -a'
|
21
|
+
refute_nil arg_config[:all].description, 'The --all option should have a description'
|
22
|
+
refute arg_config[:all].required, 'The --all option should not be required'
|
23
|
+
|
24
|
+
assert_equal 0, cli_class.instance_method(:list).arity, 'The list command should take no arguments'
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_search_args
|
28
|
+
arg_config = cli_class.all_commands['search'].options
|
29
|
+
assert_equal 2, arg_config.count, 'The search command should have 2 options'
|
30
|
+
|
31
|
+
assert_includes arg_config.keys, :all, 'The search command should have an --all option'
|
32
|
+
assert_equal :boolean, arg_config[:all].type, 'The --all option should be boolean'
|
33
|
+
assert_equal :a, arg_config[:all].aliases.first, 'The --all option should be aliased as -a'
|
34
|
+
refute_nil arg_config[:all].description, 'The --all option should have a description'
|
35
|
+
refute arg_config[:all].required, 'The --all option should not be required'
|
36
|
+
|
37
|
+
assert_includes arg_config.keys, :exact, 'The search command should have an --exact option'
|
38
|
+
assert_equal :boolean, arg_config[:exact].type, 'The --exact option should be boolean'
|
39
|
+
assert_equal :e, arg_config[:exact].aliases.first, 'The --exact option should be aliased as -e'
|
40
|
+
refute_nil arg_config[:exact].description, 'The --exact option should have a description'
|
41
|
+
refute arg_config[:exact].required, 'The --exact option should not be required'
|
42
|
+
|
43
|
+
assert_equal 1, cli_class.instance_method(:search).arity, 'The search command should take one argument'
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_install_args
|
47
|
+
arg_config = cli_class.all_commands['install'].options
|
48
|
+
assert_equal 1, arg_config.count, 'The install command should have 1 option'
|
49
|
+
|
50
|
+
assert_includes arg_config.keys, :version, 'The install command should have a --version option'
|
51
|
+
assert_equal :string, arg_config[:version].type, 'The --version option should be a string'
|
52
|
+
assert_equal :v, arg_config[:version].aliases.first, 'The --version option should be aliased as -v'
|
53
|
+
refute_nil arg_config[:version].description, 'The --version option should have a description'
|
54
|
+
refute arg_config[:version].required, 'The --version option should not be required'
|
55
|
+
|
56
|
+
assert_equal 1, cli_class.instance_method(:install).arity, 'The install command should take one argument'
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_update_args
|
60
|
+
# TODO: allow specifying version
|
61
|
+
arg_config = cli_class.all_commands['update'].options
|
62
|
+
assert_equal 0, arg_config.count, 'The update command should have no options'
|
63
|
+
assert_equal 1, cli_class.instance_method(:update).arity, 'The update command should take one argument'
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_uninstall_args
|
67
|
+
arg_config = cli_class.all_commands['uninstall'].options
|
68
|
+
assert_equal 0, arg_config.count, 'The uninstall command should have no options'
|
69
|
+
assert_equal 1, cli_class.instance_method(:uninstall).arity, 'The uninstall command should take one argument'
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative '../../../shared/core_plugin_test_helper.rb'
|
2
|
+
|
3
|
+
#-----------------------------------------------------------------------#
|
4
|
+
# Plugin Definition
|
5
|
+
#-----------------------------------------------------------------------#
|
6
|
+
class PluginManagerCliDefinitionTests < MiniTest::Test
|
7
|
+
include CorePluginUnitHelper
|
8
|
+
|
9
|
+
def test_plugin_registered
|
10
|
+
loader = Inspec::Plugin::V2::Loader.new
|
11
|
+
loader.load_all # We want to ensure it is auto-loaded
|
12
|
+
|
13
|
+
assert registry.known_plugin?(:'inspec-plugin-manager-cli'), 'inspec-plugin-manager-cli should be registered'
|
14
|
+
assert registry.loaded_plugin?(:'inspec-plugin-manager-cli'), 'inspec-plugin-manager-cli should be loaded'
|
15
|
+
|
16
|
+
status = registry[:'inspec-plugin-manager-cli']
|
17
|
+
assert_equal 2, status.api_generation, 'inspec-plugin-manager-cli should be v2'
|
18
|
+
assert_includes status.plugin_types, :cli_command, 'inspec-plugin-manager-cli should have cli_command activators'
|
19
|
+
end
|
20
|
+
end
|
@@ -11,12 +11,32 @@ require 'ostruct'
|
|
11
11
|
|
12
12
|
# Utilities often needed
|
13
13
|
require 'fileutils'
|
14
|
+
require 'tmpdir'
|
15
|
+
require 'pathname'
|
16
|
+
require 'forwardable'
|
14
17
|
|
15
18
|
# Configure MiniTest to expose things like `let`
|
16
19
|
class Module
|
17
20
|
include Minitest::Spec::DSL
|
18
21
|
end
|
19
22
|
|
23
|
+
module Inspec
|
24
|
+
class FuncTestRunResult
|
25
|
+
attr_reader :train_result
|
26
|
+
attr_reader :payload
|
27
|
+
|
28
|
+
extend Forwardable
|
29
|
+
def_delegator :train_result, :stdout
|
30
|
+
def_delegator :train_result, :stderr
|
31
|
+
def_delegator :train_result, :exit_status
|
32
|
+
|
33
|
+
def initialize(train_result)
|
34
|
+
@train_result = train_result
|
35
|
+
@payload = OpenStruct.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
20
40
|
module CorePluginBaseHelper
|
21
41
|
let(:repo_path) { File.expand_path(File.join(__FILE__, '..', '..', '..', '..')) }
|
22
42
|
let(:inspec_bin_path) { File.join(repo_path, 'bin', 'inspec') }
|
@@ -40,11 +60,90 @@ module CorePluginFunctionalHelper
|
|
40
60
|
elsif opts.key?(:env)
|
41
61
|
prefix = opts[:env].to_a.map { |assignment| "#{assignment[0]}=#{assignment[1]}" }.join(' ')
|
42
62
|
end
|
43
|
-
TRAIN_CONNECTION.run_command("#{prefix} #{inspec_bin_path} #{command_line}")
|
63
|
+
Inspec::FuncTestRunResult.new(TRAIN_CONNECTION.run_command("#{prefix} #{inspec_bin_path} #{command_line}"))
|
64
|
+
end
|
65
|
+
|
66
|
+
# This helper does some fancy footwork to make InSpec think a plugin
|
67
|
+
# under development is temporarily installed.
|
68
|
+
# @param String command_line Invocation, without the word 'inspec'
|
69
|
+
# @param Hash opts options as for run_inspec_process, with more options:
|
70
|
+
# :pre_run: Proc(plugin_statefile_data, tmp_dir_path) - optional setup block.
|
71
|
+
# Modify plugin_statefile_data as needed; it will be written to a plugins.json
|
72
|
+
# in tmp_dir_path. You may also copy in other things to the tmp_dir_path. Your PWD
|
73
|
+
# will be in the tmp_dir, and it will exist and be empty.
|
74
|
+
# :post_run: Proc(FuncTestRunResult, tmp_dir_path) - optional result capture block.
|
75
|
+
# run_result will be populated, but you can add more to the ostruct .payload
|
76
|
+
# Your PWD will be the tmp_dir, and it will still exist (for a moment!)
|
77
|
+
def run_inspec_process_with_this_plugin(command_line, opts = {})
|
78
|
+
plugin_path = __find_plugin_path_from_caller
|
79
|
+
|
80
|
+
# If it looks like it is a core plugin under test, don't add it to the plugin file
|
81
|
+
# since the loader will auto-load it anyway
|
82
|
+
if plugin_path.include?('lib/plugins/inspec-')
|
83
|
+
plugin_file_data = __make_empty_plugin_file_data_structure
|
84
|
+
else
|
85
|
+
plugin_file_data = __make_plugin_file_data_structure_with_path(plugin_path)
|
86
|
+
end
|
87
|
+
|
88
|
+
Dir.mktmpdir do |tmp_dir|
|
89
|
+
opts[:pre_run]&.call(plugin_file_data, tmp_dir)
|
90
|
+
plugin_file_path = File.join(tmp_dir, 'plugins.json')
|
91
|
+
# HACK: If the block cleared the hash, take that to mean it will provide a plugins.json file of its own.
|
92
|
+
File.write(plugin_file_path, JSON.generate(plugin_file_data)) unless plugin_file_data.empty?
|
93
|
+
opts[:env] ||= {}
|
94
|
+
opts[:env]['INSPEC_CONFIG_DIR'] = tmp_dir
|
95
|
+
run_result = run_inspec_process(command_line, opts)
|
96
|
+
|
97
|
+
# Read the resulting plugins.json into memory, if any
|
98
|
+
if File.exist?(plugin_file_path)
|
99
|
+
run_result.payload.plugin_data = JSON.parse(File.read(plugin_file_path))
|
100
|
+
end
|
101
|
+
|
102
|
+
opts[:post_run]&.call(run_result, tmp_dir)
|
103
|
+
run_result
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def __find_plugin_path_from_caller(frames_back = 2)
|
108
|
+
caller_path = Pathname.new(caller_locations(frames_back, 1).first.absolute_path)
|
109
|
+
# Typical caller path:
|
110
|
+
# /Users/cwolfe/sandbox/inspec-resource-lister/test/functional/inspec_resource_lister_test.rb
|
111
|
+
# We want:
|
112
|
+
# /Users/cwolfe/sandbox/inspec-resource-lister/lib/inspec-resource-lister.rb
|
113
|
+
cursor = caller_path
|
114
|
+
until cursor.basename.to_s == 'test' && cursor.parent.basename.to_s =~ /^(inspec|train)-/
|
115
|
+
cursor = cursor.parent
|
116
|
+
break if cursor.nil?
|
117
|
+
end
|
118
|
+
raise 'Could not comprehend plugin project directory structure' if cursor.nil?
|
119
|
+
|
120
|
+
project_dir = cursor.parent
|
121
|
+
plugin_name = project_dir.basename
|
122
|
+
entry_point = File.join(project_dir.to_s, 'lib', plugin_name.to_s + '.rb')
|
123
|
+
raise 'Could not find plugin entry point' unless File.exist?(entry_point)
|
124
|
+
entry_point
|
125
|
+
end
|
126
|
+
|
127
|
+
def __make_plugin_file_data_structure_with_path(path)
|
128
|
+
# TODO: dry this up, refs #3350
|
129
|
+
plugin_name = File.basename(path, '.rb')
|
130
|
+
data = __make_empty_plugin_file_data_structure
|
131
|
+
data['plugins'] << {
|
132
|
+
'name' => plugin_name,
|
133
|
+
'installation_type' => 'path',
|
134
|
+
'installation_path' => path,
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
def __make_empty_plugin_file_data_structure
|
139
|
+
# TODO: dry this up, refs #3350
|
140
|
+
{
|
141
|
+
'plugins_config_version' => '1.0.0',
|
142
|
+
'plugins' => [],
|
143
|
+
}
|
44
144
|
end
|
45
145
|
end
|
46
146
|
|
47
147
|
module CorePluginUnitHelper
|
48
148
|
include CorePluginBaseHelper
|
49
|
-
require 'inspec'
|
50
149
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# docs/plugins
|
2
|
+
# Update that train plugins are now possible
|
3
|
+
|
4
|
+
# test/unit/plugin/v2/loader_test.rb
|
5
|
+
# TODO: loading all plugins does not activate Train plugins
|
6
|
+
|
7
|
+
# lib/plugins/inspec-plugin-manager-cli/test/functional/inspec-plugin_test.rb
|
8
|
+
# ~ Should be able to install a train plugin - in place, will fail until plugin published
|
9
|
+
# ~ Should be able to search for a train plugin - in place, will fail until plugin published
|
10
|
+
|
11
|
+
# test/functional/plugins_test.rb
|
12
|
+
# % Should be able to suggest a train transport plugin when an unsupported --target schema is used and a gem search is successful
|
13
|
+
# % Should be able to suggest a train transport plugin when an unrecognized profile platform declaration is used and a gem search is successful
|
14
|
+
# - Should be able to run inspec detect targeting a test target
|
data/lib/resource_support/aws.rb
CHANGED
@@ -19,6 +19,8 @@ require 'resources/aws/aws_cloudwatch_log_metric_filter'
|
|
19
19
|
require 'resources/aws/aws_config_delivery_channel'
|
20
20
|
require 'resources/aws/aws_config_recorder'
|
21
21
|
require 'resources/aws/aws_ec2_instance'
|
22
|
+
require 'resources/aws/aws_ebs_volume'
|
23
|
+
require 'resources/aws/aws_ebs_volumes'
|
22
24
|
require 'resources/aws/aws_flow_log'
|
23
25
|
require 'resources/aws/aws_ec2_instances'
|
24
26
|
require 'resources/aws/aws_ecs_cluster'
|
@@ -0,0 +1,122 @@
|
|
1
|
+
class AwsEbsVolume < Inspec.resource(1)
|
2
|
+
name 'aws_ebs_volume'
|
3
|
+
desc 'Verifies settings for an EBS volume'
|
4
|
+
|
5
|
+
example <<-EOX
|
6
|
+
describe aws_ebs_volume('vol-123456') do
|
7
|
+
it { should be_encrypted }
|
8
|
+
its('size') { should cmp 8 }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe aws_ebs_volume(name: 'my-volume') do
|
12
|
+
its('encrypted') { should eq true }
|
13
|
+
its('iops') { should cmp 100 }
|
14
|
+
end
|
15
|
+
EOX
|
16
|
+
supports platform: 'aws'
|
17
|
+
|
18
|
+
# TODO: rewrite to avoid direct injection, match other resources, use AwsSingularResourceMixin
|
19
|
+
def initialize(opts, conn = nil)
|
20
|
+
@opts = opts
|
21
|
+
@display_name = opts.is_a?(Hash) ? @opts[:name] : opts
|
22
|
+
@ec2_client = conn ? conn.ec2_client : inspec_runner.backend.aws_client(Aws::EC2::Client)
|
23
|
+
@ec2_resource = conn ? conn.ec2_resource : inspec_runner.backend.aws_resource(Aws::EC2::Resource, {})
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO: DRY up, see https://github.com/chef/inspec/issues/2633
|
27
|
+
# Copied from resource_support/aws/aws_resource_mixin.rb
|
28
|
+
def catch_aws_errors
|
29
|
+
yield
|
30
|
+
rescue Aws::Errors::MissingCredentialsError
|
31
|
+
# The AWS error here is unhelpful:
|
32
|
+
# "unable to sign request without credentials set"
|
33
|
+
Inspec::Log.error "It appears that you have not set your AWS credentials. You may set them using environment variables, or using the 'aws://region/aws_credentials_profile' target. See https://www.inspec.io/docs/reference/platforms for details."
|
34
|
+
fail_resource('No AWS credentials available')
|
35
|
+
rescue Aws::Errors::ServiceError => e
|
36
|
+
fail_resource(e.message)
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: DRY up, see https://github.com/chef/inspec/issues/2633
|
40
|
+
# Copied from resource_support/aws/aws_singular_resource_mixin.rb
|
41
|
+
def inspec_runner
|
42
|
+
# When running under inspec-cli, we have an 'inspec' method that
|
43
|
+
# returns the runner. When running under unit tests, we don't
|
44
|
+
# have that, but we still have to call this to pass something
|
45
|
+
# (nil is OK) to the backend.
|
46
|
+
# TODO: remove with https://github.com/chef/inspec-aws/issues/216
|
47
|
+
# TODO: remove after rewrite to include AwsSingularResource
|
48
|
+
inspec if respond_to?(:inspec)
|
49
|
+
end
|
50
|
+
|
51
|
+
def id
|
52
|
+
return @volume_id if defined?(@volume_id)
|
53
|
+
catch_aws_errors do
|
54
|
+
if @opts.is_a?(Hash)
|
55
|
+
first = @ec2_resource.volumes(
|
56
|
+
{
|
57
|
+
filters: [{
|
58
|
+
name: 'tag:Name',
|
59
|
+
values: [@opts[:name]],
|
60
|
+
}],
|
61
|
+
},
|
62
|
+
).first
|
63
|
+
# catch case where the volume is not known
|
64
|
+
@volume_id = first.id unless first.nil?
|
65
|
+
else
|
66
|
+
@volume_id = @opts
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
alias volume_id id
|
71
|
+
|
72
|
+
def exists?
|
73
|
+
!volume.nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
def encrypted?
|
77
|
+
volume.encrypted
|
78
|
+
end
|
79
|
+
|
80
|
+
# attributes that we want to expose
|
81
|
+
%w{
|
82
|
+
availability_zone encrypted iops kms_key_id size snapshot_id state volume_type
|
83
|
+
}.each do |attribute|
|
84
|
+
define_method attribute do
|
85
|
+
catch_aws_errors do
|
86
|
+
volume.send(attribute) if volume
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Don't document this - it's a bit hard to use. Our current doctrine
|
92
|
+
# is to use dumb things, like arrays of strings - use security_group_ids instead.
|
93
|
+
def security_groups
|
94
|
+
catch_aws_errors do
|
95
|
+
@security_groups ||= volume.security_groups.map { |sg|
|
96
|
+
{ id: sg.group_id, name: sg.group_name }
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def security_group_ids
|
102
|
+
catch_aws_errors do
|
103
|
+
@security_group_ids ||= volume.security_groups.map(&:group_id)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def tags
|
108
|
+
catch_aws_errors do
|
109
|
+
@tags ||= volume.tags.map { |tag| { key: tag.key, value: tag.value } }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
"EBS Volume #{@display_name}"
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def volume
|
120
|
+
catch_aws_errors { @volume ||= @ec2_resource.volume(id) }
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class AwsEbsVolumes < Inspec.resource(1)
|
2
|
+
name 'aws_ebs_volumes'
|
3
|
+
desc 'Verifies settings for AWS EBS Volumes in bulk'
|
4
|
+
example '
|
5
|
+
describe aws_ebs_volumes do
|
6
|
+
it { should exist }
|
7
|
+
end
|
8
|
+
'
|
9
|
+
supports platform: 'aws'
|
10
|
+
|
11
|
+
include AwsPluralResourceMixin
|
12
|
+
def validate_params(resource_params)
|
13
|
+
unless resource_params.empty?
|
14
|
+
raise ArgumentError, 'aws_ebs_volumes does not accept resource parameters.'
|
15
|
+
end
|
16
|
+
resource_params
|
17
|
+
end
|
18
|
+
|
19
|
+
# Underlying FilterTable implementation.
|
20
|
+
filter = FilterTable.create
|
21
|
+
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
|
22
|
+
filter.register_column(:volume_ids, field: :volume_id)
|
23
|
+
filter.install_filter_methods_on_resource(self, :table)
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
'EBS Volumes'
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_from_api
|
30
|
+
backend = BackendFactory.create(inspec_runner)
|
31
|
+
@table = []
|
32
|
+
pagination_opts = {}
|
33
|
+
loop do
|
34
|
+
api_result = backend.describe_volumes(pagination_opts)
|
35
|
+
@table += unpack_describe_volumes_response(api_result.volumes)
|
36
|
+
break unless api_result.next_token
|
37
|
+
pagination_opts = { next_token: api_result.next_token }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def unpack_describe_volumes_response(volumes)
|
42
|
+
volume_rows = []
|
43
|
+
volumes.each do |res|
|
44
|
+
volume_rows += res.attachments.map do |volume_struct|
|
45
|
+
{
|
46
|
+
volume_id: volume_struct.volume_id,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
volume_rows
|
51
|
+
end
|
52
|
+
|
53
|
+
class Backend
|
54
|
+
class AwsClientApi < AwsBackendBase
|
55
|
+
BackendFactory.set_default_backend(self)
|
56
|
+
self.aws_client_class = Aws::EC2::Client
|
57
|
+
|
58
|
+
def describe_volumes(query)
|
59
|
+
aws_service_client.describe_volumes(query)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/resources/port.rb
CHANGED
@@ -473,22 +473,26 @@ module Inspec::Resources
|
|
473
473
|
|
474
474
|
def parse_netstat_line(line)
|
475
475
|
# parse each line
|
476
|
-
# 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 -
|
477
|
-
|
476
|
+
# 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 - User, 8 - Inode, 9 - PID/Program name
|
477
|
+
# * UDP lines have an empty State column and the Busybox variant lacks
|
478
|
+
# the User and Inode columns.
|
479
|
+
reg = /^(?<proto>\S+)\s+(\S+)\s+(\S+)\s+(?<local_addr>\S+)\s+(?<foreign_addr>\S+)\s+(\S+)?\s+((\S+)\s+(\S+)\s+)?(?<pid_prog>\S+)/
|
480
|
+
parsed = reg.match(line)
|
481
|
+
|
478
482
|
return {} if parsed.nil? || line.match(/^proto/i)
|
479
483
|
|
480
484
|
# parse ip4 and ip6 addresses
|
481
|
-
protocol = parsed[
|
485
|
+
protocol = parsed[:proto].downcase
|
482
486
|
|
483
487
|
# detect protocol if not provided
|
484
|
-
protocol += '6' if parsed[
|
488
|
+
protocol += '6' if parsed[:local_addr].count(':') > 1 && %w{tcp udp}.include?(protocol)
|
485
489
|
|
486
490
|
# extract host and port information
|
487
|
-
host, port = parse_net_address(parsed[
|
491
|
+
host, port = parse_net_address(parsed[:local_addr], protocol)
|
488
492
|
return {} if host.nil?
|
489
493
|
|
490
494
|
# extract PID
|
491
|
-
process = parsed[
|
495
|
+
process = parsed[:pid_prog].split('/')
|
492
496
|
pid = process[0]
|
493
497
|
pid = pid.to_i if pid =~ /^\d+$/
|
494
498
|
process = process[1]
|