Repository: bridgecrewio/checkov

Issue:
Checks suppressions are unsupported in the Ansible runner 
if you attempt to suppress a check like:
```
---
- name: Verify tests
  hosts: all
  gather_facts: False
  tasks:
    - name: default
      amazon.aws.ec2_instance:
        name: "public-compute-instance"
        key_name: "prod-ssh-key"
        vpc_subnet_id: subnet-5ca1ab1e
        instance_type: c5.large
        security_group: default
        network:
          assign_public_ip: true
        image_id: ami-123456
    - name: disabled
      ec2_instance:
        name: "public-compute-instance"
        key_name: "prod-ssh-key"
        vpc_subnet_id: subnet-5ca1ab1e
        instance_type: c5.large
        security_group: default
        network:
          assign_public_ip: false
        image_id: ami-123456
        #checkov:skip=CKV_AWS_135
        ebs_optimized: false
```

Then checkov (2.3.28) crashes:
```
checkov -d .
[ kubernetes framework ]: 100%|████████████████████|[1/1], Current File Scanned=
[ ansible framework ]:   0%|                    |[0/1], Current File Scanned=tes2023-02-20 14:07:00,506 [MainThread  ] [ERROR]  Failed to invoke function /Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/common/runners/runner_registry._parallel_run with <checkov.ansible.runner.Runner object at 0x10be09780>
Traceback (most recent call last):
  File "/Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/common/parallelizer/parallel_runner.py", line 39, in func_wrapper
    result = original_func(item)
  File "/Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/common/runners/runner_registry.py", line 103, in _parallel_run
    report = runner.run(
  File "/Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/common/runners/object_runner.py", line 150, in run
    self.add_python_check_results(report=report, registry=registry, runner_filter=runner_filter, root_folder=root_folder)
  File "/Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/common/runners/object_runner.py", line 169, in add_python_check_results
    results = registry.scan(file_path, self.definitions[file_path], skipped_checks, runner_filter)  # type:ignore[arg-type] # this is overridden in the subclass
  File "/Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/yaml_doc/base_registry.py", line 179, in scan
    self._scan_yaml(
  File "/Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/yaml_doc/base_registry.py", line 127, in _scan_yaml
    skip_info = ([x for x in skipped_checks if (x["id"] == check.id and entity[STARTLINE_MARK] <= x['line_number'] <= entity[ENDLINE_MARK])] or [{}])[0]
  File "/Users/jwoolfenden/Library/Python/3.10/lib/python/site-packages/checkov/yaml_doc/base_registry.py", line 127, in <listcomp>
    skip_info = ([x for x in skipped_checks if (x["id"] == check.id and entity[STARTLINE_MARK] <= x['line_number'] <= entity[ENDLINE_MARK])] or [{}])[0]
TypeError: list indices must be integers or slices, not str
[ secrets framework ]: 100%|████████████████████|[1/1], Current File Scanned=./t

       _               _
   ___| |__   ___  ___| | _______   __
  / __| '_ \ / _ \/ __| |/ / _ \ \ / /
 | (__| | | |  __/ (__|   < (_) \ V /
  \___|_| |_|\___|\___|_|\_\___/ \_/

By bridgecrew.io | version: 2.3.28
```




Discussion:
This doesn't allow for supression:

`CKV2_ANSIBLE_1] Ensure that HTTPS url is used with uri`

Does anyone know how to get this working, unless I’m missing something obvious here ? a colleague is thinking something about `CKV2` as well ?

```
- name: do something
  # checkov:skip=CKV2_ANSIBLE_1: development ongoing
  uri:
```



