Repository: bridgecrewio/checkov

Issue:
Add OCI TF check - OCI security group allows unrestricted ingress access to port 22
Equivalent RQL query 
config from cloud.resource where cloud.type = 'oci' AND api.name = 'oci-networking-nsg' AND json.rule = (securityRules[?any((((*.destinationPortRange.min == 22 or *.destinationPortRange.max == 22) or (*.destinationPortRange.min < 22 and *.destinationPortRange.max > 22)) or (protocol equals "all") or ((tcpOptions does not exist) and (udpOptions does not exist) and (protocol does not equal 1))) and (source equals 0.0.0.0/0 and direction equals INGRESS))] exists)


Discussion:
Note that RQL queries are spotting the error while “Checkov logic wish to find the Desired state” use the referance below in order to get more data for the check needed -

_Note that RQL queries are spotting the error while “Checkov logic wish to find the Desired state” use the referance below in order to get more data for the check needed -

_This policy identifies OCI Security groups that allow unrestricted ingress access to port 22. It is recommended that no security group allows unrestricted ingress access to port 22. As a best practice, remove unfettered connectivity to remote console services, such as Secure Shell (SSH), to reduce server's exposure to risk.._

https://github.com/PaloAltoNetworks/prisma-cloud-policies/blob/master/policies/OCI-security-group-allows-unrestricted-ingress-access-to-port-22.json

Resolving patch:
diff --git a/checkov/terraform/checks/resource/oci/AbsSecurityGroupUnrestrictedIngress.py b/checkov/terraform/checks/resource/oci/AbsSecurityGroupUnrestrictedIngress.py
new file mode 100644
--- /dev/null
+++ b/checkov/terraform/checks/resource/oci/AbsSecurityGroupUnrestrictedIngress.py
@@ -0,0 +1,36 @@
+from checkov.common.models.enums import CheckResult, CheckCategories
+from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
+
+
+class AbsSecurityGroupUnrestrictedIngress(BaseResourceCheck):
+    def __init__(self, check_id: str, port: int) -> None:
+        name = f"Ensure no security groups rules allow ingress from 0.0.0.0/0 to port {port}"
+        supported_resources = ['oci_core_network_security_group_security_rule']
+        categories = [CheckCategories.NETWORKING]
+        super().__init__(name=name, id=check_id, categories=categories, supported_resources=supported_resources)
+        self.port = port
+
+    def scan_resource_conf(self, conf):
+        direction = conf.get('direction')
+        source = conf.get('source')
+        protocol = conf.get('protocol')
+        tcp_options = conf.get('tcp_options')
+        self.evaluated_keys = ["direction"]
+        if direction and direction[0] != 'INGRESS':
+            return CheckResult.UNKNOWN
+        self.evaluated_keys.append("source")
+        if source and source[0] != "0.0.0.0/0":
+            return CheckResult.PASSED
+        elif (tcp_options is None and (protocol[0] == 'all' or protocol[0] == '6')) \
+                or tcp_options and self.scan_protocol_conf(tcp_options) is False:
+            self.evaluated_keys.append("protocol")
+            return CheckResult.FAILED
+        return CheckResult.PASSED
+
+    def scan_protocol_conf(self, protocol_name):
+        """ scan tcp_options configuration"""
+        max_port = protocol_name[0]['source_port_range'][0]['max'][0]
+        min_port = protocol_name[0]['source_port_range'][0]['min'][0]
+        if min_port <= self.port <= max_port:
+            return False
+        return True
diff --git a/checkov/terraform/checks/resource/oci/SecurityGroupUnrestrictedIngress22.py b/checkov/terraform/checks/resource/oci/SecurityGroupUnrestrictedIngress22.py
new file mode 100644
--- /dev/null
+++ b/checkov/terraform/checks/resource/oci/SecurityGroupUnrestrictedIngress22.py
@@ -0,0 +1,10 @@
+from checkov.terraform.checks.resource.oci.AbsSecurityGroupUnrestrictedIngress import \
+    AbsSecurityGroupUnrestrictedIngress
+
+
+class SecurityGroupUnrestrictedIngress22(AbsSecurityGroupUnrestrictedIngress):
+    def __init__(self):
+        super().__init__(check_id="CKV_OCI_22", port=22)
+
+
+check = AbsSecurityGroupUnrestrictedIngress("CKV_OCI_22", 22)


