Repository: bridgecrewio/checkov

Issue:
False positive for CKV_AWS_143 ("Ensure S3 bucket has lock configuration enabled by default")
**Describe the bug**

Checkov expects argument `object_lock_configuration` to be an object, i.e.

```hcl
object_lock_configuration = {
  object_lock_enabled = "Enabled"
}
```

Terraform works with the above configuration, but when also declaring rules for the object lock configuration, it expects a block instead, e.g.
```hcl
object_lock_configuration {
  object_lock_enabled = "Enabled"

  rule {
    default_retention {
      mode = "GOVERNANCE"
      days = 366
    }
  }
}
```

**Expected behavior**
Checkov should pass for a `object_lock_configuration` argument block.


**Desktop (please complete the following information):**
 - OS: macOS Big Sur 11.3.1
 - Checkov Version: 2.0.135
 - Terraform version: v0.14.8




Discussion:

Resolving patch:
diff --git a/checkov/terraform/checks/resource/aws/S3BucketObjectLock.py b/checkov/terraform/checks/resource/aws/S3BucketObjectLock.py
--- a/checkov/terraform/checks/resource/aws/S3BucketObjectLock.py
+++ b/checkov/terraform/checks/resource/aws/S3BucketObjectLock.py
@@ -1,25 +1,26 @@
+from typing import Dict, List, Any
+
 from checkov.common.models.enums import CheckCategories, CheckResult
 from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceCheck
 
 
 class S3BucketObjectLock(BaseResourceCheck):
-    def __init__(self):
+    def __init__(self) -> None:
         name = "Ensure that S3 bucket has lock configuration enabled by default"
         id = "CKV_AWS_143"
-        supported_resources = ['aws_s3_bucket']
+        supported_resources = ["aws_s3_bucket"]
         categories = [CheckCategories.GENERAL_SECURITY]
         super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
 
-    def scan_resource_conf(self, conf):
-        if 'object_lock_configuration' in conf:
-            if 'object_lock_enabled' in conf['object_lock_configuration'][0]:
-                lock = conf['object_lock_configuration'][0]['object_lock_enabled']
-                if lock == "Enabled":
-                    return CheckResult.PASSED
-                else:
-                    return CheckResult.FAILED
-        else:
-            return CheckResult.PASSED
+    def scan_resource_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:
+        lock_conf = conf.get("object_lock_configuration")
+        if lock_conf and lock_conf[0]:
+            lock_enabled = lock_conf[0].get("object_lock_enabled")
+            if lock_enabled in ["Enabled", ["Enabled"]]:
+                return CheckResult.PASSED
+            return CheckResult.FAILED
+
+        return CheckResult.UNKNOWN
 
 
 check = S3BucketObjectLock()


Test patch:
diff --git a/tests/graph/terraform/runner/test_graph_builder.py b/tests/graph/terraform/runner/test_graph_builder.py
--- a/tests/graph/terraform/runner/test_graph_builder.py
+++ b/tests/graph/terraform/runner/test_graph_builder.py
@@ -29,7 +29,7 @@ def test_run_clean(self):
         runner = Runner()
         report = runner.run(root_folder=resources_path)
         self.assertEqual(5, len(report.failed_checks))
-        self.assertEqual(7, len(report.passed_checks))
+        self.assertEqual(6, len(report.passed_checks))
         self.assertEqual(0, len(report.skipped_checks))
 
     def test_run_persistent_data(self):
@@ -44,7 +44,7 @@ def test_run_persistent_data(self):
         runner.set_external_data(tf_definitions, definitions_context, breadcrumbs)
         report = runner.run(root_folder=resources_path)
         self.assertGreaterEqual(len(report.failed_checks), 4)
-        self.assertEqual(len(report.passed_checks), 7)
+        self.assertEqual(len(report.passed_checks), 6)
         self.assertEqual(len(report.skipped_checks), 0)
 
     def test_module_and_variables(self):
@@ -52,7 +52,7 @@ def test_module_and_variables(self):
         runner = Runner()
         report = runner.run(root_folder=resources_path)
         self.assertLessEqual(3, len(report.failed_checks))
-        self.assertLessEqual(13, len(report.passed_checks))
+        self.assertLessEqual(12, len(report.passed_checks))
         self.assertEqual(0, len(report.skipped_checks))
 
         found_versioning_failure = False
