Repository: bridgecrewio/checkov

Issue:
CKV_AWS_300 fails if there are more rules than one
**Describe the issue**
CKV_AWS_300 fails if there's more rules than one defined in `aws_s3_bucket_lifecycle_configuration`

**Examples**
```
resource "aws_s3_bucket_lifecycle_configuration" "bucket" {
  bucket = aws_s3_bucket.bucket.bucket

  rule {
    id = "id-1"
    status = "Enabled"

    abort_incomplete_multipart_upload {
      days_after_initiation = 1
    }
  }

  rule {
    id     = "id-2"
    status = "Enabled"

    noncurrent_version_expiration {
      noncurrent_days = 1
    }
  }
}
```

**Version (please complete the following information):**
 - Checkov Version 2.3.111

Fails with:

```
Check: CKV_AWS_300: "Ensure S3 lifecycle configuration sets period for aborting failed uploads"
	FAILED for resource: aws_s3_bucket_lifecycle_configuration.bucket
	File: s3.tf:1-1

		122 | resource "aws_s3_bucket_lifecycle_configuration" "bucket" {
		123 |   bucket = aws_s3_bucket.bucket.bucket
		124 | 
		125 |   rule {
		126 |     id = "id-1"
		127 | 
		128 |     abort_incomplete_multipart_upload {
		129 |       days_after_initiation = 1
		130 |     }
		131 | 
		132 |     status = "Enabled"
		133 |   }
		134 | 
		135 |   rule {
		136 |     id     = "id-2"
		137 |     status = "Enabled"
		138 | 
		139 |     noncurrent_version_expiration {
		140 |       noncurrent_days = 1
		141 |     }
		142 |   }
		143 | }
```


Discussion:
We've hit the same, but I guess the problem for checkov is figuring out whether you really catch _all_ incomplete multiple uploads. So the easiest is to require the action in all rules, and and then maybe let users apply `# checkov:skip=CKV_AWS_300:See rule xxx below` where needed.

Resolving patch:
diff --git a/checkov/terraform/checks/resource/aws/S3AbortIncompleteUploads.py b/checkov/terraform/checks/resource/aws/S3AbortIncompleteUploads.py
--- a/checkov/terraform/checks/resource/aws/S3AbortIncompleteUploads.py
+++ b/checkov/terraform/checks/resource/aws/S3AbortIncompleteUploads.py
@@ -1,28 +1,36 @@
+from __future__ import annotations
+
+from typing import Any
+
 from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
 from checkov.common.models.enums import CheckCategories, CheckResult
 
 
 class S3AbortIncompleteUploads(BaseResourceCheck):
-    def __init__(self):
+    def __init__(self) -> None:
         """
         If you don't set this value in a lifecycle configuration you'll end up paying for s3
         resources you never could use
         """
         name = "Ensure S3 lifecycle configuration sets period for aborting failed uploads"
         id = "CKV_AWS_300"
-        supported_resources = ('aws_s3_bucket_lifecycle_configuration',)
+        supported_resources = ("aws_s3_bucket_lifecycle_configuration",)
         categories = (CheckCategories.GENERAL_SECURITY,)
         super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
 
-    def scan_resource_conf(self, conf):
+    def scan_resource_conf(self, conf: dict[str, list[Any]]) -> CheckResult:
         self.evaluated_keys = ["rule"]
         rules = conf.get("rule")
         if rules and isinstance(rules, list):
             for idx_rule, rule in enumerate(rules):
-                if not rule.get("abort_incomplete_multipart_upload"):
-                    self.evaluated_keys = [f"rule/[{idx_rule}]/"]
-                    return CheckResult.FAILED
-            return CheckResult.PASSED
+                if (
+                    rule.get("abort_incomplete_multipart_upload")
+                    and rule.get("status") == ["Enabled"]
+                    and not rule.get("filter")
+                ):
+                    self.evaluated_keys = [f"rule/[{idx_rule}]/abort_incomplete_multipart_upload"]
+                    return CheckResult.PASSED
+
         return CheckResult.FAILED
 
 


