""" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 """ from cfnlint.helpers import VALID_PARAMETER_TYPES_LIST from cfnlint.rules import CloudFormationLintRule, RuleMatch class Equals(CloudFormationLintRule): """Check Equals Condition Function Logic""" id = "E8003" shortdesc = "Check Fn::Equals structure for validity" description = "Check Fn::Equals is a list of two elements" source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-equals" tags = ["functions", "equals"] allowed_functions = [ "Ref", "Fn::FindInMap", "Fn::Sub", "Fn::Join", "Fn::Select", "Fn::Split", "Fn::Length", "Fn::ToJsonString", ] function = "Fn::Equals" def _check_equal_values(self, element, path, valid_refs): matches = [] if len(element) == 1: for element_key, element_value in element.items(): if element_key not in self.allowed_functions: message = ( self.function + " element must be a supported function ({0})" ) matches.append( RuleMatch( path[:] + [element_key], message.format(", ".join(self.allowed_functions)), ) ) elif element_key == "Ref": valid_ref = valid_refs.get(element_value) if valid_ref: if valid_ref.get("From") == "Parameters": if valid_ref.get("Type") in VALID_PARAMETER_TYPES_LIST: message = "Every Fn::Equals object requires a list of 2 string parameters" matches.append(RuleMatch(path, message)) else: message = self.function + " element must be a supported function ({0})" matches.append( RuleMatch(path, message.format(", ".join(self.allowed_functions))) ) return matches def match(self, cfn): matches = [] # Build the list of functions trees = cfn.search_deep_keys(self.function) valid_refs = cfn.get_valid_refs() for tree in trees: # Test when in Conditions if tree[0] == "Conditions": value = tree[-1] if not isinstance(value, list): message = self.function + " must be a list of two elements" matches.append(RuleMatch(tree[:-1], message.format())) elif len(value) != 2: message = self.function + " must be a list of two elements" matches.append(RuleMatch(tree[:-1], message.format())) else: for index, element in enumerate(value): if isinstance(element, dict): matches.extend( self._check_equal_values( element, tree[:-1] + [index], valid_refs ) ) elif not isinstance(element, (str, bool, int, float)): message = ( self.function + " element must be a String, Boolean, Number, or supported function ({0})" ) matches.append( RuleMatch( tree[:-1] + [index], message.format(", ".join(self.allowed_functions)), ) ) return matches