1""" 2Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3SPDX-License-Identifier: MIT-0 4""" 5#pylint: disable=cyclic-import 6import cfnlint.rules 7 8OPERATOR = [ 9 'EQUALS', 10 'NOT_EQUALS', 11 '==', 12 '!=', 13 'IN', 14 'NOT_IN', 15 '>=', 16 '<='] 17 18 19def CreateCustomRule(rule_id, resourceType, prop, value, error_message, description, shortdesc, rule_func): 20 21 class CustomRule(cfnlint.rules.CloudFormationLintRule): 22 23 def __init__(self, rule_id, resourceType, prop, value, error_message, description, shortdesc, rule_func): 24 super(CustomRule, self).__init__() 25 self.resource_property_types.append(resourceType) 26 self.id = rule_id 27 self.property_chain = prop.split('.') 28 self.property_value = value 29 self.error_message = error_message 30 self.description = description 31 self.shortdesc = shortdesc 32 self.rule_func = rule_func 33 34 def _remaining_inset_properties(self, property_chain): 35 if len(property_chain) > 1: 36 return property_chain[1:] 37 38 return [] 39 40 def _check_value(self, value, path, property_chain, cfn): 41 matches = [] 42 if property_chain: 43 new_property_chain = self._remaining_inset_properties(property_chain) 44 matches.extend( 45 cfn.check_value( 46 value, property_chain[0], path, 47 check_value=self._check_value, 48 property_chain=new_property_chain, 49 cfn=cfn, 50 )) 51 return matches 52 if value: 53 matches.extend(self.rule_func(value, self.property_value, path)) 54 return matches 55 56 def match_resource_properties(self, properties, _, path, cfn): 57 new_property_chain = self._remaining_inset_properties(self.property_chain) 58 return cfn.check_value( 59 properties, self.property_chain[0], path, 60 check_value=self._check_value, 61 property_chain=new_property_chain, 62 cfn=cfn, 63 ) 64 65 return CustomRule(rule_id, resourceType, prop, value, error_message, description, shortdesc, rule_func) 66 67 68def CreateEqualsRule(rule_id, resourceType, prop, value, error_message): 69 def rule_func(value, expected_value, path): 70 matches = [] 71 if str(value).strip().lower() != str(expected_value).strip().lower(): 72 matches.append(cfnlint.rules.RuleMatch( 73 path, error_message or 'Must equal check failed')) 74 75 return matches 76 77 return CreateCustomRule( 78 rule_id, resourceType, prop, value, error_message, 79 shortdesc='Custom rule to check for equal values', 80 description='Created from the custom rules parameter. This rule will check if a property value is equal to the specified value.', 81 rule_func=rule_func, 82 ) 83 84 85def CreateNotEqualsRule(rule_id, resourceType, prop, value, error_message): 86 def rule_func(value, expected_values, path): 87 matches = [] 88 if str(value).strip().lower() == str(expected_values).strip().lower(): 89 matches.append(cfnlint.rules.RuleMatch( 90 path, error_message or 'Must not equal check failed')) 91 92 return matches 93 94 return CreateCustomRule( 95 rule_id, resourceType, prop, value, error_message, 96 shortdesc='Custom rule to check for not equal values', 97 description='Created from the custom rules parameter. This rule will check if a property value is NOT equal to the specified value.', 98 rule_func=rule_func, 99 ) 100 101 102def CreateGreaterRule(rule_id, resourceType, prop, value, error_message): 103 def rule_func(value, expected_value, path): 104 matches = [] 105 if checkInt(value.strip()) and checkInt(str(expected_value).strip()): 106 if value.strip().lower() < str(expected_value).strip().lower(): 107 matches.append(cfnlint.rules.RuleMatch( 108 path, error_message or 'Greater than check failed')) 109 else: 110 matches.append(cfnlint.rules.RuleMatch( 111 path, error_message or 'Given values are not numeric')) 112 113 return matches 114 115 return CreateCustomRule( 116 rule_id, resourceType, prop, value, error_message, 117 shortdesc='Custom rule to check for if a value is greater than the specified value', 118 description='Created from the custom rules parameter. This rule will check if a property value is greater than the specified value.', 119 rule_func=rule_func, 120 ) 121 122 123def CreateLesserRule(rule_id, resourceType, prop, value, error_message): 124 def rule_func(value, expected_value, path): 125 matches = [] 126 if checkInt(value.strip()) and checkInt(str(expected_value).strip()): 127 if value.strip().lower() > str(expected_value).strip().lower(): 128 matches.append(cfnlint.rules.RuleMatch( 129 path, error_message or 'Lesser than check failed')) 130 else: 131 matches.append(cfnlint.rules.RuleMatch( 132 path, error_message or 'Given values are not numeric')) 133 134 return matches 135 136 return CreateCustomRule( 137 rule_id, resourceType, prop, value, error_message, 138 shortdesc='Custom rule to check for if a value is lesser than the specified value', 139 description='Created from the custom rules parameter. This rule will check if a property value is lesser than the specified value.', 140 rule_func=rule_func, 141 ) 142 143 144def CreateInSetRule(rule_id, resourceType, prop, value, error_message): 145 def rule_func(value, expected_values, path): 146 matches = [] 147 if value not in expected_values: 148 matches.append(cfnlint.rules.RuleMatch(path, error_message or 'In set check failed')) 149 150 return matches 151 152 return CreateCustomRule( 153 rule_id, resourceType, prop, value, error_message, 154 shortdesc='Custom rule to check for if a value exists in a list of specified values', 155 description='Created from the custom rules parameter. This rule will check if a property value exists inside a list of specified values.', 156 rule_func=rule_func, 157 ) 158 159 160def CreateNotInSetRule(rule_id, resourceType, prop, value, error_message): 161 def rule_func(value, expected_values, path): 162 matches = [] 163 if value in expected_values: 164 matches.append(cfnlint.rules.RuleMatch( 165 path, error_message or 'Not in set check failed')) 166 167 return matches 168 169 return CreateCustomRule( 170 rule_id, resourceType, prop, value, error_message, 171 shortdesc='Custom rule to check for if a value does not exist in a list of specified values', 172 description='Created from the custom rules parameter. This rule will check if a property value does not exist inside a list of specified values.', 173 rule_func=rule_func, 174 ) 175 176 177def CreateInvalidRule(rule_id, operator): 178 class InvalidRule(cfnlint.rules.CloudFormationLintRule): 179 180 def __init__(self, rule_id, operator): 181 super(InvalidRule, self).__init__() 182 self.id = rule_id 183 self.operator = operator 184 self.description = 'Created from the custom rule parameter. This rule is the result of an invalid configuration of a custom rule.' 185 self.shortdesc = 'Invalid custom rule configuration' 186 187 def match(self, _): 188 message = '"{0}" not in supported operators: [{1}]' 189 return [ 190 cfnlint.rules.RuleMatch( 191 [], message.format(str(self.operator), ', '.join(OPERATOR))) 192 ] 193 194 return InvalidRule(rule_id, operator) 195 196 197def checkInt(i): 198 """ Python 2.7 Compatibility - There is no isnumeric() method """ 199 try: 200 int(i) 201 return True 202 except ValueError: 203 return False 204