We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
E3002 error thrown when using parameter lists
*cfn-lint version cfn-lint 0.8.3

*Description of issue. When using SSM parameters that are type list, the linter doesn't properly recognize them as a list and thows an error E3002 

Specifically E3002 Property PreferredAvailabilityZones should be of type List or Parameter should be a list for resource cacheRedisV1
us-east-1.yaml:249:7

The parameter section for this is:
Parameters:
  azList:
    Type: "AWS::SSM::Parameter::Value<List<String>>"
    Description: "The list of AZs from Parameter Store"
    Default: '/regionSettings/azList'

The resource is defined as:
cacheForumRedisV1:
    Type: AWS::ElastiCache::CacheCluster
    Properties:
      PreferredAvailabilityZones: !Ref azList


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/cfnlint/rules/resources/properties/Properties.py`
Content:
```
1 """
2   Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 
4   Permission is hereby granted, free of charge, to any person obtaining a copy of this
5   software and associated documentation files (the "Software"), to deal in the Software
6   without restriction, including without limitation the rights to use, copy, modify,
7   merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8   permit persons to whom the Software is furnished to do so.
9 
10   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12   PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 """
17 import six
18 from cfnlint import CloudFormationLintRule
19 from cfnlint import RuleMatch
20 import cfnlint.helpers
21 
22 
23 class Properties(CloudFormationLintRule):
24     """Check Base Resource Configuration"""
25     id = 'E3002'
26     shortdesc = 'Resource properties are valid'
27     description = 'Making sure that resources properties ' + \
28                   'are properly configured'
29     source_url = 'https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#properties'
30     tags = ['resources']
31 
32     def __init__(self):
33         super(Properties, self).__init__()
34         self.cfn = {}
35         self.resourcetypes = {}
36         self.propertytypes = {}
37         self.parameternames = {}
38 
39     def primitivetypecheck(self, value, primtype, proppath):
40         """
41             Check primitive types.
42             Only check that a primitive type is actual a primitive type:
43             - If its JSON let it go
44             - If its Conditions check each sub path of the condition
45             - If its a object make sure its a valid function and function
46             - If its a list raise an error
47 
48         """
49 
50         matches = []
51         if isinstance(value, dict) and primtype == 'Json':
52             return matches
53         if isinstance(value, dict):
54             if len(value) == 1:
55                 for sub_key, sub_value in value.items():
56                     if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:
57                         # not erroring on bad Ifs but not need to account for it
58                         # so the rule doesn't error out
59                         if isinstance(sub_value, list):
60                             if len(sub_value) == 3:
61                                 matches.extend(self.primitivetypecheck(
62                                     sub_value[1], primtype, proppath + ['Fn::If', 1]))
63                                 matches.extend(self.primitivetypecheck(
64                                     sub_value[2], primtype, proppath + ['Fn::If', 2]))
65                     elif sub_key not in ['Fn::Base64', 'Fn::GetAtt', 'Fn::GetAZs', 'Fn::ImportValue',
66                                          'Fn::Join', 'Fn::Split', 'Fn::FindInMap', 'Fn::Select', 'Ref',
67                                          'Fn::If', 'Fn::Contains', 'Fn::Sub', 'Fn::Cidr']:
68                         message = 'Property %s has an illegal function %s' % ('/'.join(map(str, proppath)), sub_key)
69                         matches.append(RuleMatch(proppath, message))
70             else:
71                 message = 'Property is an object instead of %s at %s' % (primtype, '/'.join(map(str, proppath)))
72                 matches.append(RuleMatch(proppath, message))
73         elif isinstance(value, list):
74             message = 'Property should be of type %s not List at %s' % (primtype, '/'.join(map(str, proppath)))
75             matches.append(RuleMatch(proppath, message))
76 
77         return matches
78 
79     def check_list_for_condition(self, text, prop, parenttype, resourcename, propspec, path):
80         """Checks lists that are a dict for conditions"""
81         matches = []
82         if len(text[prop]) == 1:
83             for sub_key, sub_value in text[prop].items():
84                 if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:
85                     if len(sub_value) == 3:
86                         for if_i, if_v in enumerate(sub_value[1:]):
87                             condition_path = path[:] + [sub_key, if_i + 1]
88                             if isinstance(if_v, list):
89                                 for index, item in enumerate(if_v):
90                                     arrproppath = condition_path[:]
91 
92                                     arrproppath.append(index)
93                                     matches.extend(self.propertycheck(
94                                         item, propspec['ItemType'],
95                                         parenttype, resourcename, arrproppath, False))
96                             elif isinstance(if_v, dict):
97                                 if len(if_v) == 1:
98                                     for d_k, d_v in if_v.items():
99                                         if d_k != 'Ref' or d_v != 'AWS::NoValue':
100                                             message = 'Property {0} should be of type List for resource {1} at {2}'
101                                             matches.append(
102                                                 RuleMatch(
103                                                     condition_path,
104                                                     message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))
105                                 else:
106                                     message = 'Property {0} should be of type List for resource {1} at {2}'
107                                     matches.append(
108                                         RuleMatch(
109                                             condition_path,
110                                             message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))
111                             else:
112                                 message = 'Property {0} should be of type List for resource {1} at {2}'
113                                 matches.append(
114                                     RuleMatch(
115                                         condition_path,
116                                         message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))
117 
118                     else:
119                         message = 'Invalid !If condition specified at %s' % ('/'.join(map(str, path)))
120                         matches.append(RuleMatch(path, message))
121                 else:
122                     message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))
123                     matches.append(RuleMatch(path, message))
124         else:
125             message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))
126             matches.append(RuleMatch(path, message))
127 
128         return matches
129 
130     def check_exceptions(self, parenttype, proptype, text):
131         """
132             Checks for exceptions to the spec
133             - Start with handling exceptions for templated code.
134         """
135         templated_exceptions = {
136             'AWS::ApiGateway::RestApi': ['BodyS3Location'],
137             'AWS::Lambda::Function': ['Code'],
138             'AWS::ElasticBeanstalk::ApplicationVersion': ['SourceBundle'],
139         }
140 
141         exceptions = templated_exceptions.get(parenttype, [])
142         if proptype in exceptions:
143             if isinstance(text, six.string_types):
144                 return True
145 
146         return False
147 
148     def propertycheck(self, text, proptype, parenttype, resourcename, path, root):
149         """Check individual properties"""
150 
151         parameternames = self.parameternames
152         matches = []
153         if root:
154             specs = self.resourcetypes
155             resourcetype = parenttype
156         else:
157             specs = self.propertytypes
158             resourcetype = str.format('{0}.{1}', parenttype, proptype)
159             # Handle tags
160             if resourcetype not in specs:
161                 if proptype in specs:
162                     resourcetype = proptype
163                 else:
164                     resourcetype = str.format('{0}.{1}', parenttype, proptype)
165             else:
166                 resourcetype = str.format('{0}.{1}', parenttype, proptype)
167 
168         resourcespec = specs[resourcetype].get('Properties', {})
169         supports_additional_properties = specs[resourcetype].get('AdditionalProperties', False)
170 
171         if text == 'AWS::NoValue':
172             return matches
173         if not isinstance(text, dict):
174             if not self.check_exceptions(parenttype, proptype, text):
175                 message = 'Expecting an object at %s' % ('/'.join(map(str, path)))
176                 matches.append(RuleMatch(path, message))
177             return matches
178 
179         for prop in text:
180             proppath = path[:]
181             proppath.append(prop)
182             if prop not in resourcespec:
183                 if prop in cfnlint.helpers.CONDITION_FUNCTIONS:
184                     cond_values = self.cfn.get_condition_values(text[prop])
185                     for cond_value in cond_values:
186                         matches.extend(self.propertycheck(
187                             cond_value['Value'], proptype, parenttype, resourcename,
188                             proppath + cond_value['Path'], root))
189                 elif not supports_additional_properties:
190                     message = 'Invalid Property %s' % ('/'.join(map(str, proppath)))
191                     matches.append(RuleMatch(proppath, message))
192             else:
193                 if 'Type' in resourcespec[prop]:
194                     if resourcespec[prop]['Type'] == 'List':
195                         if 'PrimitiveItemType' not in resourcespec[prop]:
196                             if isinstance(text[prop], list):
197                                 for index, item in enumerate(text[prop]):
198                                     arrproppath = proppath[:]
199                                     arrproppath.append(index)
200                                     matches.extend(self.propertycheck(
201                                         item, resourcespec[prop]['ItemType'],
202                                         parenttype, resourcename, arrproppath, False))
203                             elif (isinstance(text[prop], dict)):
204                                 # A list can be be specific as a Conditional
205                                 matches.extend(
206                                     self.check_list_for_condition(
207                                         text, prop, parenttype, resourcename, resourcespec[prop], proppath)
208                                 )
209                             else:
210                                 message = 'Property {0} should be of type List for resource {1}'
211                                 matches.append(
212                                     RuleMatch(
213                                         proppath,
214                                         message.format(prop, resourcename)))
215                         else:
216                             if isinstance(text[prop], list):
217                                 primtype = resourcespec[prop]['PrimitiveItemType']
218                                 for index, item in enumerate(text[prop]):
219                                     arrproppath = proppath[:]
220                                     arrproppath.append(index)
221                                     matches.extend(self.primitivetypecheck(item, primtype, arrproppath))
222                             elif isinstance(text[prop], dict):
223                                 if 'Ref' in text[prop]:
224                                     ref = text[prop]['Ref']
225                                     if ref in parameternames:
226                                         param_type = self.cfn.template['Parameters'][ref]['Type']
227                                         if param_type:
228                                             if not param_type.startswith('List<') and not param_type == 'CommaDelimitedList':
229                                                 message = 'Property {0} should be of type List or Parameter should ' \
230                                                           'be a list for resource {1}'
231                                                 matches.append(
232                                                     RuleMatch(
233                                                         proppath,
234                                                         message.format(prop, resourcename)))
235                                     else:
236                                         message = 'Property {0} should be of type List for resource {1}'
237                                         matches.append(
238                                             RuleMatch(
239                                                 proppath,
240                                                 message.format(prop, resourcename)))
241                             else:
242                                 message = 'Property {0} should be of type List for resource {1}'
243                                 matches.append(
244                                     RuleMatch(
245                                         proppath,
246                                         message.format(prop, resourcename)))
247                     else:
248                         if resourcespec[prop]['Type'] not in ['Map']:
249                             matches.extend(self.propertycheck(
250                                 text[prop], resourcespec[prop]['Type'], parenttype,
251                                 resourcename, proppath, False))
252                 elif 'PrimitiveType' in resourcespec[prop]:
253                     primtype = resourcespec[prop]['PrimitiveType']
254                     matches.extend(self.primitivetypecheck(text[prop], primtype, proppath))
255 
256         return matches
257 
258     def match(self, cfn):
259         """Check CloudFormation Properties"""
260         matches = []
261         self.cfn = cfn
262 
263         resourcespecs = cfnlint.helpers.RESOURCE_SPECS[cfn.regions[0]]
264         self.resourcetypes = resourcespecs['ResourceTypes']
265         self.propertytypes = resourcespecs['PropertyTypes']
266         self.parameternames = self.cfn.get_parameter_names()
267         for resourcename, resourcevalue in cfn.get_resources().items():
268             if 'Properties' in resourcevalue and 'Type' in resourcevalue:
269                 resourcetype = resourcevalue.get('Type', None)
270                 if resourcetype.startswith('Custom::'):
271                     resourcetype = 'AWS::CloudFormation::CustomResource'
272                 if resourcetype in self.resourcetypes:
273                     path = ['Resources', resourcename, 'Properties']
274                     matches.extend(self.propertycheck(
275                         resourcevalue.get('Properties', {}), '',
276                         resourcetype, resourcename, path, True))
277 
278         return matches
279
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/cfnlint/rules/resources/properties/Properties.py b/src/cfnlint/rules/resources/properties/Properties.py
--- a/src/cfnlint/rules/resources/properties/Properties.py
+++ b/src/cfnlint/rules/resources/properties/Properties.py
@@ -225,7 +225,7 @@
                                     if ref in parameternames:
                                         param_type = self.cfn.template['Parameters'][ref]['Type']
                                         if param_type:
