prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  False alarm from new W4002
  *cfn-lint version: 0.34.0*
  
  [Here](https://gist.github.com/schmiddy/44a779032a930995d22ee2722a18f163) is an example template which causes a false alarm like this:
  
  ```
  $ cfn-lint /tmp/example.yml 
  W4002 As the resource "metadata" section contains reference to a "NoEcho" parameter DBUser, CloudFormation will display the parameter value in plaintext
  /tmp/example.yml:21:7
  
  W4002 As the resource "metadata" section contains reference to a "NoEcho" parameter DBPass, CloudFormation will display the parameter value in plaintext
  /tmp/example.yml:21:7
  ```
  
  The problem seems to be that the rule is looking for any mention of the parameter name, even as a text description that is not actually referencing the parameter.
  
  
  </issue>
  <code>
  [start of src/cfnlint/rules/resources/NoEcho.py]
  1 """
  2 Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3 SPDX-License-Identifier: MIT-0
  4 """
  5 from cfnlint.helpers import bool_compare
  6 from cfnlint.rules import CloudFormationLintRule
  7 from cfnlint.rules import RuleMatch
  8 
  9 
  10 class NoEcho(CloudFormationLintRule):
  11     id = 'W4002'
  12     shortdesc = 'Check for NoEcho References'
  13     description = 'Check if there is a NoEcho enabled parameter referenced within a resources Metadata section'
  14     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties'
  15     tags = ['resources', 'NoEcho']
  16 
  17     def match(self, cfn):
  18         matches = []
  19         no_echo_params = []
  20         parameters = cfn.get_parameters()
  21         for parameter_name, parameter_value in parameters.items():
  22             noecho = parameter_value.get('NoEcho', default=False)
  23             if bool_compare(noecho, True):
  24                 no_echo_params.append(parameter_name)
  25 
  26         if not no_echo_params:
  27             return no_echo_params
  28 
  29         resource_properties = cfn.get_resources()
  30         resource_dict = {key: resource_properties[key] for key in resource_properties if
  31                          isinstance(resource_properties[key], dict)}
  32         for resource_name, resource_values in resource_dict.items():
  33             resource_values = {key: resource_values[key] for key in resource_values if
  34                                isinstance(resource_values[key], dict)}
  35             metadata = resource_values.get('Metadata', {})
  36             if metadata is not None:
  37                 for prop_name, properties in metadata.items():
  38                     if isinstance(properties, dict):
  39                         for property_value in properties.values():
  40                             for param in no_echo_params and no_echo_params:
  41                                 if str(property_value).find(str(param)) > -1:
  42                                     path = ['Resources', resource_name, 'Metadata', prop_name]
  43                                     matches.append(RuleMatch(path, 'As the resource "metadata" section contains '
  44                                                                    'reference to a "NoEcho" parameter ' + str(param)
  45                                                              + ', CloudFormation will display the parameter value in '
  46                                                                'plaintext'))
  47         return matches
  48
  [end of src/cfnlint/rules/resources/NoEcho.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/src/cfnlint/rules/resources/NoEcho.py b/src/cfnlint/rules/resources/NoEcho.py
  --- a/src/cfnlint/rules/resources/NoEcho.py
  +++ b/src/cfnlint/rules/resources/NoEcho.py
  @@ -2,6 +2,7 @@
   Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   SPDX-License-Identifier: MIT-0
   """
  +import six
   from cfnlint.helpers import bool_compare
   from cfnlint.rules import CloudFormationLintRule
   from cfnlint.rules import RuleMatch
  @@ -14,34 +15,58 @@
       source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties'
       tags = ['resources', 'NoEcho']
   
  -    def match(self, cfn):
  -        matches = []
  +    def _get_no_echo_params(self, cfn):
  +        """ Get no Echo Params"""
           no_echo_params = []
  -        parameters = cfn.get_parameters()
  -        for parameter_name, parameter_value in parameters.items():
  +        for parameter_name, parameter_value in cfn.get_parameters().items():
               noecho = parameter_value.get('NoEcho', default=False)
               if bool_compare(noecho, True):
                   no_echo_params.append(parameter_name)
   
  +        return no_echo_params
  +
  +    def _check_ref(self, cfn, no_echo_params):
  +        """ Check Refs """
  +        matches = []
  +        refs = cfn.search_deep_keys('Ref')
  +        for ref in refs:
  +            if ref[-1] in no_echo_params:
  +                if len(ref) > 3:
  +                    if ref[0] == 'Resources' and ref[2] == 'Metadata':
  +                        matches.append(RuleMatch(ref, 'As the resource "metadata" section contains ' +
  +                                                 'reference to a "NoEcho" parameter ' +
  +                                                 str(ref[-1]) +
  +                                                 ', CloudFormation will display the parameter value in ' +
  +                                                 'plaintext'))
  +
  +        return matches
  +
  +    def _check_sub(self, cfn, no_echo_params):
  +        """ Check Subs """
  +        matches = []
  +        subs = cfn.search_deep_keys('Fn::Sub')
  +        for sub in subs:
  +            if isinstance(sub[-1], six.string_types):
  +                params = cfn.get_sub_parameters(sub[-1])
  +                for param in params:
  +                    if param in no_echo_params:
  +                        if len(sub) > 2:
  +                            if sub[0] == 'Resources' and sub[2] == 'Metadata':
  +
  +                                matches.append(RuleMatch(sub[:-1], 'As the resource "metadata" section contains ' +
  +                                                         'reference to a "NoEcho" parameter ' +
  +                                                         str(param) +
  +                                                         ', CloudFormation will display the parameter value in ' +
  +                                                         'plaintext'))
  +
  +        return matches
  +
  +    def match(self, cfn):
  +        matches = []
  +        no_echo_params = self._get_no_echo_params(cfn)
           if not no_echo_params:
  -            return no_echo_params
  -
  -        resource_properties = cfn.get_resources()
  -        resource_dict = {key: resource_properties[key] for key in resource_properties if
  -                         isinstance(resource_properties[key], dict)}
  -        for resource_name, resource_values in resource_dict.items():
  -            resource_values = {key: resource_values[key] for key in resource_values if
  -                               isinstance(resource_values[key], dict)}
  -            metadata = resource_values.get('Metadata', {})
  -            if metadata is not None:
  -                for prop_name, properties in metadata.items():
  -                    if isinstance(properties, dict):
  -                        for property_value in properties.values():
  -                            for param in no_echo_params and no_echo_params:
  -                                if str(property_value).find(str(param)) > -1:
  -                                    path = ['Resources', resource_name, 'Metadata', prop_name]
  -                                    matches.append(RuleMatch(path, 'As the resource "metadata" section contains '
  -                                                                   'reference to a "NoEcho" parameter ' + str(param)
  -                                                             + ', CloudFormation will display the parameter value in '
  -                                                               'plaintext'))
  +            return matches
  +        matches.extend(self._check_ref(cfn, no_echo_params))
  +        matches.extend(self._check_sub(cfn, no_echo_params))
  +
           return matches
  
verification_info: {"golden_diff": "diff --git a/src/cfnlint/rules/resources/NoEcho.py b/src/cfnlint/rules/resources/NoEcho.py\n--- a/src/cfnlint/rules/resources/NoEcho.py\n+++ b/src/cfnlint/rules/resources/NoEcho.py\n@@ -2,6 +2,7 @@\n Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n SPDX-License-Identifier: MIT-0\n \"\"\"\n+import six\n from cfnlint.helpers import bool_compare\n from cfnlint.rules import CloudFormationLintRule\n from cfnlint.rules import RuleMatch\n@@ -14,34 +15,58 @@\n     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties'\n     tags = ['resources', 'NoEcho']\n \n-    def match(self, cfn):\n-        matches = []\n+    def _get_no_echo_params(self, cfn):\n+        \"\"\" Get no Echo Params\"\"\"\n         no_echo_params = []\n-        parameters = cfn.get_parameters()\n-        for parameter_name, parameter_value in parameters.items():\n+        for parameter_name, parameter_value in cfn.get_parameters().items():\n             noecho = parameter_value.get('NoEcho', default=False)\n             if bool_compare(noecho, True):\n                 no_echo_params.append(parameter_name)\n \n+        return no_echo_params\n+\n+    def _check_ref(self, cfn, no_echo_params):\n+        \"\"\" Check Refs \"\"\"\n+        matches = []\n+        refs = cfn.search_deep_keys('Ref')\n+        for ref in refs:\n+            if ref[-1] in no_echo_params:\n+                if len(ref) > 3:\n+                    if ref[0] == 'Resources' and ref[2] == 'Metadata':\n+                        matches.append(RuleMatch(ref, 'As the resource \"metadata\" section contains ' +\n+                                                 'reference to a \"NoEcho\" parameter ' +\n+                                                 str(ref[-1]) +\n+                                                 ', CloudFormation will display the parameter value in ' +\n+                                                 'plaintext'))\n+\n+        return matches\n+\n+    def _check_sub(self, cfn, no_echo_params):\n+        \"\"\" Check Subs \"\"\"\n+        matches = []\n+        subs = cfn.search_deep_keys('Fn::Sub')\n+        for sub in subs:\n+            if isinstance(sub[-1], six.string_types):\n+                params = cfn.get_sub_parameters(sub[-1])\n+                for param in params:\n+                    if param in no_echo_params:\n+                        if len(sub) > 2:\n+                            if sub[0] == 'Resources' and sub[2] == 'Metadata':\n+\n+                                matches.append(RuleMatch(sub[:-1], 'As the resource \"metadata\" section contains ' +\n+                                                         'reference to a \"NoEcho\" parameter ' +\n+                                                         str(param) +\n+                                                         ', CloudFormation will display the parameter value in ' +\n+                                                         'plaintext'))\n+\n+        return matches\n+\n+    def match(self, cfn):\n+        matches = []\n+        no_echo_params = self._get_no_echo_params(cfn)\n         if not no_echo_params:\n-            return no_echo_params\n-\n-        resource_properties = cfn.get_resources()\n-        resource_dict = {key: resource_properties[key] for key in resource_properties if\n-                         isinstance(resource_properties[key], dict)}\n-        for resource_name, resource_values in resource_dict.items():\n-            resource_values = {key: resource_values[key] for key in resource_values if\n-                               isinstance(resource_values[key], dict)}\n-            metadata = resource_values.get('Metadata', {})\n-            if metadata is not None:\n-                for prop_name, properties in metadata.items():\n-                    if isinstance(properties, dict):\n-                        for property_value in properties.values():\n-                            for param in no_echo_params and no_echo_params:\n-                                if str(property_value).find(str(param)) > -1:\n-                                    path = ['Resources', resource_name, 'Metadata', prop_name]\n-                                    matches.append(RuleMatch(path, 'As the resource \"metadata\" section contains '\n-                                                                   'reference to a \"NoEcho\" parameter ' + str(param)\n-                                                             + ', CloudFormation will display the parameter value in '\n-                                                               'plaintext'))\n+            return matches\n+        matches.extend(self._check_ref(cfn, no_echo_params))\n+        matches.extend(self._check_sub(cfn, no_echo_params))\n+\n         return matches\n", "issue": "False alarm from new W4002\n*cfn-lint version: 0.34.0*\r\n\r\n[Here](https://gist.github.com/schmiddy/44a779032a930995d22ee2722a18f163) is an example template which causes a false alarm like this:\r\n\r\n```\r\n$ cfn-lint /tmp/example.yml \r\nW4002 As the resource \"metadata\" section contains reference to a \"NoEcho\" parameter DBUser, CloudFormation will display the parameter value in plaintext\r\n/tmp/example.yml:21:7\r\n\r\nW4002 As the resource \"metadata\" section contains reference to a \"NoEcho\" parameter DBPass, CloudFormation will display the parameter value in plaintext\r\n/tmp/example.yml:21:7\r\n```\r\n\r\nThe problem seems to be that the rule is looking for any mention of the parameter name, even as a text description that is not actually referencing the parameter.\r\n\n", "before_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nfrom cfnlint.helpers import bool_compare\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\n\n\nclass NoEcho(CloudFormationLintRule):\n    id = 'W4002'\n    shortdesc = 'Check for NoEcho References'\n    description = 'Check if there is a NoEcho enabled parameter referenced within a resources Metadata section'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#parameters-section-structure-properties'\n    tags = ['resources', 'NoEcho']\n\n    def match(self, cfn):\n        matches = []\n        no_echo_params = []\n        parameters = cfn.get_parameters()\n        for parameter_name, parameter_value in parameters.items():\n            noecho = parameter_value.get('NoEcho', default=False)\n            if bool_compare(noecho, True):\n                no_echo_params.append(parameter_name)\n\n        if not no_echo_params:\n            return no_echo_params\n\n        resource_properties = cfn.get_resources()\n        resource_dict = {key: resource_properties[key] for key in resource_properties if\n                         isinstance(resource_properties[key], dict)}\n        for resource_name, resource_values in resource_dict.items():\n            resource_values = {key: resource_values[key] for key in resource_values if\n                               isinstance(resource_values[key], dict)}\n            metadata = resource_values.get('Metadata', {})\n            if metadata is not None:\n                for prop_name, properties in metadata.items():\n                    if isinstance(properties, dict):\n                        for property_value in properties.values():\n                            for param in no_echo_params and no_echo_params:\n                                if str(property_value).find(str(param)) > -1:\n                                    path = ['Resources', resource_name, 'Metadata', prop_name]\n                                    matches.append(RuleMatch(path, 'As the resource \"metadata\" section contains '\n                                                                   'reference to a \"NoEcho\" parameter ' + str(param)\n                                                             + ', CloudFormation will display the parameter value in '\n                                                               'plaintext'))\n        return matches\n", "path": "src/cfnlint/rules/resources/NoEcho.py"}]}