inspec-iggy 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +8 -25
- data/README.md +5 -5
- data/inspec-iggy.gemspec +12 -12
- data/lib/inspec-iggy.rb +1 -1
- data/lib/inspec-iggy/cloudformation/cli_command.rb +28 -37
- data/lib/inspec-iggy/cloudformation/generate.rb +24 -24
- data/lib/inspec-iggy/file_helper.rb +2 -2
- data/lib/inspec-iggy/iggy_cli_command.rb +18 -0
- data/lib/inspec-iggy/inspec_helper.rb +166 -178
- data/lib/inspec-iggy/platforms/aws_helper.rb +28 -11
- data/lib/inspec-iggy/platforms/azure_helper.rb +10 -7
- data/lib/inspec-iggy/platforms/gcp_helper.rb +127 -126
- data/lib/inspec-iggy/plugin.rb +9 -3
- data/lib/inspec-iggy/profile_helper.rb +27 -27
- data/lib/inspec-iggy/terraform/cli_command.rb +37 -46
- data/lib/inspec-iggy/terraform/generate.rb +56 -36
- data/lib/inspec-iggy/terraform/negative.rb +42 -23
- data/lib/inspec-iggy/version.rb +1 -1
- metadata +5 -4
@@ -1,89 +1,80 @@
|
|
1
1
|
# Terraform CLI command and options
|
2
2
|
|
3
|
-
require
|
3
|
+
require "inspec/plugin/v2"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
5
|
+
require "inspec-iggy/version"
|
6
|
+
require "inspec-iggy/profile_helper"
|
7
|
+
require "inspec-iggy/terraform/generate"
|
8
|
+
require "inspec-iggy/terraform/negative"
|
9
9
|
|
10
10
|
module InspecPlugins::Iggy
|
11
11
|
module Terraform
|
12
12
|
class CliCommand < Inspec.plugin(2, :cli_command)
|
13
|
-
subcommand_desc
|
14
|
-
|
15
|
-
# Thor.map(Hash) allows you to make aliases for commands.
|
16
|
-
map('-v' => 'version') # Treat `inspec terraform -v`` as `inspec terraform version`
|
17
|
-
map('--version' => 'version') # Treat `inspec terraform -version`` as `inspec terraform version`
|
18
|
-
|
19
|
-
desc 'version', 'Display version information', hide: true
|
20
|
-
def version
|
21
|
-
say("Iggy v#{InspecPlugins::Iggy::VERSION}")
|
22
|
-
end
|
13
|
+
subcommand_desc "terraform SUBCOMMAND ...", "Generate an InSpec profile from Terraform"
|
23
14
|
|
24
15
|
class_option :debug,
|
25
|
-
desc:
|
16
|
+
desc: "Verbose debugging messages",
|
26
17
|
type: :boolean,
|
27
18
|
default: false
|
28
19
|
|
29
20
|
class_option :copyright,
|
30
|
-
desc:
|
31
|
-
default:
|
21
|
+
desc: "Name of the copyright holder",
|
22
|
+
default: "The Authors"
|
32
23
|
|
33
24
|
class_option :email,
|
34
|
-
desc:
|
35
|
-
default:
|
25
|
+
desc: "Email address of the author",
|
26
|
+
default: "you@example.com"
|
36
27
|
|
37
28
|
class_option :license,
|
38
|
-
desc:
|
39
|
-
default:
|
29
|
+
desc: "License for the profile",
|
30
|
+
default: "Apache-2.0"
|
40
31
|
|
41
32
|
class_option :maintainer,
|
42
|
-
desc:
|
43
|
-
default:
|
33
|
+
desc: "Name of the copyright holder",
|
34
|
+
default: "The Authors"
|
44
35
|
|
45
36
|
class_option :summary,
|
46
|
-
desc:
|
47
|
-
default:
|
37
|
+
desc: "One line summary for the profile",
|
38
|
+
default: "An InSpec Compliance Profile"
|
48
39
|
|
49
40
|
class_option :title,
|
50
|
-
desc:
|
51
|
-
default:
|
41
|
+
desc: "Human-readable name for the profile",
|
42
|
+
default: "InSpec Profile"
|
52
43
|
|
53
44
|
class_option :version,
|
54
|
-
desc:
|
55
|
-
default:
|
45
|
+
desc: "Specify the profile version",
|
46
|
+
default: "0.1.0"
|
56
47
|
|
57
48
|
class_option :overwrite,
|
58
|
-
desc:
|
49
|
+
desc: "Overwrites existing profile directory",
|
59
50
|
type: :boolean,
|
60
51
|
default: false
|
61
52
|
|
62
53
|
class_option :name,
|
63
|
-
aliases:
|
54
|
+
aliases: "-n",
|
64
55
|
required: true,
|
65
|
-
desc:
|
56
|
+
desc: "Name of profile to be generated"
|
66
57
|
|
67
58
|
class_option :tfstate,
|
68
|
-
aliases:
|
69
|
-
desc:
|
70
|
-
default:
|
59
|
+
aliases: "-t",
|
60
|
+
desc: "Specify path to the input terraform.tfstate",
|
61
|
+
default: "terraform.tfstate"
|
71
62
|
|
72
63
|
class_option :platform,
|
73
|
-
desc:
|
64
|
+
desc: "The InSpec platform providing the necessary resources (aws, azure, or gcp)"
|
74
65
|
|
75
66
|
class_option :resourcepath,
|
76
|
-
desc:
|
67
|
+
desc: "Specify path to the InSpec Resource Pack providing the necessary resources"
|
77
68
|
|
78
|
-
desc
|
69
|
+
desc "generate [options]", "Generate InSpec compliance controls from terraform.tfstate"
|
79
70
|
def generate
|
80
71
|
Inspec::Log.level = :debug if options[:debug]
|
81
72
|
platform = options[:platform]
|
82
73
|
resource_path = options[:resourcepath]
|
83
74
|
# require validation that if platform or resourcepath are passed, both are available
|
84
|
-
if platform
|
85
|
-
unless platform
|
86
|
-
error
|
75
|
+
if platform || resource_path
|
76
|
+
unless platform && resource_path
|
77
|
+
error "You must pass both --platform and --resourcepath if using either"
|
87
78
|
exit(1)
|
88
79
|
end
|
89
80
|
end
|
@@ -93,15 +84,15 @@ module InspecPlugins::Iggy
|
|
93
84
|
exit 0
|
94
85
|
end
|
95
86
|
|
96
|
-
desc
|
87
|
+
desc "negative [options]", "Generate negative InSpec compliance controls from terraform.tfstate"
|
97
88
|
def negative
|
98
89
|
Inspec::Log.level = :debug if options[:debug]
|
99
90
|
platform = options[:platform]
|
100
91
|
resource_path = options[:resourcepath]
|
101
92
|
# require validation that if platform or resourcepath are passed, both are available
|
102
|
-
if platform
|
103
|
-
unless platform
|
104
|
-
error
|
93
|
+
if platform || resource_path
|
94
|
+
unless platform && resource_path
|
95
|
+
error "You must pass both --platform and --resourcepath if using either"
|
105
96
|
exit(1)
|
106
97
|
end
|
107
98
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# parses Terraform d.tfstate files
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "inspec/objects/control"
|
4
|
+
require "inspec/objects/ruby_helper"
|
5
|
+
require "inspec/objects/describe"
|
6
6
|
|
7
|
-
require
|
8
|
-
require
|
7
|
+
require "inspec-iggy/file_helper"
|
8
|
+
require "inspec-iggy/inspec_helper"
|
9
9
|
|
10
10
|
module InspecPlugins::Iggy::Terraform
|
11
11
|
class Generate
|
@@ -30,31 +30,30 @@ module InspecPlugins::Iggy::Terraform
|
|
30
30
|
def self.parse_resources(tfstate, resource_path, _platform)
|
31
31
|
# iterate over the resources
|
32
32
|
resources = {}
|
33
|
-
tfstate[
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
resource_attributes = tf_resources[tf_res]['primary']['attributes']
|
33
|
+
tf_resources = tfstate["resources"]
|
34
|
+
tf_resources.each do |tf_res|
|
35
|
+
resource_type = tf_res["type"]
|
36
|
+
next if resource_type.eql?("random_id") # this is a Terraform resource, not a provider resource
|
37
|
+
|
38
|
+
# load resource pack resources
|
39
|
+
InspecPlugins::Iggy::InspecHelper.load_resource_pack(resource_path) if resource_path
|
40
|
+
|
41
|
+
# add translation layer
|
42
|
+
if InspecPlugins::Iggy::InspecHelper::TRANSLATED_RESOURCES.key?(resource_type)
|
43
|
+
Inspec::Log.debug "Iggy::Terraform::Generate.parse_resources resource_type = #{resource_type} #{InspecPlugins::Iggy::InspecHelper::TRANSLATED_RESOURCES[resource_type]} TRANSLATED"
|
44
|
+
resource_type = InspecPlugins::Iggy::InspecHelper::TRANSLATED_RESOURCES[resource_type]
|
45
|
+
end
|
46
|
+
resources[resource_type] = {} if resources[resource_type].nil?
|
47
|
+
# does this match an InSpec resource?
|
48
|
+
if InspecPlugins::Iggy::InspecHelper.available_resources.include?(resource_type)
|
49
|
+
Inspec::Log.debug "Iggy::Terraform::Generate.parse_resources resource_type = #{resource_type} MATCHED"
|
50
|
+
tf_res["instances"].each do |instance|
|
51
|
+
resource_id = instance["attributes"]["id"]
|
52
|
+
resource_attributes = instance["attributes"]
|
54
53
|
resources[resource_type][resource_id] = resource_attributes
|
55
|
-
else
|
56
|
-
Inspec::Log.debug "Iggy::Terraform.Generate.parse_generate resource_type = #{resource_type} SKIPPED"
|
57
54
|
end
|
55
|
+
else
|
56
|
+
Inspec::Log.debug "Iggy::Terraform.Generate.parse_generate resource_type = #{resource_type} SKIPPED"
|
58
57
|
end
|
59
58
|
end
|
60
59
|
resources
|
@@ -71,12 +70,27 @@ module InspecPlugins::Iggy::Terraform
|
|
71
70
|
ctrl.id = "#{resource_type}::#{resource_id}"
|
72
71
|
ctrl.title = "InSpec-Iggy #{resource_type}::#{resource_id}"
|
73
72
|
ctrl.descriptions[:default] = "#{resource_type}::#{resource_id} from the source file #{absolutename}\nGenerated by InSpec-Iggy v#{InspecPlugins::Iggy::VERSION}"
|
74
|
-
ctrl.impact =
|
73
|
+
ctrl.impact = "1.0"
|
75
74
|
|
76
75
|
describe = Inspec::Describe.new
|
77
|
-
case platform
|
78
|
-
when
|
79
|
-
|
76
|
+
case platform # this may need to get refactored away once Azure is tested
|
77
|
+
when "aws"
|
78
|
+
qualifier = [resource_type, {}]
|
79
|
+
if InspecPlugins::Iggy::InspecHelper.available_resource_qualifiers(platform).key?(resource_type) # there are additional qualifiers
|
80
|
+
first = true
|
81
|
+
InspecPlugins::Iggy::InspecHelper.available_resource_qualifiers(platform)[resource_type].each do |parameter|
|
82
|
+
Inspec::Log.debug "Iggy::Terraform::Generate.parse_controls #{resource_type} qualifier found = #{parameter} MATCHED"
|
83
|
+
if first # this is the id for the resource
|
84
|
+
value = resources[resource_type][resource_id]["id"] # pull value out of the tf attributes
|
85
|
+
first = false
|
86
|
+
else
|
87
|
+
value = resources[resource_type][resource_id][parameter.to_s] # pull value out of the tf attributes
|
88
|
+
end
|
89
|
+
qualifier[1][parameter] = value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
describe.qualifier.push(qualifier)
|
93
|
+
when "azure" # rubocop:disable Lint/EmptyWhen
|
80
94
|
# this is a hack for azure, we need a better longterm solution
|
81
95
|
# if resource.start_with?('azure_')
|
82
96
|
# name = resource_id.split('/').last
|
@@ -91,11 +105,11 @@ module InspecPlugins::Iggy::Terraform
|
|
91
105
|
# resource_group = resource_id.split('resourceGroups/').last.split('/').first
|
92
106
|
# describe.qualifier.push([resource_type, name: name, group_name: resource_group])
|
93
107
|
# end
|
94
|
-
when
|
108
|
+
when "gcp"
|
95
109
|
qualifier = [resource_type, {}]
|
96
110
|
if InspecPlugins::Iggy::InspecHelper.available_resource_qualifiers(platform).key?(resource_type)
|
97
111
|
InspecPlugins::Iggy::InspecHelper.available_resource_qualifiers(platform)[resource_type].each do |parameter|
|
98
|
-
Inspec::Log.debug "Iggy::Terraform::Generate.parse_controls #{resource_type}
|
112
|
+
Inspec::Log.debug "Iggy::Terraform::Generate.parse_controls #{resource_type} qualifier found = #{parameter} MATCHED"
|
99
113
|
value = resources[resource_type][resource_id][parameter.to_s] # pull value out of the tf attributes
|
100
114
|
qualifier[1][parameter] = value
|
101
115
|
end
|
@@ -104,7 +118,7 @@ module InspecPlugins::Iggy::Terraform
|
|
104
118
|
end
|
105
119
|
|
106
120
|
# ensure the resource exists unless Azure, which currently doesn't support it as of InSpec 2.2
|
107
|
-
describe.add_test(nil,
|
121
|
+
describe.add_test(nil, "exist", nil) unless resource_type.start_with?("azure_")
|
108
122
|
|
109
123
|
# if there's a match, see if there are matching InSpec properties
|
110
124
|
inspec_properties = InspecPlugins::Iggy::InspecHelper.resource_properties(resource_type, platform)
|
@@ -113,7 +127,13 @@ module InspecPlugins::Iggy::Terraform
|
|
113
127
|
if inspec_properties.member?(attr)
|
114
128
|
Inspec::Log.debug "Iggy::Terraform::Generate.parse_controls #{resource_type} inspec_property = #{attr} MATCHED"
|
115
129
|
value = resources[resource_type][resource_id][attr]
|
116
|
-
|
130
|
+
if value
|
131
|
+
# check to see if there is a translate for this attr
|
132
|
+
property = InspecPlugins::Iggy::InspecHelper.translated_resource_property(platform, resource_type, attr)
|
133
|
+
describe.add_test(property, "cmp", value)
|
134
|
+
else
|
135
|
+
Inspec::Log.debug "Iggy::Terraform::Generate.parse_controls #{resource_type} inspec_property = #{attr} SKIPPED FOR NIL"
|
136
|
+
end
|
117
137
|
else
|
118
138
|
Inspec::Log.debug "Iggy::Terraform::Generate.parse_controls #{resource_type} inspec_property = #{attr} SKIPPED"
|
119
139
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# returns negative of Terraform tfstate file coverage
|
2
2
|
|
3
|
-
require
|
4
|
-
require 'inspec/objects/ruby_helper'
|
5
|
-
require 'inspec/objects/describe'
|
3
|
+
require "hashie"
|
6
4
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
5
|
+
require "inspec/objects/control"
|
6
|
+
require "inspec/objects/ruby_helper"
|
7
|
+
require "inspec/objects/describe"
|
8
|
+
|
9
|
+
require "inspec-iggy/file_helper"
|
10
|
+
require "inspec-iggy/inspec_helper"
|
11
|
+
require "inspec-iggy/terraform/generate"
|
10
12
|
|
11
13
|
module InspecPlugins::Iggy::Terraform
|
12
14
|
class Negative
|
@@ -33,21 +35,21 @@ module InspecPlugins::Iggy::Terraform
|
|
33
35
|
unmatched_controls = []
|
34
36
|
unmatched_resources.each do |unmatched|
|
35
37
|
unresources = InspecPlugins::Iggy::InspecHelper.available_resource_iterators(platform)[unmatched]
|
36
|
-
iterator = unresources[
|
38
|
+
iterator = unresources["iterator"]
|
37
39
|
ctrl = Inspec::Control.new
|
38
40
|
ctrl.id = "NEGATIVE-COVERAGE:#{iterator}"
|
39
41
|
ctrl.title = "InSpec-Iggy NEGATIVE-COVERAGE:#{iterator}"
|
40
42
|
ctrl.descriptions[:default] = "NEGATIVE-COVERAGE:#{iterator} from the source file #{sourcefile}\nGenerated by InSpec-Iggy v#{InspecPlugins::Iggy::VERSION}"
|
41
|
-
ctrl.impact =
|
43
|
+
ctrl.impact = "1.0"
|
42
44
|
describe = Inspec::Describe.new
|
43
45
|
qualifier = [iterator, {}]
|
44
|
-
unresources[
|
46
|
+
unresources["qualifiers"].each do |parameter|
|
45
47
|
Inspec::Log.debug "Terraform::Negative.parse_unmatched_resources #{iterator} qualifier found = #{parameter} MATCHED"
|
46
48
|
value = resources.deep_find(parameter.to_s) # value comes from another likely source. Assumption is values are consistent for this type of field
|
47
49
|
qualifier[1][parameter] = value
|
48
50
|
end
|
49
51
|
describe.qualifier.push(qualifier)
|
50
|
-
describe.add_test(nil,
|
52
|
+
describe.add_test(nil, "exist", nil, { negated: true }) # last field is negated
|
51
53
|
ctrl.add_test(describe)
|
52
54
|
unmatched_controls.push(ctrl)
|
53
55
|
end
|
@@ -66,8 +68,8 @@ module InspecPlugins::Iggy::Terraform
|
|
66
68
|
Inspec::Log.warn "No iterator matching #{resource} for #{platform} found!"
|
67
69
|
next
|
68
70
|
else
|
69
|
-
iterator = resource_iterators[
|
70
|
-
index = resource_iterators[
|
71
|
+
iterator = resource_iterators["iterator"]
|
72
|
+
index = resource_iterators["index"]
|
71
73
|
Inspec::Log.debug "Terraform::Negative.parse_matched_resources iterator:#{iterator} index:#{index}"
|
72
74
|
end
|
73
75
|
# Nothing but the finest bespoke hand-built InSpec
|
@@ -78,25 +80,42 @@ module InspecPlugins::Iggy::Terraform
|
|
78
80
|
ctrl += " Generated by InSpec-Iggy v#{InspecPlugins::Iggy::VERSION}\"\n\n"
|
79
81
|
ctrl += " impact 1.0\n"
|
80
82
|
# get the qualifiers for the resource iterator
|
81
|
-
ctrl += " (#{iterator}({ "
|
82
|
-
resource_iterators[
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
ctrl += " (#{iterator}.where({ "
|
84
|
+
if resource_iterators["qualifiers"]
|
85
|
+
resource_iterators["qualifiers"].each do |parameter|
|
86
|
+
Inspec::Log.debug "Terraform::Negative.parse_matched_resources #{iterator} qualifier found = #{parameter} MATCHED"
|
87
|
+
value = resources[resource].deep_find(parameter.to_s) # value comes from resources being evaluated. Assumption is values are consistent for this type of field
|
88
|
+
unless value
|
89
|
+
Inspec::Log.warn "Terraform::Negative.parse_matched_resources #{resource} no #{parameter} value found, searching outside scope."
|
90
|
+
value = resources.deep_find(parameter.to_s)
|
91
|
+
end
|
92
|
+
ctrl += "#{parameter}: '#{value}', "
|
93
|
+
end
|
86
94
|
end
|
87
95
|
ctrl += "}).#{index} - [\n"
|
88
96
|
# iterate over the resources
|
89
97
|
resources[resource].keys.each do |resource_name|
|
90
98
|
ctrl += " '#{resource_name}',\n"
|
91
99
|
end
|
92
|
-
ctrl += " ]).each do |
|
93
|
-
ctrl += " describe #{resource}({
|
100
|
+
ctrl += " ]).each do |id|\n"
|
101
|
+
ctrl += " describe #{resource}({ "
|
94
102
|
# iterate over resource qualifiers
|
103
|
+
first = true
|
95
104
|
InspecPlugins::Iggy::InspecHelper.available_resource_qualifiers(platform)[resource].each do |parameter|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
105
|
+
if first # index is first
|
106
|
+
ctrl += "#{parameter}: id, "
|
107
|
+
first = false
|
108
|
+
next
|
109
|
+
end
|
110
|
+
property = parameter.to_s
|
111
|
+
properties = InspecPlugins::Iggy::InspecHelper.available_translated_resource_properties(platform, resource)
|
112
|
+
if properties && properties.value?(parameter.to_s)
|
113
|
+
property = properties.key(parameter.to_s) # translate back if necessary
|
114
|
+
end
|
115
|
+
# instead of looking up the key, find by value?
|
116
|
+
Inspec::Log.debug "Iggy::Terraform::Negative.parse_matched_resources #{resource} qualifier found = #{property} MATCHED"
|
117
|
+
value = resources[resource].deep_find(property) # value comes from resources being evaluated. Assumption is values are consistent for this type of field
|
118
|
+
ctrl += "#{property}: '#{value}', "
|
100
119
|
end
|
101
120
|
ctrl += "}) do\n"
|
102
121
|
ctrl += " it { should_not exist }\n"
|
data/lib/inspec-iggy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec-iggy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Ray
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inspec
|
@@ -16,7 +16,7 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '3'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '5'
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
29
|
+
version: '3'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '5'
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/inspec-iggy/cloudformation/cli_command.rb
|
46
46
|
- lib/inspec-iggy/cloudformation/generate.rb
|
47
47
|
- lib/inspec-iggy/file_helper.rb
|
48
|
+
- lib/inspec-iggy/iggy_cli_command.rb
|
48
49
|
- lib/inspec-iggy/inspec_helper.rb
|
49
50
|
- lib/inspec-iggy/platforms/aws_helper.rb
|
50
51
|
- lib/inspec-iggy/platforms/azure_helper.rb
|