diff --git a/tests/terraform/checks/resource/aws/example_S3BucketObjectLock/main.tf b/tests/terraform/checks/resource/aws/example_S3BucketObjectLock/main.tf
new file mode 100644
--- /dev/null
+++ b/tests/terraform/checks/resource/aws/example_S3BucketObjectLock/main.tf
@@ -0,0 +1,46 @@
+# pass
+
+resource "aws_s3_bucket" "enabled_via_object" {
+  bucket = "test-bucket"
+  acl    = "private"
+
+  object_lock_configuration = {
+    object_lock_enabled = "Enabled"
+  }
+}
+
+resource "aws_s3_bucket" "enabled_via_block" {
+  bucket = "test-bucket"
+  acl    = "private"
+
+  object_lock_configuration {
+    object_lock_enabled = "Enabled"
+  }
+}
+
+# failure
+
+resource "aws_s3_bucket" "disabled_via_object" {
+  bucket = "test-bucket"
+  acl    = "private"
+
+  object_lock_configuration = {
+    object_lock_enabled = "Disabled"
+  }
+}
+
+resource "aws_s3_bucket" "disabled_via_block" {
+  bucket = "test-bucket"
+  acl    = "private"
+
+  object_lock_configuration {
+    object_lock_enabled = "Disabled"
+  }
+}
+
+# unknown
+
+resource "aws_s3_bucket" "default" {
+  bucket = "test-bucket"
+  acl    = "private"
+}
diff --git a/tests/terraform/checks/resource/aws/test_S3BucketObjectLock.py b/tests/terraform/checks/resource/aws/test_S3BucketObjectLock.py
--- a/tests/terraform/checks/resource/aws/test_S3BucketObjectLock.py
+++ b/tests/terraform/checks/resource/aws/test_S3BucketObjectLock.py
@@ -1,67 +1,40 @@
+import os
 import unittest
-import hcl2
 
+from checkov.runner_filter import RunnerFilter
 from checkov.terraform.checks.resource.aws.S3BucketObjectLock import check
-from checkov.common.models.enums import CheckResult
+from checkov.terraform.runner import Runner
 
 
 class TestS3BucketObjectLock(unittest.TestCase):
+    def test(self):
+        runner = Runner()
+        current_dir = os.path.dirname(os.path.realpath(__file__))
 
-    def test_failure(self):
-        hcl_res = hcl2.loads("""
-                resource "aws_s3_bucket" "test" {
-                      bucket = "my-tf-test-bucket"
-                      acl    = "private"
-                    
-                      tags = {
-                        Name        = "My bucket"
-                        Environment = "Dev"
-                      }
-                      object_lock_configuration = {
-                        object_lock_enabled = "Disabled"
-                      }
+        test_files_dir = current_dir + "/example_S3BucketObjectLock"
+        report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id]))
+        summary = report.get_summary()
 
-                }
-        """)
-        resource_conf = hcl_res['resource'][0]['aws_s3_bucket']['test']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.FAILED, scan_result)
+        passing_resources = {
+            "aws_s3_bucket.enabled_via_object",
+            "aws_s3_bucket.enabled_via_block",
+        }
+        failing_resources = {
+            "aws_s3_bucket.disabled_via_object",
+            "aws_s3_bucket.disabled_via_block",
+        }
 
-    def test_success(self):
-        hcl_res = hcl2.loads("""
-                resource "aws_s3_bucket" "test" {
-                      bucket = "my-tf-test-bucket"
-                      acl    = "private"
-                    
-                      tags = {
-                        Name        = "My bucket"
-                        Environment = "Dev"
-                      }
-                      object_lock_configuration = {
-                        object_lock_enabled = "Enabled"
-                      }
-                }
-        """)
-        resource_conf = hcl_res['resource'][0]['aws_s3_bucket']['test']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.PASSED, scan_result)
+        passed_check_resources = set([c.resource for c in report.passed_checks])
+        failed_check_resources = set([c.resource for c in report.failed_checks])
 
-    def test_success_no_param(self):
-        hcl_res = hcl2.loads("""
-                resource "aws_s3_bucket" "test" {
-                      bucket = "my-tf-test-bucket"
-                      acl    = "private"
-                    
-                      tags = {
-                        Name        = "My bucket"
-                        Environment = "Dev"
-                      }
-                }
-        """)
-        resource_conf = hcl_res['resource'][0]['aws_s3_bucket']['test']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.PASSED, scan_result)
+        self.assertEqual(summary["passed"], 2)
+        self.assertEqual(summary["failed"], 2)
+        self.assertEqual(summary["skipped"], 0)
+        self.assertEqual(summary["parsing_errors"], 0)
 
+        self.assertEqual(passing_resources, passed_check_resources)
+        self.assertEqual(failing_resources, failed_check_resources)
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     unittest.main()