""" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 """ from cfnlint.rules import CloudFormationLintRule, RuleMatch class FindInMapKeys(CloudFormationLintRule): """Check if FindInMap values are correct""" id = "W1011" shortdesc = "FindInMap keys exist in the map" description = ( "Checks the keys in a FindInMap to make sure they exist. " "Check only if the Map Name is a string and if the key is a string." ) source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-findinmap.html" tags = ["functions", "findinmap"] def check_keys(self, map_name, keys, mappings, tree): """Check the validity of the first key""" matches = [] first_key = keys[0] second_key = keys[1] if isinstance(second_key, (str, int)): if isinstance(map_name, (str)): mapping = mappings.get(map_name) if mapping: if isinstance(first_key, (str, int)): if isinstance(map_name, (str)): if mapping.get(first_key) is None: message = 'FindInMap first key "{0}" doesn\'t exist in map "{1}" at {3}' matches.append( RuleMatch( tree[:] + [1], message.format( first_key, map_name, first_key, "/".join(map(str, tree)), ), ) ) if mapping.get(first_key): # Don't double error if they first key doesn't exist if mapping.get(first_key, {}).get(second_key) is None: message = 'FindInMap second key "{0}" doesn\'t exist in map "{1}" under "{2}" at {3}' matches.append( RuleMatch( tree[:] + [2], message.format( second_key, map_name, first_key, "/".join(map(str, tree)), ), ) ) else: for key, value in mapping.items(): if value.get(second_key) is None: message = 'FindInMap second key "{0}" doesn\'t exist in map "{1}" under "{2}" at {3}' matches.append( RuleMatch( tree[:] + [2], message.format( second_key, map_name, key, "/".join(map(str, tree)), ), ) ) return matches def match(self, cfn): matches = [] findinmaps = cfn.search_deep_keys("Fn::FindInMap") mappings = cfn.get_mappings() for findinmap in findinmaps: tree = findinmap[:-1] map_obj = findinmap[-1] if len(map_obj) == 3: matches.extend(self.check_keys(map_obj[0], map_obj[1:], mappings, tree)) return matches