cfn-model 0.0.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 +7 -0
- data/bin/cfn_parse +8 -0
- data/lib/cfn-model/model/bucket_policy.rb +13 -0
- data/lib/cfn-model/model/cfn_model.rb +64 -0
- data/lib/cfn-model/model/ec2_instance.rb +15 -0
- data/lib/cfn-model/model/ec2_network_interface.rb +18 -0
- data/lib/cfn-model/model/iam_group.rb +15 -0
- data/lib/cfn-model/model/iam_managed_policy.rb +14 -0
- data/lib/cfn-model/model/iam_policy.rb +14 -0
- data/lib/cfn-model/model/iam_role.rb +14 -0
- data/lib/cfn-model/model/iam_user.rb +16 -0
- data/lib/cfn-model/model/iam_user_to_group_addition.rb +10 -0
- data/lib/cfn-model/model/load_balancer.rb +37 -0
- data/lib/cfn-model/model/model_element.rb +101 -0
- data/lib/cfn-model/model/policy.rb +10 -0
- data/lib/cfn-model/model/policy_document.rb +52 -0
- data/lib/cfn-model/model/principal.rb +34 -0
- data/lib/cfn-model/model/queue_policy.rb +13 -0
- data/lib/cfn-model/model/references.rb +52 -0
- data/lib/cfn-model/model/security_group.rb +18 -0
- data/lib/cfn-model/model/security_group_egress.rb +29 -0
- data/lib/cfn-model/model/security_group_ingress.rb +38 -0
- data/lib/cfn-model/model/statement.rb +38 -0
- data/lib/cfn-model/model/topic_policy.rb +13 -0
- data/lib/cfn-model/parser/cfn_parser.rb +126 -0
- data/lib/cfn-model/parser/ec2_instance_parser.rb +10 -0
- data/lib/cfn-model/parser/ec2_network_interface_parser.rb +10 -0
- data/lib/cfn-model/parser/iam_group_parser.rb +17 -0
- data/lib/cfn-model/parser/iam_role_parser.rb +20 -0
- data/lib/cfn-model/parser/iam_user_parser.rb +58 -0
- data/lib/cfn-model/parser/load_balancer_parser.rb +10 -0
- data/lib/cfn-model/parser/load_balancer_v2_parser.rb +15 -0
- data/lib/cfn-model/parser/parser_error.rb +13 -0
- data/lib/cfn-model/parser/parser_registry.rb +34 -0
- data/lib/cfn-model/parser/policy_document_parser.rb +44 -0
- data/lib/cfn-model/parser/security_group_parser.rb +83 -0
- data/lib/cfn-model/parser/with_policy_document_parser.rb +10 -0
- data/lib/cfn-model/schema/AWS_CloudFront_Distribution.yml +42 -0
- data/lib/cfn-model/schema/AWS_EC2_Instance.yml +146 -0
- data/lib/cfn-model/schema/AWS_EC2_NetworkInterface.yml +62 -0
- data/lib/cfn-model/schema/AWS_EC2_NetworkInterfaceAttachment.yml +24 -0
- data/lib/cfn-model/schema/AWS_EC2_SecurityGroup.yml +71 -0
- data/lib/cfn-model/schema/AWS_EC2_SecurityGroupEgress.yml +27 -0
- data/lib/cfn-model/schema/AWS_EC2_SecurityGroupIngress.yml +27 -0
- data/lib/cfn-model/schema/AWS_ElasticLoadBalancingV2_LoadBalancer.yml +56 -0
- data/lib/cfn-model/schema/AWS_ElasticLoadBalancing_LoadBalancer.yml +188 -0
- data/lib/cfn-model/schema/AWS_IAM_Group.yml +23 -0
- data/lib/cfn-model/schema/AWS_IAM_ManagedPolicy.yml +34 -0
- data/lib/cfn-model/schema/AWS_IAM_Policy.yml +36 -0
- data/lib/cfn-model/schema/AWS_IAM_Role.yml +28 -0
- data/lib/cfn-model/schema/AWS_IAM_User.yml +38 -0
- data/lib/cfn-model/schema/AWS_IAM_UserToGroupAddition.yml +23 -0
- data/lib/cfn-model/schema/AWS_Lambda_Permission.yml +24 -0
- data/lib/cfn-model/schema/AWS_S3_BucketPolicy.yml +21 -0
- data/lib/cfn-model/schema/AWS_SNS_TopicPolicy.yml +23 -0
- data/lib/cfn-model/schema/AWS_SQS_QueuePolicy.yml +23 -0
- data/lib/cfn-model/schema/schema.yml.erb +17 -0
- data/lib/cfn-model/validator/cloudformation_validator.rb +12 -0
- data/lib/cfn-model/validator/reference_validator.rb +83 -0
- data/lib/cfn-model/validator/resource_type_validator.rb +34 -0
- data/lib/cfn-model/validator/schema_generator.rb +86 -0
- data/lib/cfn-model.rb +2 -0
- metadata +120 -0
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'cfn-model/validator/cloudformation_validator'
|
3
|
+
require 'cfn-model/validator/reference_validator'
|
4
|
+
require_relative 'parser_registry'
|
5
|
+
require_relative 'parser_error'
|
6
|
+
Dir["#{__dir__}/../model/*.rb"].each { |model| require "cfn-model/model/#{File.basename(model, '.rb')}" }
|
7
|
+
|
8
|
+
##
|
9
|
+
# This class is the heart of the matter. It will take a CloudFormation template
|
10
|
+
# and return a CfnModel object to represent the underlying document in a way
|
11
|
+
# that is hopefully more convenient for (cfn-nag rule) developers to work with
|
12
|
+
#
|
13
|
+
class CfnParser
|
14
|
+
# this will convert any !Ref or !GetAtt into tranditional hash like in json
|
15
|
+
YAML.add_domain_type('', 'Ref') { |type, val| { 'Ref' => val } }
|
16
|
+
|
17
|
+
%w(GetAtt Join Base64 Sub Split Select ImportValue GetAZs FindInMap And Or If Not).each do |function_name|
|
18
|
+
YAML.add_domain_type('', function_name) { |type, val| { "Fn::#{function_name}" => val } }
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Given raw json/yml CloudFormation template, returns a CfnModel object
|
23
|
+
# or raise ParserErrors if something is amiss with the format
|
24
|
+
def parse(cloudformation_yml)
|
25
|
+
cfn_hash = pre_validate_model cloudformation_yml
|
26
|
+
|
27
|
+
cfn_model = CfnModel.new
|
28
|
+
cfn_model.raw_model = cfn_hash
|
29
|
+
|
30
|
+
# pass 1: wire properties into ModelElement objects
|
31
|
+
transform_hash_into_model_elements cfn_hash, cfn_model
|
32
|
+
|
33
|
+
# pass 2: tie together separate resources only where necessary to make life easier for rule logic
|
34
|
+
post_process_resource_model_elements cfn_model
|
35
|
+
|
36
|
+
cfn_model
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def post_process_resource_model_elements(cfn_model)
|
42
|
+
cfn_model.resources.each do |_, resource|
|
43
|
+
resource_parser_class = ParserRegistry.instance.registry[resource.class.to_s]
|
44
|
+
|
45
|
+
next if resource_parser_class.nil?
|
46
|
+
|
47
|
+
resource_parser = resource_parser_class.new
|
48
|
+
resource_parser.parse(cfn_model: cfn_model,
|
49
|
+
resource: resource)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# pass 0: validate basic syntax so we can make some assumptions down stream
|
54
|
+
# even within the parsing code
|
55
|
+
def transform_hash_into_model_elements(cfn_hash, cfn_model)
|
56
|
+
cfn_hash['Resources'].each do |resource_name, resource|
|
57
|
+
resource_class = class_from_type_name resource['Type']
|
58
|
+
|
59
|
+
resource_object = resource_class.new
|
60
|
+
resource_object.logical_resource_id = resource_name
|
61
|
+
resource_object.resource_type = resource['Type']
|
62
|
+
|
63
|
+
assign_fields_based_upon_properties resource_object, resource
|
64
|
+
|
65
|
+
cfn_model.resources[resource_name] = resource_object
|
66
|
+
end
|
67
|
+
cfn_model
|
68
|
+
end
|
69
|
+
|
70
|
+
def pre_validate_model(cloudformation_yml)
|
71
|
+
errors = CloudFormationValidator.new.validate cloudformation_yml
|
72
|
+
if !errors.nil? && !errors.empty?
|
73
|
+
raise ParserError.new('Basic CloudFormation syntax error', errors)
|
74
|
+
end
|
75
|
+
|
76
|
+
cfn_hash = YAML.load cloudformation_yml
|
77
|
+
|
78
|
+
unresolved_refs = ReferenceValidator.new.unresolved_references(cfn_hash)
|
79
|
+
unless unresolved_refs.empty?
|
80
|
+
raise ParserError.new("Unresolved logical resource ids: #{unresolved_refs.to_a}")
|
81
|
+
end
|
82
|
+
cfn_hash
|
83
|
+
end
|
84
|
+
|
85
|
+
def assign_fields_based_upon_properties(resource_object, resource)
|
86
|
+
unless resource['Properties'].nil?
|
87
|
+
resource['Properties'].each do |property_name, property_value|
|
88
|
+
resource_object.send("#{initial_lower(property_name)}=", property_value)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def class_from_type_name(type_name)
|
94
|
+
begin
|
95
|
+
resource_class = Object.const_get type_name, inherit=false
|
96
|
+
rescue NameError
|
97
|
+
# puts "Never seen class: #{type_name} so going dynamic"
|
98
|
+
resource_class = generate_resource_class_from_type type_name
|
99
|
+
end
|
100
|
+
resource_class
|
101
|
+
end
|
102
|
+
|
103
|
+
def initial_lower(str)
|
104
|
+
str.slice(0).downcase + str[1..(str.length)]
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate_resource_class_from_type(type_name)
|
108
|
+
resource_class = Class.new(ModelElement)
|
109
|
+
|
110
|
+
module_names = type_name.split('::')
|
111
|
+
if module_names.first == 'Custom'
|
112
|
+
Object.const_set(module_names[1], resource_class)
|
113
|
+
elsif module_names.first == 'AWS'
|
114
|
+
begin
|
115
|
+
module_constant = AWS.const_get(module_names[1])
|
116
|
+
rescue NameError
|
117
|
+
module_constant = Module.new
|
118
|
+
module_constant.const_set(module_names[1], module_constant)
|
119
|
+
end
|
120
|
+
module_constant.const_set(module_names[2], resource_class)
|
121
|
+
else
|
122
|
+
raise "Unknown namespace in resource type: #{module_names.first}"
|
123
|
+
end
|
124
|
+
resource_class
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Ec2InstanceParser
|
2
|
+
def parse(cfn_model:, resource:)
|
3
|
+
ec2_instance = resource
|
4
|
+
|
5
|
+
ec2_instance.security_groups = ec2_instance.securityGroupIds.map do |security_group_reference|
|
6
|
+
cfn_model.find_security_group_by_group_id(security_group_reference)
|
7
|
+
end
|
8
|
+
ec2_instance
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Ec2NetworkInterfaceParser
|
2
|
+
def parse(cfn_model:, resource:)
|
3
|
+
network_interface = resource
|
4
|
+
|
5
|
+
network_interface.security_groups = network_interface.groupSet.map do |security_group_reference|
|
6
|
+
cfn_model.find_security_group_by_group_id(security_group_reference)
|
7
|
+
end
|
8
|
+
network_interface
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'cfn-model/model/iam_role'
|
2
|
+
require 'cfn-model/model/policy'
|
3
|
+
require_relative 'policy_document_parser'
|
4
|
+
|
5
|
+
class IamGroupParser
|
6
|
+
def parse(cfn_model:, resource:)
|
7
|
+
iam_group = resource
|
8
|
+
|
9
|
+
iam_group.policy_objects = iam_group.policies.map do |policy|
|
10
|
+
new_policy = Policy.new
|
11
|
+
new_policy.policyName = policy['PolicyName']
|
12
|
+
new_policy.policyDocument = PolicyDocumentParser.new.parse(policy['PolicyDocument'])
|
13
|
+
new_policy
|
14
|
+
end
|
15
|
+
iam_group
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'cfn-model/model/iam_role'
|
2
|
+
require 'cfn-model/model/policy'
|
3
|
+
require_relative 'policy_document_parser'
|
4
|
+
|
5
|
+
class IamRoleParser
|
6
|
+
def parse(cfn_model:, resource:)
|
7
|
+
iam_role = resource
|
8
|
+
|
9
|
+
iam_role.assume_role_policy_document = PolicyDocumentParser.new.parse(iam_role.assumeRolePolicyDocument)
|
10
|
+
|
11
|
+
iam_role.policy_objects = iam_role.policies.map do |policy|
|
12
|
+
new_policy = Policy.new
|
13
|
+
new_policy.policy_name = policy['PolicyName']
|
14
|
+
new_policy.policy_document = PolicyDocumentParser.new.parse(policy['PolicyDocument'])
|
15
|
+
new_policy
|
16
|
+
end
|
17
|
+
|
18
|
+
iam_role
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'cfn-model/model/policy_document'
|
2
|
+
require 'cfn-model/model/policy'
|
3
|
+
require_relative 'policy_document_parser'
|
4
|
+
|
5
|
+
class IamUserParser
|
6
|
+
def parse(cfn_model:, resource:)
|
7
|
+
iam_user = resource
|
8
|
+
|
9
|
+
iam_user.policy_objects = iam_user.policies.map do |policy|
|
10
|
+
new_policy = Policy.new
|
11
|
+
new_policy.policy_name = policy['PolicyName']
|
12
|
+
new_policy.policy_document = PolicyDocumentParser.new.parse(policy['PolicyDocument'])
|
13
|
+
new_policy
|
14
|
+
end
|
15
|
+
|
16
|
+
iam_user.groups.each { |group_name| iam_user.group_names << group_name }
|
17
|
+
|
18
|
+
user_to_group_additions = cfn_model.resources_by_type 'AWS::IAM::UserToGroupAddition'
|
19
|
+
user_to_group_additions.each do |user_to_group_addition|
|
20
|
+
|
21
|
+
if user_to_group_addition_has_username(user_to_group_addition.users,iam_user)
|
22
|
+
iam_user.group_names << user_to_group_addition.groupName
|
23
|
+
|
24
|
+
# we need to figure out the story on resolving Refs i think for this to be real
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def user_to_group_addition_has_username(addition_user_names, user_to_find)
|
32
|
+
addition_user_names.each do |addition_user_name|
|
33
|
+
if addition_user_name == user_to_find.userName
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
|
37
|
+
if addition_user_name.is_a? Hash
|
38
|
+
if !addition_user_name['Ref'].nil?
|
39
|
+
if addition_user_name['Ref'] == user_to_find.logical_resource_id
|
40
|
+
return true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
# def resolve_user_logical_resource_id(user)
|
49
|
+
# if not user['Ref'].nil?
|
50
|
+
# user['Ref']
|
51
|
+
# elsif not user['Fn::GetAtt'].nil?
|
52
|
+
# fail 'Arn not legal for user to group addition'
|
53
|
+
# else
|
54
|
+
# @dangler
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class LoadBalancerParser
|
2
|
+
def parse(cfn_model:, resource:)
|
3
|
+
load_balancer = resource
|
4
|
+
|
5
|
+
load_balancer.security_groups = load_balancer.securityGroups.map do |security_group_reference|
|
6
|
+
cfn_model.find_security_group_by_group_id(security_group_reference)
|
7
|
+
end
|
8
|
+
load_balancer
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class LoadBalancerV2Parser
|
2
|
+
def parse(cfn_model:, resource:)
|
3
|
+
load_balancer = resource
|
4
|
+
|
5
|
+
#could be a List<Subnet::Id>
|
6
|
+
# if load_balancer.subnets.size < 2
|
7
|
+
# raise ParserError.new("Load Balancer must have at least two subnets: #{load_balancer.logical_resource_id}")
|
8
|
+
# end
|
9
|
+
|
10
|
+
load_balancer.security_groups = load_balancer.securityGroups.map do |security_group_reference|
|
11
|
+
cfn_model.find_security_group_by_group_id(security_group_reference)
|
12
|
+
end
|
13
|
+
load_balancer
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ParserError < RuntimeError
|
2
|
+
attr_accessor :errors
|
3
|
+
|
4
|
+
def initialize(message, validation_errors=nil)
|
5
|
+
super(message)
|
6
|
+
@message = message
|
7
|
+
@errors = validation_errors
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{@message}#{@errors.nil? ? '' : ':'}#{@errors.to_s}"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Dir["#{__dir__}/*_parser.rb"].each { |model| require "cfn-model/parser/#{File.basename(model, '.rb')}" }
|
2
|
+
|
3
|
+
class ParserRegistry
|
4
|
+
attr_reader :registry
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@registry = {
|
8
|
+
'AWS::EC2::SecurityGroup' => SecurityGroupParser,
|
9
|
+
'AWS::EC2::NetworkInterface' => Ec2NetworkInterfaceParser,
|
10
|
+
'AWS::EC2::Instance' => Ec2InstanceParser,
|
11
|
+
'AWS::ElasticLoadBalancing::LoadBalancer' => LoadBalancerParser,
|
12
|
+
'AWS::ElasticLoadBalancingV2::LoadBalancer' => LoadBalancerV2Parser,
|
13
|
+
'AWS::IAM::Group' => IamGroupParser,
|
14
|
+
'AWS::IAM::User' => IamUserParser,
|
15
|
+
'AWS::IAM::Role' => IamRoleParser,
|
16
|
+
'AWS::IAM::Policy' => WithPolicyDocumentParser,
|
17
|
+
'AWS::IAM::ManagedPolicy' => WithPolicyDocumentParser,
|
18
|
+
'AWS::S3::BucketPolicy' => WithPolicyDocumentParser,
|
19
|
+
'AWS::SNS::TopicPolicy' => WithPolicyDocumentParser,
|
20
|
+
'AWS::SQS::QueuePolicy' => WithPolicyDocumentParser
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.instance
|
25
|
+
if @instance.nil?
|
26
|
+
@instance = ParserRegistry.new
|
27
|
+
end
|
28
|
+
@instance
|
29
|
+
end
|
30
|
+
|
31
|
+
# def add_parser(resource_name, parser)
|
32
|
+
# @registry[resource_name] = parser
|
33
|
+
# end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'cfn-model/model/iam_policy'
|
2
|
+
require 'cfn-model/model/policy_document'
|
3
|
+
|
4
|
+
class PolicyDocumentParser
|
5
|
+
def parse(raw_policy_document)
|
6
|
+
policy_document = PolicyDocument.new
|
7
|
+
|
8
|
+
policy_document.version = raw_policy_document['Version']
|
9
|
+
|
10
|
+
policy_document.statements = streamline_array(raw_policy_document['Statement']) do |statement|
|
11
|
+
parse_statement statement
|
12
|
+
end
|
13
|
+
|
14
|
+
policy_document
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def parse_statement(raw_statement)
|
20
|
+
statement = Statement.new
|
21
|
+
statement.effect = raw_statement['Effect']
|
22
|
+
statement.sid = raw_statement['Sid']
|
23
|
+
statement.condition = raw_statement['Condition']
|
24
|
+
statement.actions = streamline_array(raw_statement['Action'])
|
25
|
+
statement.not_actions = streamline_array(raw_statement['NotAction'])
|
26
|
+
statement.resources = streamline_array(raw_statement['Resource'])
|
27
|
+
statement.not_resources = streamline_array(raw_statement['NotResource'])
|
28
|
+
statement.principal = raw_statement['Principal']
|
29
|
+
statement.not_principal = raw_statement['NotPrincipal']
|
30
|
+
statement
|
31
|
+
end
|
32
|
+
|
33
|
+
def streamline_array(one_or_more)
|
34
|
+
return [] if one_or_more.nil?
|
35
|
+
|
36
|
+
if one_or_more.is_a?(String) || one_or_more.is_a?(Hash)
|
37
|
+
[block_given? ? yield(one_or_more) : one_or_more]
|
38
|
+
elsif one_or_more.is_a? Array
|
39
|
+
one_or_more.map { |one| block_given? ? yield(one) : one }
|
40
|
+
else
|
41
|
+
raise "unexpected object in streamline_array: #{one_or_more} #{one_or_more.class}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative 'parser_error'
|
2
|
+
require 'cfn-model/model/security_group_egress'
|
3
|
+
require 'cfn-model/model/security_group_ingress'
|
4
|
+
require 'cfn-model/model/references'
|
5
|
+
|
6
|
+
class SecurityGroupParser
|
7
|
+
|
8
|
+
def parse(cfn_model:, resource:)
|
9
|
+
security_group = resource
|
10
|
+
|
11
|
+
objectify_egress security_group
|
12
|
+
|
13
|
+
objectify_ingress security_group
|
14
|
+
|
15
|
+
wire_ingress_rules_to_security_group(cfn_model: cfn_model, security_group: security_group)
|
16
|
+
wire_egress_rules_to_security_group(cfn_model: cfn_model, security_group: security_group)
|
17
|
+
security_group
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def objectify_ingress(security_group)
|
23
|
+
if security_group.securityGroupIngress.is_a? Hash
|
24
|
+
security_group.securityGroupIngress = [security_group.securityGroupIngress]
|
25
|
+
end
|
26
|
+
|
27
|
+
security_group.ingresses = security_group.securityGroupIngress.map do |ingress|
|
28
|
+
ingress_object = AWS::EC2::SecurityGroupIngress.new
|
29
|
+
ingress.each do |k,v|
|
30
|
+
ingress_object.send("#{initialLower(k)}=", v)
|
31
|
+
end
|
32
|
+
#ingress_object.valid?
|
33
|
+
ingress_object
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def objectify_egress(security_group)
|
38
|
+
if security_group.securityGroupEgress.is_a? Hash
|
39
|
+
security_group.securityGroupEgress = [security_group.securityGroupEgress]
|
40
|
+
end
|
41
|
+
|
42
|
+
security_group.egresses = security_group.securityGroupEgress.map do |egress|
|
43
|
+
egress_object = AWS::EC2::SecurityGroupEgress.new
|
44
|
+
egress.each do |k,v|
|
45
|
+
egress_object.send("#{initialLower(k)}=", v)
|
46
|
+
end
|
47
|
+
#egress_object.valid?
|
48
|
+
egress_object
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialLower(str)
|
53
|
+
str.slice(0).downcase + str[1..(str.length)]
|
54
|
+
end
|
55
|
+
|
56
|
+
def wire_ingress_rules_to_security_group(cfn_model:, security_group:)
|
57
|
+
ingress_rules = cfn_model.resources_by_type 'AWS::EC2::SecurityGroupIngress'
|
58
|
+
ingress_rules.each do |security_group_ingress|
|
59
|
+
group_id = References.resolve_security_group_id(security_group_ingress.groupId)
|
60
|
+
|
61
|
+
# standalone ingress rules are legal - referencing an external security group
|
62
|
+
next if group_id.nil?
|
63
|
+
|
64
|
+
if security_group.logical_resource_id == group_id
|
65
|
+
security_group.ingresses << security_group_ingress
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def wire_egress_rules_to_security_group(cfn_model:, security_group:)
|
71
|
+
egress_rules = cfn_model.resources_by_type 'AWS::EC2::SecurityGroupEgress'
|
72
|
+
egress_rules.each do |security_group_egress|
|
73
|
+
group_id = References.resolve_security_group_id(security_group_egress.groupId)
|
74
|
+
|
75
|
+
# standalone ingress rules are legal - referencing an external security group
|
76
|
+
next if group_id.nil?
|
77
|
+
|
78
|
+
if security_group.logical_resource_id == group_id
|
79
|
+
security_group.egresses << security_group_egress
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'cfn-model/model/iam_policy'
|
2
|
+
require 'cfn-model/model/policy_document'
|
3
|
+
require_relative 'policy_document_parser'
|
4
|
+
|
5
|
+
class WithPolicyDocumentParser
|
6
|
+
def parse(cfn_model:, resource:)
|
7
|
+
resource.policy_document = PolicyDocumentParser.new.parse(resource.policyDocument)
|
8
|
+
resource
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
---
|
2
|
+
type: map
|
3
|
+
mapping:
|
4
|
+
Type:
|
5
|
+
type: str
|
6
|
+
required: yes
|
7
|
+
pattern: /AWS::CloudFront::Distribution/
|
8
|
+
Properties:
|
9
|
+
type: map
|
10
|
+
required: yes
|
11
|
+
mapping:
|
12
|
+
DistributionConfig:
|
13
|
+
type: map
|
14
|
+
required: yes
|
15
|
+
mapping:
|
16
|
+
Aliases:
|
17
|
+
type: seq
|
18
|
+
required: no
|
19
|
+
sequence:
|
20
|
+
- type: any
|
21
|
+
CacheBehaviors:
|
22
|
+
type: seq
|
23
|
+
required: no
|
24
|
+
sequence:
|
25
|
+
- type: any
|
26
|
+
DefaultCacheBehavior:
|
27
|
+
type: any
|
28
|
+
required: yes
|
29
|
+
Enabled:
|
30
|
+
type: any
|
31
|
+
required: yes
|
32
|
+
Origins:
|
33
|
+
type: seq
|
34
|
+
required: yes
|
35
|
+
sequence:
|
36
|
+
- type: any
|
37
|
+
=:
|
38
|
+
type: any
|
39
|
+
=:
|
40
|
+
type: any
|
41
|
+
=:
|
42
|
+
type: any
|
@@ -0,0 +1,146 @@
|
|
1
|
+
---
|
2
|
+
type: map
|
3
|
+
mapping:
|
4
|
+
Type:
|
5
|
+
type: str
|
6
|
+
required: yes
|
7
|
+
pattern: /AWS::EC2::Instance/
|
8
|
+
Properties:
|
9
|
+
type: map
|
10
|
+
required: yes
|
11
|
+
mapping:
|
12
|
+
BlockDeviceMappings:
|
13
|
+
type: seq
|
14
|
+
required: no
|
15
|
+
sequence:
|
16
|
+
- type: map
|
17
|
+
mapping:
|
18
|
+
DeviceName:
|
19
|
+
type: any
|
20
|
+
required: yes
|
21
|
+
=:
|
22
|
+
type: any
|
23
|
+
ImageId:
|
24
|
+
type: any
|
25
|
+
required: yes
|
26
|
+
Ipv6Addresses:
|
27
|
+
type: seq
|
28
|
+
required: no
|
29
|
+
sequence:
|
30
|
+
- type: map
|
31
|
+
mapping:
|
32
|
+
Ipv6Address:
|
33
|
+
type: any
|
34
|
+
required: yes
|
35
|
+
=:
|
36
|
+
type: any
|
37
|
+
NetworkInterfaces:
|
38
|
+
type: seq
|
39
|
+
required: no
|
40
|
+
sequence:
|
41
|
+
- type: map
|
42
|
+
mapping:
|
43
|
+
DeviceIndex:
|
44
|
+
type: any
|
45
|
+
required: yes
|
46
|
+
GroupSet:
|
47
|
+
type: seq
|
48
|
+
required: no
|
49
|
+
sequence:
|
50
|
+
- type: any
|
51
|
+
Ipv6Addresses:
|
52
|
+
type: seq
|
53
|
+
required: no
|
54
|
+
sequence:
|
55
|
+
- type: map
|
56
|
+
mapping:
|
57
|
+
Ipv6Address:
|
58
|
+
type: any
|
59
|
+
required: yes
|
60
|
+
=:
|
61
|
+
type: any
|
62
|
+
PrivateIpAddresses:
|
63
|
+
type: seq
|
64
|
+
required: no
|
65
|
+
sequence:
|
66
|
+
- type: map
|
67
|
+
mapping:
|
68
|
+
PrivateIpAddress:
|
69
|
+
type: any
|
70
|
+
required: yes
|
71
|
+
Primary:
|
72
|
+
type: any
|
73
|
+
required: yes
|
74
|
+
=:
|
75
|
+
type: any
|
76
|
+
=:
|
77
|
+
type: any
|
78
|
+
|
79
|
+
# sigh this could be List<AWS::EC2::SecurityGroup::Id> so can't enfore seq
|
80
|
+
SecurityGroupIds:
|
81
|
+
type: any
|
82
|
+
required: no
|
83
|
+
|
84
|
+
# sigh this could be List<AWS::EC2::SecurityGroup::GroupName> so can't enfore seq
|
85
|
+
SecurityGroups:
|
86
|
+
type: any
|
87
|
+
required: no
|
88
|
+
|
89
|
+
SsmAssociations:
|
90
|
+
type: seq
|
91
|
+
required: no
|
92
|
+
sequence:
|
93
|
+
- type: map
|
94
|
+
mapping:
|
95
|
+
AssociationParameters:
|
96
|
+
type: seq
|
97
|
+
required: no
|
98
|
+
sequence:
|
99
|
+
- type: map
|
100
|
+
mapping:
|
101
|
+
Key:
|
102
|
+
type: any
|
103
|
+
required: yes
|
104
|
+
Value:
|
105
|
+
type: seq
|
106
|
+
required: yes
|
107
|
+
sequence:
|
108
|
+
- type: any
|
109
|
+
=:
|
110
|
+
type: any
|
111
|
+
DocumentName:
|
112
|
+
required: yes
|
113
|
+
type: any
|
114
|
+
|
115
|
+
Tags:
|
116
|
+
type: seq
|
117
|
+
required: no
|
118
|
+
sequence:
|
119
|
+
- type: map
|
120
|
+
mapping:
|
121
|
+
Key:
|
122
|
+
type: any
|
123
|
+
required: yes
|
124
|
+
Value:
|
125
|
+
type: any
|
126
|
+
required: yes
|
127
|
+
=:
|
128
|
+
type: any
|
129
|
+
Volumes:
|
130
|
+
type: seq
|
131
|
+
required: no
|
132
|
+
sequence:
|
133
|
+
- type: map
|
134
|
+
mapping:
|
135
|
+
Device:
|
136
|
+
type: any
|
137
|
+
required: yes
|
138
|
+
VolumeId:
|
139
|
+
type: any
|
140
|
+
required: yes
|
141
|
+
=:
|
142
|
+
type: any
|
143
|
+
=:
|
144
|
+
type: any
|
145
|
+
=:
|
146
|
+
type: any
|