We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
"Property ... should be of type List or Parameter", when using a CommaDelimitedList
*cfn-lint version: (`cfn-lint --version`)*

`cfn-lint 0.3.5`

*Description of issue.*

Our CloudFormation template contains a `CommaDelimitedList` parameter:

    "Aliases" : {
        "Type": "CommaDelimitedList",
        "Default": "foo.com, bar.com"
    },

We reference this parameter under `Aliases` in our CloudFront distribution. The `Aliases` key [accepts a list of string values](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-aliases) - which our `CommaDelimitedList` [provides](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html) - and the CloudFormation console correctly processes the template:

    "Distribution" : {
        "Type" : "AWS::CloudFront::Distribution",
        "Properties" : {
            "DistributionConfig" : {
                "Aliases" : { "Ref" : "Aliases" },
                ...
            }
        }
    }

However `cfn-lint` flags this as an error:

`E3002 Property Aliases should be of type List or Parameter should be a list for resource Distribution`

If we wrap the reference in a list, we can get a pass from `cfn-lint`:

    "Distribution" : {
        "Type" : "AWS::CloudFront::Distribution",
        "Properties" : {
            "DistributionConfig" : {
                "Aliases" : [
                    { "Ref" : "Aliases" }
                ]
                ...
            }
        }
    }

However that property is now evaluating to a list of lists:

    [ [ "foo.com", "bar.com" ] ]

This is an incorrect parameter and causes the CloudFormation console to fail with `Value of property Aliases must be of type List of String.`

It seems like `cfn-lint` may be interpreting the `CommaDelimitedList` type incorrectly? Possibly as a string, rather than a list of strings? Let me know if I can provide any more detail. 