-                                            if not param_type.startswith('List<') and not param_type == 'CommaDelimitedList':
+                                            if 'List<' not in param_type and '<List' not in param_type and not param_type == 'CommaDelimitedList':
                                                 message = 'Property {0} should be of type List or Parameter should ' \
                                                           'be a list for resource {1}'
                                                 matches.append(

{"golden_diff": "diff --git a/src/cfnlint/rules/resources/properties/Properties.py b/src/cfnlint/rules/resources/properties/Properties.py\n--- a/src/cfnlint/rules/resources/properties/Properties.py\n+++ b/src/cfnlint/rules/resources/properties/Properties.py\n@@ -225,7 +225,7 @@\n                                     if ref in parameternames:\n                                         param_type = self.cfn.template['Parameters'][ref]['Type']\n                                         if param_type:\n-                                            if not param_type.startswith('List<') and not param_type == 'CommaDelimitedList':\n+                                            if 'List<' not in param_type and '<List' not in param_type and not param_type == 'CommaDelimitedList':\n                                                 message = 'Property {0} should be of type List or Parameter should ' \\\n                                                           'be a list for resource {1}'\n                                                 matches.append(\n", "issue": "E3002 error thrown when using parameter lists\n*cfn-lint version cfn-lint 0.8.3\r\n\r\n*Description of issue. When using SSM parameters that are type list, the linter doesn't properly recognize them as a list and thows an error E3002 \r\n\r\nSpecifically E3002 Property PreferredAvailabilityZones should be of type List or Parameter should be a list for resource cacheRedisV1\r\nus-east-1.yaml:249:7\r\n\r\nThe parameter section for this is:\r\nParameters:\r\n  azList:\r\n    Type: \"AWS::SSM::Parameter::Value<List<String>>\"\r\n    Description: \"The list of AZs from Parameter Store\"\r\n    Default: '/regionSettings/azList'\r\n\r\nThe resource is defined as:\r\ncacheForumRedisV1:\r\n    Type: AWS::ElastiCache::CacheCluster\r\n    Properties:\r\n      PreferredAvailabilityZones: !Ref azList\r\n\n", "before_files": [{"content": "\"\"\"\n  Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport six\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\nimport cfnlint.helpers\n\n\nclass Properties(CloudFormationLintRule):\n    \"\"\"Check Base Resource Configuration\"\"\"\n    id = 'E3002'\n    shortdesc = 'Resource properties are valid'\n    description = 'Making sure that resources properties ' + \\\n                  'are properly configured'\n    source_url = 'https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#properties'\n    tags = ['resources']\n\n    def __init__(self):\n        super(Properties, self).__init__()\n        self.cfn = {}\n        self.resourcetypes = {}\n        self.propertytypes = {}\n        self.parameternames = {}\n\n    def primitivetypecheck(self, value, primtype, proppath):\n        \"\"\"\n            Check primitive types.\n            Only check that a primitive type is actual a primitive type:\n            - If its JSON let it go\n            - If its Conditions check each sub path of the condition\n            - If its a object make sure its a valid function and function\n            - If its a list raise an error\n\n        \"\"\"\n\n        matches = []\n        if isinstance(value, dict) and primtype == 'Json':\n            return matches\n        if isinstance(value, dict):\n            if len(value) == 1:\n                for sub_key, sub_value in value.items():\n                    if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:\n                        # not erroring on bad Ifs but not need to account for it\n                        # so the rule doesn't error out\n                        if isinstance(sub_value, list):\n                            if len(sub_value) == 3:\n                                matches.extend(self.primitivetypecheck(\n                                    sub_value[1], primtype, proppath + ['Fn::If', 1]))\n                                matches.extend(self.primitivetypecheck(\n                                    sub_value[2], primtype, proppath + ['Fn::If', 2]))\n                    elif sub_key not in ['Fn::Base64', 'Fn::GetAtt', 'Fn::GetAZs', 'Fn::ImportValue',\n                                         'Fn::Join', 'Fn::Split', 'Fn::FindInMap', 'Fn::Select', 'Ref',\n                                         'Fn::If', 'Fn::Contains', 'Fn::Sub', 'Fn::Cidr']:\n                        message = 'Property %s has an illegal function %s' % ('/'.join(map(str, proppath)), sub_key)\n                        matches.append(RuleMatch(proppath, message))\n            else:\n                message = 'Property is an object instead of %s at %s' % (primtype, '/'.join(map(str, proppath)))\n                matches.append(RuleMatch(proppath, message))\n        elif isinstance(value, list):\n            message = 'Property should be of type %s not List at %s' % (primtype, '/'.join(map(str, proppath)))\n            matches.append(RuleMatch(proppath, message))\n\n        return matches\n\n    def check_list_for_condition(self, text, prop, parenttype, resourcename, propspec, path):\n        \"\"\"Checks lists that are a dict for conditions\"\"\"\n        matches = []\n        if len(text[prop]) == 1:\n            for sub_key, sub_value in text[prop].items():\n                if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:\n                    if len(sub_value) == 3:\n                        for if_i, if_v in enumerate(sub_value[1:]):\n                            condition_path = path[:] + [sub_key, if_i + 1]\n                            if isinstance(if_v, list):\n                                for index, item in enumerate(if_v):\n                                    arrproppath = condition_path[:]\n\n                                    arrproppath.append(index)\n                                    matches.extend(self.propertycheck(\n                                        item, propspec['ItemType'],\n                                        parenttype, resourcename, arrproppath, False))\n                            elif isinstance(if_v, dict):\n                                if len(if_v) == 1:\n                                    for d_k, d_v in if_v.items():\n                                        if d_k != 'Ref' or d_v != 'AWS::NoValue':\n                                            message = 'Property {0} should be of type List for resource {1} at {2}'\n                                            matches.append(\n                                                RuleMatch(\n                                                    condition_path,\n                                                    message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))\n                                else:\n                                    message = 'Property {0} should be of type List for resource {1} at {2}'\n                                    matches.append(\n                                        RuleMatch(\n                                            condition_path,\n                                            message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))\n                            else:\n                                message = 'Property {0} should be of type List for resource {1} at {2}'\n                                matches.append(\n                                    RuleMatch(\n                                        condition_path,\n                                        message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))\n\n                    else:\n                        message = 'Invalid !If condition specified at %s' % ('/'.join(map(str, path)))\n                        matches.append(RuleMatch(path, message))\n                else:\n                    message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))\n                    matches.append(RuleMatch(path, message))\n        else:\n            message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))\n            matches.append(RuleMatch(path, message))\n\n        return matches\n\n    def check_exceptions(self, parenttype, proptype, text):\n        \"\"\"\n            Checks for exceptions to the spec\n            - Start with handling exceptions for templated code.\n        \"\"\"\n        templated_exceptions = {\n            'AWS::ApiGateway::RestApi': ['BodyS3Location'],\n            'AWS::Lambda::Function': ['Code'],\n            'AWS::ElasticBeanstalk::ApplicationVersion': ['SourceBundle'],\n        }\n\n        exceptions = templated_exceptions.get(parenttype, [])\n        if proptype in exceptions:\n            if isinstance(text, six.string_types):\n                return True\n\n        return False\n\n    def propertycheck(self, text, proptype, parenttype, resourcename, path, root):\n        \"\"\"Check individual properties\"\"\"\n\n        parameternames = self.parameternames\n        matches = []\n        if root:\n            specs = self.resourcetypes\n            resourcetype = parenttype\n        else:\n            specs = self.propertytypes\n            resourcetype = str.format('{0}.{1}', parenttype, proptype)\n            # Handle tags\n            if resourcetype not in specs:\n                if proptype in specs:\n                    resourcetype = proptype\n                else:\n                    resourcetype = str.format('{0}.{1}', parenttype, proptype)\n            else:\n                resourcetype = str.format('{0}.{1}', parenttype, proptype)\n\n        resourcespec = specs[resourcetype].get('Properties', {})\n        supports_additional_properties = specs[resourcetype].get('AdditionalProperties', False)\n\n        if text == 'AWS::NoValue':\n            return matches\n        if not isinstance(text, dict):\n            if not self.check_exceptions(parenttype, proptype, text):\n                message = 'Expecting an object at %s' % ('/'.join(map(str, path)))\n                matches.append(RuleMatch(path, message))\n            return matches\n\n        for prop in text:\n            proppath = path[:]\n            proppath.append(prop)\n            if prop not in resourcespec:\n                if prop in cfnlint.helpers.CONDITION_FUNCTIONS:\n                    cond_values = self.cfn.get_condition_values(text[prop])\n                    for cond_value in cond_values:\n                        matches.extend(self.propertycheck(\n                            cond_value['Value'], proptype, parenttype, resourcename,\n                            proppath + cond_value['Path'], root))\n                elif not supports_additional_properties:\n                    message = 'Invalid Property %s' % ('/'.join(map(str, proppath)))\n                    matches.append(RuleMatch(proppath, message))\n            else:\n                if 'Type' in resourcespec[prop]:\n                    if resourcespec[prop]['Type'] == 'List':\n                        if 'PrimitiveItemType' not in resourcespec[prop]:\n                            if isinstance(text[prop], list):\n                                for index, item in enumerate(text[prop]):\n                                    arrproppath = proppath[:]\n                                    arrproppath.append(index)\n                                    matches.extend(self.propertycheck(\n                                        item, resourcespec[prop]['ItemType'],\n                                        parenttype, resourcename, arrproppath, False))\n                            elif (isinstance(text[prop], dict)):\n                                # A list can be be specific as a Conditional\n                                matches.extend(\n                                    self.check_list_for_condition(\n                                        text, prop, parenttype, resourcename, resourcespec[prop], proppath)\n                                )\n                            else:\n                                message = 'Property {0} should be of type List for resource {1}'\n                                matches.append(\n                                    RuleMatch(\n                                        proppath,\n                                        message.format(prop, resourcename)))\n                        else:\n                            if isinstance(text[prop], list):\n                                primtype = resourcespec[prop]['PrimitiveItemType']\n                                for index, item in enumerate(text[prop]):\n                                    arrproppath = proppath[:]\n                                    arrproppath.append(index)\n                                    matches.extend(self.primitivetypecheck(item, primtype, arrproppath))\n                            elif isinstance(text[prop], dict):\n                                if 'Ref' in text[prop]:\n                                    ref = text[prop]['Ref']\n                                    if ref in parameternames:\n                                        param_type = self.cfn.template['Parameters'][ref]['Type']\n                                        if param_type:\n                                            if not param_type.startswith('List<') and not param_type == 'CommaDelimitedList':\n                                                message = 'Property {0} should be of type List or Parameter should ' \\\n                                                          'be a list for resource {1}'\n                                                matches.append(\n                                                    RuleMatch(\n                                                        proppath,\n                                                        message.format(prop, resourcename)))\n                                    else:\n                                        message = 'Property {0} should be of type List for resource {1}'\n                                        matches.append(\n                                            RuleMatch(\n                                                proppath,\n                                                message.format(prop, resourcename)))\n                            else:\n                                message = 'Property {0} should be of type List for resource {1}'\n                                matches.append(\n                                    RuleMatch(\n                                        proppath,\n                                        message.format(prop, resourcename)))\n                    else:\n                        if resourcespec[prop]['Type'] not in ['Map']:\n                            matches.extend(self.propertycheck(\n                                text[prop], resourcespec[prop]['Type'], parenttype,\n                                resourcename, proppath, False))\n                elif 'PrimitiveType' in resourcespec[prop]:\n                    primtype = resourcespec[prop]['PrimitiveType']\n                    matches.extend(self.primitivetypecheck(text[prop], primtype, proppath))\n\n        return matches\n\n    def match(self, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = []\n        self.cfn = cfn\n\n        resourcespecs = cfnlint.helpers.RESOURCE_SPECS[cfn.regions[0]]\n        self.resourcetypes = resourcespecs['ResourceTypes']\n        self.propertytypes = resourcespecs['PropertyTypes']\n        self.parameternames = self.cfn.get_parameter_names()\n        for resourcename, resourcevalue in cfn.get_resources().items():\n            if 'Properties' in resourcevalue and 'Type' in resourcevalue:\n                resourcetype = resourcevalue.get('Type', None)\n                if resourcetype.startswith('Custom::'):\n                    resourcetype = 'AWS::CloudFormation::CustomResource'\n                if resourcetype in self.resourcetypes:\n                    path = ['Resources', resourcename, 'Properties']\n                    matches.extend(self.propertycheck(\n                        resourcevalue.get('Properties', {}), '',\n                        resourcetype, resourcename, path, True))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/Properties.py"}], "after_files": [{"content": "\"\"\"\n  Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport six\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\nimport cfnlint.helpers\n\n\nclass Properties(CloudFormationLintRule):\n    \"\"\"Check Base Resource Configuration\"\"\"\n    id = 'E3002'\n    shortdesc = 'Resource properties are valid'\n    description = 'Making sure that resources properties ' + \\\n                  'are properly configured'\n    source_url = 'https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#properties'\n    tags = ['resources']\n\n    def __init__(self):\n        super(Properties, self).__init__()\n        self.cfn = {}\n        self.resourcetypes = {}\n        self.propertytypes = {}\n        self.parameternames = {}\n\n    def primitivetypecheck(self, value, primtype, proppath):\n        \"\"\"\n            Check primitive types.\n            Only check that a primitive type is actual a primitive type:\n            - If its JSON let it go\n            - If its Conditions check each sub path of the condition\n            - If its a object make sure its a valid function and function\n            - If its a list raise an error\n\n        \"\"\"\n\n        matches = []\n        if isinstance(value, dict) and primtype == 'Json':\n            return matches\n        if isinstance(value, dict):\n            if len(value) == 1:\n                for sub_key, sub_value in value.items():\n                    if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:\n                        # not erroring on bad Ifs but not need to account for it\n                        # so the rule doesn't error out\n                        if isinstance(sub_value, list):\n                            if len(sub_value) == 3:\n                                matches.extend(self.primitivetypecheck(\n                                    sub_value[1], primtype, proppath + ['Fn::If', 1]))\n                                matches.extend(self.primitivetypecheck(\n                                    sub_value[2], primtype, proppath + ['Fn::If', 2]))\n                    elif sub_key not in ['Fn::Base64', 'Fn::GetAtt', 'Fn::GetAZs', 'Fn::ImportValue',\n                                         'Fn::Join', 'Fn::Split', 'Fn::FindInMap', 'Fn::Select', 'Ref',\n                                         'Fn::If', 'Fn::Contains', 'Fn::Sub', 'Fn::Cidr']:\n                        message = 'Property %s has an illegal function %s' % ('/'.join(map(str, proppath)), sub_key)\n                        matches.append(RuleMatch(proppath, message))\n            else:\n                message = 'Property is an object instead of %s at %s' % (primtype, '/'.join(map(str, proppath)))\n                matches.append(RuleMatch(proppath, message))\n        elif isinstance(value, list):\n            message = 'Property should be of type %s not List at %s' % (primtype, '/'.join(map(str, proppath)))\n            matches.append(RuleMatch(proppath, message))\n\n        return matches\n\n    def check_list_for_condition(self, text, prop, parenttype, resourcename, propspec, path):\n        \"\"\"Checks lists that are a dict for conditions\"\"\"\n        matches = []\n        if len(text[prop]) == 1:\n            for sub_key, sub_value in text[prop].items():\n                if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:\n                    if len(sub_value) == 3:\n                        for if_i, if_v in enumerate(sub_value[1:]):\n                            condition_path = path[:] + [sub_key, if_i + 1]\n                            if isinstance(if_v, list):\n                                for index, item in enumerate(if_v):\n                                    arrproppath = condition_path[:]\n\n                                    arrproppath.append(index)\n                                    matches.extend(self.propertycheck(\n                                        item, propspec['ItemType'],\n                                        parenttype, resourcename, arrproppath, False))\n                            elif isinstance(if_v, dict):\n                                if len(if_v) == 1:\n                                    for d_k, d_v in if_v.items():\n                                        if d_k != 'Ref' or d_v != 'AWS::NoValue':\n                                            message = 'Property {0} should be of type List for resource {1} at {2}'\n                                            matches.append(\n                                                RuleMatch(\n                                                    condition_path,\n                                                    message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))\n                                else:\n                                    message = 'Property {0} should be of type List for resource {1} at {2}'\n                                    matches.append(\n                                        RuleMatch(\n                                            condition_path,\n                                            message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))\n                            else:\n                                message = 'Property {0} should be of type List for resource {1} at {2}'\n                                matches.append(\n                                    RuleMatch(\n                                        condition_path,\n                                        message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))\n\n                    else:\n                        message = 'Invalid !If condition specified at %s' % ('/'.join(map(str, path)))\n                        matches.append(RuleMatch(path, message))\n                else:\n                    message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))\n                    matches.append(RuleMatch(path, message))\n        else:\n            message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))\n            matches.append(RuleMatch(path, message))\n\n        return matches\n\n    def check_exceptions(self, parenttype, proptype, text):\n        \"\"\"\n            Checks for exceptions to the spec\n            - Start with handling exceptions for templated code.\n        \"\"\"\n        templated_exceptions = {\n            'AWS::ApiGateway::RestApi': ['BodyS3Location'],\n            'AWS::Lambda::Function': ['Code'],\n            'AWS::ElasticBeanstalk::ApplicationVersion': ['SourceBundle'],\n        }\n\n        exceptions = templated_exceptions.get(parenttype, [])\n        if proptype in exceptions:\n            if isinstance(text, six.string_types):\n                return True\n\n        return False\n\n    def propertycheck(self, text, proptype, parenttype, resourcename, path, root):\n        \"\"\"Check individual properties\"\"\"\n\n        parameternames = self.parameternames\n        matches = []\n        if root:\n            specs = self.resourcetypes\n            resourcetype = parenttype\n        else:\n            specs = self.propertytypes\n            resourcetype = str.format('{0}.{1}', parenttype, proptype)\n            # Handle tags\n            if resourcetype not in specs:\n                if proptype in specs:\n                    resourcetype = proptype\n                else:\n                    resourcetype = str.format('{0}.{1}', parenttype, proptype)\n            else:\n                resourcetype = str.format('{0}.{1}', parenttype, proptype)\n\n        resourcespec = specs[resourcetype].get('Properties', {})\n        supports_additional_properties = specs[resourcetype].get('AdditionalProperties', False)\n\n        if text == 'AWS::NoValue':\n            return matches\n        if not isinstance(text, dict):\n            if not self.check_exceptions(parenttype, proptype, text):\n                message = 'Expecting an object at %s' % ('/'.join(map(str, path)))\n                matches.append(RuleMatch(path, message))\n            return matches\n\n        for prop in text:\n            proppath = path[:]\n            proppath.append(prop)\n            if prop not in resourcespec:\n                if prop in cfnlint.helpers.CONDITION_FUNCTIONS:\n                    cond_values = self.cfn.get_condition_values(text[prop])\n                    for cond_value in cond_values:\n                        matches.extend(self.propertycheck(\n                            cond_value['Value'], proptype, parenttype, resourcename,\n                            proppath + cond_value['Path'], root))\n                elif not supports_additional_properties:\n                    message = 'Invalid Property %s' % ('/'.join(map(str, proppath)))\n                    matches.append(RuleMatch(proppath, message))\n            else:\n                if 'Type' in resourcespec[prop]:\n                    if resourcespec[prop]['Type'] == 'List':\n                        if 'PrimitiveItemType' not in resourcespec[prop]:\n                            if isinstance(text[prop], list):\n                                for index, item in enumerate(text[prop]):\n                                    arrproppath = proppath[:]\n                                    arrproppath.append(index)\n                                    matches.extend(self.propertycheck(\n                                        item, resourcespec[prop]['ItemType'],\n                                        parenttype, resourcename, arrproppath, False))\n                            elif (isinstance(text[prop], dict)):\n                                # A list can be be specific as a Conditional\n                                matches.extend(\n                                    self.check_list_for_condition(\n                                        text, prop, parenttype, resourcename, resourcespec[prop], proppath)\n                                )\n                            else:\n                                message = 'Property {0} should be of type List for resource {1}'\n                                matches.append(\n                                    RuleMatch(\n                                        proppath,\n                                        message.format(prop, resourcename)))\n                        else:\n                            if isinstance(text[prop], list):\n                                primtype = resourcespec[prop]['PrimitiveItemType']\n                                for index, item in enumerate(text[prop]):\n                                    arrproppath = proppath[:]\n                                    arrproppath.append(index)\n                                    matches.extend(self.primitivetypecheck(item, primtype, arrproppath))\n                            elif isinstance(text[prop], dict):\n                                if 'Ref' in text[prop]:\n                                    ref = text[prop]['Ref']\n                                    if ref in parameternames:\n                                        param_type = self.cfn.template['Parameters'][ref]['Type']\n                                        if param_type:\n                                            if 'List<' not in param_type and '<List' not in param_type and not param_type == 'CommaDelimitedList':\n                                                message = 'Property {0} should be of type List or Parameter should ' \\\n                                                          'be a list for resource {1}'\n                                                matches.append(\n                                                    RuleMatch(\n                                                        proppath,\n                                                        message.format(prop, resourcename)))\n                                    else:\n                                        message = 'Property {0} should be of type List for resource {1}'\n                                        matches.append(\n                                            RuleMatch(\n                                                proppath,\n                                                message.format(prop, resourcename)))\n                            else:\n                                message = 'Property {0} should be of type List for resource {1}'\n                                matches.append(\n                                    RuleMatch(\n                                        proppath,\n                                        message.format(prop, resourcename)))\n                    else:\n                        if resourcespec[prop]['Type'] not in ['Map']:\n                            matches.extend(self.propertycheck(\n                                text[prop], resourcespec[prop]['Type'], parenttype,\n                                resourcename, proppath, False))\n                elif 'PrimitiveType' in resourcespec[prop]:\n                    primtype = resourcespec[prop]['PrimitiveType']\n                    matches.extend(self.primitivetypecheck(text[prop], primtype, proppath))\n\n        return matches\n\n    def match(self, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = []\n        self.cfn = cfn\n\n        resourcespecs = cfnlint.helpers.RESOURCE_SPECS[cfn.regions[0]]\n        self.resourcetypes = resourcespecs['ResourceTypes']\n        self.propertytypes = resourcespecs['PropertyTypes']\n        self.parameternames = self.cfn.get_parameter_names()\n        for resourcename, resourcevalue in cfn.get_resources().items():\n            if 'Properties' in resourcevalue and 'Type' in resourcevalue:\n                resourcetype = resourcevalue.get('Type', None)\n                if resourcetype.startswith('Custom::'):\n                    resourcetype = 'AWS::CloudFormation::CustomResource'\n                if resourcetype in self.resourcetypes:\n                    path = ['Resources', resourcename, 'Properties']\n                    matches.extend(self.propertycheck(\n                        resourcevalue.get('Properties', {}), '',\n                        resourcetype, resourcename, path, True))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/Properties.py"}]}