We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Delimiter in wrong position when using multi-output with empty error list
**Describe the bug**
The "--- OUTPUT DELIMITER ---" message appears below two outputs when running on a project without any errors to output. This causes problems when trying to use parsers to split the output.

**To Reproduce**
Steps to reproduce the behavior:
1. mkdir -p /tmp/checkov-bug
2. cd /tmp/checkov-bug
3. docker run -it -v $PWD:/app -w /app bridgecrew/checkov:2.0.591 -o cli -o junitxml -d .

Output is:
       _               _              
   ___| |__   ___  ___| | _______   __
  / __| '_ \ / _ \/ __| |/ / _ \ \ / /
 | (__| | | |  __/ (__|   < (_) \ V / 
  \___|_| |_|\___|\___|_|\_\___/ \_/  
                                      
By bridgecrew.io | version: 2.0.590 
Update available 2.0.590 -> 2.0.591
Run pip3 install -U checkov to update 

\<?xml version="1.0" ?\>
\<testsuites/\>

--- OUTPUT DELIMITER ---


**Expected behavior**

The expected behaviour would be for the XML snippet to be below "--- OUTPUT DELIMITER ---", since it was the second output option passed to checkov.

**Screenshots**

**Desktop (please complete the following information):**
 - OS: Docker image bridgecrew/checkov
 - Checkov Version 2.0.591

**Additional context**


--- 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/common/runners/runner_registry.py`
Content:
```
1 import argparse
2 import itertools
3 from json import dumps, JSONEncoder
4 from lark import Tree
5 import datetime
6 import logging
7 import os
8 from abc import abstractmethod
9 from typing import List, Union, Dict, Any, Tuple, Optional
10 
11 from typing_extensions import Literal
12 
13 from checkov.common.bridgecrew.integration_features.integration_feature_registry import integration_feature_registry
14 from checkov.common.output.baseline import Baseline
15 from checkov.common.output.report import Report
16 from checkov.common.runners.base_runner import BaseRunner
17 from checkov.common.util import data_structures_utils
18 from checkov.runner_filter import RunnerFilter
19 from checkov.terraform.context_parsers.registry import parser_registry
20 from checkov.terraform.runner import Runner as tf_runner
21 from checkov.terraform.parser import Parser
22 from checkov.common.parallelizer.parallel_runner import parallel_runner
23 from checkov.common.util.ext_cyclonedx_xml import ExtXml
24 from checkov.common.util.banner import tool as tool_name
25 
26 CHECK_BLOCK_TYPES = frozenset(["resource", "data", "provider", "module"])
27 OUTPUT_CHOICES = ["cli", "cyclonedx", "json", "junitxml", "github_failed_only", "sarif"]
28 OUTPUT_DELIMITER = "\n--- OUTPUT DELIMITER ---\n"
29 
30 class OutputEncoder(JSONEncoder):
31     def default(self, obj):
32         if isinstance(obj, set):
33             return list(obj)
34         elif isinstance(obj, Tree):
35             return str(obj)
36         elif isinstance(obj, datetime.date):
37             return str(obj)
38         return super().default(obj)
39 
40 class RunnerRegistry:
41     runners: List[BaseRunner] = []
42     scan_reports: List[Report] = []
43     banner = ""
44 
45     def __init__(self, banner: str, runner_filter: RunnerFilter, *runners: BaseRunner) -> None:
46         self.logger = logging.getLogger(__name__)
47         self.runner_filter = runner_filter
48         self.runners = list(runners)
49         self.banner = banner
50         self.scan_reports = []
51         self.filter_runner_framework()
52         self.tool = tool_name
53 
54     @abstractmethod
55     def extract_entity_details(self, entity: Dict[str, Any]) -> Tuple[str, str, Dict[str, Any]]:
56         raise NotImplementedError()
57 
58     def run(
59         self,
60         root_folder: Optional[str] = None,
61         external_checks_dir: Optional[List[str]] = None,
62         files: Optional[List[str]] = None,
63         guidelines: Optional[Dict[str, str]] = None,
64         collect_skip_comments: bool = True,
65         repo_root_for_plan_enrichment: Optional[List[Union[str, os.PathLike]]] = None,
66     ) -> List[Report]:
67         integration_feature_registry.run_pre_runner()
68         if len(self.runners) == 1:
69             reports = [self.runners[0].run(root_folder, external_checks_dir=external_checks_dir, files=files,
70                                            runner_filter=self.runner_filter, collect_skip_comments=collect_skip_comments)]
71         else:
72             reports = parallel_runner.run_function(
73                 lambda runner: runner.run(root_folder, external_checks_dir=external_checks_dir, files=files,
74                                           runner_filter=self.runner_filter, collect_skip_comments=collect_skip_comments),
75                 self.runners, 1)
76 
77         for scan_report in reports:
78             self._handle_report(scan_report, guidelines, repo_root_for_plan_enrichment)
79         return self.scan_reports
80 
81     def _handle_report(self, scan_report, guidelines, repo_root_for_plan_enrichment):
82         integration_feature_registry.run_post_runner(scan_report)
83         if guidelines:
84             RunnerRegistry.enrich_report_with_guidelines(scan_report, guidelines)
85         if repo_root_for_plan_enrichment:
86             enriched_resources = RunnerRegistry.get_enriched_resources(repo_root_for_plan_enrichment)
87             scan_report = Report("terraform_plan").enrich_plan_report(scan_report, enriched_resources)
88             scan_report = Report("terraform_plan").handle_skipped_checks(scan_report, enriched_resources)
89         self.scan_reports.append(scan_report)
90 
91     def print_reports(
92         self,
93         scan_reports: List[Report],
94         config: argparse.Namespace,
95         url: Optional[str] = None,
96         created_baseline_path: Optional[str] = None,
97         baseline: Optional[Baseline] = None,
98     ) -> Literal[0, 1]:
99         output_formats = set(config.output)
100 
101         if "cli" in config.output and not config.quiet:
102             print(f"{self.banner}\n")
103         exit_codes = []
104         report_jsons = []
105         sarif_reports = []
106         junit_reports = []
107         cyclonedx_reports = []
108         for report in scan_reports:
109             if not report.is_empty():
110                 if "json" in config.output:
111                     report_jsons.append(report.get_dict(is_quiet=config.quiet, url=url))
112                 if "junitxml" in config.output:
113                     junit_reports.append(report)
114                     # report.print_junit_xml()
115                 if "github_failed_only" in config.output:
116                     report.print_failed_github_md(use_bc_ids=config.output_bc_ids)
117                 if "sarif" in config.output:
118                     sarif_reports.append(report)
119                 if "cli" in config.output:
120                     report.print_console(
121                         is_quiet=config.quiet,
122                         is_compact=config.compact,
123                         created_baseline_path=created_baseline_path,
124                         baseline=baseline,
125                         use_bc_ids=config.output_bc_ids,
126                     )
127                     if url:
128                         print("More details: {}".format(url))
129                     output_formats.discard("cli")
130                     if output_formats:
131                         print(OUTPUT_DELIMITER)
132                 if "cyclonedx" in config.output:
133                     cyclonedx_reports.append(report)
134             exit_codes.append(report.get_exit_code(config.soft_fail, config.soft_fail_on, config.hard_fail_on))
135 
136         if "sarif" in config.output:
137             master_report = Report("merged")
138             print(self.banner)
139             for report in sarif_reports:
140                 report.print_console(
141                         is_quiet=config.quiet,
142                         is_compact=config.compact,
143                         created_baseline_path=created_baseline_path,
144                         baseline=baseline,
145                         use_bc_ids=config.output_bc_ids,
146                 )
147                 master_report.failed_checks += report.failed_checks
148                 master_report.skipped_checks += report.skipped_checks
149             if url:
150                 print("More details: {}".format(url))
151             master_report.write_sarif_output(self.tool)
152             output_formats.remove("sarif")
153             if output_formats:
154                 print(OUTPUT_DELIMITER)
155         if "json" in config.output:
156             if not report_jsons:
157                 print(dumps(Report(None).get_summary(), indent=4))
158             elif len(report_jsons) == 1:
159                 print(dumps(report_jsons[0], indent=4, cls=OutputEncoder))
160             else:
161                 print(dumps(report_jsons, indent=4, cls=OutputEncoder))
162             output_formats.remove("json")
163             if output_formats:
164                 print(OUTPUT_DELIMITER)
165         if "junitxml" in config.output:
166             if len(junit_reports) == 1:
167                 junit_reports[0].print_junit_xml(use_bc_ids=config.output_bc_ids)
168             else:
169                 master_report = Report(None)
170                 for report in junit_reports:
171                     master_report.skipped_checks += report.skipped_checks
172                     master_report.passed_checks += report.passed_checks
173                     master_report.failed_checks += report.failed_checks
174                 master_report.print_junit_xml(use_bc_ids=config.output_bc_ids)
175             output_formats.remove("junitxml")
176             if output_formats:
177                 print(OUTPUT_DELIMITER)
178 
179         if "cyclonedx" in config.output:
180             if cyclonedx_reports:
181                 # More than one Report - combine Reports first
182                 report = Report(None)
183                 for r in cyclonedx_reports:
184                     report.passed_checks += r.passed_checks
185                     report.skipped_checks += r.skipped_checks
186                     report.failed_checks += r.failed_checks
187             else:
188                 report = cyclonedx_reports[0]
189             cyclonedx_output = ExtXml(bom=report.get_cyclonedx_bom())
190             print(cyclonedx_output.output_as_string())
191             output_formats.remove("cyclonedx")
192             if output_formats:
193                 print(OUTPUT_DELIMITER)
194 
195         exit_code = 1 if 1 in exit_codes else 0
196         return exit_code
197 
198     def filter_runner_framework(self) -> None:
199         if not self.runner_filter:
200             return
201         if self.runner_filter.framework is None:
202             return
203         if self.runner_filter.framework == "all":
204             return
205         self.runners = [runner for runner in self.runners if runner.check_type == self.runner_filter.framework]
206 
207     def remove_runner(self, runner: BaseRunner) -> None:
208         if runner in self.runners:
209             self.runners.remove(runner)
210 
211     @staticmethod
212     def enrich_report_with_guidelines(scan_report: Report, guidelines: Dict[str, str]) -> None:
213         for record in itertools.chain(scan_report.failed_checks, scan_report.passed_checks, scan_report.skipped_checks):
214             if record.check_id in guidelines:
215                 record.set_guideline(guidelines[record.check_id])
216 
217     @staticmethod
218     def get_enriched_resources(repo_roots: List[Union[str, os.PathLike]]) -> Dict[str, Dict[str, Any]]:
219         repo_definitions = {}
220         for repo_root in repo_roots:
221             tf_definitions = {}
222             parsing_errors = {}
223             Parser().parse_directory(
224                 directory=repo_root,  # assume plan file is in the repo-root
225                 out_definitions=tf_definitions,
226                 out_parsing_errors=parsing_errors,
227             )
228             repo_definitions[repo_root] = { 'tf_definitions': tf_definitions, 'parsing_errors': parsing_errors }
229 
230         enriched_resources = {}
231         for repo_root, parse_results in repo_definitions.items():
232             for full_file_path, definition in parse_results['tf_definitions'].items():
233                 definitions_context = parser_registry.enrich_definitions_context((full_file_path, definition))
234                 abs_scanned_file, _ = tf_runner._strip_module_referrer(full_file_path)
235                 scanned_file = os.path.relpath(abs_scanned_file, repo_root)
236                 for block_type, block_value in definition.items():
237                     if block_type in CHECK_BLOCK_TYPES:
238                         for entity in block_value:
239                             context_parser = parser_registry.context_parsers[block_type]
240                             definition_path = context_parser.get_entity_context_path(entity)
241                             entity_id = ".".join(definition_path)
242                             entity_context_path = [block_type] + definition_path
243                             entity_context = data_structures_utils.get_inner_dict(
244                                 definitions_context[full_file_path], entity_context_path
245                             )
246                             entity_lines_range = [
247                                 entity_context.get("start_line"),
248                                 entity_context.get("end_line"),
249                             ]
250                             entity_code_lines = entity_context.get("code_lines")
251                             skipped_checks = entity_context.get("skipped_checks")
252                             enriched_resources[entity_id] = {
253                                 "entity_code_lines": entity_code_lines,
254                                 "entity_lines_range": entity_lines_range,
255                                 "scanned_file": scanned_file,
256                                 "skipped_checks": skipped_checks,
257                             }
258         return enriched_resources
259
```
--- 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/common/runners/runner_registry.py b/checkov/common/runners/runner_registry.py
--- a/checkov/common/runners/runner_registry.py
+++ b/checkov/common/runners/runner_registry.py
@@ -101,6 +101,7 @@
         if "cli" in config.output and not config.quiet:
             print(f"{self.banner}\n")
         exit_codes = []
+        cli_reports = []
         report_jsons = []
         sarif_reports = []
         junit_reports = []
@@ -117,22 +118,25 @@
                 if "sarif" in config.output:
                     sarif_reports.append(report)
                 if "cli" in config.output:
-                    report.print_console(
-                        is_quiet=config.quiet,
-                        is_compact=config.compact,
-                        created_baseline_path=created_baseline_path,
-                        baseline=baseline,
-                        use_bc_ids=config.output_bc_ids,
-                    )
-                    if url:
-                        print("More details: {}".format(url))
-                    output_formats.discard("cli")
-                    if output_formats:
-                        print(OUTPUT_DELIMITER)
+                    cli_reports.append(report)
                 if "cyclonedx" in config.output:
                     cyclonedx_reports.append(report)
             exit_codes.append(report.get_exit_code(config.soft_fail, config.soft_fail_on, config.hard_fail_on))
 
+        if "cli" in config.output:
+            for report in cli_reports:
+                report.print_console(
+                    is_quiet=config.quiet,
+                    is_compact=config.compact,
+                    created_baseline_path=created_baseline_path,
+                    baseline=baseline,
+                    use_bc_ids=config.output_bc_ids,
+                )
+            if url:
+                print("More details: {}".format(url))
+            output_formats.remove("cli")
+            if output_formats:
+                print(OUTPUT_DELIMITER)
         if "sarif" in config.output:
             master_report = Report("merged")
             print(self.banner)

{"golden_diff": "diff --git a/checkov/common/runners/runner_registry.py b/checkov/common/runners/runner_registry.py\n--- a/checkov/common/runners/runner_registry.py\n+++ b/checkov/common/runners/runner_registry.py\n@@ -101,6 +101,7 @@\n         if \"cli\" in config.output and not config.quiet:\n             print(f\"{self.banner}\\n\")\n         exit_codes = []\n+        cli_reports = []\n         report_jsons = []\n         sarif_reports = []\n         junit_reports = []\n@@ -117,22 +118,25 @@\n                 if \"sarif\" in config.output:\n                     sarif_reports.append(report)\n                 if \"cli\" in config.output:\n-                    report.print_console(\n-                        is_quiet=config.quiet,\n-                        is_compact=config.compact,\n-                        created_baseline_path=created_baseline_path,\n-                        baseline=baseline,\n-                        use_bc_ids=config.output_bc_ids,\n-                    )\n-                    if url:\n-                        print(\"More details: {}\".format(url))\n-                    output_formats.discard(\"cli\")\n-                    if output_formats:\n-                        print(OUTPUT_DELIMITER)\n+                    cli_reports.append(report)\n                 if \"cyclonedx\" in config.output:\n                     cyclonedx_reports.append(report)\n             exit_codes.append(report.get_exit_code(config.soft_fail, config.soft_fail_on, config.hard_fail_on))\n \n+        if \"cli\" in config.output:\n+            for report in cli_reports:\n+                report.print_console(\n+                    is_quiet=config.quiet,\n+                    is_compact=config.compact,\n+                    created_baseline_path=created_baseline_path,\n+                    baseline=baseline,\n+                    use_bc_ids=config.output_bc_ids,\n+                )\n+            if url:\n+                print(\"More details: {}\".format(url))\n+            output_formats.remove(\"cli\")\n+            if output_formats:\n+                print(OUTPUT_DELIMITER)\n         if \"sarif\" in config.output:\n             master_report = Report(\"merged\")\n             print(self.banner)\n", "issue": "Delimiter in wrong position when using multi-output with empty error list\n**Describe the bug**\r\nThe \"--- OUTPUT DELIMITER ---\" message appears below two outputs when running on a project without any errors to output. This causes problems when trying to use parsers to split the output.\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. mkdir -p /tmp/checkov-bug\r\n2. cd /tmp/checkov-bug\r\n3. docker run -it -v $PWD:/app -w /app bridgecrew/checkov:2.0.591 -o cli -o junitxml -d .\r\n\r\nOutput is:\r\n       _               _              \r\n   ___| |__   ___  ___| | _______   __\r\n  / __| '_ \\ / _ \\/ __| |/ / _ \\ \\ / /\r\n | (__| | | |  __/ (__|   < (_) \\ V / \r\n  \\___|_| |_|\\___|\\___|_|\\_\\___/ \\_/  \r\n                                      \r\nBy bridgecrew.io | version: 2.0.590 \r\nUpdate available 2.0.590 -> 2.0.591\r\nRun pip3 install -U checkov to update \r\n\r\n\\<?xml version=\"1.0\" ?\\>\r\n\\<testsuites/\\>\r\n\r\n--- OUTPUT DELIMITER ---\r\n\r\n\r\n**Expected behavior**\r\n\r\nThe expected behaviour would be for the XML snippet to be below \"--- OUTPUT DELIMITER ---\", since it was the second output option passed to checkov.\r\n\r\n**Screenshots**\r\n\r\n**Desktop (please complete the following information):**\r\n - OS: Docker image bridgecrew/checkov\r\n - Checkov Version 2.0.591\r\n\r\n**Additional context**\r\n\n", "before_files": [{"content": "import argparse\nimport itertools\nfrom json import dumps, JSONEncoder\nfrom lark import Tree\nimport datetime\nimport logging\nimport os\nfrom abc import abstractmethod\nfrom typing import List, Union, Dict, Any, Tuple, Optional\n\nfrom typing_extensions import Literal\n\nfrom checkov.common.bridgecrew.integration_features.integration_feature_registry import integration_feature_registry\nfrom checkov.common.output.baseline import Baseline\nfrom checkov.common.output.report import Report\nfrom checkov.common.runners.base_runner import BaseRunner\nfrom checkov.common.util import data_structures_utils\nfrom checkov.runner_filter import RunnerFilter\nfrom checkov.terraform.context_parsers.registry import parser_registry\nfrom checkov.terraform.runner import Runner as tf_runner\nfrom checkov.terraform.parser import Parser\nfrom checkov.common.parallelizer.parallel_runner import parallel_runner\nfrom checkov.common.util.ext_cyclonedx_xml import ExtXml\nfrom checkov.common.util.banner import tool as tool_name\n\nCHECK_BLOCK_TYPES = frozenset([\"resource\", \"data\", \"provider\", \"module\"])\nOUTPUT_CHOICES = [\"cli\", \"cyclonedx\", \"json\", \"junitxml\", \"github_failed_only\", \"sarif\"]\nOUTPUT_DELIMITER = \"\\n--- OUTPUT DELIMITER ---\\n\"\n\nclass OutputEncoder(JSONEncoder):\n    def default(self, obj):\n        if isinstance(obj, set):\n            return list(obj)\n        elif isinstance(obj, Tree):\n            return str(obj)\n        elif isinstance(obj, datetime.date):\n            return str(obj)\n        return super().default(obj)\n\nclass RunnerRegistry:\n    runners: List[BaseRunner] = []\n    scan_reports: List[Report] = []\n    banner = \"\"\n\n    def __init__(self, banner: str, runner_filter: RunnerFilter, *runners: BaseRunner) -> None:\n        self.logger = logging.getLogger(__name__)\n        self.runner_filter = runner_filter\n        self.runners = list(runners)\n        self.banner = banner\n        self.scan_reports = []\n        self.filter_runner_framework()\n        self.tool = tool_name\n\n    @abstractmethod\n    def extract_entity_details(self, entity: Dict[str, Any]) -> Tuple[str, str, Dict[str, Any]]:\n        raise NotImplementedError()\n\n    def run(\n        self,\n        root_folder: Optional[str] = None,\n        external_checks_dir: Optional[List[str]] = None,\n        files: Optional[List[str]] = None,\n        guidelines: Optional[Dict[str, str]] = None,\n        collect_skip_comments: bool = True,\n        repo_root_for_plan_enrichment: Optional[List[Union[str, os.PathLike]]] = None,\n    ) -> List[Report]:\n        integration_feature_registry.run_pre_runner()\n        if len(self.runners) == 1:\n            reports = [self.runners[0].run(root_folder, external_checks_dir=external_checks_dir, files=files,\n                                           runner_filter=self.runner_filter, collect_skip_comments=collect_skip_comments)]\n        else:\n            reports = parallel_runner.run_function(\n                lambda runner: runner.run(root_folder, external_checks_dir=external_checks_dir, files=files,\n                                          runner_filter=self.runner_filter, collect_skip_comments=collect_skip_comments),\n                self.runners, 1)\n\n        for scan_report in reports:\n            self._handle_report(scan_report, guidelines, repo_root_for_plan_enrichment)\n        return self.scan_reports\n\n    def _handle_report(self, scan_report, guidelines, repo_root_for_plan_enrichment):\n        integration_feature_registry.run_post_runner(scan_report)\n        if guidelines:\n            RunnerRegistry.enrich_report_with_guidelines(scan_report, guidelines)\n        if repo_root_for_plan_enrichment:\n            enriched_resources = RunnerRegistry.get_enriched_resources(repo_root_for_plan_enrichment)\n            scan_report = Report(\"terraform_plan\").enrich_plan_report(scan_report, enriched_resources)\n            scan_report = Report(\"terraform_plan\").handle_skipped_checks(scan_report, enriched_resources)\n        self.scan_reports.append(scan_report)\n\n    def print_reports(\n        self,\n        scan_reports: List[Report],\n        config: argparse.Namespace,\n        url: Optional[str] = None,\n        created_baseline_path: Optional[str] = None,\n        baseline: Optional[Baseline] = None,\n    ) -> Literal[0, 1]:\n        output_formats = set(config.output)\n\n        if \"cli\" in config.output and not config.quiet:\n            print(f\"{self.banner}\\n\")\n        exit_codes = []\n        report_jsons = []\n        sarif_reports = []\n        junit_reports = []\n        cyclonedx_reports = []\n        for report in scan_reports:\n            if not report.is_empty():\n                if \"json\" in config.output:\n                    report_jsons.append(report.get_dict(is_quiet=config.quiet, url=url))\n                if \"junitxml\" in config.output:\n                    junit_reports.append(report)\n                    # report.print_junit_xml()\n                if \"github_failed_only\" in config.output:\n                    report.print_failed_github_md(use_bc_ids=config.output_bc_ids)\n                if \"sarif\" in config.output:\n                    sarif_reports.append(report)\n                if \"cli\" in config.output:\n                    report.print_console(\n                        is_quiet=config.quiet,\n                        is_compact=config.compact,\n                        created_baseline_path=created_baseline_path,\n                        baseline=baseline,\n                        use_bc_ids=config.output_bc_ids,\n                    )\n                    if url:\n                        print(\"More details: {}\".format(url))\n                    output_formats.discard(\"cli\")\n                    if output_formats:\n                        print(OUTPUT_DELIMITER)\n                if \"cyclonedx\" in config.output:\n                    cyclonedx_reports.append(report)\n            exit_codes.append(report.get_exit_code(config.soft_fail, config.soft_fail_on, config.hard_fail_on))\n\n        if \"sarif\" in config.output:\n            master_report = Report(\"merged\")\n            print(self.banner)\n            for report in sarif_reports:\n                report.print_console(\n                        is_quiet=config.quiet,\n                        is_compact=config.compact,\n                        created_baseline_path=created_baseline_path,\n                        baseline=baseline,\n                        use_bc_ids=config.output_bc_ids,\n                )\n                master_report.failed_checks += report.failed_checks\n                master_report.skipped_checks += report.skipped_checks\n            if url:\n                print(\"More details: {}\".format(url))\n            master_report.write_sarif_output(self.tool)\n            output_formats.remove(\"sarif\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n        if \"json\" in config.output:\n            if not report_jsons:\n                print(dumps(Report(None).get_summary(), indent=4))\n            elif len(report_jsons) == 1:\n                print(dumps(report_jsons[0], indent=4, cls=OutputEncoder))\n            else:\n                print(dumps(report_jsons, indent=4, cls=OutputEncoder))\n            output_formats.remove(\"json\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n        if \"junitxml\" in config.output:\n            if len(junit_reports) == 1:\n                junit_reports[0].print_junit_xml(use_bc_ids=config.output_bc_ids)\n            else:\n                master_report = Report(None)\n                for report in junit_reports:\n                    master_report.skipped_checks += report.skipped_checks\n                    master_report.passed_checks += report.passed_checks\n                    master_report.failed_checks += report.failed_checks\n                master_report.print_junit_xml(use_bc_ids=config.output_bc_ids)\n            output_formats.remove(\"junitxml\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n\n        if \"cyclonedx\" in config.output:\n            if cyclonedx_reports:\n                # More than one Report - combine Reports first\n                report = Report(None)\n                for r in cyclonedx_reports:\n                    report.passed_checks += r.passed_checks\n                    report.skipped_checks += r.skipped_checks\n                    report.failed_checks += r.failed_checks\n            else:\n                report = cyclonedx_reports[0]\n            cyclonedx_output = ExtXml(bom=report.get_cyclonedx_bom())\n            print(cyclonedx_output.output_as_string())\n            output_formats.remove(\"cyclonedx\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n\n        exit_code = 1 if 1 in exit_codes else 0\n        return exit_code\n\n    def filter_runner_framework(self) -> None:\n        if not self.runner_filter:\n            return\n        if self.runner_filter.framework is None:\n            return\n        if self.runner_filter.framework == \"all\":\n            return\n        self.runners = [runner for runner in self.runners if runner.check_type == self.runner_filter.framework]\n\n    def remove_runner(self, runner: BaseRunner) -> None:\n        if runner in self.runners:\n            self.runners.remove(runner)\n\n    @staticmethod\n    def enrich_report_with_guidelines(scan_report: Report, guidelines: Dict[str, str]) -> None:\n        for record in itertools.chain(scan_report.failed_checks, scan_report.passed_checks, scan_report.skipped_checks):\n            if record.check_id in guidelines:\n                record.set_guideline(guidelines[record.check_id])\n\n    @staticmethod\n    def get_enriched_resources(repo_roots: List[Union[str, os.PathLike]]) -> Dict[str, Dict[str, Any]]:\n        repo_definitions = {}\n        for repo_root in repo_roots:\n            tf_definitions = {}\n            parsing_errors = {}\n            Parser().parse_directory(\n                directory=repo_root,  # assume plan file is in the repo-root\n                out_definitions=tf_definitions,\n                out_parsing_errors=parsing_errors,\n            )\n            repo_definitions[repo_root] = { 'tf_definitions': tf_definitions, 'parsing_errors': parsing_errors }\n\n        enriched_resources = {}\n        for repo_root, parse_results in repo_definitions.items():\n            for full_file_path, definition in parse_results['tf_definitions'].items():\n                definitions_context = parser_registry.enrich_definitions_context((full_file_path, definition))\n                abs_scanned_file, _ = tf_runner._strip_module_referrer(full_file_path)\n                scanned_file = os.path.relpath(abs_scanned_file, repo_root)\n                for block_type, block_value in definition.items():\n                    if block_type in CHECK_BLOCK_TYPES:\n                        for entity in block_value:\n                            context_parser = parser_registry.context_parsers[block_type]\n                            definition_path = context_parser.get_entity_context_path(entity)\n                            entity_id = \".\".join(definition_path)\n                            entity_context_path = [block_type] + definition_path\n                            entity_context = data_structures_utils.get_inner_dict(\n                                definitions_context[full_file_path], entity_context_path\n                            )\n                            entity_lines_range = [\n                                entity_context.get(\"start_line\"),\n                                entity_context.get(\"end_line\"),\n                            ]\n                            entity_code_lines = entity_context.get(\"code_lines\")\n                            skipped_checks = entity_context.get(\"skipped_checks\")\n                            enriched_resources[entity_id] = {\n                                \"entity_code_lines\": entity_code_lines,\n                                \"entity_lines_range\": entity_lines_range,\n                                \"scanned_file\": scanned_file,\n                                \"skipped_checks\": skipped_checks,\n                            }\n        return enriched_resources\n", "path": "checkov/common/runners/runner_registry.py"}], "after_files": [{"content": "import argparse\nimport itertools\nfrom json import dumps, JSONEncoder\nfrom lark import Tree\nimport datetime\nimport logging\nimport os\nfrom abc import abstractmethod\nfrom typing import List, Union, Dict, Any, Tuple, Optional\n\nfrom typing_extensions import Literal\n\nfrom checkov.common.bridgecrew.integration_features.integration_feature_registry import integration_feature_registry\nfrom checkov.common.output.baseline import Baseline\nfrom checkov.common.output.report import Report\nfrom checkov.common.runners.base_runner import BaseRunner\nfrom checkov.common.util import data_structures_utils\nfrom checkov.runner_filter import RunnerFilter\nfrom checkov.terraform.context_parsers.registry import parser_registry\nfrom checkov.terraform.runner import Runner as tf_runner\nfrom checkov.terraform.parser import Parser\nfrom checkov.common.parallelizer.parallel_runner import parallel_runner\nfrom checkov.common.util.ext_cyclonedx_xml import ExtXml\nfrom checkov.common.util.banner import tool as tool_name\n\nCHECK_BLOCK_TYPES = frozenset([\"resource\", \"data\", \"provider\", \"module\"])\nOUTPUT_CHOICES = [\"cli\", \"cyclonedx\", \"json\", \"junitxml\", \"github_failed_only\", \"sarif\"]\nOUTPUT_DELIMITER = \"\\n--- OUTPUT DELIMITER ---\\n\"\n\nclass OutputEncoder(JSONEncoder):\n    def default(self, obj):\n        if isinstance(obj, set):\n            return list(obj)\n        elif isinstance(obj, Tree):\n            return str(obj)\n        elif isinstance(obj, datetime.date):\n            return str(obj)\n        return super().default(obj)\n\nclass RunnerRegistry:\n    runners: List[BaseRunner] = []\n    scan_reports: List[Report] = []\n    banner = \"\"\n\n    def __init__(self, banner: str, runner_filter: RunnerFilter, *runners: BaseRunner) -> None:\n        self.logger = logging.getLogger(__name__)\n        self.runner_filter = runner_filter\n        self.runners = list(runners)\n        self.banner = banner\n        self.scan_reports = []\n        self.filter_runner_framework()\n        self.tool = tool_name\n\n    @abstractmethod\n    def extract_entity_details(self, entity: Dict[str, Any]) -> Tuple[str, str, Dict[str, Any]]:\n        raise NotImplementedError()\n\n    def run(\n        self,\n        root_folder: Optional[str] = None,\n        external_checks_dir: Optional[List[str]] = None,\n        files: Optional[List[str]] = None,\n        guidelines: Optional[Dict[str, str]] = None,\n        collect_skip_comments: bool = True,\n        repo_root_for_plan_enrichment: Optional[List[Union[str, os.PathLike]]] = None,\n    ) -> List[Report]:\n        integration_feature_registry.run_pre_runner()\n        if len(self.runners) == 1:\n            reports = [self.runners[0].run(root_folder, external_checks_dir=external_checks_dir, files=files,\n                                           runner_filter=self.runner_filter, collect_skip_comments=collect_skip_comments)]\n        else:\n            reports = parallel_runner.run_function(\n                lambda runner: runner.run(root_folder, external_checks_dir=external_checks_dir, files=files,\n                                          runner_filter=self.runner_filter, collect_skip_comments=collect_skip_comments),\n                self.runners, 1)\n\n        for scan_report in reports:\n            self._handle_report(scan_report, guidelines, repo_root_for_plan_enrichment)\n        return self.scan_reports\n\n    def _handle_report(self, scan_report, guidelines, repo_root_for_plan_enrichment):\n        integration_feature_registry.run_post_runner(scan_report)\n        if guidelines:\n            RunnerRegistry.enrich_report_with_guidelines(scan_report, guidelines)\n        if repo_root_for_plan_enrichment:\n            enriched_resources = RunnerRegistry.get_enriched_resources(repo_root_for_plan_enrichment)\n            scan_report = Report(\"terraform_plan\").enrich_plan_report(scan_report, enriched_resources)\n            scan_report = Report(\"terraform_plan\").handle_skipped_checks(scan_report, enriched_resources)\n        self.scan_reports.append(scan_report)\n\n    def print_reports(\n        self,\n        scan_reports: List[Report],\n        config: argparse.Namespace,\n        url: Optional[str] = None,\n        created_baseline_path: Optional[str] = None,\n        baseline: Optional[Baseline] = None,\n    ) -> Literal[0, 1]:\n        output_formats = set(config.output)\n\n        if \"cli\" in config.output and not config.quiet:\n            print(f\"{self.banner}\\n\")\n        exit_codes = []\n        cli_reports = []\n        report_jsons = []\n        sarif_reports = []\n        junit_reports = []\n        cyclonedx_reports = []\n        for report in scan_reports:\n            if not report.is_empty():\n                if \"json\" in config.output:\n                    report_jsons.append(report.get_dict(is_quiet=config.quiet, url=url))\n                if \"junitxml\" in config.output:\n                    junit_reports.append(report)\n                    # report.print_junit_xml()\n                if \"github_failed_only\" in config.output:\n                    report.print_failed_github_md(use_bc_ids=config.output_bc_ids)\n                if \"sarif\" in config.output:\n                    sarif_reports.append(report)\n                if \"cli\" in config.output:\n                    cli_reports.append(report)\n                if \"cyclonedx\" in config.output:\n                    cyclonedx_reports.append(report)\n            exit_codes.append(report.get_exit_code(config.soft_fail, config.soft_fail_on, config.hard_fail_on))\n\n        if \"cli\" in config.output:\n            for report in cli_reports:\n                report.print_console(\n                    is_quiet=config.quiet,\n                    is_compact=config.compact,\n                    created_baseline_path=created_baseline_path,\n                    baseline=baseline,\n                    use_bc_ids=config.output_bc_ids,\n                )\n            if url:\n                print(\"More details: {}\".format(url))\n            output_formats.remove(\"cli\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n        if \"sarif\" in config.output:\n            master_report = Report(\"merged\")\n            print(self.banner)\n            for report in sarif_reports:\n                report.print_console(\n                        is_quiet=config.quiet,\n                        is_compact=config.compact,\n                        created_baseline_path=created_baseline_path,\n                        baseline=baseline,\n                        use_bc_ids=config.output_bc_ids,\n                )\n                master_report.failed_checks += report.failed_checks\n                master_report.skipped_checks += report.skipped_checks\n            if url:\n                print(\"More details: {}\".format(url))\n            master_report.write_sarif_output(self.tool)\n            output_formats.remove(\"sarif\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n        if \"json\" in config.output:\n            if not report_jsons:\n                print(dumps(Report(None).get_summary(), indent=4))\n            elif len(report_jsons) == 1:\n                print(dumps(report_jsons[0], indent=4, cls=OutputEncoder))\n            else:\n                print(dumps(report_jsons, indent=4, cls=OutputEncoder))\n            output_formats.remove(\"json\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n        if \"junitxml\" in config.output:\n            if len(junit_reports) == 1:\n                junit_reports[0].print_junit_xml(use_bc_ids=config.output_bc_ids)\n            else:\n                master_report = Report(None)\n                for report in junit_reports:\n                    master_report.skipped_checks += report.skipped_checks\n                    master_report.passed_checks += report.passed_checks\n                    master_report.failed_checks += report.failed_checks\n                master_report.print_junit_xml(use_bc_ids=config.output_bc_ids)\n            output_formats.remove(\"junitxml\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n\n        if \"cyclonedx\" in config.output:\n            if cyclonedx_reports:\n                # More than one Report - combine Reports first\n                report = Report(None)\n                for r in cyclonedx_reports:\n                    report.passed_checks += r.passed_checks\n                    report.skipped_checks += r.skipped_checks\n                    report.failed_checks += r.failed_checks\n            else:\n                report = cyclonedx_reports[0]\n            cyclonedx_output = ExtXml(bom=report.get_cyclonedx_bom())\n            print(cyclonedx_output.output_as_string())\n            output_formats.remove(\"cyclonedx\")\n            if output_formats:\n                print(OUTPUT_DELIMITER)\n\n        exit_code = 1 if 1 in exit_codes else 0\n        return exit_code\n\n    def filter_runner_framework(self) -> None:\n        if not self.runner_filter:\n            return\n        if self.runner_filter.framework is None:\n            return\n        if self.runner_filter.framework == \"all\":\n            return\n        self.runners = [runner for runner in self.runners if runner.check_type == self.runner_filter.framework]\n\n    def remove_runner(self, runner: BaseRunner) -> None:\n        if runner in self.runners:\n            self.runners.remove(runner)\n\n    @staticmethod\n    def enrich_report_with_guidelines(scan_report: Report, guidelines: Dict[str, str]) -> None:\n        for record in itertools.chain(scan_report.failed_checks, scan_report.passed_checks, scan_report.skipped_checks):\n            if record.check_id in guidelines:\n                record.set_guideline(guidelines[record.check_id])\n\n    @staticmethod\n    def get_enriched_resources(repo_roots: List[Union[str, os.PathLike]]) -> Dict[str, Dict[str, Any]]:\n        repo_definitions = {}\n        for repo_root in repo_roots:\n            tf_definitions = {}\n            parsing_errors = {}\n            Parser().parse_directory(\n                directory=repo_root,  # assume plan file is in the repo-root\n                out_definitions=tf_definitions,\n                out_parsing_errors=parsing_errors,\n            )\n            repo_definitions[repo_root] = { 'tf_definitions': tf_definitions, 'parsing_errors': parsing_errors }\n\n        enriched_resources = {}\n        for repo_root, parse_results in repo_definitions.items():\n            for full_file_path, definition in parse_results['tf_definitions'].items():\n                definitions_context = parser_registry.enrich_definitions_context((full_file_path, definition))\n                abs_scanned_file, _ = tf_runner._strip_module_referrer(full_file_path)\n                scanned_file = os.path.relpath(abs_scanned_file, repo_root)\n                for block_type, block_value in definition.items():\n                    if block_type in CHECK_BLOCK_TYPES:\n                        for entity in block_value:\n                            context_parser = parser_registry.context_parsers[block_type]\n                            definition_path = context_parser.get_entity_context_path(entity)\n                            entity_id = \".\".join(definition_path)\n                            entity_context_path = [block_type] + definition_path\n                            entity_context = data_structures_utils.get_inner_dict(\n                                definitions_context[full_file_path], entity_context_path\n                            )\n                            entity_lines_range = [\n                                entity_context.get(\"start_line\"),\n                                entity_context.get(\"end_line\"),\n                            ]\n                            entity_code_lines = entity_context.get(\"code_lines\")\n                            skipped_checks = entity_context.get(\"skipped_checks\")\n                            enriched_resources[entity_id] = {\n                                \"entity_code_lines\": entity_code_lines,\n                                \"entity_lines_range\": entity_lines_range,\n                                \"scanned_file\": scanned_file,\n                                \"skipped_checks\": skipped_checks,\n                            }\n        return enriched_resources\n", "path": "checkov/common/runners/runner_registry.py"}]}