Repository: bridgecrewio/checkov

Issue:
baseline output can change resource order for each run
If I generate a baseline file and I have then made some improvements to my Terraform code and I run the baseline again. What I am finding is that the order of the resources for each file can often change which then shows up as a diff against the prevous baseline file - when in reality nothing has change but the order of the resources in the findings array in the baseline output file 

I was wondering could the findings array just be sorted before being output? Then the resource order should be fixed and any actual diffs should be real changes to check_ids (which is sorted already) or new resources being added?

e.g. this is a diff from two runs of generating a baseline file nothing has actually change just resources moved around in the array.

```
@@ -100,13 +100,12 @@
             "file": "/main.tf",
             "findings": [
                 {
-                    "resource": "aws_s3_bucket.canary_artifacts",
+                    "resource": "aws_s3_bucket.backups",
                     "check_ids": [
                         "CKV2_AWS_6",
                         "CKV_AWS_144",
                         "CKV_AWS_145",
-                        "CKV_AWS_18",
-                        "CKV_AWS_21"
+                        "CKV_AWS_18"
                     ]
                 },
                 {
@@ -119,12 +118,13 @@
                     ]
                 },
                 {
-                    "resource": "aws_s3_bucket.lambdas",
+                    "resource": "aws_s3_bucket.canary_artifacts",
                     "check_ids": [
                         "CKV2_AWS_6",
                         "CKV_AWS_144",
                         "CKV_AWS_145",
-                        "CKV_AWS_18"
+                        "CKV_AWS_18",
+                        "CKV_AWS_21"
                     ]
                 },
                 {
@@ -137,7 +137,7 @@
                     ]
                 },
                 {
-                    "resource": "aws_s3_bucket.backups",
+                    "resource": "aws_s3_bucket.lambdas",
                     "check_ids": [
                         "CKV2_AWS_6",
                         "CKV_AWS_144",
```


Discussion:
hey @james-mullen-bjss thanks for reaching out! Yeah makes sense to order them, especially when added to your SCM. When I'm at it, I will also order the file paths 😄 

Resolving patch:
diff --git a/checkov/common/output/baseline.py b/checkov/common/output/baseline.py
--- a/checkov/common/output/baseline.py
+++ b/checkov/common/output/baseline.py
@@ -2,6 +2,8 @@
 
 import json
 from collections import defaultdict
+from operator import itemgetter
+
 from checkov.common.models.enums import CheckResult
 from typing import Any, TYPE_CHECKING
 
@@ -56,9 +58,9 @@ def to_dict(self) -> dict[str, Any]:
             formatted_findings = []
             for finding in findings:
                 formatted_findings.append({"resource": finding["resource"], "check_ids": finding["check_ids"]})
-            failed_checks_list.append({"file": file, "findings": formatted_findings})
+            failed_checks_list.append({"file": file, "findings": sorted(formatted_findings, key=itemgetter("resource"))})
 
-        resp = {"failed_checks": failed_checks_list}
+        resp = {"failed_checks": sorted(failed_checks_list, key=itemgetter("file"))}
         return resp
 
     def compare_and_reduce_reports(self, scan_reports: list[Report]) -> None:


Test patch:
diff --git a/tests/common/output/fixtures/main_2.tf b/tests/common/output/fixtures/main_2.tf
new file mode 100644
--- /dev/null
+++ b/tests/common/output/fixtures/main_2.tf
@@ -0,0 +1,17 @@
+resource "aws_s3_bucket" "destination_3" {
+  # checkov:skip=CKV_AWS_19: no encryption needed
+  bucket = "tf-test-bucket-destination-12345"
+  acl = var.acl
+  versioning {
+    enabled = var.is_enabled
+  }
+}
+
+resource "aws_s3_bucket" "destination_2" {
+  # checkov:skip=CKV_AWS_19: no encryption needed
+  bucket = "tf-test-bucket-destination-12345"
+  acl = var.acl
+  versioning {
+    enabled = var.is_enabled
+  }
+}
diff --git a/tests/common/output/test_baseline.py b/tests/common/output/test_baseline.py
new file mode 100644
--- /dev/null
+++ b/tests/common/output/test_baseline.py
@@ -0,0 +1,47 @@
+import argparse
+from pathlib import Path
+
+from checkov.common.output.baseline import Baseline
+from checkov.runner_filter import RunnerFilter
+from checkov.terraform.runner import Runner
+
+
+def test_to_dict():
+    # given
+    test_folder = Path(__file__).parent / "fixtures"
+    checks = ["CKV_AWS_18", "CKV_AWS_19", "CKV_AWS_21", "CKV2_AWS_6"]  # 1 pass, 2 fail, 1 skip
+    report = Runner().run(root_folder=str(test_folder), runner_filter=RunnerFilter(checks=checks))
+
+    baseline = Baseline()
+    baseline.add_findings_from_report(report)
+
+    # when
+    output = baseline.to_dict()
+
+    # then
+    assert output == {
+        "failed_checks": [
+            {
+                "file": "/main.tf",
+                "findings": [
+                    {
+                        "resource": "aws_s3_bucket.destination",
+                        "check_ids": ["CKV2_AWS_6", "CKV_AWS_18"],
+                    }
+                ],
+            },
+            {
+                "file": "/main_2.tf",
+                "findings": [
+                    {
+                        "resource": "aws_s3_bucket.destination_2",
+                        "check_ids": ["CKV2_AWS_6", "CKV_AWS_18"],
+                    },
+                    {
+                        "resource": "aws_s3_bucket.destination_3",
+                        "check_ids": ["CKV2_AWS_6", "CKV_AWS_18"],
+                    },
+                ],
+            },
+        ]
+    }