Resolving patch:
diff --git a/checkov/common/checks/object_registry.py b/checkov/common/checks/object_registry.py
--- a/checkov/common/checks/object_registry.py
+++ b/checkov/common/checks/object_registry.py
@@ -24,7 +24,7 @@ def _scan_array(
         self,
         scanned_file: str,
         check: BaseCheck,
-        skip_info: _SkippedCheck,
+        skip_info: list[_SkippedCheck],
         entity: dict[str, Any],
         entity_name: str,
         entity_type: str,
@@ -41,7 +41,7 @@ def _scan_array(
                         entity_type,
                         results,
                         scanned_file,
-                        skip_info,
+                        skip_info[0],
                     )
         if isinstance(entity, list):
             for item in entity:
@@ -53,7 +53,7 @@ def _scan_array(
                         entity_type,
                         results,
                         scanned_file,
-                        skip_info,
+                        skip_info[0],
                     )
                     if result == CheckResult.FAILED:
                         break
@@ -62,7 +62,7 @@ def _scan_object(
         self,
         scanned_file: str,
         check: BaseCheck,
-        skip_info: _SkippedCheck,
+        skip_info: list[_SkippedCheck],
         entity: dict[str, Any],
         entity_name: str,
         entity_type: str,
@@ -76,21 +76,21 @@ def _scan_object(
                 entity_type,
                 results,
                 scanned_file,
-                skip_info,
+                skip_info[0],
             )
 
     def _scan_document(
         self,
         scanned_file: str,
         check: BaseCheck,
-        skip_info: _SkippedCheck,
+        skip_info: list[_SkippedCheck],
         entity: dict[str, Any],
         entity_name: str,
         entity_type: str,
         results: dict[str, Any],
     ) -> None:
         self.update_result(
-            check, entity, entity_name, entity_type, results, scanned_file, skip_info
+            check, entity, entity_name, entity_type, results, scanned_file, skip_info[0]
         )
 
     def _scan(
@@ -105,7 +105,7 @@ def _scan(
         results: dict[str, Any],
     ) -> None:
         for check in checks:
-            skip_info = ([x for x in skipped_checks if x["id"] == check.id] or [{}])[0]
+            skip_infos = ([x for x in skipped_checks if x["id"] == check.id] or [{}])
 
             if runner_filter.should_run_check(check=check, report_type=self.report_type):
                 scanner = self._scanner.get(check.block_type, self._scan_document)
@@ -124,7 +124,7 @@ def _scan(
                 scanner(
                     scanned_file,
                     check,
-                    skip_info,
+                    skip_infos,
                     target,
                     entity_name,
                     entity_type,
diff --git a/checkov/common/typing.py b/checkov/common/typing.py
--- a/checkov/common/typing.py
+++ b/checkov/common/typing.py
@@ -16,7 +16,7 @@
 _BaseRunner = TypeVar("_BaseRunner", bound="BaseRunner[Any]")
 
 _ScannerCallableAlias: TypeAlias = Callable[
-    [str, "BaseCheck", "_SkippedCheck", "dict[str, Any]", str, str, "dict[str, Any]"], None
+    [str, "BaseCheck", "list[_SkippedCheck]", "dict[str, Any]", str, str, "dict[str, Any]"], None
 ]
 
 _Resource: TypeAlias = str
diff --git a/checkov/yaml_doc/base_registry.py b/checkov/yaml_doc/base_registry.py
--- a/checkov/yaml_doc/base_registry.py
+++ b/checkov/yaml_doc/base_registry.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import logging
 from typing import Any, Dict, List, cast
 
 from checkov.common.checks.base_check import BaseCheck
@@ -24,9 +25,14 @@ def __init__(self, report_type: str) -> None:
         }
 
     def _scan_yaml_array(
-            self, scanned_file: str, check: BaseCheck, skip_info: _SkippedCheck, entity: Dict[str, Any],
-            entity_name: str,
-            entity_type: str, results: Dict[str, Any]
+        self,
+        scanned_file: str,
+        check: BaseCheck,
+        skip_infos: list[_SkippedCheck],
+        entity: Dict[str, Any],
+        entity_name: str,
+        entity_type: str,
+        results: Dict[str, Any],
     ) -> None:
         if isinstance(entity, dict):
             analyzed_entities = jmespath.search(entity_type, entity)
@@ -40,13 +46,18 @@ def _scan_yaml_array(
                             entity_type=entity_type,
                             results=results,
                             scanned_file=scanned_file,
-                            skip_info=skip_info,
+                            skip_info=skip_infos[0],
                         )
             if isinstance(analyzed_entities, list):
                 for item in analyzed_entities:
                     if isinstance(item, str):
                         item = self.set_lines_for_item(item)
                     if STARTLINE_MARK != item and ENDLINE_MARK != item:
+                        skip_info: "_SkippedCheck" = {}
+                        if skip_infos and skip_infos[0]:
+                            # multiple items could be found, so we need to skip the correct one(s)
+                            skip_info = ([skip for skip in skip_infos if item[STARTLINE_MARK] <= skip["line_number"] <= item[ENDLINE_MARK]] or [{}])[0]
+
                         self.update_result(
                             check,
                             item,
@@ -63,6 +74,11 @@ def _scan_yaml_array(
                     if isinstance(item, str):
                         item = self.set_lines_for_item(item)
                     if STARTLINE_MARK != item and ENDLINE_MARK != item:
+                        skip_info = {}
+                        if skip_infos and skip_infos[0]:
+                            # multiple items could be found, so we need to skip the correct one(s)
+                            skip_info = ([skip for skip in skip_infos if item[STARTLINE_MARK] <= skip["line_number"] <= item[ENDLINE_MARK]] or [{}])[0]
+
                         self.update_result(
                             check,
                             item,
@@ -82,15 +98,20 @@ def _scan_yaml_array(
                             entity_type,
                             results,
                             scanned_file,
-                            skip_info
+                            skip_infos[0]
                         )
                         if result == CheckResult.FAILED:
                             break
 
     def _scan_yaml_object(
-            self, scanned_file: str, check: BaseCheck, skip_info: _SkippedCheck, entity: Dict[str, Any],
-            entity_name: str,
-            entity_type: str, results: Dict[str, Any]
+        self,
+        scanned_file: str,
+        check: BaseCheck,
+        skip_infos: list[_SkippedCheck],
+        entity: Dict[str, Any],
+        entity_name: str,
+        entity_type: str,
+        results: Dict[str, Any],
     ) -> None:
         if entity_name in entity:
             self.update_result(
@@ -100,16 +121,27 @@ def _scan_yaml_object(
                 entity_type,
                 results,
                 scanned_file,
-                skip_info
+                skip_infos[0]
             )
 
     def _scan_yaml_document(
-            self, scanned_file: str, check: BaseCheck, skip_info: _SkippedCheck, entity: Dict[str, Any],
-            entity_name: str,
-            entity_type: str, results: Dict[str, Any]
+        self,
+        scanned_file: str,
+        check: BaseCheck,
+        skip_info: list[_SkippedCheck],
+        entity: Dict[str, Any],
+        entity_name: str,
+        entity_type: str,
+        results: Dict[str, Any],
     ) -> None:
         self.update_result(
-            check, entity, entity_name, entity_type, results, scanned_file, skip_info
+            check,
+            entity,
+            entity_name,
+            entity_type,
+            results,
+            scanned_file,
+            skip_info[0]
         )
 
     def _scan_yaml(
@@ -124,7 +156,22 @@ def _scan_yaml(
             results: Dict[str, Any],
     ) -> None:
         for check in checks:
-            skip_info = ([x for x in skipped_checks if (x["id"] == check.id and entity[STARTLINE_MARK] <= x['line_number'] <= entity[ENDLINE_MARK])] or [{}])[0]
+            skip_infos: "list[_SkippedCheck]" = [{}]
+            if isinstance(entity, dict):
+                skip_infos = [
+                    skip
+                    for skip in skipped_checks
+                    if skip["id"] == check.id and entity[STARTLINE_MARK] <= skip["line_number"] <= entity[ENDLINE_MARK]
+                ] or [{}]
+            elif isinstance(entity, list):
+                skip_infos = [
+                    skip
+                    for skip in skipped_checks
+                    for e in entity
+                    if skip["id"] == check.id and e[STARTLINE_MARK] <= skip['line_number'] <= e[ENDLINE_MARK]
+                ] or [{}]
+            else:
+                logging.info(f"Unexpected entity type {type(entity)} for {entity}")
 
             if runner_filter.should_run_check(check=check, report_type=self.report_type):
                 scanner = self._scanner.get(check.block_type, self._scan_yaml_document)
@@ -143,7 +190,7 @@ def _scan_yaml(
                 scanner(
                     scanned_file,
                     check,
-                    skip_info,
+                    skip_infos,
                     target,
                     entity_name,
                     entity_type,
@@ -221,7 +268,7 @@ def update_result(
                 "check": check,
                 "result": result,
                 "suppress_comment": check_result["suppress_comment"],
-                "results_configuration": None,
+                "results_configuration": entity_configuration,
             }
             return result
 


Test patch:
diff --git a/tests/ansible/examples/skip.yml b/tests/ansible/examples/skip.yml
new file mode 100644
--- /dev/null
+++ b/tests/ansible/examples/skip.yml
@@ -0,0 +1,22 @@
+- hosts: localhost
+  gather_facts: false
+  tasks:
+    - name: Launch ec2 instances 1
+      #checkov:skip=CKV_AWS_135
+      amazon.aws.ec2_instance:
+        name: "bc-office-hours"
+        vpc_subnet_id: subnet-012d94ee641ab4277
+        instance_type: t3.micro
+        security_group: sg-04acc4e02a5b71244
+        image_id: "{{ ami_latest.image_id }}"
+        state: running
+
+    - name: Launch ec2 instances 2
+      amazon.aws.ec2_instance:
+        #checkov:skip=CKV_AWS_88
+        name: "bc-office-hours"
+        vpc_subnet_id: subnet-012d94ee641ab4277
+        instance_type: t3.micro
+        security_group: sg-04acc4e02a5b71244
+        image_id: "{{ ami_latest.image_id }}"
+        state: running
diff --git a/tests/ansible/test_runner.py b/tests/ansible/test_runner.py
--- a/tests/ansible/test_runner.py
+++ b/tests/ansible/test_runner.py
@@ -94,6 +94,38 @@ def test_runner_failing_check(graph_connector):
     assert summary["parsing_errors"] == 0
 
 
+@pytest.mark.parametrize(
+    "graph_connector",
+    [
+        NetworkxConnector,
+        IgraphConnector,
+    ],
+)
+def test_runner_skipping_check(graph_connector):
+    # given
+    test_file = EXAMPLES_DIR / "skip.yml"
+
+    # when
+    report = Runner(db_connector=graph_connector()).run(
+        root_folder="", files=[str(test_file)], runner_filter=RunnerFilter(checks=["CKV_AWS_88", "CKV_AWS_135"])
+    )
+
+    # then
+    summary = report.get_summary()
+
+    assert summary["passed"] == 0
+    assert summary["failed"] == 2
+    assert summary["skipped"] == 2
+    assert summary["parsing_errors"] == 0
+
+    assert {check.check_id for check in report.skipped_checks} == {"CKV_AWS_88", "CKV_AWS_135"}
+
+    check_88 = next(check for check in report.skipped_checks if check.check_id == "CKV_AWS_88")
+    check_135 = next(check for check in report.skipped_checks if check.check_id == "CKV_AWS_135")
+    assert check_88.resource == "tasks.amazon.aws.ec2_instance.Launch ec2 instances 2"
+    assert check_135.resource == "tasks.amazon.aws.ec2_instance.Launch ec2 instances 1"
+
+
 @pytest.mark.parametrize(
     "graph_connector",
     [