We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Incorrect E1029 errors when literal YAML block style is used
*cfn-lint version: 0.51.0

Embedded parameters are being flagged for not being wrapped in a `!Sub`, but they are in fact wrapped in a `!Sub`.

Please provide as much information as possible:

Sample template (extraneous lines have been removed):
```yaml
Resources:
  SomeStackset:
    Type: AWS::CloudFormation::StackSet
    Properties:
      TemplateBody: |
        Resources:
          SomeRole:
            Type: AWS::IAM::Role
            Properties:
              Policies:
                -
                  PolicyName: SomeName
                  PolicyDocument:
                    Version: 2012-10-17
                    Statement:
                      -
                        Effect: Allow
                        Action:
                          - iam:GetSomething
                        Resource:
                          - !Sub arn:aws:iam::${AWS::AccountId}:role/SomeRole*
```

The error is an [E1029](https://github.com/aws-cloudformation/cfn-lint/blob/main/src/cfnlint/rules/functions/SubNeeded.py), and the line number referenced is the line number where the YAML literal appears (here, the line that contains `TemplateBody: |`)

This appears to be a new issue with [release 0.51.0](https://github.com/aws-cloudformation/cfn-lint/releases/tag/v0.51.0). The code in question above hasn't changed in my repo for a long time, and I don't get the error on previous releases.


--- 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/functions/SubNeeded.py`
Content:
```
1 """
2 Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 SPDX-License-Identifier: MIT-0
4 """
5 from functools import reduce  # pylint: disable=redefined-builtin
6 import re
7 import copy
8 import six
9 from cfnlint.rules import CloudFormationLintRule
10 from cfnlint.rules import RuleMatch
11 
12 
13 class SubNeeded(CloudFormationLintRule):
14     """Check if a substitution string exists without a substitution function"""
15     id = 'E1029'
16     shortdesc = 'Sub is required if a variable is used in a string'
17     description = 'If a substitution variable exists in a string but isn\'t wrapped with the Fn::Sub function the deployment will fail.'
18     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html'
19     tags = ['functions', 'sub']
20 
21     def __init__(self):
22         """Init"""
23         super(SubNeeded, self).__init__()
24         self.config_definition = {
25             'custom_excludes': {
26                 'default': '',
27                 'type': 'string'
28             }
29         }
30         self.configure()
31         self.subParameterRegex = re.compile(r'(\$\{[A-Za-z0-9_:\.]+\})')
32 
33     def _match_values(self, cfnelem, path):
34         """Recursively search for values matching the searchRegex"""
35         values = []
36         if isinstance(cfnelem, dict):
37             for key in cfnelem:
38                 pathprop = path[:]
39                 pathprop.append(key)
40                 values.extend(self._match_values(cfnelem[key], pathprop))
41         elif isinstance(cfnelem, list):
42             for index, item in enumerate(cfnelem):
43                 pathprop = path[:]
44                 pathprop.append(index)
45                 values.extend(self._match_values(item, pathprop))
46         else:
47             # Leaf node
48             if isinstance(cfnelem, six.string_types):  # and re.match(searchRegex, cfnelem):
49                 for variable in re.findall(self.subParameterRegex, cfnelem):
50                     values.append(path + [variable])
51 
52         return values
53 
54     def match_values(self, cfn):
55         """
56             Search for values in all parts of the templates that match the searchRegex
57         """
58         results = []
59         results.extend(self._match_values(cfn.template, []))
60         # Globals are removed during a transform.  They need to be checked manually
61         results.extend(self._match_values(cfn.template.get('Globals', {}), []))
62         return results
63 
64     def _api_exceptions(self, value):
65         """ Key value exceptions """
66         parameter_search = re.compile(r'^\$\{stageVariables\..*\}$')
67         return re.match(parameter_search, value)
68 
69     def _variable_custom_excluded(self, value):
70         """ User-defined exceptions for variables, anywhere in the file """
71         custom_excludes = self.config['custom_excludes']
72         if custom_excludes:
73             custom_search = re.compile(custom_excludes)
74             return re.match(custom_search, value)
75         return False
76 
77     def match(self, cfn):
78         matches = []
79 
80         refs = cfn.get_valid_refs()
81         getatts = cfn.get_valid_getatts()
82 
83         # Get a list of paths to every leaf node string containing at least one ${parameter}
84         parameter_string_paths = self.match_values(cfn)
85         # We want to search all of the paths to check if each one contains an 'Fn::Sub'
86         for parameter_string_path in parameter_string_paths:
87             # Get variable
88             var = parameter_string_path[-1]
89 
90             # Step Function State Machine has a Definition Substitution that allows usage of special variables outside of a !Sub
91             # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-statemachine-definitionsubstitutions.html
92 
93             if 'DefinitionString' in parameter_string_path:
94                 modified_parameter_string_path = copy.copy(parameter_string_path)
95                 index = parameter_string_path.index('DefinitionString')
96                 modified_parameter_string_path[index] = 'DefinitionSubstitutions'
97                 modified_parameter_string_path = modified_parameter_string_path[:index+1]
98                 modified_parameter_string_path.append(var[2:-1])
99                 if reduce(lambda c, k: c.get(k, {}), modified_parameter_string_path, cfn.template):
100                     continue
101 
102             # Exclude variables that match custom exclude filters, if configured
103             # (for third-party tools that pre-process templates before uploading them to AWS)
104             if self._variable_custom_excluded(var):
105                 continue
106 
107             # Exclude literals (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html)
108             if var.startswith('${!'):
109                 continue
110 
111             var_stripped = var[2:-1].strip()
112 
113             # If we didn't find an 'Fn::Sub' it means a string containing a ${parameter} may not be evaluated correctly
114             if not 'Fn::Sub' in parameter_string_path:
115                 if (var_stripped in refs or var_stripped in getatts) or 'DefinitionString' in parameter_string_path:
116                     # Remove the last item (the variable) to prevent multiple errors on 1 line errors
117                     path = parameter_string_path[:-1]
118                     message = 'Found an embedded parameter "{}" outside of an "Fn::Sub" at {}'.format(
119                         var, '/'.join(map(str, path)))
120                     matches.append(RuleMatch(path, message))
121 
122         return matches
123
```
--- 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/functions/SubNeeded.py b/src/cfnlint/rules/functions/SubNeeded.py
--- a/src/cfnlint/rules/functions/SubNeeded.py
+++ b/src/cfnlint/rules/functions/SubNeeded.py
@@ -18,6 +18,8 @@
     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html'
     tags = ['functions', 'sub']
 
+    exceptions = ['TemplateBody']
+
     def __init__(self):
         """Init"""
         super(SubNeeded, self).__init__()
@@ -111,7 +113,7 @@
             var_stripped = var[2:-1].strip()
 
             # If we didn't find an 'Fn::Sub' it means a string containing a ${parameter} may not be evaluated correctly
-            if not 'Fn::Sub' in parameter_string_path:
+            if not 'Fn::Sub' in parameter_string_path and parameter_string_path[-2] not in self.exceptions:
                 if (var_stripped in refs or var_stripped in getatts) or 'DefinitionString' in parameter_string_path:
                     # Remove the last item (the variable) to prevent multiple errors on 1 line errors
                     path = parameter_string_path[:-1]

{"golden_diff": "diff --git a/src/cfnlint/rules/functions/SubNeeded.py b/src/cfnlint/rules/functions/SubNeeded.py\n--- a/src/cfnlint/rules/functions/SubNeeded.py\n+++ b/src/cfnlint/rules/functions/SubNeeded.py\n@@ -18,6 +18,8 @@\n     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html'\n     tags = ['functions', 'sub']\n \n+    exceptions = ['TemplateBody']\n+\n     def __init__(self):\n         \"\"\"Init\"\"\"\n         super(SubNeeded, self).__init__()\n@@ -111,7 +113,7 @@\n             var_stripped = var[2:-1].strip()\n \n             # If we didn't find an 'Fn::Sub' it means a string containing a ${parameter} may not be evaluated correctly\n-            if not 'Fn::Sub' in parameter_string_path:\n+            if not 'Fn::Sub' in parameter_string_path and parameter_string_path[-2] not in self.exceptions:\n                 if (var_stripped in refs or var_stripped in getatts) or 'DefinitionString' in parameter_string_path:\n                     # Remove the last item (the variable) to prevent multiple errors on 1 line errors\n                     path = parameter_string_path[:-1]\n", "issue": "Incorrect E1029 errors when literal YAML block style is used\n*cfn-lint version: 0.51.0\r\n\r\nEmbedded parameters are being flagged for not being wrapped in a `!Sub`, but they are in fact wrapped in a `!Sub`.\r\n\r\nPlease provide as much information as possible:\r\n\r\nSample template (extraneous lines have been removed):\r\n```yaml\r\nResources:\r\n  SomeStackset:\r\n    Type: AWS::CloudFormation::StackSet\r\n    Properties:\r\n      TemplateBody: |\r\n        Resources:\r\n          SomeRole:\r\n            Type: AWS::IAM::Role\r\n            Properties:\r\n              Policies:\r\n                -\r\n                  PolicyName: SomeName\r\n                  PolicyDocument:\r\n                    Version: 2012-10-17\r\n                    Statement:\r\n                      -\r\n                        Effect: Allow\r\n                        Action:\r\n                          - iam:GetSomething\r\n                        Resource:\r\n                          - !Sub arn:aws:iam::${AWS::AccountId}:role/SomeRole*\r\n```\r\n\r\nThe error is an [E1029](https://github.com/aws-cloudformation/cfn-lint/blob/main/src/cfnlint/rules/functions/SubNeeded.py), and the line number referenced is the line number where the YAML literal appears (here, the line that contains `TemplateBody: |`)\r\n\r\nThis appears to be a new issue with [release 0.51.0](https://github.com/aws-cloudformation/cfn-lint/releases/tag/v0.51.0). The code in question above hasn't changed in my repo for a long time, and I don't get the error on previous releases.\r\n\n", "before_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nfrom functools import reduce  # pylint: disable=redefined-builtin\nimport re\nimport copy\nimport six\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\n\n\nclass SubNeeded(CloudFormationLintRule):\n    \"\"\"Check if a substitution string exists without a substitution function\"\"\"\n    id = 'E1029'\n    shortdesc = 'Sub is required if a variable is used in a string'\n    description = 'If a substitution variable exists in a string but isn\\'t wrapped with the Fn::Sub function the deployment will fail.'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html'\n    tags = ['functions', 'sub']\n\n    def __init__(self):\n        \"\"\"Init\"\"\"\n        super(SubNeeded, self).__init__()\n        self.config_definition = {\n            'custom_excludes': {\n                'default': '',\n                'type': 'string'\n            }\n        }\n        self.configure()\n        self.subParameterRegex = re.compile(r'(\\$\\{[A-Za-z0-9_:\\.]+\\})')\n\n    def _match_values(self, cfnelem, path):\n        \"\"\"Recursively search for values matching the searchRegex\"\"\"\n        values = []\n        if isinstance(cfnelem, dict):\n            for key in cfnelem:\n                pathprop = path[:]\n                pathprop.append(key)\n                values.extend(self._match_values(cfnelem[key], pathprop))\n        elif isinstance(cfnelem, list):\n            for index, item in enumerate(cfnelem):\n                pathprop = path[:]\n                pathprop.append(index)\n                values.extend(self._match_values(item, pathprop))\n        else:\n            # Leaf node\n            if isinstance(cfnelem, six.string_types):  # and re.match(searchRegex, cfnelem):\n                for variable in re.findall(self.subParameterRegex, cfnelem):\n                    values.append(path + [variable])\n\n        return values\n\n    def match_values(self, cfn):\n        \"\"\"\n            Search for values in all parts of the templates that match the searchRegex\n        \"\"\"\n        results = []\n        results.extend(self._match_values(cfn.template, []))\n        # Globals are removed during a transform.  They need to be checked manually\n        results.extend(self._match_values(cfn.template.get('Globals', {}), []))\n        return results\n\n    def _api_exceptions(self, value):\n        \"\"\" Key value exceptions \"\"\"\n        parameter_search = re.compile(r'^\\$\\{stageVariables\\..*\\}$')\n        return re.match(parameter_search, value)\n\n    def _variable_custom_excluded(self, value):\n        \"\"\" User-defined exceptions for variables, anywhere in the file \"\"\"\n        custom_excludes = self.config['custom_excludes']\n        if custom_excludes:\n            custom_search = re.compile(custom_excludes)\n            return re.match(custom_search, value)\n        return False\n\n    def match(self, cfn):\n        matches = []\n\n        refs = cfn.get_valid_refs()\n        getatts = cfn.get_valid_getatts()\n\n        # Get a list of paths to every leaf node string containing at least one ${parameter}\n        parameter_string_paths = self.match_values(cfn)\n        # We want to search all of the paths to check if each one contains an 'Fn::Sub'\n        for parameter_string_path in parameter_string_paths:\n            # Get variable\n            var = parameter_string_path[-1]\n\n            # Step Function State Machine has a Definition Substitution that allows usage of special variables outside of a !Sub\n            # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-statemachine-definitionsubstitutions.html\n\n            if 'DefinitionString' in parameter_string_path:\n                modified_parameter_string_path = copy.copy(parameter_string_path)\n                index = parameter_string_path.index('DefinitionString')\n                modified_parameter_string_path[index] = 'DefinitionSubstitutions'\n                modified_parameter_string_path = modified_parameter_string_path[:index+1]\n                modified_parameter_string_path.append(var[2:-1])\n                if reduce(lambda c, k: c.get(k, {}), modified_parameter_string_path, cfn.template):\n                    continue\n\n            # Exclude variables that match custom exclude filters, if configured\n            # (for third-party tools that pre-process templates before uploading them to AWS)\n            if self._variable_custom_excluded(var):\n                continue\n\n            # Exclude literals (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html)\n            if var.startswith('${!'):\n                continue\n\n            var_stripped = var[2:-1].strip()\n\n            # If we didn't find an 'Fn::Sub' it means a string containing a ${parameter} may not be evaluated correctly\n            if not 'Fn::Sub' in parameter_string_path:\n                if (var_stripped in refs or var_stripped in getatts) or 'DefinitionString' in parameter_string_path:\n                    # Remove the last item (the variable) to prevent multiple errors on 1 line errors\n                    path = parameter_string_path[:-1]\n                    message = 'Found an embedded parameter \"{}\" outside of an \"Fn::Sub\" at {}'.format(\n                        var, '/'.join(map(str, path)))\n                    matches.append(RuleMatch(path, message))\n\n        return matches\n", "path": "src/cfnlint/rules/functions/SubNeeded.py"}], "after_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nfrom functools import reduce  # pylint: disable=redefined-builtin\nimport re\nimport copy\nimport six\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\n\n\nclass SubNeeded(CloudFormationLintRule):\n    \"\"\"Check if a substitution string exists without a substitution function\"\"\"\n    id = 'E1029'\n    shortdesc = 'Sub is required if a variable is used in a string'\n    description = 'If a substitution variable exists in a string but isn\\'t wrapped with the Fn::Sub function the deployment will fail.'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html'\n    tags = ['functions', 'sub']\n\n    exceptions = ['TemplateBody']\n\n    def __init__(self):\n        \"\"\"Init\"\"\"\n        super(SubNeeded, self).__init__()\n        self.config_definition = {\n            'custom_excludes': {\n                'default': '',\n                'type': 'string'\n            }\n        }\n        self.configure()\n        self.subParameterRegex = re.compile(r'(\\$\\{[A-Za-z0-9_:\\.]+\\})')\n\n    def _match_values(self, cfnelem, path):\n        \"\"\"Recursively search for values matching the searchRegex\"\"\"\n        values = []\n        if isinstance(cfnelem, dict):\n            for key in cfnelem:\n                pathprop = path[:]\n                pathprop.append(key)\n                values.extend(self._match_values(cfnelem[key], pathprop))\n        elif isinstance(cfnelem, list):\n            for index, item in enumerate(cfnelem):\n                pathprop = path[:]\n                pathprop.append(index)\n                values.extend(self._match_values(item, pathprop))\n        else:\n            # Leaf node\n            if isinstance(cfnelem, six.string_types):  # and re.match(searchRegex, cfnelem):\n                for variable in re.findall(self.subParameterRegex, cfnelem):\n                    values.append(path + [variable])\n\n        return values\n\n    def match_values(self, cfn):\n        \"\"\"\n            Search for values in all parts of the templates that match the searchRegex\n        \"\"\"\n        results = []\n        results.extend(self._match_values(cfn.template, []))\n        # Globals are removed during a transform.  They need to be checked manually\n        results.extend(self._match_values(cfn.template.get('Globals', {}), []))\n        return results\n\n    def _api_exceptions(self, value):\n        \"\"\" Key value exceptions \"\"\"\n        parameter_search = re.compile(r'^\\$\\{stageVariables\\..*\\}$')\n        return re.match(parameter_search, value)\n\n    def _variable_custom_excluded(self, value):\n        \"\"\" User-defined exceptions for variables, anywhere in the file \"\"\"\n        custom_excludes = self.config['custom_excludes']\n        if custom_excludes:\n            custom_search = re.compile(custom_excludes)\n            return re.match(custom_search, value)\n        return False\n\n    def match(self, cfn):\n        matches = []\n\n        refs = cfn.get_valid_refs()\n        getatts = cfn.get_valid_getatts()\n\n        # Get a list of paths to every leaf node string containing at least one ${parameter}\n        parameter_string_paths = self.match_values(cfn)\n        # We want to search all of the paths to check if each one contains an 'Fn::Sub'\n        for parameter_string_path in parameter_string_paths:\n            # Get variable\n            var = parameter_string_path[-1]\n\n            # Step Function State Machine has a Definition Substitution that allows usage of special variables outside of a !Sub\n            # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stepfunctions-statemachine-definitionsubstitutions.html\n\n            if 'DefinitionString' in parameter_string_path:\n                modified_parameter_string_path = copy.copy(parameter_string_path)\n                index = parameter_string_path.index('DefinitionString')\n                modified_parameter_string_path[index] = 'DefinitionSubstitutions'\n                modified_parameter_string_path = modified_parameter_string_path[:index+1]\n                modified_parameter_string_path.append(var[2:-1])\n                if reduce(lambda c, k: c.get(k, {}), modified_parameter_string_path, cfn.template):\n                    continue\n\n            # Exclude variables that match custom exclude filters, if configured\n            # (for third-party tools that pre-process templates before uploading them to AWS)\n            if self._variable_custom_excluded(var):\n                continue\n\n            # Exclude literals (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html)\n            if var.startswith('${!'):\n                continue\n\n            var_stripped = var[2:-1].strip()\n\n            # If we didn't find an 'Fn::Sub' it means a string containing a ${parameter} may not be evaluated correctly\n            if not 'Fn::Sub' in parameter_string_path and parameter_string_path[-2] not in self.exceptions:\n                if (var_stripped in refs or var_stripped in getatts) or 'DefinitionString' in parameter_string_path:\n                    # Remove the last item (the variable) to prevent multiple errors on 1 line errors\n                    path = parameter_string_path[:-1]\n                    message = 'Found an embedded parameter \"{}\" outside of an \"Fn::Sub\" at {}'.format(\n                        var, '/'.join(map(str, path)))\n                    matches.append(RuleMatch(path, message))\n\n        return matches\n", "path": "src/cfnlint/rules/functions/SubNeeded.py"}]}