We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AWS::ApiGateway::Authorizer - E3008 Property "RestApiId" has no valid Fn::GetAtt options
Given the following template:

```yaml
AWSTemplateFormatVersion: '2010-09-09'

Resources:
  ProvisionedProduct:
    Type: AWS::ServiceCatalog::CloudFormationProvisionedProduct
    Properties:
      ProductName: example
      ProvisioningArtifactName: v1

  Authoriser:
    Type: AWS::ApiGateway::Authorizer
    Properties:
      RestApiId: !GetAtt ProvisionedProduct.Outputs.RestApiId
      Type: TOKEN
```

The following error is output when using cfn-lint 0.42.0:

```
E3008 Property "RestApiId" has no valid Fn::GetAtt options at Resources/Authoriser/Properties/RestApiId/Fn::GetAtt
template.yml:13:7
```

--- 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/ValueRefGetAtt.py`
Content:
```
1 """
2 Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 SPDX-License-Identifier: MIT-0
4 """
5 import six
6 from cfnlint.rules import CloudFormationLintRule
7 from cfnlint.rules import RuleMatch
8 from cfnlint.helpers import RESOURCE_SPECS
9 import cfnlint.helpers
10 
11 
12 class ValueRefGetAtt(CloudFormationLintRule):
13     """Check if Resource Properties are correct"""
14     id = 'E3008'
15     shortdesc = 'Check values of properties for valid Refs and GetAtts'
16     description = 'Checks resource properties for Ref and GetAtt values'
17     tags = ['resources', 'ref', 'getatt']
18 
19     def initialize(self, cfn):
20         """Initialize the rule"""
21         for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):
22             self.resource_property_types.append(resource_type_spec)
23         for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):
24             self.resource_sub_property_types.append(property_type_spec)
25 
26     def is_value_a_list(self, path, property_name):
27         """
28             Determines if the value checked is a list or a value in a list
29             We need to handle conditions in the path that could be nested, etc.
30             ['Resources', 'LoadBalancer', 'Properties', 'Subnets', 'Fn::If', 2, 'Fn::If', 2]
31             Numbers preceeded by a Fn::If should be removed and check repeated.
32         """
33         if path[-1] != property_name:
34             # Property doesn't match the property name
35             # Check if its a number and a condition
36             if isinstance(path[-1], int) and path[-2] == 'Fn::If':
37                 return self.is_value_a_list(path[:-2], property_name)
38 
39             return False
40 
41         return True
42 
43     def check_value_ref(self, value, path, **kwargs):
44         """Check Ref"""
45         matches = list()
46         cfn = kwargs.get('cfn')
47         value_specs = kwargs.get('value_specs', {}).get('Ref')
48         list_value_specs = kwargs.get('list_value_specs', {}).get('Ref')
49         property_type = kwargs.get('property_type')
50         property_name = kwargs.get('property_name')
51         if path[-1] == 'Ref' and property_type == 'List' and self.is_value_a_list(path[:-1], property_name):
52             specs = list_value_specs
53         else:
54             specs = value_specs
55 
56         if not specs:
57             # If no Ref's are specified, just skip
58             # Opposite of GetAtt you will always have a Ref to a Parameter so if this is
59             # None it just hasn't been defined and we can skip
60             return matches
61 
62         if value in cfn.template.get('Parameters', {}):
63             param = cfn.template.get('Parameters').get(value, {})
64             parameter_type = param.get('Type')
65             valid_parameter_types = []
66             for parameter in specs.get('Parameters'):
67                 for param_type in RESOURCE_SPECS.get(cfn.regions[0]).get('ParameterTypes').get(parameter):
68                     valid_parameter_types.append(param_type)
69 
70             if not specs.get('Parameters'):
71                 message = 'Property "{0}" has no valid Refs to Parameters at {1}'
72                 matches.append(RuleMatch(path, message.format(
73                     property_name, '/'.join(map(str, path)))))
74             elif parameter_type not in valid_parameter_types:
75                 message = 'Property "{0}" can Ref to parameter of types [{1}] at {2}'
76                 matches.append(
77                     RuleMatch(
78                         path,
79                         message.format(
80                             property_name,
81                             ', '.join(map(str, valid_parameter_types)),
82                             '/'.join(map(str, path)))))
83         if value in cfn.template.get('Resources', {}):
84             resource = cfn.template.get('Resources').get(value, {})
85             resource_type = resource.get('Type')
86             if not specs.get('Resources'):
87                 message = 'Property "{0}" has no valid Refs to Resources at {1}'
88                 matches.append(RuleMatch(path, message.format(
89                     property_name, '/'.join(map(str, path)))))
90             elif resource_type not in specs.get('Resources'):
91                 message = 'Property "{0}" can Ref to resources of types [{1}] at {2}'
92                 matches.append(
93                     RuleMatch(
94                         path,
95                         message.format(
96                             property_name,
97                             ', '.join(map(str, specs.get('Resources'))),
98                             '/'.join(map(str, path)))))
99 
100         return matches
101 
102     def check_value_getatt(self, value, path, **kwargs):
103         """Check GetAtt"""
104         matches = []
105         cfn = kwargs.get('cfn')
106         value_specs = kwargs.get('value_specs', {}).get('GetAtt')
107         list_value_specs = kwargs.get('list_value_specs', {}).get('GetAtt')
108         property_type = kwargs.get('property_type')
109         property_name = kwargs.get('property_name')
110         # You can sometimes get a list or a string with . in it
111         if isinstance(value, list):
112             resource_name = value[0]
113             if len(value[1:]) == 1:
114                 resource_attribute = value[1].split('.')
115             else:
116                 resource_attribute = value[1:]
117         elif isinstance(value, six.string_types):
118             resource_name = value.split('.')[0]
119             resource_attribute = value.split('.')[1:]
120         is_value_a_list = self.is_value_a_list(path[:-1], property_name)
121         if path[-1] == 'Fn::GetAtt' and property_type == 'List' and is_value_a_list:
122             specs = list_value_specs
123         else:
124             specs = value_specs
125 
126         resource_type = cfn.template.get('Resources', {}).get(resource_name, {}).get('Type')
127 
128         if cfnlint.helpers.is_custom_resource(resource_type):
129             #  A custom resource voids the spec.  Move on
130             return matches
131 
132         if resource_type == 'AWS::CloudFormation::Stack' and resource_attribute[0] == 'Outputs':
133             # Nested Stack Outputs
134             # if its a string type we are good and return matches
135             # if its a list its a failure as Outputs can only be strings
136             if is_value_a_list and property_type == 'List':
137                 message = 'CloudFormation stack outputs need to be strings not lists at {0}'
138                 matches.append(RuleMatch(path, message.format('/'.join(map(str, path)))))
139 
140             return matches
141 
142         if specs is None:
143             # GetAtt specs aren't specified skip
144             return matches
145         if not specs:
146             # GetAtt is specified but empty so there are no valid options
147             message = 'Property "{0}" has no valid Fn::GetAtt options at {1}'
148             matches.append(RuleMatch(path, message.format(property_name, '/'.join(map(str, path)))))
149             return matches
150 
151         if resource_type not in specs:
152             message = 'Property "{0}" can Fn::GetAtt to a resource of types [{1}] at {2}'
153             matches.append(
154                 RuleMatch(
155                     path,
156                     message.format(
157                         property_name,
158                         ', '.join(map(str, specs)),
159                         '/'.join(map(str, path)))))
160         elif '.'.join(map(str, resource_attribute)) != specs[resource_type]:
161             message = 'Property "{0}" can Fn::GetAtt to a resource attribute "{1}" at {2}'
162             matches.append(
163                 RuleMatch(
164                     path,
165                     message.format(
166                         property_name,
167                         specs[resource_type],
168                         '/'.join(map(str, path)))))
169 
170         return matches
171 
172     def check(self, cfn, properties, value_specs, property_specs, path):
173         """Check itself"""
174         matches = list()
175         for p_value, p_path in properties.items_safe(path[:]):
176             for prop in p_value:
177                 if prop in value_specs:
178                     value = value_specs.get(prop).get('Value', {})
179                     if value:
180                         value_type = value.get('ValueType', '')
181                         list_value_type = value.get('ListValueType', '')
182                         property_type = property_specs.get('Properties').get(prop).get('Type')
183                         matches.extend(
184                             cfn.check_value(
185                                 p_value, prop, p_path,
186                                 check_ref=self.check_value_ref,
187                                 check_get_att=self.check_value_getatt,
188                                 value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(
189                                     'ValueTypes').get(value_type, {}),
190                                 list_value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(
191                                     'ValueTypes').get(list_value_type, {}),
192                                 cfn=cfn, property_type=property_type, property_name=prop
193                             )
194                         )
195 
196         return matches
197 
198     def match_resource_sub_properties(self, properties, property_type, path, cfn):
199         """Match for sub properties"""
200         matches = list()
201 
202         specs = RESOURCE_SPECS.get(cfn.regions[0]).get(
203             'PropertyTypes').get(property_type, {}).get('Properties', {})
204         property_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes').get(property_type)
205         matches.extend(self.check(cfn, properties, specs, property_specs, path))
206 
207         return matches
208 
209     def match_resource_properties(self, properties, resource_type, path, cfn):
210         """Check CloudFormation Properties"""
211         matches = list()
212 
213         specs = RESOURCE_SPECS.get(cfn.regions[0]).get(
214             'ResourceTypes').get(resource_type, {}).get('Properties', {})
215         resource_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes').get(resource_type)
216         matches.extend(self.check(cfn, properties, specs, resource_specs, path))
217 
218         return matches
219
```
--- 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/ValueRefGetAtt.py b/src/cfnlint/rules/resources/properties/ValueRefGetAtt.py
--- a/src/cfnlint/rules/resources/properties/ValueRefGetAtt.py
+++ b/src/cfnlint/rules/resources/properties/ValueRefGetAtt.py
@@ -129,7 +129,7 @@
             #  A custom resource voids the spec.  Move on
             return matches
 
-        if resource_type == 'AWS::CloudFormation::Stack' and resource_attribute[0] == 'Outputs':
+        if resource_type in ['AWS::CloudFormation::Stack', 'AWS::ServiceCatalog::CloudFormationProvisionedProduct'] and resource_attribute[0] == 'Outputs':
             # Nested Stack Outputs
             # if its a string type we are good and return matches
             # if its a list its a failure as Outputs can only be strings

{"golden_diff": "diff --git a/src/cfnlint/rules/resources/properties/ValueRefGetAtt.py b/src/cfnlint/rules/resources/properties/ValueRefGetAtt.py\n--- a/src/cfnlint/rules/resources/properties/ValueRefGetAtt.py\n+++ b/src/cfnlint/rules/resources/properties/ValueRefGetAtt.py\n@@ -129,7 +129,7 @@\n             #  A custom resource voids the spec.  Move on\n             return matches\n \n-        if resource_type == 'AWS::CloudFormation::Stack' and resource_attribute[0] == 'Outputs':\n+        if resource_type in ['AWS::CloudFormation::Stack', 'AWS::ServiceCatalog::CloudFormationProvisionedProduct'] and resource_attribute[0] == 'Outputs':\n             # Nested Stack Outputs\n             # if its a string type we are good and return matches\n             # if its a list its a failure as Outputs can only be strings\n", "issue": "AWS::ApiGateway::Authorizer - E3008 Property \"RestApiId\" has no valid Fn::GetAtt options\nGiven the following template:\r\n\r\n```yaml\r\nAWSTemplateFormatVersion: '2010-09-09'\r\n\r\nResources:\r\n  ProvisionedProduct:\r\n    Type: AWS::ServiceCatalog::CloudFormationProvisionedProduct\r\n    Properties:\r\n      ProductName: example\r\n      ProvisioningArtifactName: v1\r\n\r\n  Authoriser:\r\n    Type: AWS::ApiGateway::Authorizer\r\n    Properties:\r\n      RestApiId: !GetAtt ProvisionedProduct.Outputs.RestApiId\r\n      Type: TOKEN\r\n```\r\n\r\nThe following error is output when using cfn-lint 0.42.0:\r\n\r\n```\r\nE3008 Property \"RestApiId\" has no valid Fn::GetAtt options at Resources/Authoriser/Properties/RestApiId/Fn::GetAtt\r\ntemplate.yml:13:7\r\n```\n", "before_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport six\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\nfrom cfnlint.helpers import RESOURCE_SPECS\nimport cfnlint.helpers\n\n\nclass ValueRefGetAtt(CloudFormationLintRule):\n    \"\"\"Check if Resource Properties are correct\"\"\"\n    id = 'E3008'\n    shortdesc = 'Check values of properties for valid Refs and GetAtts'\n    description = 'Checks resource properties for Ref and GetAtt values'\n    tags = ['resources', 'ref', 'getatt']\n\n    def initialize(self, cfn):\n        \"\"\"Initialize the rule\"\"\"\n        for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):\n            self.resource_property_types.append(resource_type_spec)\n        for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):\n            self.resource_sub_property_types.append(property_type_spec)\n\n    def is_value_a_list(self, path, property_name):\n        \"\"\"\n            Determines if the value checked is a list or a value in a list\n            We need to handle conditions in the path that could be nested, etc.\n            ['Resources', 'LoadBalancer', 'Properties', 'Subnets', 'Fn::If', 2, 'Fn::If', 2]\n            Numbers preceeded by a Fn::If should be removed and check repeated.\n        \"\"\"\n        if path[-1] != property_name:\n            # Property doesn't match the property name\n            # Check if its a number and a condition\n            if isinstance(path[-1], int) and path[-2] == 'Fn::If':\n                return self.is_value_a_list(path[:-2], property_name)\n\n            return False\n\n        return True\n\n    def check_value_ref(self, value, path, **kwargs):\n        \"\"\"Check Ref\"\"\"\n        matches = list()\n        cfn = kwargs.get('cfn')\n        value_specs = kwargs.get('value_specs', {}).get('Ref')\n        list_value_specs = kwargs.get('list_value_specs', {}).get('Ref')\n        property_type = kwargs.get('property_type')\n        property_name = kwargs.get('property_name')\n        if path[-1] == 'Ref' and property_type == 'List' and self.is_value_a_list(path[:-1], property_name):\n            specs = list_value_specs\n        else:\n            specs = value_specs\n\n        if not specs:\n            # If no Ref's are specified, just skip\n            # Opposite of GetAtt you will always have a Ref to a Parameter so if this is\n            # None it just hasn't been defined and we can skip\n            return matches\n\n        if value in cfn.template.get('Parameters', {}):\n            param = cfn.template.get('Parameters').get(value, {})\n            parameter_type = param.get('Type')\n            valid_parameter_types = []\n            for parameter in specs.get('Parameters'):\n                for param_type in RESOURCE_SPECS.get(cfn.regions[0]).get('ParameterTypes').get(parameter):\n                    valid_parameter_types.append(param_type)\n\n            if not specs.get('Parameters'):\n                message = 'Property \"{0}\" has no valid Refs to Parameters at {1}'\n                matches.append(RuleMatch(path, message.format(\n                    property_name, '/'.join(map(str, path)))))\n            elif parameter_type not in valid_parameter_types:\n                message = 'Property \"{0}\" can Ref to parameter of types [{1}] at {2}'\n                matches.append(\n                    RuleMatch(\n                        path,\n                        message.format(\n                            property_name,\n                            ', '.join(map(str, valid_parameter_types)),\n                            '/'.join(map(str, path)))))\n        if value in cfn.template.get('Resources', {}):\n            resource = cfn.template.get('Resources').get(value, {})\n            resource_type = resource.get('Type')\n            if not specs.get('Resources'):\n                message = 'Property \"{0}\" has no valid Refs to Resources at {1}'\n                matches.append(RuleMatch(path, message.format(\n                    property_name, '/'.join(map(str, path)))))\n            elif resource_type not in specs.get('Resources'):\n                message = 'Property \"{0}\" can Ref to resources of types [{1}] at {2}'\n                matches.append(\n                    RuleMatch(\n                        path,\n                        message.format(\n                            property_name,\n                            ', '.join(map(str, specs.get('Resources'))),\n                            '/'.join(map(str, path)))))\n\n        return matches\n\n    def check_value_getatt(self, value, path, **kwargs):\n        \"\"\"Check GetAtt\"\"\"\n        matches = []\n        cfn = kwargs.get('cfn')\n        value_specs = kwargs.get('value_specs', {}).get('GetAtt')\n        list_value_specs = kwargs.get('list_value_specs', {}).get('GetAtt')\n        property_type = kwargs.get('property_type')\n        property_name = kwargs.get('property_name')\n        # You can sometimes get a list or a string with . in it\n        if isinstance(value, list):\n            resource_name = value[0]\n            if len(value[1:]) == 1:\n                resource_attribute = value[1].split('.')\n            else:\n                resource_attribute = value[1:]\n        elif isinstance(value, six.string_types):\n            resource_name = value.split('.')[0]\n            resource_attribute = value.split('.')[1:]\n        is_value_a_list = self.is_value_a_list(path[:-1], property_name)\n        if path[-1] == 'Fn::GetAtt' and property_type == 'List' and is_value_a_list:\n            specs = list_value_specs\n        else:\n            specs = value_specs\n\n        resource_type = cfn.template.get('Resources', {}).get(resource_name, {}).get('Type')\n\n        if cfnlint.helpers.is_custom_resource(resource_type):\n            #  A custom resource voids the spec.  Move on\n            return matches\n\n        if resource_type == 'AWS::CloudFormation::Stack' and resource_attribute[0] == 'Outputs':\n            # Nested Stack Outputs\n            # if its a string type we are good and return matches\n            # if its a list its a failure as Outputs can only be strings\n            if is_value_a_list and property_type == 'List':\n                message = 'CloudFormation stack outputs need to be strings not lists at {0}'\n                matches.append(RuleMatch(path, message.format('/'.join(map(str, path)))))\n\n            return matches\n\n        if specs is None:\n            # GetAtt specs aren't specified skip\n            return matches\n        if not specs:\n            # GetAtt is specified but empty so there are no valid options\n            message = 'Property \"{0}\" has no valid Fn::GetAtt options at {1}'\n            matches.append(RuleMatch(path, message.format(property_name, '/'.join(map(str, path)))))\n            return matches\n\n        if resource_type not in specs:\n            message = 'Property \"{0}\" can Fn::GetAtt to a resource of types [{1}] at {2}'\n            matches.append(\n                RuleMatch(\n                    path,\n                    message.format(\n                        property_name,\n                        ', '.join(map(str, specs)),\n                        '/'.join(map(str, path)))))\n        elif '.'.join(map(str, resource_attribute)) != specs[resource_type]:\n            message = 'Property \"{0}\" can Fn::GetAtt to a resource attribute \"{1}\" at {2}'\n            matches.append(\n                RuleMatch(\n                    path,\n                    message.format(\n                        property_name,\n                        specs[resource_type],\n                        '/'.join(map(str, path)))))\n\n        return matches\n\n    def check(self, cfn, properties, value_specs, property_specs, path):\n        \"\"\"Check itself\"\"\"\n        matches = list()\n        for p_value, p_path in properties.items_safe(path[:]):\n            for prop in p_value:\n                if prop in value_specs:\n                    value = value_specs.get(prop).get('Value', {})\n                    if value:\n                        value_type = value.get('ValueType', '')\n                        list_value_type = value.get('ListValueType', '')\n                        property_type = property_specs.get('Properties').get(prop).get('Type')\n                        matches.extend(\n                            cfn.check_value(\n                                p_value, prop, p_path,\n                                check_ref=self.check_value_ref,\n                                check_get_att=self.check_value_getatt,\n                                value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                    'ValueTypes').get(value_type, {}),\n                                list_value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                    'ValueTypes').get(list_value_type, {}),\n                                cfn=cfn, property_type=property_type, property_name=prop\n                            )\n                        )\n\n        return matches\n\n    def match_resource_sub_properties(self, properties, property_type, path, cfn):\n        \"\"\"Match for sub properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'PropertyTypes').get(property_type, {}).get('Properties', {})\n        property_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes').get(property_type)\n        matches.extend(self.check(cfn, properties, specs, property_specs, path))\n\n        return matches\n\n    def match_resource_properties(self, properties, resource_type, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'ResourceTypes').get(resource_type, {}).get('Properties', {})\n        resource_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes').get(resource_type)\n        matches.extend(self.check(cfn, properties, specs, resource_specs, path))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/ValueRefGetAtt.py"}], "after_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport six\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\nfrom cfnlint.helpers import RESOURCE_SPECS\nimport cfnlint.helpers\n\n\nclass ValueRefGetAtt(CloudFormationLintRule):\n    \"\"\"Check if Resource Properties are correct\"\"\"\n    id = 'E3008'\n    shortdesc = 'Check values of properties for valid Refs and GetAtts'\n    description = 'Checks resource properties for Ref and GetAtt values'\n    tags = ['resources', 'ref', 'getatt']\n\n    def initialize(self, cfn):\n        \"\"\"Initialize the rule\"\"\"\n        for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):\n            self.resource_property_types.append(resource_type_spec)\n        for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):\n            self.resource_sub_property_types.append(property_type_spec)\n\n    def is_value_a_list(self, path, property_name):\n        \"\"\"\n            Determines if the value checked is a list or a value in a list\n            We need to handle conditions in the path that could be nested, etc.\n            ['Resources', 'LoadBalancer', 'Properties', 'Subnets', 'Fn::If', 2, 'Fn::If', 2]\n            Numbers preceeded by a Fn::If should be removed and check repeated.\n        \"\"\"\n        if path[-1] != property_name:\n            # Property doesn't match the property name\n            # Check if its a number and a condition\n            if isinstance(path[-1], int) and path[-2] == 'Fn::If':\n                return self.is_value_a_list(path[:-2], property_name)\n\n            return False\n\n        return True\n\n    def check_value_ref(self, value, path, **kwargs):\n        \"\"\"Check Ref\"\"\"\n        matches = list()\n        cfn = kwargs.get('cfn')\n        value_specs = kwargs.get('value_specs', {}).get('Ref')\n        list_value_specs = kwargs.get('list_value_specs', {}).get('Ref')\n        property_type = kwargs.get('property_type')\n        property_name = kwargs.get('property_name')\n        if path[-1] == 'Ref' and property_type == 'List' and self.is_value_a_list(path[:-1], property_name):\n            specs = list_value_specs\n        else:\n            specs = value_specs\n\n        if not specs:\n            # If no Ref's are specified, just skip\n            # Opposite of GetAtt you will always have a Ref to a Parameter so if this is\n            # None it just hasn't been defined and we can skip\n            return matches\n\n        if value in cfn.template.get('Parameters', {}):\n            param = cfn.template.get('Parameters').get(value, {})\n            parameter_type = param.get('Type')\n            valid_parameter_types = []\n            for parameter in specs.get('Parameters'):\n                for param_type in RESOURCE_SPECS.get(cfn.regions[0]).get('ParameterTypes').get(parameter):\n                    valid_parameter_types.append(param_type)\n\n            if not specs.get('Parameters'):\n                message = 'Property \"{0}\" has no valid Refs to Parameters at {1}'\n                matches.append(RuleMatch(path, message.format(\n                    property_name, '/'.join(map(str, path)))))\n            elif parameter_type not in valid_parameter_types:\n                message = 'Property \"{0}\" can Ref to parameter of types [{1}] at {2}'\n                matches.append(\n                    RuleMatch(\n                        path,\n                        message.format(\n                            property_name,\n                            ', '.join(map(str, valid_parameter_types)),\n                            '/'.join(map(str, path)))))\n        if value in cfn.template.get('Resources', {}):\n            resource = cfn.template.get('Resources').get(value, {})\n            resource_type = resource.get('Type')\n            if not specs.get('Resources'):\n                message = 'Property \"{0}\" has no valid Refs to Resources at {1}'\n                matches.append(RuleMatch(path, message.format(\n                    property_name, '/'.join(map(str, path)))))\n            elif resource_type not in specs.get('Resources'):\n                message = 'Property \"{0}\" can Ref to resources of types [{1}] at {2}'\n                matches.append(\n                    RuleMatch(\n                        path,\n                        message.format(\n                            property_name,\n                            ', '.join(map(str, specs.get('Resources'))),\n                            '/'.join(map(str, path)))))\n\n        return matches\n\n    def check_value_getatt(self, value, path, **kwargs):\n        \"\"\"Check GetAtt\"\"\"\n        matches = []\n        cfn = kwargs.get('cfn')\n        value_specs = kwargs.get('value_specs', {}).get('GetAtt')\n        list_value_specs = kwargs.get('list_value_specs', {}).get('GetAtt')\n        property_type = kwargs.get('property_type')\n        property_name = kwargs.get('property_name')\n        # You can sometimes get a list or a string with . in it\n        if isinstance(value, list):\n            resource_name = value[0]\n            if len(value[1:]) == 1:\n                resource_attribute = value[1].split('.')\n            else:\n                resource_attribute = value[1:]\n        elif isinstance(value, six.string_types):\n            resource_name = value.split('.')[0]\n            resource_attribute = value.split('.')[1:]\n        is_value_a_list = self.is_value_a_list(path[:-1], property_name)\n        if path[-1] == 'Fn::GetAtt' and property_type == 'List' and is_value_a_list:\n            specs = list_value_specs\n        else:\n            specs = value_specs\n\n        resource_type = cfn.template.get('Resources', {}).get(resource_name, {}).get('Type')\n\n        if cfnlint.helpers.is_custom_resource(resource_type):\n            #  A custom resource voids the spec.  Move on\n            return matches\n\n        if resource_type in ['AWS::CloudFormation::Stack', 'AWS::ServiceCatalog::CloudFormationProvisionedProduct'] and resource_attribute[0] == 'Outputs':\n            # Nested Stack Outputs\n            # if its a string type we are good and return matches\n            # if its a list its a failure as Outputs can only be strings\n            if is_value_a_list and property_type == 'List':\n                message = 'CloudFormation stack outputs need to be strings not lists at {0}'\n                matches.append(RuleMatch(path, message.format('/'.join(map(str, path)))))\n\n            return matches\n\n        if specs is None:\n            # GetAtt specs aren't specified skip\n            return matches\n        if not specs:\n            # GetAtt is specified but empty so there are no valid options\n            message = 'Property \"{0}\" has no valid Fn::GetAtt options at {1}'\n            matches.append(RuleMatch(path, message.format(property_name, '/'.join(map(str, path)))))\n            return matches\n\n        if resource_type not in specs:\n            message = 'Property \"{0}\" can Fn::GetAtt to a resource of types [{1}] at {2}'\n            matches.append(\n                RuleMatch(\n                    path,\n                    message.format(\n                        property_name,\n                        ', '.join(map(str, specs)),\n                        '/'.join(map(str, path)))))\n        elif '.'.join(map(str, resource_attribute)) != specs[resource_type]:\n            message = 'Property \"{0}\" can Fn::GetAtt to a resource attribute \"{1}\" at {2}'\n            matches.append(\n                RuleMatch(\n                    path,\n                    message.format(\n                        property_name,\n                        specs[resource_type],\n                        '/'.join(map(str, path)))))\n\n        return matches\n\n    def check(self, cfn, properties, value_specs, property_specs, path):\n        \"\"\"Check itself\"\"\"\n        matches = list()\n        for p_value, p_path in properties.items_safe(path[:]):\n            for prop in p_value:\n                if prop in value_specs:\n                    value = value_specs.get(prop).get('Value', {})\n                    if value:\n                        value_type = value.get('ValueType', '')\n                        list_value_type = value.get('ListValueType', '')\n                        property_type = property_specs.get('Properties').get(prop).get('Type')\n                        matches.extend(\n                            cfn.check_value(\n                                p_value, prop, p_path,\n                                check_ref=self.check_value_ref,\n                                check_get_att=self.check_value_getatt,\n                                value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                    'ValueTypes').get(value_type, {}),\n                                list_value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                    'ValueTypes').get(list_value_type, {}),\n                                cfn=cfn, property_type=property_type, property_name=prop\n                            )\n                        )\n\n        return matches\n\n    def match_resource_sub_properties(self, properties, property_type, path, cfn):\n        \"\"\"Match for sub properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'PropertyTypes').get(property_type, {}).get('Properties', {})\n        property_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes').get(property_type)\n        matches.extend(self.check(cfn, properties, specs, property_specs, path))\n\n        return matches\n\n    def match_resource_properties(self, properties, resource_type, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'ResourceTypes').get(resource_type, {}).get('Properties', {})\n        resource_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes').get(resource_type)\n        matches.extend(self.check(cfn, properties, specs, resource_specs, path))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/ValueRefGetAtt.py"}]}