We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sarif report creates invalid uri for folder with spaces
**Describe the issue**
SonarQube will not import SARIF report from Checkov correctly because of invalid URI in SARIF

1) Scan folders with spaces that has some issues
example:
Secrets/Access Tokens/Azure/main.tf 

2) Output result as sarif
3) Resulting file is not valid SARIF due to invalid URI

The field Secrets/Access Tokens/Azure/main.tf corresponds to the results/locations/physicalLocation/artifactLocation/uri object in the SARIF report. There is character the space in the URI. This is not expected. The URI field shouldn’t have any spaces.
This is against specification of URI, which forbids spaces in URIs.


Because of this problem , import of issues in directories with spaces will fail in SonarQube and possibly other tools

--- 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/output/sarif.py`
Content:
```
1 from __future__ import annotations
2 
3 import itertools
4 import json
5 from typing import TYPE_CHECKING, Any
6 
7 from checkov.common.models.enums import CheckResult
8 from checkov.common.output.cyclonedx_consts import SCA_CHECKTYPES
9 from checkov.common.util.http_utils import valid_url
10 from checkov.version import version
11 
12 if TYPE_CHECKING:
13     from checkov.common.output.record import Record
14     from checkov.common.output.report import Report
15 
16 SEVERITY_TO_SARIF_LEVEL = {
17     "critical": "error",
18     "high": "error",
19     "medium": "warning",
20     "low": "note",
21     "none": "none",
22 }
23 
24 
25 SEVERITY_TO_SCORE = {
26     "critical": "10.0",
27     "high": "8.9",
28     "medium": "6.9",
29     "low": "3.9",
30     "none": "0.0",
31 }
32 
33 
34 class Sarif:
35     def __init__(self, reports: list[Report], tool: str | None) -> None:
36         self.reports = reports
37         self.rule_index_map: "dict[str, int]" = {}
38         self.tool = tool if tool else "Bridgecrew"
39 
40         self.json = self.create_json()
41 
42     def create_json(self) -> dict[str, Any]:
43         return {
44             "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
45             "version": "2.1.0",
46             "runs": self._create_runs(),
47         }
48 
49     def _create_runs(self) -> list[dict[str, Any]]:
50         information_uri = "https://docs.bridgecrew.io" if self.tool.lower() == "bridgecrew" else "https://checkov.io"
51         rules = self._create_rules()  # needs to be invoked before _create_results()
52         results = self._create_results()
53 
54         return [
55             {
56                 "tool": {
57                     "driver": {
58                         "name": self.tool,
59                         "version": version,
60                         "informationUri": information_uri,
61                         "rules": rules,
62                         "organization": "bridgecrew",
63                     }
64                 },
65                 "results": results,
66             }
67         ]
68 
69     def _create_rules(self) -> list[dict[str, Any]]:
70         rule_idx = 0
71         rules: "list[dict[str, Any]]" = []
72 
73         for report in self.reports:
74             if report.check_type in SCA_CHECKTYPES:
75                 for record in itertools.chain(report.failed_checks, report.skipped_checks):
76                     rule = None
77                     if record.check_id.startswith("BC_LIC"):
78                         rule = self._create_license_rule(check_type=report.check_type, record=record)
79                     elif record.check_id.startswith(("BC_VUL", "CKV_CVE")):
80                         rule = self._create_cve_rule(check_type=report.check_type, record=record)
81 
82                     if rule and rule["id"] not in self.rule_index_map:
83                         self.rule_index_map[rule["id"]] = rule_idx
84                         rules.append(rule)
85                         rule_idx += 1
86             else:
87                 for record in itertools.chain(report.failed_checks, report.skipped_checks):
88                     if record.check_id not in self.rule_index_map:
89                         rule = self._create_iac_rule(check_type=report.check_type, record=record)
90                         self.rule_index_map[rule["id"]] = rule_idx
91                         rules.append(rule)
92                         rule_idx += 1
93 
94         return rules
95 
96     def _create_iac_rule(self, check_type: str, record: Record) -> dict[str, Any]:
97         rule = {
98             "id": self._create_rule_id(check_type=check_type, record=record),
99             "name": record.short_description or record.check_name,
100             "shortDescription": {
101                 "text": record.short_description or record.check_name,
102             },
103             "fullDescription": {
104                 "text": record.description or record.check_name,
105             },
106             "help": {
107                 "text": f"{record.check_name}\nResource: {record.resource}",
108             },
109             "defaultConfiguration": {"level": "error"},
110         }
111 
112         # Adding 'properties' dictionary only if 'record.severity' exists
113         if record.severity:
114             rule["properties"] = {
115                 "security-severity": SEVERITY_TO_SCORE.get(record.severity.name.lower(), "0.0"),
116             }
117 
118         help_uri = record.guideline
119         if valid_url(help_uri):
120             rule["helpUri"] = help_uri
121 
122         return rule
123 
124     def _create_cve_rule(self, check_type: str, record: Record) -> dict[str, Any] | None:
125         details = record.vulnerability_details
126         if not details:
127             # this shouldn't happen
128             return None
129 
130         rule = {
131             "id": self._create_rule_id(check_type=check_type, record=record),
132             "name": record.short_description or record.check_name,
133             "shortDescription": {
134                 "text": record.short_description or record.check_name,
135             },
136             "fullDescription": {
137                 "text": record.description or record.check_name,
138             },
139             "help": {
140                 "text": f"{record.check_name}\nResource: {record.resource}\nStatus: {details.get('status')}",
141             },
142             "defaultConfiguration": {"level": "error"},
143         }
144 
145         # Add properties dictionary with security-severity
146         cvss = details.get("cvss")
147         if cvss:
148             # use CVSS, if exists
149             rule["properties"] = {
150                 "security-severity": str(cvss),
151             }
152         elif record.severity:
153             # otherwise severity, if exists
154             rule["properties"] = {
155                 "security-severity": SEVERITY_TO_SCORE.get(record.severity.name.lower(), "0.0"),
156             }
157 
158         help_uri = details.get("link")
159         if valid_url(help_uri):
160             rule["helpUri"] = help_uri
161 
162         return rule
163 
164     def _create_license_rule(self, check_type: str, record: Record) -> dict[str, Any] | None:
165         details = record.vulnerability_details
166         if not details:
167             # this shouldn't happen
168             return None
169 
170         rule = {
171             "id": self._create_rule_id(check_type=check_type, record=record),
172             "name": record.short_description or record.check_name,
173             "shortDescription": {
174                 "text": record.short_description or record.check_name,
175             },
176             "fullDescription": {
177                 "text": f"Package {details['package_name']}@{details['package_version']} has license {details['license']}",
178             },
179             "help": {
180                 "text": f"{record.check_name}\nResource: {record.resource}",
181             },
182             "defaultConfiguration": {"level": "error"},
183         }
184 
185         # Adding 'properties' dictionary only if 'record.severity' exists
186         if record.severity:
187             rule["properties"] = {
188                 "security-severity": SEVERITY_TO_SCORE.get(record.severity.name.lower(), "0.0"),
189             }
190 
191         help_uri = record.guideline
192         if valid_url(help_uri):
193             rule["helpUri"] = help_uri
194 
195         return rule
196 
197     def _create_results(self) -> list[dict[str, Any]]:
198         results: "list[dict[str, Any]]" = []
199 
200         for report in self.reports:
201             for record in itertools.chain(report.failed_checks, report.skipped_checks):
202                 level = "warning"
203                 if record.severity:
204                     level = SEVERITY_TO_SARIF_LEVEL.get(record.severity.name.lower(), "none")
205                 elif record.check_result.get("result") == CheckResult.FAILED:
206                     level = "error"
207 
208                 rule_id = self._create_rule_id(check_type=report.check_type, record=record)
209                 if not rule_id or rule_id not in self.rule_index_map:
210                     # can happen if data is missing
211                     continue
212 
213                 result = {
214                     "ruleId": rule_id,
215                     "ruleIndex": self.rule_index_map[rule_id],
216                     "level": level,
217                     "attachments": [{"description": detail} for detail in record.details],
218                     "message": {
219                         "text": record.short_description or record.check_name,
220                     },
221                     "locations": [
222                         {
223                             "physicalLocation": {
224                                 "artifactLocation": {"uri": record.repo_file_path.lstrip("/")},
225                                 "region": {
226                                     "startLine": int(record.file_line_range[0]) or 1,
227                                     "endLine": int(record.file_line_range[1]) or 1,
228                                     "snippet": {"text": "".join(line for _, line in record.code_block)},
229                                 },
230                             }
231                         }
232                     ],
233                 }
234 
235                 if record.check_result.get("result") == CheckResult.SKIPPED:
236                     # sca_package suppression can only be enabled via flag
237                     # other runners only report in source suppression
238                     kind = "external" if record.vulnerability_details else "inSource"
239                     justification = record.check_result.get("suppress_comment")
240                     if justification is None:
241                         justification = "No comment provided"
242 
243                     result["suppressions"] = [
244                         {
245                             "kind": kind,
246                             "justification": justification,
247                         }
248                     ]
249 
250                 results.append(result)
251 
252         return results
253 
254     def _create_rule_id(self, check_type: str, record: Record) -> str | None:
255         if check_type in SCA_CHECKTYPES:
256             details = record.vulnerability_details
257             if not details:
258                 # this shouldn't happen
259                 return None
260 
261             if record.check_id.startswith("BC_LIC"):
262                 return f"{details['license']}_{details['package_name']}@{details['package_version']}".replace(" ", "_")
263             elif record.check_id.startswith(("BC_VUL", "CKV_CVE")):
264                 return f"{details['id']}_{details['package_name']}@{details['package_version']}".replace(" ", "_")
265         else:
266             return record.check_id
267 
268         return None
269 
270     def write_sarif_output(self) -> None:
271         try:
272             with open("results.sarif", "w") as f:
273                 f.write(json.dumps(self.json))
274                 print("\nWrote output in SARIF format to the file 'results.sarif'")
275         except EnvironmentError as e:
276             print("\nAn error occurred while writing SARIF results to file: results.sarif")
277             print(f"More details: \n {e}")
278
```
--- 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/output/sarif.py b/checkov/common/output/sarif.py
--- a/checkov/common/output/sarif.py
+++ b/checkov/common/output/sarif.py
@@ -3,6 +3,7 @@
 import itertools
 import json
 from typing import TYPE_CHECKING, Any
+from urllib.parse import quote
 
 from checkov.common.models.enums import CheckResult
 from checkov.common.output.cyclonedx_consts import SCA_CHECKTYPES
@@ -221,7 +222,7 @@
                     "locations": [
                         {
                             "physicalLocation": {
-                                "artifactLocation": {"uri": record.repo_file_path.lstrip("/")},
+                                "artifactLocation": {"uri": quote(record.repo_file_path.lstrip("/"))},
                                 "region": {
                                     "startLine": int(record.file_line_range[0]) or 1,
                                     "endLine": int(record.file_line_range[1]) or 1,

{"golden_diff": "diff --git a/checkov/common/output/sarif.py b/checkov/common/output/sarif.py\n--- a/checkov/common/output/sarif.py\n+++ b/checkov/common/output/sarif.py\n@@ -3,6 +3,7 @@\n import itertools\n import json\n from typing import TYPE_CHECKING, Any\n+from urllib.parse import quote\n \n from checkov.common.models.enums import CheckResult\n from checkov.common.output.cyclonedx_consts import SCA_CHECKTYPES\n@@ -221,7 +222,7 @@\n                     \"locations\": [\n                         {\n                             \"physicalLocation\": {\n-                                \"artifactLocation\": {\"uri\": record.repo_file_path.lstrip(\"/\")},\n+                                \"artifactLocation\": {\"uri\": quote(record.repo_file_path.lstrip(\"/\"))},\n                                 \"region\": {\n                                     \"startLine\": int(record.file_line_range[0]) or 1,\n                                     \"endLine\": int(record.file_line_range[1]) or 1,\n", "issue": "Sarif report creates invalid uri for folder with spaces\n**Describe the issue**\r\nSonarQube will not import SARIF report from Checkov correctly because of invalid URI in SARIF\r\n\r\n1) Scan folders with spaces that has some issues\r\nexample:\r\nSecrets/Access Tokens/Azure/main.tf \r\n\r\n2) Output result as sarif\r\n3) Resulting file is not valid SARIF due to invalid URI\r\n\r\nThe field Secrets/Access Tokens/Azure/main.tf corresponds to the results/locations/physicalLocation/artifactLocation/uri object in the SARIF report. There is character the space in the URI. This is not expected. The URI field shouldn\u2019t have any spaces.\r\nThis is against specification of URI, which forbids spaces in URIs.\r\n\r\n\r\nBecause of this problem , import of issues in directories with spaces will fail in SonarQube and possibly other tools\n", "before_files": [{"content": "from __future__ import annotations\n\nimport itertools\nimport json\nfrom typing import TYPE_CHECKING, Any\n\nfrom checkov.common.models.enums import CheckResult\nfrom checkov.common.output.cyclonedx_consts import SCA_CHECKTYPES\nfrom checkov.common.util.http_utils import valid_url\nfrom checkov.version import version\n\nif TYPE_CHECKING:\n    from checkov.common.output.record import Record\n    from checkov.common.output.report import Report\n\nSEVERITY_TO_SARIF_LEVEL = {\n    \"critical\": \"error\",\n    \"high\": \"error\",\n    \"medium\": \"warning\",\n    \"low\": \"note\",\n    \"none\": \"none\",\n}\n\n\nSEVERITY_TO_SCORE = {\n    \"critical\": \"10.0\",\n    \"high\": \"8.9\",\n    \"medium\": \"6.9\",\n    \"low\": \"3.9\",\n    \"none\": \"0.0\",\n}\n\n\nclass Sarif:\n    def __init__(self, reports: list[Report], tool: str | None) -> None:\n        self.reports = reports\n        self.rule_index_map: \"dict[str, int]\" = {}\n        self.tool = tool if tool else \"Bridgecrew\"\n\n        self.json = self.create_json()\n\n    def create_json(self) -> dict[str, Any]:\n        return {\n            \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n            \"version\": \"2.1.0\",\n            \"runs\": self._create_runs(),\n        }\n\n    def _create_runs(self) -> list[dict[str, Any]]:\n        information_uri = \"https://docs.bridgecrew.io\" if self.tool.lower() == \"bridgecrew\" else \"https://checkov.io\"\n        rules = self._create_rules()  # needs to be invoked before _create_results()\n        results = self._create_results()\n\n        return [\n            {\n                \"tool\": {\n                    \"driver\": {\n                        \"name\": self.tool,\n                        \"version\": version,\n                        \"informationUri\": information_uri,\n                        \"rules\": rules,\n                        \"organization\": \"bridgecrew\",\n                    }\n                },\n                \"results\": results,\n            }\n        ]\n\n    def _create_rules(self) -> list[dict[str, Any]]:\n        rule_idx = 0\n        rules: \"list[dict[str, Any]]\" = []\n\n        for report in self.reports:\n            if report.check_type in SCA_CHECKTYPES:\n                for record in itertools.chain(report.failed_checks, report.skipped_checks):\n                    rule = None\n                    if record.check_id.startswith(\"BC_LIC\"):\n                        rule = self._create_license_rule(check_type=report.check_type, record=record)\n                    elif record.check_id.startswith((\"BC_VUL\", \"CKV_CVE\")):\n                        rule = self._create_cve_rule(check_type=report.check_type, record=record)\n\n                    if rule and rule[\"id\"] not in self.rule_index_map:\n                        self.rule_index_map[rule[\"id\"]] = rule_idx\n                        rules.append(rule)\n                        rule_idx += 1\n            else:\n                for record in itertools.chain(report.failed_checks, report.skipped_checks):\n                    if record.check_id not in self.rule_index_map:\n                        rule = self._create_iac_rule(check_type=report.check_type, record=record)\n                        self.rule_index_map[rule[\"id\"]] = rule_idx\n                        rules.append(rule)\n                        rule_idx += 1\n\n        return rules\n\n    def _create_iac_rule(self, check_type: str, record: Record) -> dict[str, Any]:\n        rule = {\n            \"id\": self._create_rule_id(check_type=check_type, record=record),\n            \"name\": record.short_description or record.check_name,\n            \"shortDescription\": {\n                \"text\": record.short_description or record.check_name,\n            },\n            \"fullDescription\": {\n                \"text\": record.description or record.check_name,\n            },\n            \"help\": {\n                \"text\": f\"{record.check_name}\\nResource: {record.resource}\",\n            },\n            \"defaultConfiguration\": {\"level\": \"error\"},\n        }\n\n        # Adding 'properties' dictionary only if 'record.severity' exists\n        if record.severity:\n            rule[\"properties\"] = {\n                \"security-severity\": SEVERITY_TO_SCORE.get(record.severity.name.lower(), \"0.0\"),\n            }\n\n        help_uri = record.guideline\n        if valid_url(help_uri):\n            rule[\"helpUri\"] = help_uri\n\n        return rule\n\n    def _create_cve_rule(self, check_type: str, record: Record) -> dict[str, Any] | None:\n        details = record.vulnerability_details\n        if not details:\n            # this shouldn't happen\n            return None\n\n        rule = {\n            \"id\": self._create_rule_id(check_type=check_type, record=record),\n            \"name\": record.short_description or record.check_name,\n            \"shortDescription\": {\n                \"text\": record.short_description or record.check_name,\n            },\n            \"fullDescription\": {\n                \"text\": record.description or record.check_name,\n            },\n            \"help\": {\n                \"text\": f\"{record.check_name}\\nResource: {record.resource}\\nStatus: {details.get('status')}\",\n            },\n            \"defaultConfiguration\": {\"level\": \"error\"},\n        }\n\n        # Add properties dictionary with security-severity\n        cvss = details.get(\"cvss\")\n        if cvss:\n            # use CVSS, if exists\n            rule[\"properties\"] = {\n                \"security-severity\": str(cvss),\n            }\n        elif record.severity:\n            # otherwise severity, if exists\n            rule[\"properties\"] = {\n                \"security-severity\": SEVERITY_TO_SCORE.get(record.severity.name.lower(), \"0.0\"),\n            }\n\n        help_uri = details.get(\"link\")\n        if valid_url(help_uri):\n            rule[\"helpUri\"] = help_uri\n\n        return rule\n\n    def _create_license_rule(self, check_type: str, record: Record) -> dict[str, Any] | None:\n        details = record.vulnerability_details\n        if not details:\n            # this shouldn't happen\n            return None\n\n        rule = {\n            \"id\": self._create_rule_id(check_type=check_type, record=record),\n            \"name\": record.short_description or record.check_name,\n            \"shortDescription\": {\n                \"text\": record.short_description or record.check_name,\n            },\n            \"fullDescription\": {\n                \"text\": f\"Package {details['package_name']}@{details['package_version']} has license {details['license']}\",\n            },\n            \"help\": {\n                \"text\": f\"{record.check_name}\\nResource: {record.resource}\",\n            },\n            \"defaultConfiguration\": {\"level\": \"error\"},\n        }\n\n        # Adding 'properties' dictionary only if 'record.severity' exists\n        if record.severity:\n            rule[\"properties\"] = {\n                \"security-severity\": SEVERITY_TO_SCORE.get(record.severity.name.lower(), \"0.0\"),\n            }\n\n        help_uri = record.guideline\n        if valid_url(help_uri):\n            rule[\"helpUri\"] = help_uri\n\n        return rule\n\n    def _create_results(self) -> list[dict[str, Any]]:\n        results: \"list[dict[str, Any]]\" = []\n\n        for report in self.reports:\n            for record in itertools.chain(report.failed_checks, report.skipped_checks):\n                level = \"warning\"\n                if record.severity:\n                    level = SEVERITY_TO_SARIF_LEVEL.get(record.severity.name.lower(), \"none\")\n                elif record.check_result.get(\"result\") == CheckResult.FAILED:\n                    level = \"error\"\n\n                rule_id = self._create_rule_id(check_type=report.check_type, record=record)\n                if not rule_id or rule_id not in self.rule_index_map:\n                    # can happen if data is missing\n                    continue\n\n                result = {\n                    \"ruleId\": rule_id,\n                    \"ruleIndex\": self.rule_index_map[rule_id],\n                    \"level\": level,\n                    \"attachments\": [{\"description\": detail} for detail in record.details],\n                    \"message\": {\n                        \"text\": record.short_description or record.check_name,\n                    },\n                    \"locations\": [\n                        {\n                            \"physicalLocation\": {\n                                \"artifactLocation\": {\"uri\": record.repo_file_path.lstrip(\"/\")},\n                                \"region\": {\n                                    \"startLine\": int(record.file_line_range[0]) or 1,\n                                    \"endLine\": int(record.file_line_range[1]) or 1,\n                                    \"snippet\": {\"text\": \"\".join(line for _, line in record.code_block)},\n                                },\n                            }\n                        }\n                    ],\n                }\n\n                if record.check_result.get(\"result\") == CheckResult.SKIPPED:\n                    # sca_package suppression can only be enabled via flag\n                    # other runners only report in source suppression\n                    kind = \"external\" if record.vulnerability_details else \"inSource\"\n                    justification = record.check_result.get(\"suppress_comment\")\n                    if justification is None:\n                        justification = \"No comment provided\"\n\n                    result[\"suppressions\"] = [\n                        {\n                            \"kind\": kind,\n                            \"justification\": justification,\n                        }\n                    ]\n\n                results.append(result)\n\n        return results\n\n    def _create_rule_id(self, check_type: str, record: Record) -> str | None:\n        if check_type in SCA_CHECKTYPES:\n            details = record.vulnerability_details\n            if not details:\n                # this shouldn't happen\n                return None\n\n            if record.check_id.startswith(\"BC_LIC\"):\n                return f\"{details['license']}_{details['package_name']}@{details['package_version']}\".replace(\" \", \"_\")\n            elif record.check_id.startswith((\"BC_VUL\", \"CKV_CVE\")):\n                return f\"{details['id']}_{details['package_name']}@{details['package_version']}\".replace(\" \", \"_\")\n        else:\n            return record.check_id\n\n        return None\n\n    def write_sarif_output(self) -> None:\n        try:\n            with open(\"results.sarif\", \"w\") as f:\n                f.write(json.dumps(self.json))\n                print(\"\\nWrote output in SARIF format to the file 'results.sarif'\")\n        except EnvironmentError as e:\n            print(\"\\nAn error occurred while writing SARIF results to file: results.sarif\")\n            print(f\"More details: \\n {e}\")\n", "path": "checkov/common/output/sarif.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport itertools\nimport json\nfrom typing import TYPE_CHECKING, Any\nfrom urllib.parse import quote\n\nfrom checkov.common.models.enums import CheckResult\nfrom checkov.common.output.cyclonedx_consts import SCA_CHECKTYPES\nfrom checkov.common.util.http_utils import valid_url\nfrom checkov.version import version\n\nif TYPE_CHECKING:\n    from checkov.common.output.record import Record\n    from checkov.common.output.report import Report\n\nSEVERITY_TO_SARIF_LEVEL = {\n    \"critical\": \"error\",\n    \"high\": \"error\",\n    \"medium\": \"warning\",\n    \"low\": \"note\",\n    \"none\": \"none\",\n}\n\n\nSEVERITY_TO_SCORE = {\n    \"critical\": \"10.0\",\n    \"high\": \"8.9\",\n    \"medium\": \"6.9\",\n    \"low\": \"3.9\",\n    \"none\": \"0.0\",\n}\n\n\nclass Sarif:\n    def __init__(self, reports: list[Report], tool: str | None) -> None:\n        self.reports = reports\n        self.rule_index_map: \"dict[str, int]\" = {}\n        self.tool = tool if tool else \"Bridgecrew\"\n\n        self.json = self.create_json()\n\n    def create_json(self) -> dict[str, Any]:\n        return {\n            \"$schema\": \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n            \"version\": \"2.1.0\",\n            \"runs\": self._create_runs(),\n        }\n\n    def _create_runs(self) -> list[dict[str, Any]]:\n        information_uri = \"https://docs.bridgecrew.io\" if self.tool.lower() == \"bridgecrew\" else \"https://checkov.io\"\n        rules = self._create_rules()  # needs to be invoked before _create_results()\n        results = self._create_results()\n\n        return [\n            {\n                \"tool\": {\n                    \"driver\": {\n                        \"name\": self.tool,\n                        \"version\": version,\n                        \"informationUri\": information_uri,\n                        \"rules\": rules,\n                        \"organization\": \"bridgecrew\",\n                    }\n                },\n                \"results\": results,\n            }\n        ]\n\n    def _create_rules(self) -> list[dict[str, Any]]:\n        rule_idx = 0\n        rules: \"list[dict[str, Any]]\" = []\n\n        for report in self.reports:\n            if report.check_type in SCA_CHECKTYPES:\n                for record in itertools.chain(report.failed_checks, report.skipped_checks):\n                    rule = None\n                    if record.check_id.startswith(\"BC_LIC\"):\n                        rule = self._create_license_rule(check_type=report.check_type, record=record)\n                    elif record.check_id.startswith((\"BC_VUL\", \"CKV_CVE\")):\n                        rule = self._create_cve_rule(check_type=report.check_type, record=record)\n\n                    if rule and rule[\"id\"] not in self.rule_index_map:\n                        self.rule_index_map[rule[\"id\"]] = rule_idx\n                        rules.append(rule)\n                        rule_idx += 1\n            else:\n                for record in itertools.chain(report.failed_checks, report.skipped_checks):\n                    if record.check_id not in self.rule_index_map:\n                        rule = self._create_iac_rule(check_type=report.check_type, record=record)\n                        self.rule_index_map[rule[\"id\"]] = rule_idx\n                        rules.append(rule)\n                        rule_idx += 1\n\n        return rules\n\n    def _create_iac_rule(self, check_type: str, record: Record) -> dict[str, Any]:\n        rule = {\n            \"id\": self._create_rule_id(check_type=check_type, record=record),\n            \"name\": record.short_description or record.check_name,\n            \"shortDescription\": {\n                \"text\": record.short_description or record.check_name,\n            },\n            \"fullDescription\": {\n                \"text\": record.description or record.check_name,\n            },\n            \"help\": {\n                \"text\": f\"{record.check_name}\\nResource: {record.resource}\",\n            },\n            \"defaultConfiguration\": {\"level\": \"error\"},\n        }\n\n        # Adding 'properties' dictionary only if 'record.severity' exists\n        if record.severity:\n            rule[\"properties\"] = {\n                \"security-severity\": SEVERITY_TO_SCORE.get(record.severity.name.lower(), \"0.0\"),\n            }\n\n        help_uri = record.guideline\n        if valid_url(help_uri):\n            rule[\"helpUri\"] = help_uri\n\n        return rule\n\n    def _create_cve_rule(self, check_type: str, record: Record) -> dict[str, Any] | None:\n        details = record.vulnerability_details\n        if not details:\n            # this shouldn't happen\n            return None\n\n        rule = {\n            \"id\": self._create_rule_id(check_type=check_type, record=record),\n            \"name\": record.short_description or record.check_name,\n            \"shortDescription\": {\n                \"text\": record.short_description or record.check_name,\n            },\n            \"fullDescription\": {\n                \"text\": record.description or record.check_name,\n            },\n            \"help\": {\n                \"text\": f\"{record.check_name}\\nResource: {record.resource}\\nStatus: {details.get('status')}\",\n            },\n            \"defaultConfiguration\": {\"level\": \"error\"},\n        }\n\n        # Add properties dictionary with security-severity\n        cvss = details.get(\"cvss\")\n        if cvss:\n            # use CVSS, if exists\n            rule[\"properties\"] = {\n                \"security-severity\": str(cvss),\n            }\n        elif record.severity:\n            # otherwise severity, if exists\n            rule[\"properties\"] = {\n                \"security-severity\": SEVERITY_TO_SCORE.get(record.severity.name.lower(), \"0.0\"),\n            }\n\n        help_uri = details.get(\"link\")\n        if valid_url(help_uri):\n            rule[\"helpUri\"] = help_uri\n\n        return rule\n\n    def _create_license_rule(self, check_type: str, record: Record) -> dict[str, Any] | None:\n        details = record.vulnerability_details\n        if not details:\n            # this shouldn't happen\n            return None\n\n        rule = {\n            \"id\": self._create_rule_id(check_type=check_type, record=record),\n            \"name\": record.short_description or record.check_name,\n            \"shortDescription\": {\n                \"text\": record.short_description or record.check_name,\n            },\n            \"fullDescription\": {\n                \"text\": f\"Package {details['package_name']}@{details['package_version']} has license {details['license']}\",\n            },\n            \"help\": {\n                \"text\": f\"{record.check_name}\\nResource: {record.resource}\",\n            },\n            \"defaultConfiguration\": {\"level\": \"error\"},\n        }\n\n        # Adding 'properties' dictionary only if 'record.severity' exists\n        if record.severity:\n            rule[\"properties\"] = {\n                \"security-severity\": SEVERITY_TO_SCORE.get(record.severity.name.lower(), \"0.0\"),\n            }\n\n        help_uri = record.guideline\n        if valid_url(help_uri):\n            rule[\"helpUri\"] = help_uri\n\n        return rule\n\n    def _create_results(self) -> list[dict[str, Any]]:\n        results: \"list[dict[str, Any]]\" = []\n\n        for report in self.reports:\n            for record in itertools.chain(report.failed_checks, report.skipped_checks):\n                level = \"warning\"\n                if record.severity:\n                    level = SEVERITY_TO_SARIF_LEVEL.get(record.severity.name.lower(), \"none\")\n                elif record.check_result.get(\"result\") == CheckResult.FAILED:\n                    level = \"error\"\n\n                rule_id = self._create_rule_id(check_type=report.check_type, record=record)\n                if not rule_id or rule_id not in self.rule_index_map:\n                    # can happen if data is missing\n                    continue\n\n                result = {\n                    \"ruleId\": rule_id,\n                    \"ruleIndex\": self.rule_index_map[rule_id],\n                    \"level\": level,\n                    \"attachments\": [{\"description\": detail} for detail in record.details],\n                    \"message\": {\n                        \"text\": record.short_description or record.check_name,\n                    },\n                    \"locations\": [\n                        {\n                            \"physicalLocation\": {\n                                \"artifactLocation\": {\"uri\": quote(record.repo_file_path.lstrip(\"/\"))},\n                                \"region\": {\n                                    \"startLine\": int(record.file_line_range[0]) or 1,\n                                    \"endLine\": int(record.file_line_range[1]) or 1,\n                                    \"snippet\": {\"text\": \"\".join(line for _, line in record.code_block)},\n                                },\n                            }\n                        }\n                    ],\n                }\n\n                if record.check_result.get(\"result\") == CheckResult.SKIPPED:\n                    # sca_package suppression can only be enabled via flag\n                    # other runners only report in source suppression\n                    kind = \"external\" if record.vulnerability_details else \"inSource\"\n                    justification = record.check_result.get(\"suppress_comment\")\n                    if justification is None:\n                        justification = \"No comment provided\"\n\n                    result[\"suppressions\"] = [\n                        {\n                            \"kind\": kind,\n                            \"justification\": justification,\n                        }\n                    ]\n\n                results.append(result)\n\n        return results\n\n    def _create_rule_id(self, check_type: str, record: Record) -> str | None:\n        if check_type in SCA_CHECKTYPES:\n            details = record.vulnerability_details\n            if not details:\n                # this shouldn't happen\n                return None\n\n            if record.check_id.startswith(\"BC_LIC\"):\n                return f\"{details['license']}_{details['package_name']}@{details['package_version']}\".replace(\" \", \"_\")\n            elif record.check_id.startswith((\"BC_VUL\", \"CKV_CVE\")):\n                return f\"{details['id']}_{details['package_name']}@{details['package_version']}\".replace(\" \", \"_\")\n        else:\n            return record.check_id\n\n        return None\n\n    def write_sarif_output(self) -> None:\n        try:\n            with open(\"results.sarif\", \"w\") as f:\n                f.write(json.dumps(self.json))\n                print(\"\\nWrote output in SARIF format to the file 'results.sarif'\")\n        except EnvironmentError as e:\n            print(\"\\nAn error occurred while writing SARIF results to file: results.sarif\")\n            print(f\"More details: \\n {e}\")\n", "path": "checkov/common/output/sarif.py"}]}