inspec-iggy 0.6.0 → 0.7.0
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 +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
|