Repository: bridgecrewio/checkov

Issue:
Add OCI TF check - OCI Network Security Groups (NSG) has stateful security rules
Equivalent RQL query

config from cloud.resource where cloud.type = 'oci' AND api.name = 'oci-networking-nsg' AND json.rule = securityRules[?any( direction equals INGRESS and (isStateless does not exist or isStateless is false) )] 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 -

_This policy identifies the OCI Network Security Groups (NSG) security rules that have stateful ingress rules configured. It is recommended that Network Security Groups (NSG) security rules are configured with stateless ingress rules to slow the impact of a denial-of-service (DoS) attack._

https://github.com/PaloAltoNetworks/prisma-cloud-policies/blob/master/policies/OCI-Network-Security-Groups-NSG-has-stateful-security-rules.json

Resolving patch:
diff --git a/checkov/terraform/checks/resource/oci/SecurityGroupsIngressStatelessSecurityRules.py b/checkov/terraform/checks/resource/oci/SecurityGroupsIngressStatelessSecurityRules.py
new file mode 100644
--- /dev/null
+++ b/checkov/terraform/checks/resource/oci/SecurityGroupsIngressStatelessSecurityRules.py
@@ -0,0 +1,24 @@
+from checkov.common.models.enums import CheckCategories, CheckResult
+from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
+
+
+class SecurityGroupsIngressStatelessSecurityRules(BaseResourceCheck):
+    def __init__(self):
+        name = "Ensure security group has stateless ingress security rules"
+        id = "CKV_OCI_21"
+        supported_resources = ['oci_core_network_security_group_security_rule']
+        categories = [CheckCategories.NETWORKING]
+        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
+
+    def scan_resource_conf(self, conf):
+        stateless = conf.get('stateless')
+        direction = conf.get('direction')
+        self.evaluated_keys = ["direction"]
+        if direction[0] == 'INGRESS':
+            self.evaluated_keys.append("stateless")
+            if stateless is None or stateless[0] is False:
+                return CheckResult.FAILED
+            return CheckResult.PASSED
+        return CheckResult.UNKNOWN
+
+check = SecurityGroupsIngressStatelessSecurityRules()


Test patch:
diff --git a/tests/terraform/checks/resource/oci/example_SecurityGroupsIngressStatelessSecurityRules/main.tf b/tests/terraform/checks/resource/oci/example_SecurityGroupsIngressStatelessSecurityRules/main.tf
new file mode 100644
--- /dev/null
+++ b/tests/terraform/checks/resource/oci/example_SecurityGroupsIngressStatelessSecurityRules/main.tf
@@ -0,0 +1,34 @@
+
+resource "oci_core_network_security_group_security_rule" "pass" {
+  network_security_group_id = oci_core_network_security_group.test_network_security_group.id
+  direction                 = "INGRESS"
+  protocol                  = var.network_security_group_security_rule_protocol
+  stateless                 = true
+}
+
+resource "oci_core_network_security_group_security_rule" "fail" {
+  network_security_group_id = oci_core_network_security_group.test_network_security_group.id
+  direction                 = "INGRESS"
+  protocol                  = var.network_security_group_security_rule_protocol
+  stateless                 = false
+}
+
+resource "oci_core_network_security_group_security_rule" "fail1" {
+  network_security_group_id = oci_core_network_security_group.test_network_security_group.id
+  direction                 = "INGRESS"
+  protocol                  = var.network_security_group_security_rule_protocol
+}
+
+resource "oci_core_network_security_group_security_rule" "skip" {
+  network_security_group_id = oci_core_network_security_group.test_network_security_group.id
+  direction                 = "EGRESS"
+  protocol                  = var.network_security_group_security_rule_protocol
+  stateless                 = true
+}
+
+resource "oci_core_network_security_group_security_rule" "skip1" {
+  network_security_group_id = oci_core_network_security_group.test_network_security_group.id
+  direction                 = "EGRESS"
+  protocol                  = var.network_security_group_security_rule_protocol
+  stateless                 = false
+}
diff --git a/tests/terraform/checks/resource/oci/test_SecurityGroupsIngressStatelessSecurityRules.py b/tests/terraform/checks/resource/oci/test_SecurityGroupsIngressStatelessSecurityRules.py
new file mode 100644
--- /dev/null
+++ b/tests/terraform/checks/resource/oci/test_SecurityGroupsIngressStatelessSecurityRules.py
@@ -0,0 +1,39 @@
+import os
+import unittest
+
+from checkov.runner_filter import RunnerFilter
+from checkov.terraform.checks.resource.oci.SecurityGroupsIngressStatelessSecurityRules import check
+from checkov.terraform.runner import Runner
+
+
+class TestSecurityGroupsIngressStatelessSecurityRules(unittest.TestCase):
+    def test(self):
+        runner = Runner()
+        current_dir = os.path.dirname(os.path.realpath(__file__))
+
+        test_files_dir = current_dir + "/example_SecurityGroupsIngressStatelessSecurityRules"
+        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.pass",
+        }
+
+        failing_resources = {
+            "oci_core_network_security_group_security_rule.fail",
+            "oci_core_network_security_group_security_rule.fail1",
+        }
+
+        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"], 1)
+        self.assertEqual(summary["failed"], 2)
+        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()