Repository: bridgecrewio/checkov

Issue:
How to Check a Data Source connected to a Resource
**Describe the issue**
I want to check if the attribute "group" of the resource "azuredevops_group_membership" refers to a data source "azuredevops_group" with the attribute "name" = "Build Administrators" for example.

**Examples**
Snippet from [terraform registry](https://registry.terraform.io/providers/microsoft/azuredevops/latest/docs/resources/group_membership)
```terraform
data "azuredevops_group" "example" {
  project_id = azuredevops_project.example.id
  name       = "Build Administrators"
}

resource "azuredevops_group_membership" "example" {
  group = data.azuredevops_group.example.descriptor
  members = [
    azuredevops_user_entitlement.example.descriptor
  ]
}
```
I tryed creating a custom policy in python but I didn't understand how I could make this work, I was only able to create a policy to check if the attribute name of the data azuredevops_group was equal to "Build Administrators":

```python
from typing import Dict, List, Any

from checkov.terraform.checks.data.base_check import BaseDataCheck
from checkov.common.models.enums import CheckResult, CheckCategories

class NoBuildAdministratorCreated(BaseDataCheck):
    def __init__(self) -> None:
        name = 'Ensure no build administrator is created on file'
        id = "CKV_ADO_9000"
        supported_data = ["azuredevops_group"]
        categories = [CheckCategories.GENERAL_SECURITY]
        super().__init__(name=name, id=id, categories=categories, supported_data=supported_data)

    def scan_data_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:

        if (conf.get("name", "Build Administrators")):
            return CheckResult.FAILED
    
        return CheckResult.PASSED

check = NoBuildAdministratorCreated()
```

**Version (please complete the following information):**
 - Checkov Version 2.0.1223

**Additional context**
My goal is to check if people are creating admin groups inside of a terraform file. I'm kinda new to reading documentations and code libraries of open source projects so I'm having a bit of a hard time understanding how to use the checkov python scan functions to create custom policies. So any advice or code example to help me understand better how it works and what is this **conf** would be much appreciated, thanks!



Discussion:

Resolving patch:
diff --git a/checkov/common/checks_infra/solvers/attribute_solvers/base_attribute_solver.py b/checkov/common/checks_infra/solvers/attribute_solvers/base_attribute_solver.py
--- a/checkov/common/checks_infra/solvers/attribute_solvers/base_attribute_solver.py
+++ b/checkov/common/checks_infra/solvers/attribute_solvers/base_attribute_solver.py
@@ -12,7 +12,9 @@
 from checkov.common.graph.graph_builder import CustomAttributes
 from checkov.common.graph.graph_builder.graph_components.block_types import BlockType
 from checkov.common.util.var_utils import is_terraform_variable_dependent, is_cloudformation_variable_dependent
+from checkov.terraform.graph_builder.graph_components.block_types import BlockType as TerraformBlockType
 
+SUPPORTED_BLOCK_TYPES = {BlockType.RESOURCE, TerraformBlockType.DATA}
 WILDCARD_PATTERN = re.compile(r"(\S+[.][*][.]*)+")
 
 
@@ -32,7 +34,7 @@ def run(self, graph_connector: DiGraph) -> Tuple[List[Dict[str, Any]], List[Dict
         failed_vertices: List[Dict[str, Any]] = []
         for _, data in graph_connector.nodes(data=True):
             if (not self.resource_types or data.get(CustomAttributes.RESOURCE_TYPE) in self.resource_types) \
-                    and data.get(CustomAttributes.BLOCK_TYPE) == BlockType.RESOURCE:
+                    and data.get(CustomAttributes.BLOCK_TYPE) in SUPPORTED_BLOCK_TYPES:
                 jobs.append(executer.submit(self._process_node, data, passed_vertices, failed_vertices))
 
         concurrent.futures.wait(jobs)


Test patch:
diff --git a/tests/terraform/graph/checks_infra/connection_solvers/connection_exist_solver/S3BucketPolicyDataSource.yaml b/tests/terraform/graph/checks_infra/connection_solvers/connection_exist_solver/S3BucketPolicyDataSource.yaml
new file mode 100644
--- /dev/null
+++ b/tests/terraform/graph/checks_infra/connection_solvers/connection_exist_solver/S3BucketPolicyDataSource.yaml
@@ -0,0 +1,27 @@
+metadata:
+  id: "S3BucketPolicyDataSource"
+definition:
+  and:
+    - cond_type: filter
+      attribute: resource_type
+      operator: within
+      value:
+        - aws_s3_bucket
+    - cond_type: connection
+      resource_types:
+        - aws_s3_bucket
+      connected_resource_types:
+        - aws_s3_bucket_policy
+      operator: exists
+    - cond_type: connection
+      resource_types:
+        - aws_s3_bucket_policy
+      connected_resource_types:
+        - data.aws_iam_policy_document
+      operator: exists
+    - cond_type: attribute
+      resource_types:
+        - data.aws_iam_policy_document
+      attribute: statement.resources
+      operator: not_contains
+      value: "*"
\ No newline at end of file
diff --git a/tests/terraform/graph/checks_infra/connection_solvers/connection_exist_solver/test_solver.py b/tests/terraform/graph/checks_infra/connection_solvers/connection_exist_solver/test_solver.py
--- a/tests/terraform/graph/checks_infra/connection_solvers/connection_exist_solver/test_solver.py
+++ b/tests/terraform/graph/checks_infra/connection_solvers/connection_exist_solver/test_solver.py
@@ -30,6 +30,14 @@ def test_output_connection(self):
 
         self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
 
+    def test_data_connection(self):
+        root_folder = "../../../resources/s3_bucket_policy"
+        check_id = "S3BucketPolicyDataSource"
+        should_pass = ["aws_s3_bucket.good"]
+        should_fail = ["aws_s3_bucket.bad"]
+        expected_results = {check_id: {"should_pass": should_pass, "should_fail": should_fail}}
+
+        self.run_test(root_folder=root_folder, expected_results=expected_results, check_id=check_id)
 
     def test_reduce_graph_by_target_types(self):
         # given
diff --git a/tests/terraform/graph/resources/s3_bucket_policy/main.tf b/tests/terraform/graph/resources/s3_bucket_policy/main.tf
new file mode 100644
--- /dev/null
+++ b/tests/terraform/graph/resources/s3_bucket_policy/main.tf
@@ -0,0 +1,45 @@
+resource "aws_s3_bucket" "good" {
+  bucket = "bucket_good"
+}
+
+resource "aws_s3_bucket_policy" "good" {
+  bucket = aws_s3_bucket.good.id
+  policy = data.aws_iam_policy_document.good.json
+}
+
+data "aws_iam_policy_document" "good" {
+  version = "2012-10-17"
+
+  statement {
+    effect = "Allow"
+    actions = [
+      "s3:Describe*",
+    ]
+    resources = [
+      "arn:aws:s3:::examplebucket",
+    ]
+  }
+}
+
+resource "aws_s3_bucket" "bad" {
+  bucket = "bucket_good"
+}
+
+resource "aws_s3_bucket_policy" "bad" {
+  bucket = aws_s3_bucket.bad.id
+  policy = data.aws_iam_policy_document.bad.json
+}
+
+data "aws_iam_policy_document" "bad" {
+  version = "2012-10-17"
+
+  statement {
+    effect = "Allow"
+    actions = [
+      "s3:Describe*",
+    ]
+    resources = [
+      "*",
+    ]
+  }
+}