--- 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 from cfnlint import CloudFormationLintRule
18 from cfnlint import RuleMatch
19 import cfnlint.helpers
20 
21 
22 class Properties(CloudFormationLintRule):
23     """Check Base Resource Configuration"""
24     id = 'E3002'
25     shortdesc = 'Resource properties are valid'
26     description = 'Making sure that resources properties ' + \
27                   'are properly configured'
28     source_url = 'https://github.com/awslabs/cfn-python-lint'
29     tags = ['resources']
30 
31     def __init__(self):
32         self.cfn = {}
33         self.resourcetypes = {}
34         self.propertytypes = {}
35         self.parameternames = {}
36 
37     def primitivetypecheck(self, value, primtype, proppath):
38         """
39             Check primitive types.
40             Only check that a primitive type is actual a primitive type:
41             - If its JSON let it go
42             - If its Conditions check each sub path of the condition
43             - If its a object make sure its a valid function and function
44             - If its a list raise an error
45 
46         """
47 
48         matches = list()
49         if isinstance(value, dict) and primtype == 'Json':
50             return matches
51         if isinstance(value, dict):
52             if len(value) == 1:
53                 for sub_key, sub_value in value.items():
54                     if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:
55                         # not erroring on bad Ifs but not need to account for it
56                         # so the rule doesn't error out
57                         if isinstance(sub_value, list):
58                             if len(sub_value) == 3:
59                                 matches.extend(self.primitivetypecheck(
60                                     sub_value[1], primtype, proppath + ['Fn::If', 1]))
61                                 matches.extend(self.primitivetypecheck(
62                                     sub_value[2], primtype, proppath + ['Fn::If', 2]))
63                     elif sub_key not in ['Fn::Base64', 'Fn::GetAtt', 'Fn::GetAZs', 'Fn::ImportValue',
64                                          'Fn::Join', 'Fn::Split', 'Fn::FindInMap', 'Fn::Select', 'Ref',
65                                          'Fn::If', 'Fn::Contains', 'Fn::Sub', 'Fn::Cidr']:
66                         message = 'Property %s has an illegal function %s' % ('/'.join(map(str, proppath)), sub_key)
67                         matches.append(RuleMatch(proppath, message))
68             else:
69                 message = 'Property is an object instead of %s at %s' % (primtype, '/'.join(map(str, proppath)))
70                 matches.append(RuleMatch(proppath, message))
71         elif isinstance(value, list):
72             message = 'Property should be of type %s not List at %s' % (primtype, '/'.join(map(str, proppath)))
73             matches.append(RuleMatch(proppath, message))
74 
75         return matches
76 
77     def check_list_for_condition(self, text, prop, parenttype, resourcename, propspec, path):
78         """Checks lists that are a dict for conditions"""
79         matches = list()
80         if len(text[prop]) == 1:
81             for sub_key, sub_value in text[prop].items():
82                 if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:
83                     if len(sub_value) == 3:
84                         if isinstance(sub_value[1], list):
85                             for index, item in enumerate(sub_value[1]):
86                                 arrproppath = path[:]
87 
88                                 arrproppath.append(index)
89                                 matches.extend(self.propertycheck(
90                                     item, propspec['ItemType'],
91                                     parenttype, resourcename, arrproppath, False))
92                         else:
93                             message = 'Property {0} should be of type List for resource {1}'
94                             matches.append(
95                                 RuleMatch(
96                                     path,
97                                     message.format(prop, resourcename)))
98 
99                         if isinstance(sub_value[2], list):
100                             for index, item in enumerate(sub_value[2]):
101                                 arrproppath = path[:]
102 
103                                 arrproppath.append(index)
104                                 matches.extend(self.propertycheck(
105                                     item, propspec['ItemType'],
106                                     parenttype, resourcename, arrproppath, False))
107                         else:
108                             message = 'Property {0} should be of type List for resource {1} at {2}'
109                             matches.append(
110                                 RuleMatch(
111                                     path + [2],
112                                     message.format(prop, resourcename, ('/'.join(str(x) for x in path)))))
113 
114                     else:
115                         message = 'Invalid !If condition specified at %s' % ('/'.join(map(str, path)))
116                         matches.append(RuleMatch(path, message))
117                 else:
118                     message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))
119                     matches.append(RuleMatch(path, message))
120         else:
121             message = 'Property is an object instead of List at %s' % ('/'.join(map(str, path)))
122             matches.append(RuleMatch(path, message))
123 
124         return matches
125 
126     def propertycheck(self, text, proptype, parenttype, resourcename, path, root):
127         """Check individual properties"""
128 
129         parameternames = self.parameternames
130         matches = list()
131         if root:
132             specs = self.resourcetypes
133             resourcetype = parenttype
134         else:
135             specs = self.propertytypes
136             resourcetype = str.format('{0}.{1}', parenttype, proptype)
137             # Handle tags
138             if resourcetype not in specs:
139                 if proptype in specs:
140                     resourcetype = proptype
141                 else:
142                     resourcetype = str.format('{0}.{1}', parenttype, proptype)
143             else:
144                 resourcetype = str.format('{0}.{1}', parenttype, proptype)
145 
146         resourcespec = specs[resourcetype].get('Properties', {})
147         if text == 'AWS::NoValue':
148             return matches
149         if not isinstance(text, dict):
150             message = 'Expecting an object at %s' % ('/'.join(map(str, path)))
151             matches.append(RuleMatch(path, message))
152             return matches
153         for prop in text:
154             proppath = path[:]
155             proppath.append(prop)
156             if prop not in resourcespec:
157                 if prop in cfnlint.helpers.CONDITION_FUNCTIONS:
158                     cond_values = self.cfn.get_condition_values(text[prop])
159                     for cond_value in cond_values:
160                         matches.extend(self.propertycheck(
161                             cond_value['Value'], proptype, parenttype, resourcename,
162                             proppath + cond_value['Path'], root))
163                 elif prop != 'Metadata':
164                     message = 'Invalid Property %s' % ('/'.join(map(str, proppath)))
165                     matches.append(RuleMatch(proppath, message))
166             else:
167                 if 'Type' in resourcespec[prop]:
168                     if resourcespec[prop]['Type'] == 'List':
169                         if 'PrimitiveItemType' not in resourcespec[prop]:
170                             if isinstance(text[prop], list):
171                                 for index, item in enumerate(text[prop]):
172                                     arrproppath = proppath[:]
173                                     arrproppath.append(index)
174                                     matches.extend(self.propertycheck(
175                                         item, resourcespec[prop]['ItemType'],
176                                         parenttype, resourcename, arrproppath, False))
177                             elif (isinstance(text[prop], dict)):
178                                 # A list can be be specific as a Conditional
179                                 matches.extend(
180                                     self.check_list_for_condition(
181                                         text, prop, parenttype, resourcename, resourcespec[prop], proppath)
182                                 )
183                             else:
184                                 message = 'Property {0} should be of type List for resource {1}'
185                                 matches.append(
186                                     RuleMatch(
187                                         proppath,
188                                         message.format(prop, resourcename)))
189                         else:
190                             if isinstance(text[prop], list):
191                                 primtype = resourcespec[prop]['PrimitiveItemType']
192                                 for index, item in enumerate(text[prop]):
193                                     arrproppath = proppath[:]
194                                     arrproppath.append(index)
195                                     matches.extend(self.primitivetypecheck(item, primtype, arrproppath))
196                             elif isinstance(text[prop], dict):
197                                 if 'Ref' in text[prop]:
198                                     ref = text[prop]['Ref']
199                                     if ref in parameternames:
200                                         if 'Type' in self.cfn.template['Parameters'][ref]:
201                                             if not self.cfn.template['Parameters'][ref]['Type'].startswith('List<'):
202                                                 message = 'Property {0} should be of type List or Parameter should ' \
203                                                           'be a list for resource {1}'
204                                                 matches.append(
205                                                     RuleMatch(
206                                                         proppath,
207                                                         message.format(prop, resourcename)))
208                                     else:
209                                         message = 'Property {0} should be of type List for resource {1}'
210                                         matches.append(
211                                             RuleMatch(
212                                                 proppath,
213                                                 message.format(prop, resourcename)))
214                             else:
215                                 message = 'Property {0} should be of type List for resource {1}'
216                                 matches.append(
217                                     RuleMatch(
218                                         proppath,
219                                         message.format(prop, resourcename)))
220                     else:
221                         if resourcespec[prop]['Type'] not in ['Map']:
222                             matches.extend(self.propertycheck(
223                                 text[prop], resourcespec[prop]['Type'], parenttype,
224                                 resourcename, proppath, False))
225                 elif 'PrimitiveType' in resourcespec[prop]:
226                     primtype = resourcespec[prop]['PrimitiveType']
227                     matches.extend(self.primitivetypecheck(text[prop], primtype, proppath))
228 
229         return matches
230 
231     def match(self, cfn):
232         """Check CloudFormation Properties"""
233         matches = list()
234         self.cfn = cfn
235 
236         resourcespecs = cfnlint.helpers.RESOURCE_SPECS[cfn.regions[0]]
237         self.resourcetypes = resourcespecs['ResourceTypes']
238         self.propertytypes = resourcespecs['PropertyTypes']
239         self.parameternames = self.cfn.get_parameter_names()
240         for resourcename, resourcevalue in cfn.get_resources().items():
241             if 'Properties' in resourcevalue and 'Type' in resourcevalue:
242                 resourcetype = resourcevalue.get('Type', None)
243                 if resourcetype in self.resourcetypes:
244                     path = ['Resources', resourcename, 'Properties']
245                     matches.extend(self.propertycheck(
246                         resourcevalue.get('Properties', {}), '',
247                         resourcetype, resourcename, path, True))
248 
249         return matches
250
```
--- 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
@@ -197,8 +197,9 @@
                                 if 'Ref' in text[prop]:
                                     ref = text[prop]['Ref']
                                     if ref in parameternames:
-                                        if 'Type' in self.cfn.template['Parameters'][ref]:
-                                            if not self.cfn.template['Parameters'][ref]['Type'].startswith('List<'):
+                                        param_type = self.cfn.template['Parameters'][ref]['Type']
+                                        if param_type:
+                                            if not param_type.startswith('List<') 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@@ -197,8 +197,9 @@\n                                 if 'Ref' in text[prop]:\n                                     ref = text[prop]['Ref']\n                                     if ref in parameternames:\n-                                        if 'Type' in self.cfn.template['Parameters'][ref]:\n-                                            if not self.cfn.template['Parameters'][ref]['Type'].startswith('List<'):\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", "issue": "\"Property ... should be of type List or Parameter\", when using a CommaDelimitedList\n*cfn-lint version: (`cfn-lint --version`)*\r\n\r\n`cfn-lint 0.3.5`\r\n\r\n*Description of issue.*\r\n\r\nOur CloudFormation template contains a `CommaDelimitedList` parameter:\r\n\r\n    \"Aliases\" : {\r\n        \"Type\": \"CommaDelimitedList\",\r\n        \"Default\": \"foo.com, bar.com\"\r\n    },\r\n\r\nWe reference this parameter under `Aliases` in our CloudFront distribution. The `Aliases` key [accepts a list of string values](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-aliases) - which our `CommaDelimitedList` [provides](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html) - and the CloudFormation console correctly processes the template:\r\n\r\n    \"Distribution\" : {\r\n        \"Type\" : \"AWS::CloudFront::Distribution\",\r\n        \"Properties\" : {\r\n            \"DistributionConfig\" : {\r\n                \"Aliases\" : { \"Ref\" : \"Aliases\" },\r\n                ...\r\n            }\r\n        }\r\n    }\r\n\r\nHowever `cfn-lint` flags this as an error:\r\n\r\n`E3002 Property Aliases should be of type List or Parameter should be a list for resource Distribution`\r\n\r\nIf we wrap the reference in a list, we can get a pass from `cfn-lint`:\r\n\r\n    \"Distribution\" : {\r\n        \"Type\" : \"AWS::CloudFront::Distribution\",\r\n        \"Properties\" : {\r\n            \"DistributionConfig\" : {\r\n                \"Aliases\" : [\r\n                    { \"Ref\" : \"Aliases\" }\r\n                ]\r\n                ...\r\n            }\r\n        }\r\n    }\r\n\r\nHowever that property is now evaluating to a list of lists:\r\n\r\n    [ [ \"foo.com\", \"bar.com\" ] ]\r\n\r\nThis is an incorrect parameter and causes the CloudFormation console to fail with `Value of property Aliases must be of type List of String.`\r\n\r\nIt seems like `cfn-lint` may be interpreting the `CommaDelimitedList` type incorrectly? Possibly as a string, rather than a list of strings? Let me know if I can provide any more detail. \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\"\"\"\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'\n    tags = ['resources']\n\n    def __init__(self):\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 = list()\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 = list()\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                        if isinstance(sub_value[1], list):\n                            for index, item in enumerate(sub_value[1]):\n                                arrproppath = path[:]\n\n                                arrproppath.append(index)\n                                matches.extend(self.propertycheck(\n                                    item, propspec['ItemType'],\n                                    parenttype, resourcename, arrproppath, False))\n                        else:\n                            message = 'Property {0} should be of type List for resource {1}'\n                            matches.append(\n                                RuleMatch(\n                                    path,\n                                    message.format(prop, resourcename)))\n\n                        if isinstance(sub_value[2], list):\n                            for index, item in enumerate(sub_value[2]):\n                                arrproppath = path[:]\n\n                                arrproppath.append(index)\n                                matches.extend(self.propertycheck(\n                                    item, propspec['ItemType'],\n                                    parenttype, resourcename, arrproppath, False))\n                        else:\n                            message = 'Property {0} should be of type List for resource {1} at {2}'\n                            matches.append(\n                                RuleMatch(\n                                    path + [2],\n                                    message.format(prop, resourcename, ('/'.join(str(x) for x in 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 propertycheck(self, text, proptype, parenttype, resourcename, path, root):\n        \"\"\"Check individual properties\"\"\"\n\n        parameternames = self.parameternames\n        matches = list()\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        if text == 'AWS::NoValue':\n            return matches\n        if not isinstance(text, dict):\n            message = 'Expecting an object at %s' % ('/'.join(map(str, path)))\n            matches.append(RuleMatch(path, message))\n            return matches\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 prop != 'Metadata':\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                                        if 'Type' in self.cfn.template['Parameters'][ref]:\n                                            if not self.cfn.template['Parameters'][ref]['Type'].startswith('List<'):\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 = list()\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 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\"\"\"\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'\n    tags = ['resources']\n\n    def __init__(self):\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 = list()\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 = list()\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                        if isinstance(sub_value[1], list):\n                            for index, item in enumerate(sub_value[1]):\n                                arrproppath = path[:]\n\n                                arrproppath.append(index)\n                                matches.extend(self.propertycheck(\n                                    item, propspec['ItemType'],\n                                    parenttype, resourcename, arrproppath, False))\n                        else:\n                            message = 'Property {0} should be of type List for resource {1}'\n                            matches.append(\n                                RuleMatch(\n                                    path,\n                                    message.format(prop, resourcename)))\n\n                        if isinstance(sub_value[2], list):\n                            for index, item in enumerate(sub_value[2]):\n                                arrproppath = path[:]\n\n                                arrproppath.append(index)\n                                matches.extend(self.propertycheck(\n                                    item, propspec['ItemType'],\n                                    parenttype, resourcename, arrproppath, False))\n                        else:\n                            message = 'Property {0} should be of type List for resource {1} at {2}'\n                            matches.append(\n                                RuleMatch(\n                                    path + [2],\n                                    message.format(prop, resourcename, ('/'.join(str(x) for x in 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 propertycheck(self, text, proptype, parenttype, resourcename, path, root):\n        \"\"\"Check individual properties\"\"\"\n\n        parameternames = self.parameternames\n        matches = list()\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        if text == 'AWS::NoValue':\n            return matches\n        if not isinstance(text, dict):\n            message = 'Expecting an object at %s' % ('/'.join(map(str, path)))\n            matches.append(RuleMatch(path, message))\n            return matches\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 prop != 'Metadata':\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 = list()\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 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"}]}