Test patch:
diff --git a/tests/terraform/checks/resource/aws/example_S3AbortIncompleteUploads/main.tf b/tests/terraform/checks/resource/aws/example_S3AbortIncompleteUploads/main.tf
--- a/tests/terraform/checks/resource/aws/example_S3AbortIncompleteUploads/main.tf
+++ b/tests/terraform/checks/resource/aws/example_S3AbortIncompleteUploads/main.tf
@@ -1,3 +1,5 @@
+# fail
+
 resource "aws_s3_bucket_lifecycle_configuration" "fail" {
   # Must have bucket versioning enabled first
   depends_on = [aws_s3_bucket_versioning.versioning]
@@ -81,6 +83,21 @@ resource "aws_s3_bucket_lifecycle_configuration" "fail2" {
   }
 }
 
+resource "aws_s3_bucket_lifecycle_configuration" "fail3" {
+  bucket = aws_s3_bucket.bucket.id
+
+  rule {
+    abort_incomplete_multipart_upload {
+      days_after_initiation = 7
+    }
+    id = "log"
+
+    status = "Disabled"
+  }
+}
+
+# pass
+
 resource "aws_s3_bucket_lifecycle_configuration" "pass2" {
   bucket = aws_s3_bucket.bucket.id
 
@@ -94,17 +111,6 @@ resource "aws_s3_bucket_lifecycle_configuration" "pass2" {
       days = 90
     }
 
-    filter {
-      and {
-        prefix = "log/"
-
-        tags = {
-          rule      = "log"
-          autoclean = "true"
-        }
-      }
-    }
-
     status = "Enabled"
 
     transition {
@@ -118,6 +124,14 @@ resource "aws_s3_bucket_lifecycle_configuration" "pass2" {
     }
   }
 
+  rule {
+    id     = "id-2"
+    status = "Enabled"
+
+    noncurrent_version_expiration {
+      noncurrent_days = 1
+    }
+  }
 }
 
 resource "aws_s3_bucket_lifecycle_configuration" "pass" {
@@ -163,10 +177,6 @@ resource "aws_s3_bucket_lifecycle_configuration" "pass" {
     }
     id = "tmp"
 
-    filter {
-      prefix = "tmp/"
-    }
-
     expiration {
       date = "2023-01-13T00:00:00Z"
     }
diff --git a/tests/terraform/checks/resource/aws/test_S3AbortIncompleteUploads.py b/tests/terraform/checks/resource/aws/test_S3AbortIncompleteUploads.py
--- a/tests/terraform/checks/resource/aws/test_S3AbortIncompleteUploads.py
+++ b/tests/terraform/checks/resource/aws/test_S3AbortIncompleteUploads.py
@@ -1,5 +1,6 @@
 import os
 import unittest
+from pathlib import Path
 
 from checkov.runner_filter import RunnerFilter
 from checkov.terraform.checks.resource.aws.S3AbortIncompleteUploads import check
@@ -8,11 +9,13 @@
 
 class TestS3AbortIncompleteUploads(unittest.TestCase):
     def test(self):
-        runner = Runner()
-        current_dir = os.path.dirname(os.path.realpath(__file__))
+        # given
+        test_files_dir = Path(__file__).parent / "example_S3AbortIncompleteUploads"
 
-        test_files_dir = current_dir + "/example_S3AbortIncompleteUploads"
-        report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id]))
+        # when
+        report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id]))
+
+        # then
         summary = report.get_summary()
 
         passing_resources = {
@@ -22,10 +25,11 @@ def test(self):
         failing_resources = {
             "aws_s3_bucket_lifecycle_configuration.fail",
             "aws_s3_bucket_lifecycle_configuration.fail2",
+            "aws_s3_bucket_lifecycle_configuration.fail3",
         }
 
-        passed_check_resources = set([c.resource for c in report.passed_checks])
-        failed_check_resources = set([c.resource for c in report.failed_checks])
+        passed_check_resources = {c.resource for c in report.passed_checks}
+        failed_check_resources = {c.resource for c in report.failed_checks}
 
         self.assertEqual(summary["passed"], len(passing_resources))
         self.assertEqual(summary["failed"], len(failing_resources))