From 01a54247e3b0207e6f6cceaf49f406fe75cc076a Mon Sep 17 00:00:00 2001 From: Kevin DeJong Date: Thu, 9 May 2024 10:06:46 -0700 Subject: [PATCH] Validate GetAtts are to a list when being used for a list (#3224) * Validate GetAtts are to a list when being used for a list * Add in testing for getatt for list --- .../rules/resources/properties/Properties.py | 20 +++++++++++++++++++ src/cfnlint/template/template.py | 6 ------ .../templates/bad/object_should_be_list.yaml | 16 +++++++++++++++ .../resources/properties/test_properties.py | 2 +- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/cfnlint/rules/resources/properties/Properties.py b/src/cfnlint/rules/resources/properties/Properties.py index c930e400fe..40c8994964 100644 --- a/src/cfnlint/rules/resources/properties/Properties.py +++ b/src/cfnlint/rules/resources/properties/Properties.py @@ -493,6 +493,26 @@ def propertycheck(self, text, proptype, parenttype, resourcename, path, root): message.format(prop, resourcename), ) ) + elif "Fn::GetAtt" in text[prop]: + getatt = text[prop]["Fn::GetAtt"] + if isinstance(getatt, str): + getatt = getatt.split(".", 1) + valid_getatts = self.cfn.get_valid_getatts() + if getatt[0] in valid_getatts: + if getatt[1] in valid_getatts[getatt[0]]: + getatt_prop = valid_getatts[getatt[0]][ + getatt[1] + ] + if getatt_prop.get("Type") != "List": + message = "Property {0} should be of type List for resource {1}" + matches.append( + RuleMatch( + proppath, + message.format( + prop, resourcename + ), + ) + ) else: if len(text[prop]) == 1: for k in text[prop].keys(): diff --git a/src/cfnlint/template/template.py b/src/cfnlint/template/template.py index cae26fd011..3f102220b5 100644 --- a/src/cfnlint/template/template.py +++ b/src/cfnlint/template/template.py @@ -423,16 +423,10 @@ def build_output_string(resource_type, property_name): valtype = value["Type"] if isinstance(valtype, str): if valtype.startswith(astrik_string_types): - LOGGER.debug( - "Cant build an appropriate getatt list from %s", valtype - ) results[name] = {"*": {"PrimitiveItemType": "String"}} elif valtype.startswith(astrik_unknown_types) or valtype.endswith( "::MODULE" ): - LOGGER.debug( - "Cant build an appropriate getatt list from %s", valtype - ) results[name] = {"*": {}} else: if value["Type"] in resourcetypes: diff --git a/test/fixtures/templates/bad/object_should_be_list.yaml b/test/fixtures/templates/bad/object_should_be_list.yaml index cd07443472..9fe714041d 100644 --- a/test/fixtures/templates/bad/object_should_be_list.yaml +++ b/test/fixtures/templates/bad/object_should_be_list.yaml @@ -677,3 +677,19 @@ Resources: - - AttributeName: !Ref PartitionKeyName KeyType: HASH - "String2" + EC2Instance: + Type: AWS::EC2::Instance + Properties: + InstanceType: t2.micro + ImageId: XXXXXXXXXXXXXXXXXXXXX + Tags: + - Key: Name + Value: !Ref AWS::StackName + SSMAssociation: + Type: AWS::SSM::Association + Properties: + Name: "SSM Document Name" + ScheduleExpression: rate(2 days) + Targets: + - Key: InstanceIds + Values: !GetAtt EC2Instance.InstanceId diff --git a/test/unit/rules/resources/properties/test_properties.py b/test/unit/rules/resources/properties/test_properties.py index b06c6bd556..7a88f4916f 100644 --- a/test/unit/rules/resources/properties/test_properties.py +++ b/test/unit/rules/resources/properties/test_properties.py @@ -37,7 +37,7 @@ def test_file_negative(self): def test_file_negative_2(self): """Failure test""" self.helper_file_negative( - "test/fixtures/templates/bad/object_should_be_list.yaml", 4 + "test/fixtures/templates/bad/object_should_be_list.yaml", 5 ) def test_file_negative_3(self):