forked from aws-cloudformation/cfn-lint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Cidr.py
163 lines (145 loc) · 8.21 KB
/
Cidr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
import re
import six
from cfnlint.rules import CloudFormationLintRule
from cfnlint.rules import RuleMatch
from cfnlint.helpers import REGEX_CIDR
class Cidr(CloudFormationLintRule):
"""Check if Cidr values are correct"""
id = 'E1024'
shortdesc = 'Cidr validation of parameters'
description = 'Making sure the function CIDR is a list with valid values'
source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html'
tags = ['functions', 'cidr']
def check_parameter_count(self, cfn, parameter_name):
"""Check Count Parameter if used"""
matches = []
parameter_obj = cfn.get_parameters().get(parameter_name, {})
if parameter_obj:
tree = ['Parameters', parameter_name]
parameter_type = parameter_obj.get('Type')
if parameter_type == 'Number':
max_value = parameter_obj.get('MaxValue')
min_value = parameter_obj.get('MinValue')
if (not min_value) or min_value < 1 or min_value > 256:
message = 'Parameter for Cidr count have MinValue between 1 and 256 at {0}'
matches.append(RuleMatch(
tree + ['MinValue'], message.format('/'.join(map(str, tree + ['MinValue'])))))
if (not max_value) or max_value < 1 or max_value > 256:
message = 'Parameter for Cidr count have MaxValue between 1 and 256 at {0}'
matches.append(RuleMatch(
tree + ['MaxValue'], message.format('/'.join(map(str, tree + ['MaxValue'])))))
else:
message = 'Parameter for Cidr count have be of Type Number at {0}'
matches.append(RuleMatch(
tree, message.format('/'.join(map(str, tree)))))
return matches
def check_parameter_size_mask(self, cfn, parameter_name):
"""Check SizeMask Parameter if used"""
matches = []
parameter_obj = cfn.get_parameters().get(parameter_name, {})
if parameter_obj:
tree = ['Parameters', parameter_name]
parameter_type = parameter_obj.get('Type')
if parameter_type == 'Number':
max_value = parameter_obj.get('MaxValue')
min_value = parameter_obj.get('MinValue')
if (not min_value) or min_value < 1 or min_value > 256:
message = 'Parameter for Cidr sizeMask have MinValue between 1 and ' \
'128 (for ipv6) and 32 (for ipv4) at {0}'
matches.append(RuleMatch(
tree + ['MinValue'], message.format('/'.join(map(str, tree + ['MinValue'])))))
if (not max_value) or max_value < 1 or max_value > 256:
message = 'Parameter for Cidr count have MaxValue between 1 and ' \
'128 (for ipv6) and 32 (for ipv4) at {0}'
matches.append(RuleMatch(
tree + ['MaxValue'], message.format('/'.join(map(str, tree + ['MaxValue'])))))
else:
message = 'Parameter for Cidr count have be of Type Number at {0}'
matches.append(RuleMatch(
tree, message.format('/'.join(map(str, tree)))))
return matches
def match(self, cfn):
matches = []
cidr_objs = cfn.search_deep_keys('Fn::Cidr')
supported_functions = [
'Fn::FindInMap',
'Fn::Select',
'Ref',
'Fn::GetAtt',
'Fn::Sub',
'Fn::ImportValue'
]
count_parameters = []
size_mask_parameters = []
for cidr_obj in cidr_objs:
cidr_value_obj = cidr_obj[-1]
tree = cidr_obj[:-1]
if isinstance(cidr_value_obj, list):
if len(cidr_value_obj) in [2, 3]:
ip_block_obj = cidr_value_obj[0]
count_obj = cidr_value_obj[1]
if len(cidr_value_obj) == 3:
size_mask_obj = cidr_value_obj[2]
else:
size_mask_obj = None
if isinstance(ip_block_obj, dict):
if len(ip_block_obj) == 1:
for index_key, _ in ip_block_obj.items():
if index_key not in supported_functions:
message = 'Cidr ipBlock should be Cidr Range, Ref, GetAtt, Sub or Select for {0}'
matches.append(RuleMatch(
tree[:] + [0], message.format('/'.join(map(str, tree[:] + [0])))))
elif isinstance(ip_block_obj, (six.text_type, six.string_types)):
if not re.match(REGEX_CIDR, ip_block_obj):
message = 'Cidr ipBlock should be a Cidr Range based string for {0}'
matches.append(RuleMatch(
tree[:] + [0], message.format('/'.join(map(str, tree[:] + [0])))))
else:
message = 'Cidr ipBlock should be a string for {0}'
matches.append(RuleMatch(
tree[:] + [0], message.format('/'.join(map(str, tree[:] + [0])))))
if isinstance(count_obj, dict):
if len(count_obj) == 1:
for index_key, index_value in count_obj.items():
if index_key not in supported_functions:
message = 'Cidr count should be Int, Ref, or Select for {0}'
matches.append(RuleMatch(
tree[:] + [1], message.format('/'.join(map(str, tree[:] + [1])))))
if index_key == 'Ref':
count_parameters.append(index_value)
elif not isinstance(count_obj, six.integer_types):
message = 'Cidr count should be a int for {0}'
extra_args = {'actual_type': type(count_obj).__name__, 'expected_type': six.integer_types[0].__name__}
matches.append(RuleMatch(
tree[:] + [1], message.format('/'.join(map(str, tree[:] + [1]))), **extra_args))
if isinstance(size_mask_obj, dict):
if len(size_mask_obj) == 1:
for index_key, index_value in size_mask_obj.items():
if index_key not in supported_functions:
message = 'Cidr sizeMask should be Int, Ref, or Select for {0}'
matches.append(RuleMatch(
tree[:] + [2], message.format('/'.join(map(str, tree[:] + [2])))))
if index_key == 'Ref':
size_mask_parameters.append(index_value)
elif not isinstance(size_mask_obj, six.integer_types):
message = 'Cidr sizeMask should be a int for {0}'
extra_args = {'actual_type': type(count_obj).__name__, 'expected_type': six.integer_types[0].__name__}
matches.append(RuleMatch(
tree[:] + [2], message.format('/'.join(map(str, tree[:] + [2]))), **extra_args))
else:
message = 'Cidr should be a list of 2 or 3 elements for {0}'
matches.append(RuleMatch(
tree, message.format('/'.join(map(str, tree)))))
else:
message = 'Cidr should be a list of 2 or 3 elements for {0}'
matches.append(RuleMatch(
tree, message.format('/'.join(map(str, tree)))))
for count_parameter in set(count_parameters):
matches.extend(self.check_parameter_count(cfn, count_parameter))
for size_mask_parameter in set(size_mask_parameters):
matches.extend(self.check_parameter_size_mask(cfn, size_mask_parameter))
return matches