From 7563e2c74c9ac2a30d58f1463fbd567775140ad0 Mon Sep 17 00:00:00 2001 From: James Woolfenden Date: Thu, 2 Nov 2023 08:09:22 +0000 Subject: [PATCH] feat(terraform): drop and deletion checks for spanner (#5625) --- .../gcp/SpannerDatabaseDeletionProtection.py | 21 ++++++++++ .../gcp/SpannerDatabaseDropProtection.py | 21 ++++++++++ .../main.tf | 38 ++++++++++++++++++ .../main.tf | 40 +++++++++++++++++++ .../test_SpannerDatabaseDeletionProtection.py | 40 +++++++++++++++++++ .../gcp/test_SpannerDatabaseDropProtection.py | 40 +++++++++++++++++++ 6 files changed, 200 insertions(+) create mode 100644 checkov/terraform/checks/resource/gcp/SpannerDatabaseDeletionProtection.py create mode 100644 checkov/terraform/checks/resource/gcp/SpannerDatabaseDropProtection.py create mode 100644 tests/terraform/checks/resource/gcp/example_SpannerDatabaseDeletionProtection/main.tf create mode 100644 tests/terraform/checks/resource/gcp/example_SpannerDatabaseDropProtection/main.tf create mode 100644 tests/terraform/checks/resource/gcp/test_SpannerDatabaseDeletionProtection.py create mode 100644 tests/terraform/checks/resource/gcp/test_SpannerDatabaseDropProtection.py diff --git a/checkov/terraform/checks/resource/gcp/SpannerDatabaseDeletionProtection.py b/checkov/terraform/checks/resource/gcp/SpannerDatabaseDeletionProtection.py new file mode 100644 index 00000000000..235b526d64b --- /dev/null +++ b/checkov/terraform/checks/resource/gcp/SpannerDatabaseDeletionProtection.py @@ -0,0 +1,21 @@ +from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck +from checkov.common.models.enums import CheckCategories, CheckResult + + +class SpannerDatabaseDeletionProtection(BaseResourceValueCheck): + def __init__(self) -> None: + name = "Ensure Spanner Database has deletion protection enabled" + id = "CKV_GCP_119" + supported_resources = ['google_spanner_database'] + categories = [CheckCategories.GENERAL_SECURITY] + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources, + missing_block_result=CheckResult.PASSED) + + def get_inspected_key(self) -> str: + return 'deletion_protection' + + def get_expected_value(self) -> bool: + return True + + +check = SpannerDatabaseDeletionProtection() diff --git a/checkov/terraform/checks/resource/gcp/SpannerDatabaseDropProtection.py b/checkov/terraform/checks/resource/gcp/SpannerDatabaseDropProtection.py new file mode 100644 index 00000000000..faa951d9c5e --- /dev/null +++ b/checkov/terraform/checks/resource/gcp/SpannerDatabaseDropProtection.py @@ -0,0 +1,21 @@ +from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck +from checkov.common.models.enums import CheckCategories, CheckResult + + +class SpannerDatabaseDropProtection(BaseResourceValueCheck): + def __init__(self) -> None: + name = "Ensure Spanner Database has drop protection enabled" + id = "CKV_GCP_120" + supported_resources = ["google_spanner_database"] + categories = [CheckCategories.GENERAL_SECURITY] + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources, + missing_block_result=CheckResult.FAILED) + + def get_inspected_key(self) -> str: + return "enable_drop_protection" + + def get_expected_value(self) -> bool: + return True + + +check = SpannerDatabaseDropProtection() diff --git a/tests/terraform/checks/resource/gcp/example_SpannerDatabaseDeletionProtection/main.tf b/tests/terraform/checks/resource/gcp/example_SpannerDatabaseDeletionProtection/main.tf new file mode 100644 index 00000000000..f6a17615b01 --- /dev/null +++ b/tests/terraform/checks/resource/gcp/example_SpannerDatabaseDeletionProtection/main.tf @@ -0,0 +1,38 @@ +resource "google_spanner_database" "fail" { + instance = google_spanner_instance.example.name + name = "my-database" + ddl = [ + "CREATE TABLE t1 (t1 INT64 NOT NULL,) PRIMARY KEY(t1)", + "CREATE TABLE t2 (t2 INT64 NOT NULL,) PRIMARY KEY(t2)", + ] + deletion_protection = false + # encryption_config { + # kms_key_name= + # } +} + +resource "google_spanner_database" "pass" { + instance = google_spanner_instance.example.name + name = "my-database" + ddl = [ + "CREATE TABLE t1 (t1 INT64 NOT NULL,) PRIMARY KEY(t1)", + "CREATE TABLE t2 (t2 INT64 NOT NULL,) PRIMARY KEY(t2)", + ] + deletion_protection = true + encryption_config { + kms_key_name= google_kms_crypto_key.example.name + } +} + +resource "google_spanner_database" "pass2" { + instance = google_spanner_instance.example.name + name = "my-database" + ddl = [ + "CREATE TABLE t1 (t1 INT64 NOT NULL,) PRIMARY KEY(t1)", + "CREATE TABLE t2 (t2 INT64 NOT NULL,) PRIMARY KEY(t2)", + ] + + encryption_config { + kms_key_name= google_kms_crypto_key.example.name + } +} diff --git a/tests/terraform/checks/resource/gcp/example_SpannerDatabaseDropProtection/main.tf b/tests/terraform/checks/resource/gcp/example_SpannerDatabaseDropProtection/main.tf new file mode 100644 index 00000000000..c546918cbea --- /dev/null +++ b/tests/terraform/checks/resource/gcp/example_SpannerDatabaseDropProtection/main.tf @@ -0,0 +1,40 @@ +resource "google_spanner_database" "fail" { + instance = google_spanner_instance.example.name + name = "my-database" + ddl = [ + "CREATE TABLE t1 (t1 INT64 NOT NULL,) PRIMARY KEY(t1)", + "CREATE TABLE t2 (t2 INT64 NOT NULL,) PRIMARY KEY(t2)", + ] + deletion_protection = false + enable_drop_protection=false + # encryption_config { + # kms_key_name= + # } +} + +resource "google_spanner_database" "fail2" { + instance = google_spanner_instance.example.name + name = "my-database" + ddl = [ + "CREATE TABLE t1 (t1 INT64 NOT NULL,) PRIMARY KEY(t1)", + "CREATE TABLE t2 (t2 INT64 NOT NULL,) PRIMARY KEY(t2)", + ] + deletion_protection = false + # encryption_config { + # kms_key_name= + # } +} + +resource "google_spanner_database" "pass" { + instance = google_spanner_instance.example.name + name = "my-database" + ddl = [ + "CREATE TABLE t1 (t1 INT64 NOT NULL,) PRIMARY KEY(t1)", + "CREATE TABLE t2 (t2 INT64 NOT NULL,) PRIMARY KEY(t2)", + ] + deletion_protection = false + enable_drop_protection=true + encryption_config { + kms_key_name= google_kms_crypto_key.example.name + } +} diff --git a/tests/terraform/checks/resource/gcp/test_SpannerDatabaseDeletionProtection.py b/tests/terraform/checks/resource/gcp/test_SpannerDatabaseDeletionProtection.py new file mode 100644 index 00000000000..0cad04b8fda --- /dev/null +++ b/tests/terraform/checks/resource/gcp/test_SpannerDatabaseDeletionProtection.py @@ -0,0 +1,40 @@ +import unittest +import os + +from checkov.terraform.checks.resource.gcp.SpannerDatabaseDeletionProtection import check +from checkov.runner_filter import RunnerFilter +from checkov.terraform.runner import Runner + + +class TestSpannerDatabaseDeletionProtection(unittest.TestCase): + + def test(self): + runner = Runner() + current_dir = os.path.dirname(os.path.realpath(__file__)) + + test_files_dir = current_dir + "/example_SpannerDatabaseDeletionProtection" + report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id])) + summary = report.get_summary() + + passing_resources = { + 'google_spanner_database.pass', + 'google_spanner_database.pass2', + } + failing_resources = { + 'google_spanner_database.fail', + } + + 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'], 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() diff --git a/tests/terraform/checks/resource/gcp/test_SpannerDatabaseDropProtection.py b/tests/terraform/checks/resource/gcp/test_SpannerDatabaseDropProtection.py new file mode 100644 index 00000000000..25756804587 --- /dev/null +++ b/tests/terraform/checks/resource/gcp/test_SpannerDatabaseDropProtection.py @@ -0,0 +1,40 @@ +import unittest +import os + +from checkov.terraform.checks.resource.gcp.SpannerDatabaseDropProtection import check +from checkov.runner_filter import RunnerFilter +from checkov.terraform.runner import Runner + + +class TestSpannerDatabaseDropProtection(unittest.TestCase): + + def test(self): + runner = Runner() + current_dir = os.path.dirname(os.path.realpath(__file__)) + + test_files_dir = current_dir + "/example_SpannerDatabaseDropProtection" + report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id])) + summary = report.get_summary() + + passing_resources = { + 'google_spanner_database.pass', + } + failing_resources = { + 'google_spanner_database.fail', + 'google_spanner_database.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'], 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()