Repository: bridgecrewio/checkov

Issue:
Add check for OpenAPI for schemes using http
**Describe the issue**
HTTP is unsafe to use for endpoints

**Examples**
```json
...
  "paths": {
    "/": {
      "get": {
        "schemes": [
          "http"
        ],
        ...
      }
    }
  }
...
```


Discussion:

Resolving patch:
diff --git a/checkov/openapi/checks/resource/v2/PathSchemeDefineHTTP.py b/checkov/openapi/checks/resource/v2/PathSchemeDefineHTTP.py
new file mode 100644
--- /dev/null
+++ b/checkov/openapi/checks/resource/v2/PathSchemeDefineHTTP.py
@@ -0,0 +1,40 @@
+from __future__ import annotations
+
+from typing import Any
+from checkov.common.models.enums import CheckResult, CheckCategories
+from checkov.common.checks.enums import BlockType
+from checkov.openapi.checks.resource.v2.BaseOpenapiCheckV2 import BaseOpenapiCheckV2
+
+
+class PathSchemeDefineHTTP(BaseOpenapiCheckV2):
+    def __init__(self) -> None:
+        # https://learning.postman.com/docs/api-governance/api-definition/openapi2/#schemes-of-the-operation-have-http-scheme-defined
+        id = "CKV_OPENAPI_7"
+        name = "Ensure that the path scheme does not support unencrypted HTTP connection where all transmissions " \
+               "are open to interception- version 2.0 files"
+        categories = [CheckCategories.API_SECURITY]
+        supported_resources = ['security']
+        super().__init__(name=name, id=id, categories=categories, supported_entities=supported_resources,
+                         block_type=BlockType.DOCUMENT)
+
+    def scan_openapi_conf(self, conf: dict[str, Any], entity_type: str) -> tuple[CheckResult, dict[str, Any]]:
+        paths = conf.get("paths", {})
+        if not paths or not isinstance(paths, dict):
+            return CheckResult.UNKNOWN, conf
+
+        for path, http_method in paths.items():
+            if self.is_start_end_line(path):
+                continue
+            for op_name, op_val in http_method.items():
+                if self.is_start_end_line(op_name):
+                    continue
+                schemes = op_val.get('schemes')
+                if schemes and 'http' in schemes:
+                    return CheckResult.FAILED, conf
+            # If the schemes is not included, the default scheme to be used is the one used to access the Swagger
+            # definition itself, in which case the current check is not relevant.
+
+        return CheckResult.PASSED, conf
+
+
+check = PathSchemeDefineHTTP()


Test patch:
diff --git a/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/fail.json b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/fail.json
new file mode 100644
--- /dev/null
+++ b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/fail.json
@@ -0,0 +1,23 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "title": "test_fail",
+    "version": "1.0.0"
+  },
+  "paths": {
+    "/": {
+      "get": {
+        "operationId": "example",
+        "summary": "example",
+        "schemes": [
+          "http"
+        ],
+        "responses": {
+          "200": {
+            "description": "200 response"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/fail.yaml b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/fail.yaml
new file mode 100644
--- /dev/null
+++ b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/fail.yaml
@@ -0,0 +1,14 @@
+swagger: "2.0"
+info:
+  title: test_fail
+  version: 1.0.0
+paths:
+  "/":
+    get:
+      operationId: example
+      summary: example
+      schemes:
+        - http
+      responses:
+        "200":
+          description: 200 response
\ No newline at end of file
diff --git a/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/pass.json b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/pass.json
new file mode 100644
--- /dev/null
+++ b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/pass.json
@@ -0,0 +1,23 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "title": "test_pass",
+    "version": "1.0.0"
+  },
+  "paths": {
+    "/": {
+      "get": {
+        "operationId": "example",
+        "summary": "example",
+        "schemes": [
+          "https"
+        ],
+        "responses": {
+          "200": {
+            "description": "200 response"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/pass.yaml b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/pass.yaml
new file mode 100644
--- /dev/null
+++ b/tests/openapi/checks/resource/v2/example_PathSchemeDefineHTTP/pass.yaml
@@ -0,0 +1,14 @@
+swagger: "2.0"
+info:
+  title: test_pass
+  version: 1.0.0
+paths:
+  "/":
+    get:
+      operationId: example
+      summary: example
+      schemes:
+        - https
+      responses:
+        "200":
+          description: 200 response
\ No newline at end of file
diff --git a/tests/openapi/checks/resource/v2/test_PathSchemeDefineHTTP.py b/tests/openapi/checks/resource/v2/test_PathSchemeDefineHTTP.py
new file mode 100644
--- /dev/null
+++ b/tests/openapi/checks/resource/v2/test_PathSchemeDefineHTTP.py
@@ -0,0 +1,43 @@
+import os
+import unittest
+
+from checkov.openapi.checks.resource.v2.PathSchemeDefineHTTP import check
+from checkov.openapi.runner import Runner
+from checkov.runner_filter import RunnerFilter
+
+
+class TestOauth2SecurityRequirement(unittest.TestCase):
+    def test_summary(self):
+        # given
+        current_dir = os.path.dirname(os.path.realpath(__file__))
+        test_files_dir = current_dir + "/example_PathSchemeDefineHTTP"
+
+        # when
+        report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id]))
+
+        # then
+        summary = report.get_summary()
+
+        passing_resources = {
+            "/pass.yaml",
+            "/pass.json",
+        }
+        failing_resources = {
+            "/fail.yaml",
+            "/fail.json",
+        }
+
+        passed_check_resources = {c.file_path for c in report.passed_checks}
+        failed_check_resources = {c.file_path for c in report.failed_checks}
+
+        self.assertEqual(summary["passed"], len(passing_resources))
+        self.assertEqual(summary["failed"], len(failing_resources))
+        self.assertEqual(summary["skipped"], 0)
+        self.assertEqual(summary["parsing_errors"], 0)
+
+        self.assertEqual(passing_resources, passed_check_resources)
+        self.assertEqual(failing_resources, failed_check_resources)
+
+
+if __name__ == "__main__":
+    unittest.main()