From 9a50dffaa0db93d80f8a586e994dc7131d73b0ea Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:52:49 +0200 Subject: [PATCH] feat(gcp): split kms_key_rotation_enabled into enabled and max-90-days checks (#11516) --- prowler/CHANGELOG.md | 2 + prowler/compliance/gcp/ccc_gcp.json | 2 +- prowler/compliance/gcp/cis_2.0_gcp.json | 2 +- prowler/compliance/gcp/cis_3.0_gcp.json | 2 +- prowler/compliance/gcp/cis_4.0_gcp.json | 2 +- .../gcp/prowler_threatscore_gcp.json | 2 +- .../kms_key_rotation_enabled.metadata.json | 16 +- .../kms_key_rotation_enabled.py | 34 +- .../kms_key_rotation_max_90_days/__init__.py | 0 ...kms_key_rotation_max_90_days.metadata.json | 38 + .../kms_key_rotation_max_90_days.py | 44 ++ .../kms_key_rotation_enabled_test.py | 468 +---------- .../kms_key_rotation_max_90_days_test.py | 728 ++++++++++++++++++ 13 files changed, 841 insertions(+), 499 deletions(-) create mode 100644 prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/__init__.py create mode 100644 prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.metadata.json create mode 100644 prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.py create mode 100644 tests/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days_test.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 42c794a49f..dcb250d316 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - External multi-provider compliance frameworks can be registered via the `prowler.compliance.universal` entry point group [(#11490)](https://github.com/prowler-cloud/prowler/pull/11490) - AWS AI Security Framework support in the CLI dashboard [(#11475)](https://github.com/prowler-cloud/prowler/pull/11475) - `entra_service_principal_privileged_role_no_owners` check for M365 provider, failing when a service principal with a permanent Tier 0 directory role has owners on the service principal or its parent app registration [(#11070)](https://github.com/prowler-cloud/prowler/issues/11070) +- `kms_key_rotation_max_90_days` check for GCP provider, verifying KMS customer-managed keys are rotated every 90 days or less in line with the CIS Benchmark [(#11516)](https://github.com/prowler-cloud/prowler/pull/11516) - `exchange_mailbox_primary_smtp_uses_custom_domain` check for M365 provider [(#11215)](https://github.com/prowler-cloud/prowler/pull/11215) ### 🐞 Fixed @@ -24,6 +25,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - `load_and_validate_config_file` now unwraps namespaced config for every built-in and external provider, and no longer leaks the full file as the provider's config when the file is namespaced [(#10700)](https://github.com/prowler-cloud/prowler/pull/10700) - `entra_users_mfa_capable` no longer flags pre-provisioned users with future `employeeHireDate`; future-hire date comparisons now tolerate naive datetimes [(#11511)](https://github.com/prowler-cloud/prowler/pull/11511) - M365 Admin Center group enumeration now follows Microsoft Graph pagination so group-scoped checks include groups beyond the first page [(#11510)](https://github.com/prowler-cloud/prowler/pull/11510) +- GCP `kms_key_rotation_enabled` check now only verifies that automatic key rotation is enabled (any interval) instead of enforcing a 90-day period, resolving the mismatch between the check and its documentation; the CIS, Prowler ThreatScore, and CCC requirements that mandate a 90-day maximum were remapped to the new `kms_key_rotation_max_90_days` check [(#11516)](https://github.com/prowler-cloud/prowler/pull/11516) --- diff --git a/prowler/compliance/gcp/ccc_gcp.json b/prowler/compliance/gcp/ccc_gcp.json index df6b15bc5c..67a75841ef 100644 --- a/prowler/compliance/gcp/ccc_gcp.json +++ b/prowler/compliance/gcp/ccc_gcp.json @@ -889,7 +889,7 @@ } ], "Checks": [ - "kms_key_rotation_enabled" + "kms_key_rotation_max_90_days" ] }, { diff --git a/prowler/compliance/gcp/cis_2.0_gcp.json b/prowler/compliance/gcp/cis_2.0_gcp.json index d391bcc271..dd9eb62a96 100644 --- a/prowler/compliance/gcp/cis_2.0_gcp.json +++ b/prowler/compliance/gcp/cis_2.0_gcp.json @@ -150,7 +150,7 @@ "Id": "1.10", "Description": "Google Cloud Key Management Service stores cryptographic keys in a hierarchical structure designed for useful and elegant access control management. The format for the rotation schedule depends on the client library that is used. For the gcloud command-line tool, the next rotation time must be in `ISO` or `RFC3339` format, and the rotation period must be in the form `INTEGERUNIT`, where units can be one of seconds (s), minutes (m), hours (h) or days (d).", "Checks": [ - "kms_key_rotation_enabled" + "kms_key_rotation_max_90_days" ], "Attributes": [ { diff --git a/prowler/compliance/gcp/cis_3.0_gcp.json b/prowler/compliance/gcp/cis_3.0_gcp.json index b8d796c35a..958a2a9bc3 100644 --- a/prowler/compliance/gcp/cis_3.0_gcp.json +++ b/prowler/compliance/gcp/cis_3.0_gcp.json @@ -201,7 +201,7 @@ "Id": "1.10", "Description": "Ensure KMS Encryption Keys Are Rotated Within a Period of 90 Days", "Checks": [ - "kms_key_rotation_enabled" + "kms_key_rotation_max_90_days" ], "Attributes": [ { diff --git a/prowler/compliance/gcp/cis_4.0_gcp.json b/prowler/compliance/gcp/cis_4.0_gcp.json index 5cd97a87da..d7a3c5d7a2 100644 --- a/prowler/compliance/gcp/cis_4.0_gcp.json +++ b/prowler/compliance/gcp/cis_4.0_gcp.json @@ -201,7 +201,7 @@ "Id": "1.10", "Description": "Ensure KMS Encryption Keys Are Rotated Within a Period of 90 Days", "Checks": [ - "kms_key_rotation_enabled" + "kms_key_rotation_max_90_days" ], "Attributes": [ { diff --git a/prowler/compliance/gcp/prowler_threatscore_gcp.json b/prowler/compliance/gcp/prowler_threatscore_gcp.json index 46fc776fbe..923a1acfc2 100644 --- a/prowler/compliance/gcp/prowler_threatscore_gcp.json +++ b/prowler/compliance/gcp/prowler_threatscore_gcp.json @@ -117,7 +117,7 @@ "Id": "1.2.4", "Description": "Ensure KMS Encryption Keys Are Rotated Within a Period of 90 Days", "Checks": [ - "kms_key_rotation_enabled" + "kms_key_rotation_max_90_days" ], "Attributes": [ { diff --git a/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.metadata.json b/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.metadata.json index 5efe894b04..7312ffefeb 100644 --- a/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.metadata.json +++ b/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.metadata.json @@ -1,14 +1,14 @@ { "Provider": "gcp", "CheckID": "kms_key_rotation_enabled", - "CheckTitle": "KMS key is rotated at least annually", + "CheckTitle": "KMS key has automatic rotation enabled", "CheckType": [], "ServiceName": "kms", "SubServiceName": "", "ResourceIdTemplate": "", "Severity": "low", "ResourceType": "cloudkms.googleapis.com/CryptoKey", - "Description": "Google Cloud KMS customer-managed keys have **automatic rotation** enabled or a rotation interval `365` days.\n\nThe evaluation reviews each key's rotation settings to confirm periodic creation of new key versions.", + "Description": "Google Cloud KMS customer-managed keys have **automatic rotation** enabled, regardless of the rotation interval.\n\nThe evaluation reviews each key's rotation settings to confirm that a rotation period is configured so new key versions are created periodically.", "Risk": "Without timely rotation, a stolen key can decrypt an expanding volume of data, eroding **confidentiality**. Prolonged key lifetimes widen windows for misuse, impact **integrity** of protected workloads, and make emergency rollover harder, risking **availability** disruptions.", "RelatedUrl": "", "AdditionalURLs": [ @@ -17,13 +17,13 @@ ], "Remediation": { "Code": { - "CLI": "gcloud kms keys update --keyring= --location= --rotation-period=365d --next-rotation-time=", + "CLI": "gcloud kms keys update --keyring= --location= --rotation-period= --next-rotation-time=", "NativeIaC": "", - "Other": "1. In Google Cloud Console, go to Security > Key Management > Key rings\n2. Open the key ring and select the key\n3. Click Edit rotation schedule (or Set rotation schedule)\n4. Set Rotation period to 365 days or less\n5. Set Next rotation date/time\n6. Click Save", - "Terraform": "```hcl\nresource \"google_kms_crypto_key\" \"\" {\n name = \"\"\n key_ring = \"\"\n purpose = \"ENCRYPT_DECRYPT\"\n\n rotation_period = \"31536000s\" # Critical: sets automatic rotation to 365 days (<= 365 ensures PASS)\n}\n```" + "Other": "1. In Google Cloud Console, go to Security > Key Management > Key rings\n2. Open the key ring and select the key\n3. Click Edit rotation schedule (or Set rotation schedule)\n4. Set a Rotation period\n5. Set Next rotation date/time\n6. Click Save", + "Terraform": "```hcl\nresource \"google_kms_crypto_key\" \"\" {\n name = \"\"\n key_ring = \"\"\n purpose = \"ENCRYPT_DECRYPT\"\n\n rotation_period = \"7776000s\" # Critical: enables automatic rotation (any period ensures PASS)\n}\n```" }, "Recommendation": { - "Text": "Enable **auto-rotation** for customer-managed keys with an interval `365` days.\n\nAdopt a **key lifecycle** policy: enforce **least privilege** on key usage, apply **separation of duties** between key admins and users, monitor key access, and rehearse emergency rotation to minimize blast radius.", + "Text": "Enable **auto-rotation** for customer-managed keys by configuring a rotation period.\n\nAdopt a **key lifecycle** policy: enforce **least privilege** on key usage, apply **separation of duties** between key admins and users, monitor key access, and rehearse emergency rotation to minimize blast radius.", "Url": "https://hub.prowler.com/check/kms_key_rotation_enabled" } }, @@ -31,6 +31,8 @@ "encryption" ], "DependsOn": [], - "RelatedTo": [], + "RelatedTo": [ + "kms_key_rotation_max_90_days" + ], "Notes": "" } diff --git a/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.py b/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.py index 577a7c9f99..ae924ca380 100644 --- a/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.py +++ b/prowler/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled.py @@ -1,5 +1,3 @@ -import datetime - from prowler.lib.check.models import Check, Check_Report_GCP from prowler.providers.gcp.services.kms.kms_client import kms_client @@ -9,36 +7,16 @@ class kms_key_rotation_enabled(Check): findings = [] for key in kms_client.crypto_keys: report = Check_Report_GCP(metadata=self.metadata(), resource=key) - now = datetime.datetime.now() - condition_next_rotation_time = False - if key.next_rotation_time: - try: - next_rotation_time = datetime.datetime.strptime( - key.next_rotation_time, "%Y-%m-%dT%H:%M:%S.%fZ" - ) - except ValueError: - next_rotation_time = datetime.datetime.strptime( - key.next_rotation_time, "%Y-%m-%dT%H:%M:%SZ" - ) - condition_next_rotation_time = ( - abs((next_rotation_time - now).days) <= 90 - ) - condition_rotation_period = False if key.rotation_period: - condition_rotation_period = ( - int(key.rotation_period[:-1]) // (24 * 3600) <= 90 - ) - if condition_rotation_period and condition_next_rotation_time: report.status = "PASS" - report.status_extended = f"Key {key.name} is rotated every 90 days or less and the next rotation time is in less than 90 days." + report.status_extended = ( + f"Key {key.name} has automatic rotation enabled." + ) else: report.status = "FAIL" - if condition_rotation_period: - report.status_extended = f"Key {key.name} is rotated every 90 days or less but the next rotation time is in more than 90 days." - elif condition_next_rotation_time: - report.status_extended = f"Key {key.name} is not rotated every 90 days or less but the next rotation time is in less than 90 days." - else: - report.status_extended = f"Key {key.name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." + report.status_extended = ( + f"Key {key.name} does not have automatic rotation enabled." + ) findings.append(report) return findings diff --git a/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/__init__.py b/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.metadata.json b/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.metadata.json new file mode 100644 index 0000000000..f1597fee53 --- /dev/null +++ b/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.metadata.json @@ -0,0 +1,38 @@ +{ + "Provider": "gcp", + "CheckID": "kms_key_rotation_max_90_days", + "CheckTitle": "KMS key is rotated every 90 days or less", + "CheckType": [], + "ServiceName": "kms", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "low", + "ResourceType": "cloudkms.googleapis.com/CryptoKey", + "Description": "Google Cloud KMS customer-managed keys are rotated with an interval of `90` days or less, in line with the CIS Benchmark.\n\nThe evaluation reviews each key's rotation settings to confirm that both the rotation period and the next rotation time stay within 90 days.", + "Risk": "Without timely rotation, a stolen key can decrypt an expanding volume of data, eroding **confidentiality**. Prolonged key lifetimes widen windows for misuse, impact **integrity** of protected workloads, and make emergency rollover harder, risking **availability** disruptions.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://www.trendmicro.com/trendaivisiononecloudriskmanagement/knowledge-base/gcp/CloudKMS/rotate-kms-encryption-keys.html", + "https://cloud.google.com/iam/docs/manage-access-service-accounts" + ], + "Remediation": { + "Code": { + "CLI": "gcloud kms keys update --keyring= --location= --rotation-period=90d --next-rotation-time=", + "NativeIaC": "", + "Other": "1. In Google Cloud Console, go to Security > Key Management > Key rings\n2. Open the key ring and select the key\n3. Click Edit rotation schedule (or Set rotation schedule)\n4. Set Rotation period to 90 days or less\n5. Set Next rotation date/time\n6. Click Save", + "Terraform": "```hcl\nresource \"google_kms_crypto_key\" \"\" {\n name = \"\"\n key_ring = \"\"\n purpose = \"ENCRYPT_DECRYPT\"\n\n rotation_period = \"7776000s\" # Critical: sets automatic rotation to 90 days (<= 90 ensures PASS)\n}\n```" + }, + "Recommendation": { + "Text": "Enable **auto-rotation** for customer-managed keys with an interval of `90` days or less.\n\nAdopt a **key lifecycle** policy: enforce **least privilege** on key usage, apply **separation of duties** between key admins and users, monitor key access, and rehearse emergency rotation to minimize blast radius.", + "Url": "https://hub.prowler.com/check/kms_key_rotation_max_90_days" + } + }, + "Categories": [ + "encryption" + ], + "DependsOn": [], + "RelatedTo": [ + "kms_key_rotation_enabled" + ], + "Notes": "" +} diff --git a/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.py b/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.py new file mode 100644 index 0000000000..cbca4f0e91 --- /dev/null +++ b/prowler/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days.py @@ -0,0 +1,44 @@ +import datetime + +from prowler.lib.check.models import Check, Check_Report_GCP +from prowler.providers.gcp.services.kms.kms_client import kms_client + + +class kms_key_rotation_max_90_days(Check): + def execute(self) -> Check_Report_GCP: + findings = [] + for key in kms_client.crypto_keys: + report = Check_Report_GCP(metadata=self.metadata(), resource=key) + now = datetime.datetime.now() + condition_next_rotation_time = False + if key.next_rotation_time: + try: + next_rotation_time = datetime.datetime.strptime( + key.next_rotation_time, "%Y-%m-%dT%H:%M:%S.%fZ" + ) + except ValueError: + next_rotation_time = datetime.datetime.strptime( + key.next_rotation_time, "%Y-%m-%dT%H:%M:%SZ" + ) + condition_next_rotation_time = ( + abs((next_rotation_time - now).days) <= 90 + ) + condition_rotation_period = False + if key.rotation_period: + condition_rotation_period = ( + int(key.rotation_period[:-1]) // (24 * 3600) <= 90 + ) + if condition_rotation_period and condition_next_rotation_time: + report.status = "PASS" + report.status_extended = f"Key {key.name} is rotated every 90 days or less and the next rotation time is in less than 90 days." + else: + report.status = "FAIL" + if condition_rotation_period: + report.status_extended = f"Key {key.name} is rotated every 90 days or less but the next rotation time is in more than 90 days." + elif condition_next_rotation_time: + report.status_extended = f"Key {key.name} is not rotated every 90 days or less but the next rotation time is in less than 90 days." + else: + report.status_extended = f"Key {key.name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." + findings.append(report) + + return findings diff --git a/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py b/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py index 5776c858f5..6600921c9f 100644 --- a/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py +++ b/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py @@ -1,4 +1,3 @@ -import datetime from unittest import mock from tests.providers.gcp.gcp_fixtures import ( @@ -34,7 +33,7 @@ class Test_kms_key_rotation_enabled: result = check.execute() assert len(result) == 0 - def test_kms_key_no_next_rotation_time_and_no_rotation_period(self): + def test_kms_key_without_rotation_period(self): kms_client = mock.MagicMock() with ( @@ -86,14 +85,14 @@ class Test_kms_key_rotation_enabled: assert result[0].status == "FAIL" assert ( result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." + == f"Key {kms_client.crypto_keys[0].name} does not have automatic rotation enabled." ) assert result[0].resource_id == kms_client.crypto_keys[0].id assert result[0].resource_name == kms_client.crypto_keys[0].name assert result[0].location == kms_client.crypto_keys[0].location assert result[0].project_id == kms_client.crypto_keys[0].project_id - def test_kms_key_no_next_rotation_time_and_big_rotation_period(self): + def test_kms_key_with_long_rotation_period(self): kms_client = mock.MagicMock() with ( @@ -135,471 +134,26 @@ class Test_kms_key_rotation_enabled: project_id=GCP_PROJECT_ID, key_ring=keyring.name, location=keylocation.name, + # Rotation period greater than 90 days still counts as enabled rotation_period="8776000s", members=["user:jane@example.com"], ) ] - check = kms_key_rotation_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." - ) - assert result[0].resource_id == kms_client.crypto_keys[0].id - assert result[0].resource_name == kms_client.crypto_keys[0].name - assert result[0].location == kms_client.crypto_keys[0].location - assert result[0].project_id == kms_client.crypto_keys[0].project_id - - def test_kms_key_no_next_rotation_time_and_appropriate_rotation_period(self): - kms_client = mock.MagicMock() - - with ( - mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_gcp_provider(), - ), - mock.patch( - "prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled.kms_client", - new=kms_client, - ), - ): - from prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled import ( - kms_key_rotation_enabled, - ) - from prowler.providers.gcp.services.kms.kms_service import ( - CriptoKey, - KeyLocation, - KeyRing, - ) - - kms_client.project_ids = [GCP_PROJECT_ID] - kms_client.region = GCP_US_CENTER1_LOCATION - - keyring = KeyRing( - name="projects/123/locations/us-central1/keyRings/keyring1", - project_id=GCP_PROJECT_ID, - ) - - keylocation = KeyLocation( - name=GCP_US_CENTER1_LOCATION, - project_id=GCP_PROJECT_ID, - ) - - kms_client.crypto_keys = [ - CriptoKey( - name="key1", - id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", - project_id=GCP_PROJECT_ID, - key_ring=keyring.name, - location=keylocation.name, - rotation_period="7776000s", - members=["user:jane@example.com"], - ) - ] - - check = kms_key_rotation_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less but the next rotation time is in more than 90 days." - ) - assert result[0].resource_id == kms_client.crypto_keys[0].id - assert result[0].resource_name == kms_client.crypto_keys[0].name - assert result[0].location == kms_client.crypto_keys[0].location - assert result[0].project_id == kms_client.crypto_keys[0].project_id - - def test_kms_key_no_rotation_period_and_big_next_rotation_time(self): - kms_client = mock.MagicMock() - - with ( - mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_gcp_provider(), - ), - mock.patch( - "prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled.kms_client", - new=kms_client, - ), - ): - from prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled import ( - kms_key_rotation_enabled, - ) - from prowler.providers.gcp.services.kms.kms_service import ( - CriptoKey, - KeyLocation, - KeyRing, - ) - - kms_client.project_ids = [GCP_PROJECT_ID] - kms_client.region = GCP_US_CENTER1_LOCATION - - keyring = KeyRing( - name="projects/123/locations/us-central1/keyRings/keyring1", - project_id=GCP_PROJECT_ID, - ) - - keylocation = KeyLocation( - name=GCP_US_CENTER1_LOCATION, - project_id=GCP_PROJECT_ID, - ) - - kms_client.crypto_keys = [ - CriptoKey( - name="key1", - id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", - project_id=GCP_PROJECT_ID, - key_ring=keyring.name, - location=keylocation.name, - # Next rotation time of now + 100 days - next_rotation_time=( - datetime.datetime.now() - datetime.timedelta(days=+100) - ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), - members=["user:jane@example.com"], - ) - ] - - check = kms_key_rotation_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." - ) - assert result[0].resource_id == kms_client.crypto_keys[0].id - assert result[0].resource_name == kms_client.crypto_keys[0].name - assert result[0].location == kms_client.crypto_keys[0].location - assert result[0].project_id == kms_client.crypto_keys[0].project_id - - def test_kms_key_no_rotation_period_and_appropriate_next_rotation_time(self): - kms_client = mock.MagicMock() - - with ( - mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_gcp_provider(), - ), - mock.patch( - "prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled.kms_client", - new=kms_client, - ), - ): - from prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled import ( - kms_key_rotation_enabled, - ) - from prowler.providers.gcp.services.kms.kms_service import ( - CriptoKey, - KeyLocation, - KeyRing, - ) - - kms_client.project_ids = [GCP_PROJECT_ID] - kms_client.region = GCP_US_CENTER1_LOCATION - - keyring = KeyRing( - name="projects/123/locations/us-central1/keyRings/keyring1", - project_id=GCP_PROJECT_ID, - ) - - keylocation = KeyLocation( - name=GCP_US_CENTER1_LOCATION, - project_id=GCP_PROJECT_ID, - ) - - kms_client.crypto_keys = [ - CriptoKey( - name="key1", - id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", - project_id=GCP_PROJECT_ID, - key_ring=keyring.name, - location=keylocation.name, - # Next rotation time of now + 30 days - next_rotation_time=( - datetime.datetime.now() - datetime.timedelta(days=+30) - ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), - members=["user:jane@example.com"], - ) - ] - - check = kms_key_rotation_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less but the next rotation time is in less than 90 days." - ) - assert result[0].resource_id == kms_client.crypto_keys[0].id - assert result[0].resource_name == kms_client.crypto_keys[0].name - assert result[0].location == kms_client.crypto_keys[0].location - assert result[0].project_id == kms_client.crypto_keys[0].project_id - - def test_kms_key_rotation_period_greater_90_days_and_big_next_rotation_time(self): - kms_client = mock.MagicMock() - - with ( - mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_gcp_provider(), - ), - mock.patch( - "prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled.kms_client", - new=kms_client, - ), - ): - from prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled import ( - kms_key_rotation_enabled, - ) - from prowler.providers.gcp.services.kms.kms_service import ( - CriptoKey, - KeyLocation, - KeyRing, - ) - - kms_client.project_ids = [GCP_PROJECT_ID] - kms_client.region = GCP_US_CENTER1_LOCATION - - keyring = KeyRing( - name="projects/123/locations/us-central1/keyRings/keyring1", - project_id=GCP_PROJECT_ID, - ) - - keylocation = KeyLocation( - name=GCP_US_CENTER1_LOCATION, - project_id=GCP_PROJECT_ID, - ) - - kms_client.crypto_keys = [ - CriptoKey( - name="key1", - id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", - project_id=GCP_PROJECT_ID, - rotation_period="8776000s", - # Next rotation time of now + 100 days - next_rotation_time=( - datetime.datetime.now() - datetime.timedelta(days=+100) - ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), - key_ring=keyring.name, - location=keylocation.name, - members=["user:jane@example.com"], - ) - ] - - check = kms_key_rotation_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." - ) - assert result[0].resource_id == kms_client.crypto_keys[0].id - assert result[0].resource_name == kms_client.crypto_keys[0].name - assert result[0].location == kms_client.crypto_keys[0].location - assert result[0].project_id == kms_client.crypto_keys[0].project_id - - def test_kms_key_rotation_period_greater_90_days_and_appropriate_next_rotation_time( - self, - ): - kms_client = mock.MagicMock() - - with ( - mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_gcp_provider(), - ), - mock.patch( - "prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled.kms_client", - new=kms_client, - ), - ): - from prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled import ( - kms_key_rotation_enabled, - ) - from prowler.providers.gcp.services.kms.kms_service import ( - CriptoKey, - KeyLocation, - KeyRing, - ) - - kms_client.project_ids = [GCP_PROJECT_ID] - kms_client.region = GCP_US_CENTER1_LOCATION - - keyring = KeyRing( - name="projects/123/locations/us-central1/keyRings/keyring1", - project_id=GCP_PROJECT_ID, - ) - - keylocation = KeyLocation( - name=GCP_US_CENTER1_LOCATION, - project_id=GCP_PROJECT_ID, - ) - - kms_client.crypto_keys = [ - CriptoKey( - name="key1", - id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", - project_id=GCP_PROJECT_ID, - rotation_period="8776000s", - # Next rotation time of now + 30 days - next_rotation_time=( - datetime.datetime.now() - datetime.timedelta(days=+30) - ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), - key_ring=keyring.name, - location=keylocation.name, - members=["user:jane@example.com"], - ) - ] - - check = kms_key_rotation_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less but the next rotation time is in less than 90 days." - ) - assert result[0].resource_id == kms_client.crypto_keys[0].id - assert result[0].resource_name == kms_client.crypto_keys[0].name - assert result[0].location == kms_client.crypto_keys[0].location - assert result[0].project_id == kms_client.crypto_keys[0].project_id - - def test_kms_key_rotation_period_less_90_days_and_big_next_rotation_time(self): - kms_client = mock.MagicMock() - - with ( - mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_gcp_provider(), - ), - mock.patch( - "prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled.kms_client", - new=kms_client, - ), - ): - from prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled import ( - kms_key_rotation_enabled, - ) - from prowler.providers.gcp.services.kms.kms_service import ( - CriptoKey, - KeyLocation, - KeyRing, - ) - - kms_client.project_ids = [GCP_PROJECT_ID] - kms_client.region = GCP_US_CENTER1_LOCATION - - keyring = KeyRing( - name="projects/123/locations/us-central1/keyRings/keyring1", - project_id=GCP_PROJECT_ID, - ) - - keylocation = KeyLocation( - name=GCP_US_CENTER1_LOCATION, - project_id=GCP_PROJECT_ID, - ) - - kms_client.crypto_keys = [ - CriptoKey( - name="key1", - id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", - project_id=GCP_PROJECT_ID, - rotation_period="7776000s", - # Next rotation time of now + 100 days - next_rotation_time=( - datetime.datetime.now() - datetime.timedelta(days=+100) - ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), - key_ring=keyring.name, - location=keylocation.name, - members=["user:jane@example.com"], - ) - ] - - check = kms_key_rotation_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less but the next rotation time is in more than 90 days." - ) - assert result[0].resource_id == kms_client.crypto_keys[0].id - assert result[0].resource_name == kms_client.crypto_keys[0].name - assert result[0].location == kms_client.crypto_keys[0].location - assert result[0].project_id == kms_client.crypto_keys[0].project_id - - def test_kms_key_rotation_period_less_90_days_and_appropriate_next_rotation_time( - self, - ): - kms_client = mock.MagicMock() - - with ( - mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_gcp_provider(), - ), - mock.patch( - "prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled.kms_client", - new=kms_client, - ), - ): - from prowler.providers.gcp.services.kms.kms_key_rotation_enabled.kms_key_rotation_enabled import ( - kms_key_rotation_enabled, - ) - from prowler.providers.gcp.services.kms.kms_service import ( - CriptoKey, - KeyLocation, - KeyRing, - ) - - kms_client.project_ids = [GCP_PROJECT_ID] - kms_client.region = GCP_US_CENTER1_LOCATION - - keyring = KeyRing( - name="projects/123/locations/us-central1/keyRings/keyring1", - project_id=GCP_PROJECT_ID, - ) - - keylocation = KeyLocation( - name=GCP_US_CENTER1_LOCATION, - project_id=GCP_PROJECT_ID, - ) - - kms_client.crypto_keys = [ - CriptoKey( - name="key1", - id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", - project_id=GCP_PROJECT_ID, - rotation_period="7776000s", - # Next rotation time of now + 30 days - next_rotation_time=( - datetime.datetime.now() - datetime.timedelta(days=+30) - ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), - key_ring=keyring.name, - location=keylocation.name, - members=["user:jane@example.com"], - ) - ] - check = kms_key_rotation_enabled() result = check.execute() assert len(result) == 1 assert result[0].status == "PASS" assert ( result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less and the next rotation time is in less than 90 days." + == f"Key {kms_client.crypto_keys[0].name} has automatic rotation enabled." ) assert result[0].resource_id == kms_client.crypto_keys[0].id assert result[0].resource_name == kms_client.crypto_keys[0].name assert result[0].location == kms_client.crypto_keys[0].location assert result[0].project_id == kms_client.crypto_keys[0].project_id - def test_kms_key_rotation_with_fractional_seconds(self): + def test_kms_key_with_short_rotation_period(self): kms_client = mock.MagicMock() with ( @@ -639,13 +193,9 @@ class Test_kms_key_rotation_enabled: name="key1", id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", project_id=GCP_PROJECT_ID, - rotation_period="7776000s", - # Next rotation time of now + 100 days - next_rotation_time=( - datetime.datetime.now() - datetime.timedelta(days=+100) - ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), key_ring=keyring.name, location=keylocation.name, + rotation_period="7776000s", members=["user:jane@example.com"], ) ] @@ -653,10 +203,10 @@ class Test_kms_key_rotation_enabled: check = kms_key_rotation_enabled() result = check.execute() assert len(result) == 1 - assert result[0].status == "FAIL" + assert result[0].status == "PASS" assert ( result[0].status_extended - == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less but the next rotation time is in more than 90 days." + == f"Key {kms_client.crypto_keys[0].name} has automatic rotation enabled." ) assert result[0].resource_id == kms_client.crypto_keys[0].id assert result[0].resource_name == kms_client.crypto_keys[0].name diff --git a/tests/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days_test.py b/tests/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days_test.py new file mode 100644 index 0000000000..e200b9674d --- /dev/null +++ b/tests/providers/gcp/services/kms/kms_key_rotation_max_90_days/kms_key_rotation_max_90_days_test.py @@ -0,0 +1,728 @@ +import datetime +from unittest import mock + +from tests.providers.gcp.gcp_fixtures import ( + GCP_PROJECT_ID, + GCP_US_CENTER1_LOCATION, + set_mocked_gcp_provider, +) + + +class Test_kms_key_rotation_max_90_days: + def test_kms_no_key(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + kms_client.crypto_keys = [] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 0 + + def test_kms_key_no_next_rotation_time_and_no_rotation_period(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + key_ring=keyring.name, + location=keylocation.name, + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_no_next_rotation_time_and_big_rotation_period(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + key_ring=keyring.name, + location=keylocation.name, + rotation_period="8776000s", + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_no_next_rotation_time_and_appropriate_rotation_period(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + key_ring=keyring.name, + location=keylocation.name, + rotation_period="7776000s", + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less but the next rotation time is in more than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_no_rotation_period_and_big_next_rotation_time(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + key_ring=keyring.name, + location=keylocation.name, + # Next rotation time of now + 100 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=+100) + ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_no_rotation_period_and_appropriate_next_rotation_time(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + key_ring=keyring.name, + location=keylocation.name, + # Next rotation time of now + 30 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=+30) + ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less but the next rotation time is in less than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_rotation_period_greater_90_days_and_big_next_rotation_time(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + rotation_period="8776000s", + # Next rotation time of now + 100 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=+100) + ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + key_ring=keyring.name, + location=keylocation.name, + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less and the next rotation time is in more than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_rotation_period_greater_90_days_and_appropriate_next_rotation_time( + self, + ): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + rotation_period="8776000s", + # Next rotation time of now + 30 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=+30) + ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + key_ring=keyring.name, + location=keylocation.name, + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is not rotated every 90 days or less but the next rotation time is in less than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_rotation_period_less_90_days_and_big_next_rotation_time(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + rotation_period="7776000s", + # Next rotation time of now + 100 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=+100) + ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + key_ring=keyring.name, + location=keylocation.name, + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less but the next rotation time is in more than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_rotation_period_less_90_days_and_appropriate_next_rotation_time( + self, + ): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + rotation_period="7776000s", + # Next rotation time of now + 30 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=+30) + ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + key_ring=keyring.name, + location=keylocation.name, + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less and the next rotation time is in less than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_rotation_with_fractional_seconds(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + rotation_period="7776000s", + # Next rotation time of now + 100 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=+100) + ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + key_ring=keyring.name, + location=keylocation.name, + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less but the next rotation time is in more than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id + + def test_kms_key_next_rotation_time_without_fractional_seconds(self): + kms_client = mock.MagicMock() + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days.kms_client", + new=kms_client, + ), + ): + from prowler.providers.gcp.services.kms.kms_key_rotation_max_90_days.kms_key_rotation_max_90_days import ( + kms_key_rotation_max_90_days, + ) + from prowler.providers.gcp.services.kms.kms_service import ( + CriptoKey, + KeyLocation, + KeyRing, + ) + + kms_client.project_ids = [GCP_PROJECT_ID] + kms_client.region = GCP_US_CENTER1_LOCATION + + keyring = KeyRing( + name="projects/123/locations/us-central1/keyRings/keyring1", + project_id=GCP_PROJECT_ID, + ) + + keylocation = KeyLocation( + name=GCP_US_CENTER1_LOCATION, + project_id=GCP_PROJECT_ID, + ) + + kms_client.crypto_keys = [ + CriptoKey( + name="key1", + id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1", + project_id=GCP_PROJECT_ID, + rotation_period="7776000s", + # Next rotation time without fractional seconds, within 90 days + next_rotation_time=( + datetime.datetime.now() - datetime.timedelta(days=30) + ).strftime("%Y-%m-%dT%H:%M:%SZ"), + key_ring=keyring.name, + location=keylocation.name, + members=["user:jane@example.com"], + ) + ] + + check = kms_key_rotation_max_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Key {kms_client.crypto_keys[0].name} is rotated every 90 days or less and the next rotation time is in less than 90 days." + ) + assert result[0].resource_id == kms_client.crypto_keys[0].id + assert result[0].resource_name == kms_client.crypto_keys[0].name + assert result[0].location == kms_client.crypto_keys[0].location + assert result[0].project_id == kms_client.crypto_keys[0].project_id