From 41061fd430ea624fe67952515e5a969d43ae8820 Mon Sep 17 00:00:00 2001 From: Kevin DeJong Date: Fri, 18 Oct 2024 09:15:55 -0700 Subject: [PATCH] Update language extensions for empty lists (#3764) --- .../transforms/_language_extensions.py | 12 +- .../transforms/test_language_extensions.py | 188 ++++++++++++++++++ 2 files changed, 198 insertions(+), 2 deletions(-) diff --git a/src/cfnlint/template/transforms/_language_extensions.py b/src/cfnlint/template/transforms/_language_extensions.py index 6760be1b01..e1e6850228 100644 --- a/src/cfnlint/template/transforms/_language_extensions.py +++ b/src/cfnlint/template/transforms/_language_extensions.py @@ -370,15 +370,23 @@ def value( except _ResolveError: try: t_map[2].value(cfn, params) + max_length = -1 for k, v in mapping.items(): if isinstance(v, dict): if t_map[2].value(cfn, params, only_params) in v: + if ( + len(v[t_map[2].value(cfn, params, only_params)]) + <= max_length + ): + continue if isinstance(t_map[1], _ForEachValueRef): if t_map[1]._ref._value == "AWS::AccountId": global _ACCOUNT_ID _ACCOUNT_ID = k t_map[1] = _ForEachValue.create(k) - break + max_length = len( + v[t_map[2].value(cfn, params, only_params)] + ) except _ResolveError: pass @@ -529,7 +537,7 @@ def values( if self._fn: try: values = self._fn.value(cfn, {}, False) - if values: + if values is not None: if isinstance(values, list): for value in values: if isinstance(value, (str, dict)): diff --git a/test/unit/module/template/transforms/test_language_extensions.py b/test/unit/module/template/transforms/test_language_extensions.py index 7741501121..a1829d72f8 100644 --- a/test/unit/module/template/transforms/test_language_extensions.py +++ b/test/unit/module/template/transforms/test_language_extensions.py @@ -971,3 +971,191 @@ def test_transform(self): self.result, template, ) + + +class TestTransformValueEmptyList(TestCase): + def setUp(self) -> None: + + cfnlint.template.transforms._language_extensions._ACCOUNT_ID = None + + self.template_obj = convert_dict( + { + "Transform": ["AWS::LanguageExtensions"], + "Mappings": { + "Accounts": { + "111111111111": {"AppName": []}, + }, + }, + "Resources": { + "Fn::ForEach::Regions": [ + "AppName", + { + "Fn::FindInMap": [ + "Accounts", + {"Ref": "AWS::AccountId"}, + "AppName", + ] + }, + { + "${AppName}Role": { + "Type": "AWS::IAM::Role", + "Properties": { + "RoleName": {"Ref": "AppName"}, + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": ["ec2.amazonaws.com"] + }, + "Action": ["sts:AssumeRole"], + } + ], + }, + "Path": "/", + }, + } + }, + ], + }, + } + ) + + self.result = { + "Mappings": { + "Accounts": { + "111111111111": {"AppName": []}, + }, + }, + "Resources": {}, + "Transform": ["AWS::LanguageExtensions"], + } + + def test_transform(self): + self.maxDiff = None + with mock.patch( + "cfnlint.template.transforms._language_extensions._ACCOUNT_ID", None + ): + cfn = Template( + filename="", template=self.template_obj, regions=["us-east-1"] + ) + matches, template = language_extension(cfn) + self.assertListEqual(matches, []) + self.assertDictEqual( + template, + self.result, + template, + ) + + +class TestTransformValueOneEmpty(TestCase): + def setUp(self) -> None: + self.template_obj = convert_dict( + { + "Transform": ["AWS::LanguageExtensions"], + "Mappings": { + "Accounts": { + "111111111111": {"AppName": []}, + "222222222222": {"AppName": ["C", "D"]}, + "333333333333": {"AppName": []}, + }, + }, + "Resources": { + "Fn::ForEach::Regions": [ + "AppName", + { + "Fn::FindInMap": [ + "Accounts", + {"Ref": "AWS::AccountId"}, + "AppName", + ] + }, + { + "${AppName}Role": { + "Type": "AWS::IAM::Role", + "Properties": { + "RoleName": {"Ref": "AppName"}, + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": ["ec2.amazonaws.com"] + }, + "Action": ["sts:AssumeRole"], + } + ], + }, + "Path": "/", + }, + } + }, + ], + }, + } + ) + + self.result = { + "Mappings": { + "Accounts": { + "111111111111": {"AppName": []}, + "222222222222": {"AppName": ["C", "D"]}, + "333333333333": {"AppName": []}, + }, + }, + "Resources": { + "CRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": {"Service": ["ec2.amazonaws.com"]}, + } + ], + "Version": "2012-10-17", + }, + "Path": "/", + "RoleName": "C", + }, + "Type": "AWS::IAM::Role", + }, + "DRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": {"Service": ["ec2.amazonaws.com"]}, + } + ], + "Version": "2012-10-17", + }, + "Path": "/", + "RoleName": "D", + }, + "Type": "AWS::IAM::Role", + }, + }, + "Transform": ["AWS::LanguageExtensions"], + } + + def test_transform(self): + self.maxDiff = None + with mock.patch( + "cfnlint.template.transforms._language_extensions._ACCOUNT_ID", None + ): + cfn = Template( + filename="", template=self.template_obj, regions=["us-east-1"] + ) + matches, template = language_extension(cfn) + self.assertListEqual(matches, []) + self.assertDictEqual( + template, + self.result, + template, + )