Test patch:
diff --git a/tests/terraform/checks/resource/oci/example_SecurityGroupUnrestrictedIngress22/main.tf b/tests/terraform/checks/resource/oci/example_SecurityGroupUnrestrictedIngress22/main.tf
new file mode 100644
--- /dev/null
+++ b/tests/terraform/checks/resource/oci/example_SecurityGroupUnrestrictedIngress22/main.tf
@@ -0,0 +1,90 @@
+
+resource "oci_core_network_security_group_security_rule" "pass" {
+    network_security_group_id = oci_core_network_security_group.sg.id
+    direction = "EGRESS"
+    protocol = "all"
+    source = "0.0.0.0/0"
+
+    tcp_options {
+        source_port_range {
+            max = 22
+            min = 22
+        }
+    }
+}
+
+resource "oci_core_network_security_group_security_rule" "pass1" {
+    network_security_group_id = oci_core_network_security_group.sg.id
+    direction = "EGRESS"
+    protocol = "all"
+    source = "0.0.0.0/0"
+}
+
+resource "oci_core_network_security_group_security_rule" "pass2" {
+    network_security_group_id = oci_core_network_security_group.sg.id
+    direction = "INGRESS"
+    protocol = "all"
+    source = "0.0.0.0/0"
+    source_type = "CIDR_BLOCK"
+
+    tcp_options {
+        source_port_range {
+            max = 25
+            min = 25
+        }
+    }
+}
+
+resource "oci_core_network_security_group_security_rule" "pass3" {
+    network_security_group_id = oci_core_network_security_group.sg.id
+    direction = "INGRESS"
+    protocol = "all"
+    source = "0.0.0.0/0"
+    source_type = "CIDR_BLOCK"
+
+    tcp_options {
+        source_port_range {
+            max = 21
+            min = 1
+        }
+    }
+}
+
+
+resource "oci_core_network_security_group_security_rule" "fail" {
+    network_security_group_id = oci_core_network_security_group.sg.id
+    direction = "INGRESS"
+    protocol = "all"
+    source = "0.0.0.0/0"
+    source_type = "CIDR_BLOCK"
+
+    tcp_options {
+        source_port_range {
+            max = 22
+            min = 22
+        }
+    }
+}
+
+resource "oci_core_network_security_group_security_rule" "fail1" {
+    network_security_group_id = oci_core_network_security_group.sg.id
+    direction = "INGRESS"
+    protocol = "all"
+    source = "0.0.0.0/0"
+    source_type = "CIDR_BLOCK"
+
+    tcp_options {
+        source_port_range {
+            max = 25
+            min = 21
+        }
+    }
+}
+
+resource "oci_core_network_security_group_security_rule" "fail2" {
+    network_security_group_id = oci_core_network_security_group.sg.id
+    direction = "INGRESS"
+    protocol = "all"
+    source = "0.0.0.0/0"
+    source_type = "CIDR_BLOCK"
+}
diff --git a/tests/terraform/checks/resource/oci/test_SecurityGroupUnrestrictedIngress22.py b/tests/terraform/checks/resource/oci/test_SecurityGroupUnrestrictedIngress22.py
new file mode 100644
--- /dev/null
+++ b/tests/terraform/checks/resource/oci/test_SecurityGroupUnrestrictedIngress22.py
@@ -0,0 +1,42 @@
+import os
+import unittest
+
+from checkov.runner_filter import RunnerFilter
+from checkov.terraform.checks.resource.oci.SecurityGroupUnrestrictedIngress22 import check
+from checkov.terraform.runner import Runner
+
+
+class TestSecurityGroupUnrestrictedIngress22(unittest.TestCase):
+    def test(self):
+        runner = Runner()
+        current_dir = os.path.dirname(os.path.realpath(__file__))
+
+        test_files_dir = current_dir + "/example_SecurityGroupUnrestrictedIngress22"
+        report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id]))
+        summary = report.get_summary()
+
+        passing_resources = {
+            "oci_core_network_security_group_security_rule.pass2",
+            "oci_core_network_security_group_security_rule.pass3",
+
+        }
+        failing_resources = {
+            "oci_core_network_security_group_security_rule.fail",
+            "oci_core_network_security_group_security_rule.fail1",
+            "oci_core_network_security_group_security_rule.fail2",
+        }
+
+        passed_check_resources = set([c.resource for c in report.passed_checks])
+        failed_check_resources = set([c.resource for c in report.failed_checks])
+
+        self.assertEqual(summary["passed"], 2)
+        self.assertEqual(summary["failed"], 3)
+        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()