We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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.


--- 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/NoEcho.py`
Content:
```
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 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/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

{"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"}], "after_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport six\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 _get_no_echo_params(self, cfn):\n        \"\"\" Get no Echo Params\"\"\"\n        no_echo_params = []\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 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", "path": "src/cfnlint/rules/resources/NoEcho.py"}]}