We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CKV_GIT_4 GitHub "Ensure Secrets are encrypted" is based on a misinterpretation
note: I am not a user of Checkov but I contributed to the GitHub terraform provider so [this comment](https://github.com/integrations/terraform-provider-github/issues/888#issuecomment-1033071672) by @ned1313 made me aware or Checkov behaviour which I believe to be incorrect.

**Describe the issue**

The GitHub [Ensure Secrets are encrypted](https://github.com/bridgecrewio/checkov/blob/0700739e71db58e2dc7987694c33757642b7d38b/checkov/terraform/checks/resource/github/SecretsEncrypted.py) check appears to be based on a misinterpretation of the GitHub functionality and the GitHub terraform provider documentation, because of ambiguity in the argument's naming scheme and the documentation.

Secrets can be provided to GitHub in either a plain text form or an encrypted form. GitHub will encrypt any plain text secrets that arrive, and leave encrypted secrets as is. Regardless of the choice of how to provide the input (plain text or encrypted) it will be stored encrypted by GitHub, then decrypted by GitHub Actions at runtime using the private key.

The choice to use `encrypted_value` over `plaintext_value` is made when you have a secret that you are providing as an input to terraform that you do not want to end up in your terraform state in plain text. If a secret has been generated by a different terraform provider (e.g: a cloud provider access token) then it will already exist in the terraform state, so passing it as a `plaintext_value` to GitHub doesn't introduce any additional exposure.

```python
class SecretsEncrypted(BaseResourceNegativeValueCheck):
    def __init__(self) -> None:
        #  -from github docs "It is also advised that you do not store plaintext values in your code but rather populate
        #  the encrypted_value using fields from a resource, data source or variable as,
        #  while encrypted in state, these will be easily accessible in your code"
```

The full quote here the [GitHub documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_environment_secret) is...

> For the purposes of security, the contents of the `plaintext_value` field have been marked as sensitive to Terraform, but it is important to note that this does not hide it from state files. You should treat state as sensitive always. It is also advised that you do not store plaintext values in your code but rather populate the `encrypted_value` using fields from a resource, data source or variable as, while encrypted in state, these will be easily accessible in your code. 

Rather than serve to describe the behaviour of `encrypted_value` and `plaintext_value` the statement is actually just a generic warning about terraform best practices.

Here's an example of what would Checkov currently produces a warning for, despite it being a valid and secure use:

```hcl
resource "azuread_service_principal" "gh_actions" {
  application_id = azuread_application.gh_actions.application_id
  owners         = [data.azuread_client_config.current.object_id]
}

resource "azuread_service_principal_password" "gh_actions" {
  service_principal_id = azuread_service_principal.gh_actions.object_id
}

resource "github_actions_secret" "example_secret" {
  repository       = "example_repository"
  secret_name      = "example_secret_name"
  plaintext_value  = azuread_service_principal_password.gh_actions.value
}
```

The check could either be removed completely (because `plaintext_value` is appropriate to use in most situations) or, if possible, modified to only warn when the value for the `plaintext_value` argument is a string value directly written into the terraform configuration.

I hope that's clear, please let me know if I can clarify anything.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `checkov/terraform/checks/resource/base_resource_value_check.py`
Content:
```
1 from abc import abstractmethod
2 from collections.abc import Iterable
3 from typing import List, Dict, Any
4 
5 import dpath.util
6 import re
7 from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
8 from checkov.common.models.enums import CheckResult, CheckCategories
9 from checkov.common.models.consts import ANY_VALUE
10 from checkov.common.util.type_forcers import force_list
11 from checkov.terraform.graph_builder.utils import get_referenced_vertices_in_value
12 from checkov.terraform.parser_functions import handle_dynamic_values
13 from checkov.terraform.parser_utils import find_var_blocks
14 
15 
16 
17 class BaseResourceValueCheck(BaseResourceCheck):
18     def __init__(
19         self,
20         name: str,
21         id: str,
22         categories: "Iterable[CheckCategories]",
23         supported_resources: "Iterable[str]",
24         missing_block_result: CheckResult = CheckResult.FAILED,
25     ) -> None:
26         super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
27         self.missing_block_result = missing_block_result
28 
29     @staticmethod
30     def _filter_key_path(path: str) -> List[str]:
31         """
32         Filter an attribute path to contain only named attributes by dropping array indices from the path)
33         :param path: valid JSONPath of an attribute
34         :return: List of named attributes with respect to the input JSONPath order
35         """
36         return [x for x in path.split("/") if not re.search(re.compile(r"^\[?\d+]?$"), x)]
37 
38     @staticmethod
39     def _is_nesting_key(inspected_attributes: List[str], key: List[str]) -> bool:
40         """
41         Resolves whether a key is a subset of the inspected nesting attributes
42         :param inspected_attributes: list of nesting attributes
43         :param key: JSONPath key of an attribute
44         :return: True/False
45         """
46         return any(x in key for x in inspected_attributes)
47 
48     def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:
49         handle_dynamic_values(conf)
50         inspected_key = self.get_inspected_key()
51         expected_values = self.get_expected_values()
52         if dpath.search(conf, inspected_key) != {}:
53             # Inspected key exists
54             value = dpath.get(conf, inspected_key)
55             if isinstance(value, list) and len(value) == 1:
56                 value = value[0]
57             if value is None or (isinstance(value, list) and not value):
58                 return self.missing_block_result
59             if ANY_VALUE in expected_values and value is not None and (not isinstance(value, str) or value):
60                 # Key is found on the configuration - if it accepts any value, the check is PASSED
61                 return CheckResult.PASSED
62             if self._is_variable_dependant(value):
63                 # If the tested attribute is variable-dependant, then result is PASSED
64                 return CheckResult.PASSED
65             if value in expected_values:
66                 return CheckResult.PASSED
67             if get_referenced_vertices_in_value(value=value, aliases={}, resources_types=[]):
68                 # we don't provide resources_types as we want to stay provider agnostic
69                 return CheckResult.UNKNOWN
70             return CheckResult.FAILED
71         else:
72             # Look for the configuration in a bottom-up fashion
73             inspected_attributes = self._filter_key_path(inspected_key)
74             for attribute in reversed(inspected_attributes):
75                 for sub_key, sub_conf in dpath.search(conf, f"**/{attribute}", yielded=True):
76                     filtered_sub_key = self._filter_key_path(sub_key)
77                     # Only proceed with check if full path for key is similar - not partial match
78                     if inspected_attributes == filtered_sub_key:
79                         if self._is_nesting_key(inspected_attributes, filtered_sub_key):
80                             if isinstance(sub_conf, list) and len(sub_conf) == 1:
81                                 sub_conf = sub_conf[0]
82                             if sub_conf in self.get_expected_values():
83                                 return CheckResult.PASSED
84                             if self._is_variable_dependant(sub_conf):
85                                 # If the tested attribute is variable-dependant, then result is PASSED
86                                 return CheckResult.PASSED
87 
88         return self.missing_block_result
89 
90     @abstractmethod
91     def get_inspected_key(self) -> str:
92         """
93         :return: JSONPath syntax path of the checked attribute
94         """
95         raise NotImplementedError()
96 
97     def get_expected_values(self) -> List[Any]:
98         """
99         Override the method with the list of acceptable values if the check has more than one possible expected value, given
100         the inspected key
101         :return: List of expected values, defaults to a list of the expected value
102         """
103         return [self.get_expected_value()]
104 
105     def get_expected_value(self) -> Any:
106         """
107         Returns the default expected value, governed by provider best practices
108         """
109         return True
110 
111     def get_evaluated_keys(self) -> List[str]:
112         return force_list(self.get_inspected_key())
113
```

Path: `checkov/terraform/checks/resource/base_resource_check.py`
Content:
```
1 from abc import abstractmethod
2 from collections.abc import Iterable
3 from typing import Dict, List, Any, Optional
4 
5 from checkov.common.checks.base_check import BaseCheck
6 from checkov.common.models.enums import CheckResult, CheckCategories
7 from checkov.terraform.checks.resource.registry import resource_registry
8 from checkov.terraform.parser_functions import handle_dynamic_values
9 from checkov.terraform.parser_utils import find_var_blocks
10 
11 
12 class BaseResourceCheck(BaseCheck):
13     def __init__(
14         self,
15         name: str,
16         id: str,
17         categories: "Iterable[CheckCategories]",
18         supported_resources: "Iterable[str]",
19         guideline: Optional[str] = None,
20     ) -> None:
21         super().__init__(
22             name=name,
23             id=id,
24             categories=categories,
25             supported_entities=supported_resources,
26             block_type="resource",
27             guideline=guideline,
28         )
29         self.supported_resources = supported_resources
30         resource_registry.register(self)
31 
32     @staticmethod
33     def _is_variable_dependant(value: Any) -> bool:
34         if not isinstance(value, str):
35             return False
36 
37         if value.startswith(('var.', 'local.', 'module.')):
38             return True
39 
40         if "${" not in value:
41             return False
42 
43         if find_var_blocks(value):
44             return True
45         return False
46 
47     def scan_entity_conf(self, conf: Dict[str, List[Any]], entity_type: str) -> CheckResult:
48         self.entity_type = entity_type
49 
50         if conf.get("count") == [0]:
51             return CheckResult.UNKNOWN
52 
53         handle_dynamic_values(conf)
54         return self.scan_resource_conf(conf)
55 
56     @abstractmethod
57     def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:
58         """
59         self.evaluated_keys should be set with a JSONPath of the attribute inspected.
60         If not relevant it should be set to an empty array so the previous check's value gets overridden in the report.
61         """
62         raise NotImplementedError()
63
```

Path: `checkov/terraform/checks/resource/github/SecretsEncrypted.py`
Content:
```
1 from typing import List, Any
2 
3 from checkov.common.models.enums import CheckCategories
4 from checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck
5 from checkov.common.models.consts import ANY_VALUE
6 
7 
8 class SecretsEncrypted(BaseResourceNegativeValueCheck):
9     def __init__(self) -> None:
10         #  -from github docs "It is also advised that you do not store plaintext values in your code but rather populate
11         #  the encrypted_value using fields from a resource, data source or variable as,
12         #  while encrypted in state, these will be easily accessible in your code"
13         name = "Ensure Secrets are encrypted"
14         id = "CKV_GIT_4"
15         supported_resources = ["github_actions_environment_secret",
16                                "github_actions_organization_secret",
17                                "github_actions_secret"]
18         categories = [CheckCategories.ENCRYPTION]
19         super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
20 
21     def get_inspected_key(self) -> str:
22         return "plaintext_value"
23 
24     def get_forbidden_values(self) -> List[Any]:
25         return [ANY_VALUE]
26 
27 
28 check = SecretsEncrypted()
29
```
--- 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/checkov/terraform/checks/resource/base_resource_check.py b/checkov/terraform/checks/resource/base_resource_check.py
--- a/checkov/terraform/checks/resource/base_resource_check.py
+++ b/checkov/terraform/checks/resource/base_resource_check.py
@@ -9,6 +9,19 @@
 from checkov.terraform.parser_utils import find_var_blocks
 
 
+PROVIDER_PREFIXES = (
+    "aws_",
+    "azurerm_",
+    "azuread_",
+    "digitalocean_",
+    "google_",
+    "github_",
+    "linode_",
+    "oci_",
+    "openstack_",
+)
+
+
 class BaseResourceCheck(BaseCheck):
     def __init__(
         self,
@@ -34,7 +47,10 @@
         if not isinstance(value, str):
             return False
 
-        if value.startswith(('var.', 'local.', 'module.')):
+        if value.startswith(("var.", "local.", "module.")):
+            return True
+
+        if value.startswith(PROVIDER_PREFIXES):
             return True
 
         if "${" not in value:
diff --git a/checkov/terraform/checks/resource/base_resource_value_check.py b/checkov/terraform/checks/resource/base_resource_value_check.py
--- a/checkov/terraform/checks/resource/base_resource_value_check.py
+++ b/checkov/terraform/checks/resource/base_resource_value_check.py
@@ -10,8 +10,6 @@
 from checkov.common.util.type_forcers import force_list
 from checkov.terraform.graph_builder.utils import get_referenced_vertices_in_value
 from checkov.terraform.parser_functions import handle_dynamic_values
-from checkov.terraform.parser_utils import find_var_blocks
-
 
 
 class BaseResourceValueCheck(BaseResourceCheck):
diff --git a/checkov/terraform/checks/resource/github/SecretsEncrypted.py b/checkov/terraform/checks/resource/github/SecretsEncrypted.py
--- a/checkov/terraform/checks/resource/github/SecretsEncrypted.py
+++ b/checkov/terraform/checks/resource/github/SecretsEncrypted.py
@@ -1,6 +1,6 @@
-from typing import List, Any
+from typing import List, Any, Dict
 
-from checkov.common.models.enums import CheckCategories
+from checkov.common.models.enums import CheckCategories, CheckResult
 from checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck
 from checkov.common.models.consts import ANY_VALUE
 
@@ -12,12 +12,21 @@
         #  while encrypted in state, these will be easily accessible in your code"
         name = "Ensure Secrets are encrypted"
         id = "CKV_GIT_4"
-        supported_resources = ["github_actions_environment_secret",
-                               "github_actions_organization_secret",
-                               "github_actions_secret"]
-        categories = [CheckCategories.ENCRYPTION]
+        supported_resources = (
+            "github_actions_environment_secret",
+            "github_actions_organization_secret",
+            "github_actions_secret",
+        )
+        categories = (CheckCategories.ENCRYPTION,)
         super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
 
+    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:
+        plaintext = conf.get("plaintext_value")
+        if plaintext and self._is_variable_dependant(plaintext[0]):
+            return CheckResult.PASSED
+
+        return super().scan_resource_conf(conf)
+
     def get_inspected_key(self) -> str:
         return "plaintext_value"

{"golden_diff": "diff --git a/checkov/terraform/checks/resource/base_resource_check.py b/checkov/terraform/checks/resource/base_resource_check.py\n--- a/checkov/terraform/checks/resource/base_resource_check.py\n+++ b/checkov/terraform/checks/resource/base_resource_check.py\n@@ -9,6 +9,19 @@\n from checkov.terraform.parser_utils import find_var_blocks\n \n \n+PROVIDER_PREFIXES = (\n+    \"aws_\",\n+    \"azurerm_\",\n+    \"azuread_\",\n+    \"digitalocean_\",\n+    \"google_\",\n+    \"github_\",\n+    \"linode_\",\n+    \"oci_\",\n+    \"openstack_\",\n+)\n+\n+\n class BaseResourceCheck(BaseCheck):\n     def __init__(\n         self,\n@@ -34,7 +47,10 @@\n         if not isinstance(value, str):\n             return False\n \n-        if value.startswith(('var.', 'local.', 'module.')):\n+        if value.startswith((\"var.\", \"local.\", \"module.\")):\n+            return True\n+\n+        if value.startswith(PROVIDER_PREFIXES):\n             return True\n \n         if \"${\" not in value:\ndiff --git a/checkov/terraform/checks/resource/base_resource_value_check.py b/checkov/terraform/checks/resource/base_resource_value_check.py\n--- a/checkov/terraform/checks/resource/base_resource_value_check.py\n+++ b/checkov/terraform/checks/resource/base_resource_value_check.py\n@@ -10,8 +10,6 @@\n from checkov.common.util.type_forcers import force_list\n from checkov.terraform.graph_builder.utils import get_referenced_vertices_in_value\n from checkov.terraform.parser_functions import handle_dynamic_values\n-from checkov.terraform.parser_utils import find_var_blocks\n-\n \n \n class BaseResourceValueCheck(BaseResourceCheck):\ndiff --git a/checkov/terraform/checks/resource/github/SecretsEncrypted.py b/checkov/terraform/checks/resource/github/SecretsEncrypted.py\n--- a/checkov/terraform/checks/resource/github/SecretsEncrypted.py\n+++ b/checkov/terraform/checks/resource/github/SecretsEncrypted.py\n@@ -1,6 +1,6 @@\n-from typing import List, Any\n+from typing import List, Any, Dict\n \n-from checkov.common.models.enums import CheckCategories\n+from checkov.common.models.enums import CheckCategories, CheckResult\n from checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck\n from checkov.common.models.consts import ANY_VALUE\n \n@@ -12,12 +12,21 @@\n         #  while encrypted in state, these will be easily accessible in your code\"\n         name = \"Ensure Secrets are encrypted\"\n         id = \"CKV_GIT_4\"\n-        supported_resources = [\"github_actions_environment_secret\",\n-                               \"github_actions_organization_secret\",\n-                               \"github_actions_secret\"]\n-        categories = [CheckCategories.ENCRYPTION]\n+        supported_resources = (\n+            \"github_actions_environment_secret\",\n+            \"github_actions_organization_secret\",\n+            \"github_actions_secret\",\n+        )\n+        categories = (CheckCategories.ENCRYPTION,)\n         super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)\n \n+    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:\n+        plaintext = conf.get(\"plaintext_value\")\n+        if plaintext and self._is_variable_dependant(plaintext[0]):\n+            return CheckResult.PASSED\n+\n+        return super().scan_resource_conf(conf)\n+\n     def get_inspected_key(self) -> str:\n         return \"plaintext_value\"\n", "issue": "CKV_GIT_4 GitHub \"Ensure Secrets are encrypted\" is based on a misinterpretation\nnote: I am not a user of Checkov but I contributed to the GitHub terraform provider so [this comment](https://github.com/integrations/terraform-provider-github/issues/888#issuecomment-1033071672) by @ned1313 made me aware or Checkov behaviour which I believe to be incorrect.\r\n\r\n**Describe the issue**\r\n\r\nThe GitHub [Ensure Secrets are encrypted](https://github.com/bridgecrewio/checkov/blob/0700739e71db58e2dc7987694c33757642b7d38b/checkov/terraform/checks/resource/github/SecretsEncrypted.py) check appears to be based on a misinterpretation of the GitHub functionality and the GitHub terraform provider documentation, because of ambiguity in the argument's naming scheme and the documentation.\r\n\r\nSecrets can be provided to GitHub in either a plain text form or an encrypted form. GitHub will encrypt any plain text secrets that arrive, and leave encrypted secrets as is. Regardless of the choice of how to provide the input (plain text or encrypted) it will be stored encrypted by GitHub, then decrypted by GitHub Actions at runtime using the private key.\r\n\r\nThe choice to use `encrypted_value` over `plaintext_value` is made when you have a secret that you are providing as an input to terraform that you do not want to end up in your terraform state in plain text. If a secret has been generated by a different terraform provider (e.g: a cloud provider access token) then it will already exist in the terraform state, so passing it as a `plaintext_value` to GitHub doesn't introduce any additional exposure.\r\n\r\n```python\r\nclass SecretsEncrypted(BaseResourceNegativeValueCheck):\r\n    def __init__(self) -> None:\r\n        #  -from github docs \"It is also advised that you do not store plaintext values in your code but rather populate\r\n        #  the encrypted_value using fields from a resource, data source or variable as,\r\n        #  while encrypted in state, these will be easily accessible in your code\"\r\n```\r\n\r\nThe full quote here the [GitHub documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_environment_secret) is...\r\n\r\n> For the purposes of security, the contents of the `plaintext_value` field have been marked as sensitive to Terraform, but it is important to note that this does not hide it from state files. You should treat state as sensitive always. It is also advised that you do not store plaintext values in your code but rather populate the `encrypted_value` using fields from a resource, data source or variable as, while encrypted in state, these will be easily accessible in your code. \r\n\r\nRather than serve to describe the behaviour of `encrypted_value` and `plaintext_value` the statement is actually just a generic warning about terraform best practices.\r\n\r\nHere's an example of what would Checkov currently produces a warning for, despite it being a valid and secure use:\r\n\r\n```hcl\r\nresource \"azuread_service_principal\" \"gh_actions\" {\r\n  application_id = azuread_application.gh_actions.application_id\r\n  owners         = [data.azuread_client_config.current.object_id]\r\n}\r\n\r\nresource \"azuread_service_principal_password\" \"gh_actions\" {\r\n  service_principal_id = azuread_service_principal.gh_actions.object_id\r\n}\r\n\r\nresource \"github_actions_secret\" \"example_secret\" {\r\n  repository       = \"example_repository\"\r\n  secret_name      = \"example_secret_name\"\r\n  plaintext_value  = azuread_service_principal_password.gh_actions.value\r\n}\r\n```\r\n\r\nThe check could either be removed completely (because `plaintext_value` is appropriate to use in most situations) or, if possible, modified to only warn when the value for the `plaintext_value` argument is a string value directly written into the terraform configuration.\r\n\r\nI hope that's clear, please let me know if I can clarify anything.\n", "before_files": [{"content": "from abc import abstractmethod\nfrom collections.abc import Iterable\nfrom typing import List, Dict, Any\n\nimport dpath.util\nimport re\nfrom checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck\nfrom checkov.common.models.enums import CheckResult, CheckCategories\nfrom checkov.common.models.consts import ANY_VALUE\nfrom checkov.common.util.type_forcers import force_list\nfrom checkov.terraform.graph_builder.utils import get_referenced_vertices_in_value\nfrom checkov.terraform.parser_functions import handle_dynamic_values\nfrom checkov.terraform.parser_utils import find_var_blocks\n\n\n\nclass BaseResourceValueCheck(BaseResourceCheck):\n    def __init__(\n        self,\n        name: str,\n        id: str,\n        categories: \"Iterable[CheckCategories]\",\n        supported_resources: \"Iterable[str]\",\n        missing_block_result: CheckResult = CheckResult.FAILED,\n    ) -> None:\n        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)\n        self.missing_block_result = missing_block_result\n\n    @staticmethod\n    def _filter_key_path(path: str) -> List[str]:\n        \"\"\"\n        Filter an attribute path to contain only named attributes by dropping array indices from the path)\n        :param path: valid JSONPath of an attribute\n        :return: List of named attributes with respect to the input JSONPath order\n        \"\"\"\n        return [x for x in path.split(\"/\") if not re.search(re.compile(r\"^\\[?\\d+]?$\"), x)]\n\n    @staticmethod\n    def _is_nesting_key(inspected_attributes: List[str], key: List[str]) -> bool:\n        \"\"\"\n        Resolves whether a key is a subset of the inspected nesting attributes\n        :param inspected_attributes: list of nesting attributes\n        :param key: JSONPath key of an attribute\n        :return: True/False\n        \"\"\"\n        return any(x in key for x in inspected_attributes)\n\n    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:\n        handle_dynamic_values(conf)\n        inspected_key = self.get_inspected_key()\n        expected_values = self.get_expected_values()\n        if dpath.search(conf, inspected_key) != {}:\n            # Inspected key exists\n            value = dpath.get(conf, inspected_key)\n            if isinstance(value, list) and len(value) == 1:\n                value = value[0]\n            if value is None or (isinstance(value, list) and not value):\n                return self.missing_block_result\n            if ANY_VALUE in expected_values and value is not None and (not isinstance(value, str) or value):\n                # Key is found on the configuration - if it accepts any value, the check is PASSED\n                return CheckResult.PASSED\n            if self._is_variable_dependant(value):\n                # If the tested attribute is variable-dependant, then result is PASSED\n                return CheckResult.PASSED\n            if value in expected_values:\n                return CheckResult.PASSED\n            if get_referenced_vertices_in_value(value=value, aliases={}, resources_types=[]):\n                # we don't provide resources_types as we want to stay provider agnostic\n                return CheckResult.UNKNOWN\n            return CheckResult.FAILED\n        else:\n            # Look for the configuration in a bottom-up fashion\n            inspected_attributes = self._filter_key_path(inspected_key)\n            for attribute in reversed(inspected_attributes):\n                for sub_key, sub_conf in dpath.search(conf, f\"**/{attribute}\", yielded=True):\n                    filtered_sub_key = self._filter_key_path(sub_key)\n                    # Only proceed with check if full path for key is similar - not partial match\n                    if inspected_attributes == filtered_sub_key:\n                        if self._is_nesting_key(inspected_attributes, filtered_sub_key):\n                            if isinstance(sub_conf, list) and len(sub_conf) == 1:\n                                sub_conf = sub_conf[0]\n                            if sub_conf in self.get_expected_values():\n                                return CheckResult.PASSED\n                            if self._is_variable_dependant(sub_conf):\n                                # If the tested attribute is variable-dependant, then result is PASSED\n                                return CheckResult.PASSED\n\n        return self.missing_block_result\n\n    @abstractmethod\n    def get_inspected_key(self) -> str:\n        \"\"\"\n        :return: JSONPath syntax path of the checked attribute\n        \"\"\"\n        raise NotImplementedError()\n\n    def get_expected_values(self) -> List[Any]:\n        \"\"\"\n        Override the method with the list of acceptable values if the check has more than one possible expected value, given\n        the inspected key\n        :return: List of expected values, defaults to a list of the expected value\n        \"\"\"\n        return [self.get_expected_value()]\n\n    def get_expected_value(self) -> Any:\n        \"\"\"\n        Returns the default expected value, governed by provider best practices\n        \"\"\"\n        return True\n\n    def get_evaluated_keys(self) -> List[str]:\n        return force_list(self.get_inspected_key())\n", "path": "checkov/terraform/checks/resource/base_resource_value_check.py"}, {"content": "from abc import abstractmethod\nfrom collections.abc import Iterable\nfrom typing import Dict, List, Any, Optional\n\nfrom checkov.common.checks.base_check import BaseCheck\nfrom checkov.common.models.enums import CheckResult, CheckCategories\nfrom checkov.terraform.checks.resource.registry import resource_registry\nfrom checkov.terraform.parser_functions import handle_dynamic_values\nfrom checkov.terraform.parser_utils import find_var_blocks\n\n\nclass BaseResourceCheck(BaseCheck):\n    def __init__(\n        self,\n        name: str,\n        id: str,\n        categories: \"Iterable[CheckCategories]\",\n        supported_resources: \"Iterable[str]\",\n        guideline: Optional[str] = None,\n    ) -> None:\n        super().__init__(\n            name=name,\n            id=id,\n            categories=categories,\n            supported_entities=supported_resources,\n            block_type=\"resource\",\n            guideline=guideline,\n        )\n        self.supported_resources = supported_resources\n        resource_registry.register(self)\n\n    @staticmethod\n    def _is_variable_dependant(value: Any) -> bool:\n        if not isinstance(value, str):\n            return False\n\n        if value.startswith(('var.', 'local.', 'module.')):\n            return True\n\n        if \"${\" not in value:\n            return False\n\n        if find_var_blocks(value):\n            return True\n        return False\n\n    def scan_entity_conf(self, conf: Dict[str, List[Any]], entity_type: str) -> CheckResult:\n        self.entity_type = entity_type\n\n        if conf.get(\"count\") == [0]:\n            return CheckResult.UNKNOWN\n\n        handle_dynamic_values(conf)\n        return self.scan_resource_conf(conf)\n\n    @abstractmethod\n    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:\n        \"\"\"\n        self.evaluated_keys should be set with a JSONPath of the attribute inspected.\n        If not relevant it should be set to an empty array so the previous check's value gets overridden in the report.\n        \"\"\"\n        raise NotImplementedError()\n", "path": "checkov/terraform/checks/resource/base_resource_check.py"}, {"content": "from typing import List, Any\n\nfrom checkov.common.models.enums import CheckCategories\nfrom checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck\nfrom checkov.common.models.consts import ANY_VALUE\n\n\nclass SecretsEncrypted(BaseResourceNegativeValueCheck):\n    def __init__(self) -> None:\n        #  -from github docs \"It is also advised that you do not store plaintext values in your code but rather populate\n        #  the encrypted_value using fields from a resource, data source or variable as,\n        #  while encrypted in state, these will be easily accessible in your code\"\n        name = \"Ensure Secrets are encrypted\"\n        id = \"CKV_GIT_4\"\n        supported_resources = [\"github_actions_environment_secret\",\n                               \"github_actions_organization_secret\",\n                               \"github_actions_secret\"]\n        categories = [CheckCategories.ENCRYPTION]\n        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)\n\n    def get_inspected_key(self) -> str:\n        return \"plaintext_value\"\n\n    def get_forbidden_values(self) -> List[Any]:\n        return [ANY_VALUE]\n\n\ncheck = SecretsEncrypted()\n", "path": "checkov/terraform/checks/resource/github/SecretsEncrypted.py"}], "after_files": [{"content": "from abc import abstractmethod\nfrom collections.abc import Iterable\nfrom typing import List, Dict, Any\n\nimport dpath.util\nimport re\nfrom checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck\nfrom checkov.common.models.enums import CheckResult, CheckCategories\nfrom checkov.common.models.consts import ANY_VALUE\nfrom checkov.common.util.type_forcers import force_list\nfrom checkov.terraform.graph_builder.utils import get_referenced_vertices_in_value\nfrom checkov.terraform.parser_functions import handle_dynamic_values\n\n\nclass BaseResourceValueCheck(BaseResourceCheck):\n    def __init__(\n        self,\n        name: str,\n        id: str,\n        categories: \"Iterable[CheckCategories]\",\n        supported_resources: \"Iterable[str]\",\n        missing_block_result: CheckResult = CheckResult.FAILED,\n    ) -> None:\n        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)\n        self.missing_block_result = missing_block_result\n\n    @staticmethod\n    def _filter_key_path(path: str) -> List[str]:\n        \"\"\"\n        Filter an attribute path to contain only named attributes by dropping array indices from the path)\n        :param path: valid JSONPath of an attribute\n        :return: List of named attributes with respect to the input JSONPath order\n        \"\"\"\n        return [x for x in path.split(\"/\") if not re.search(re.compile(r\"^\\[?\\d+]?$\"), x)]\n\n    @staticmethod\n    def _is_nesting_key(inspected_attributes: List[str], key: List[str]) -> bool:\n        \"\"\"\n        Resolves whether a key is a subset of the inspected nesting attributes\n        :param inspected_attributes: list of nesting attributes\n        :param key: JSONPath key of an attribute\n        :return: True/False\n        \"\"\"\n        return any(x in key for x in inspected_attributes)\n\n    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:\n        handle_dynamic_values(conf)\n        inspected_key = self.get_inspected_key()\n        expected_values = self.get_expected_values()\n        if dpath.search(conf, inspected_key) != {}:\n            # Inspected key exists\n            value = dpath.get(conf, inspected_key)\n            if isinstance(value, list) and len(value) == 1:\n                value = value[0]\n            if value is None or (isinstance(value, list) and not value):\n                return self.missing_block_result\n            if ANY_VALUE in expected_values and value is not None and (not isinstance(value, str) or value):\n                # Key is found on the configuration - if it accepts any value, the check is PASSED\n                return CheckResult.PASSED\n            if self._is_variable_dependant(value):\n                # If the tested attribute is variable-dependant, then result is PASSED\n                return CheckResult.PASSED\n            if value in expected_values:\n                return CheckResult.PASSED\n            if get_referenced_vertices_in_value(value=value, aliases={}, resources_types=[]):\n                # we don't provide resources_types as we want to stay provider agnostic\n                return CheckResult.UNKNOWN\n            return CheckResult.FAILED\n        else:\n            # Look for the configuration in a bottom-up fashion\n            inspected_attributes = self._filter_key_path(inspected_key)\n            for attribute in reversed(inspected_attributes):\n                for sub_key, sub_conf in dpath.search(conf, f\"**/{attribute}\", yielded=True):\n                    filtered_sub_key = self._filter_key_path(sub_key)\n                    # Only proceed with check if full path for key is similar - not partial match\n                    if inspected_attributes == filtered_sub_key:\n                        if self._is_nesting_key(inspected_attributes, filtered_sub_key):\n                            if isinstance(sub_conf, list) and len(sub_conf) == 1:\n                                sub_conf = sub_conf[0]\n                            if sub_conf in self.get_expected_values():\n                                return CheckResult.PASSED\n                            if self._is_variable_dependant(sub_conf):\n                                # If the tested attribute is variable-dependant, then result is PASSED\n                                return CheckResult.PASSED\n\n        return self.missing_block_result\n\n    @abstractmethod\n    def get_inspected_key(self) -> str:\n        \"\"\"\n        :return: JSONPath syntax path of the checked attribute\n        \"\"\"\n        raise NotImplementedError()\n\n    def get_expected_values(self) -> List[Any]:\n        \"\"\"\n        Override the method with the list of acceptable values if the check has more than one possible expected value, given\n        the inspected key\n        :return: List of expected values, defaults to a list of the expected value\n        \"\"\"\n        return [self.get_expected_value()]\n\n    def get_expected_value(self) -> Any:\n        \"\"\"\n        Returns the default expected value, governed by provider best practices\n        \"\"\"\n        return True\n\n    def get_evaluated_keys(self) -> List[str]:\n        return force_list(self.get_inspected_key())\n", "path": "checkov/terraform/checks/resource/base_resource_value_check.py"}, {"content": "from abc import abstractmethod\nfrom collections.abc import Iterable\nfrom typing import Dict, List, Any, Optional\n\nfrom checkov.common.checks.base_check import BaseCheck\nfrom checkov.common.models.enums import CheckResult, CheckCategories\nfrom checkov.terraform.checks.resource.registry import resource_registry\nfrom checkov.terraform.parser_functions import handle_dynamic_values\nfrom checkov.terraform.parser_utils import find_var_blocks\n\n\nPROVIDER_PREFIXES = (\n    \"aws_\",\n    \"azurerm_\",\n    \"azuread_\",\n    \"digitalocean_\",\n    \"google_\",\n    \"github_\",\n    \"linode_\",\n    \"oci_\",\n    \"openstack_\",\n)\n\n\nclass BaseResourceCheck(BaseCheck):\n    def __init__(\n        self,\n        name: str,\n        id: str,\n        categories: \"Iterable[CheckCategories]\",\n        supported_resources: \"Iterable[str]\",\n        guideline: Optional[str] = None,\n    ) -> None:\n        super().__init__(\n            name=name,\n            id=id,\n            categories=categories,\n            supported_entities=supported_resources,\n            block_type=\"resource\",\n            guideline=guideline,\n        )\n        self.supported_resources = supported_resources\n        resource_registry.register(self)\n\n    @staticmethod\n    def _is_variable_dependant(value: Any) -> bool:\n        if not isinstance(value, str):\n            return False\n\n        if value.startswith((\"var.\", \"local.\", \"module.\")):\n            return True\n\n        if value.startswith(PROVIDER_PREFIXES):\n            return True\n\n        if \"${\" not in value:\n            return False\n\n        if find_var_blocks(value):\n            return True\n        return False\n\n    def scan_entity_conf(self, conf: Dict[str, List[Any]], entity_type: str) -> CheckResult:\n        self.entity_type = entity_type\n\n        if conf.get(\"count\") == [0]:\n            return CheckResult.UNKNOWN\n\n        handle_dynamic_values(conf)\n        return self.scan_resource_conf(conf)\n\n    @abstractmethod\n    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:\n        \"\"\"\n        self.evaluated_keys should be set with a JSONPath of the attribute inspected.\n        If not relevant it should be set to an empty array so the previous check's value gets overridden in the report.\n        \"\"\"\n        raise NotImplementedError()\n", "path": "checkov/terraform/checks/resource/base_resource_check.py"}, {"content": "from typing import List, Any, Dict\n\nfrom checkov.common.models.enums import CheckCategories, CheckResult\nfrom checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck\nfrom checkov.common.models.consts import ANY_VALUE\n\n\nclass SecretsEncrypted(BaseResourceNegativeValueCheck):\n    def __init__(self) -> None:\n        #  -from github docs \"It is also advised that you do not store plaintext values in your code but rather populate\n        #  the encrypted_value using fields from a resource, data source or variable as,\n        #  while encrypted in state, these will be easily accessible in your code\"\n        name = \"Ensure Secrets are encrypted\"\n        id = \"CKV_GIT_4\"\n        supported_resources = (\n            \"github_actions_environment_secret\",\n            \"github_actions_organization_secret\",\n            \"github_actions_secret\",\n        )\n        categories = (CheckCategories.ENCRYPTION,)\n        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)\n\n    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:\n        plaintext = conf.get(\"plaintext_value\")\n        if plaintext and self._is_variable_dependant(plaintext[0]):\n            return CheckResult.PASSED\n\n        return super().scan_resource_conf(conf)\n\n    def get_inspected_key(self) -> str:\n        return \"plaintext_value\"\n\n    def get_forbidden_values(self) -> List[Any]:\n        return [ANY_VALUE]\n\n\ncheck = SecretsEncrypted()\n", "path": "checkov/terraform/checks/resource/github/SecretsEncrypted.py"}]}