From b7b3c33e3e74d5820f70c36144e83d008d9bbd42 Mon Sep 17 00:00:00 2001 From: Lydia Vilchez Date: Thu, 28 May 2026 10:09:09 +0200 Subject: [PATCH 1/3] feat(googleworkspace): add rules service checks --- .../cis_1.3_googleworkspace.json | 32 +- .../cisa_scuba_0.6_googleworkspace.json | 11 +- .../services/rules/__init__.py | 0 .../__init__.py | 0 ...ege_granted_alert_configured.metadata.json | 37 ++ ...dmin_privilege_granted_alert_configured.py | 61 ++++ .../services/rules/rules_client.py | 6 + .../__init__.py | 0 ...ee_spoofing_alert_configured.metadata.json | 37 ++ ...mail_employee_spoofing_alert_configured.py | 61 ++++ .../__init__.py | 0 ...ked_attacks_alert_configured.metadata.json | 37 ++ ...ernment_backed_attacks_alert_configured.py | 61 ++++ .../__init__.py | 0 ...ed_password_alert_configured.metadata.json | 37 ++ .../rules_leaked_password_alert_configured.py | 61 ++++ .../__init__.py | 0 ...ord_changed_alert_configured.metadata.json | 37 ++ ...rules_password_changed_alert_configured.py | 61 ++++ .../services/rules/rules_service.py | 143 ++++++++ .../__init__.py | 0 ..._suspension_alert_configured.metadata.json | 37 ++ ...us_activity_suspension_alert_configured.py | 61 ++++ .../__init__.py | 0 ...cious_login_alert_configured.metadata.json | 37 ++ ...rules_suspicious_login_alert_configured.py | 61 ++++ .../__init__.py | 0 ...matic_login_alert_configured.metadata.json | 37 ++ ...ous_programmatic_login_alert_configured.py | 61 ++++ ...privilege_granted_alert_configured_test.py | 183 ++++++++++ ...employee_spoofing_alert_configured_test.py | 183 ++++++++++ ...nt_backed_attacks_alert_configured_test.py | 183 ++++++++++ ...s_leaked_password_alert_configured_test.py | 183 ++++++++++ ..._password_changed_alert_configured_test.py | 183 ++++++++++ .../services/rules/rules_service_test.py | 323 ++++++++++++++++++ ...tivity_suspension_alert_configured_test.py | 183 ++++++++++ ..._suspicious_login_alert_configured_test.py | 183 ++++++++++ ...rogrammatic_login_alert_configured_test.py | 183 ++++++++++ 38 files changed, 2754 insertions(+), 9 deletions(-) create mode 100644 prowler/providers/googleworkspace/services/rules/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_client.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_service.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/__init__.py create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json create mode 100644 prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_service_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured_test.py create mode 100644 tests/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured_test.py diff --git a/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json b/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json index 0867da103e7..eb9a65fa89d 100644 --- a/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json +++ b/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json @@ -1801,7 +1801,9 @@ { "Id": "6.1", "Description": "Ensure User's password changed is configured", - "Checks": [], + "Checks": [ + "rules_password_changed_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", @@ -1822,7 +1824,9 @@ { "Id": "6.2", "Description": "Ensure Government-backed attacks is configured", - "Checks": [], + "Checks": [ + "rules_government_backed_attacks_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", @@ -1843,7 +1847,9 @@ { "Id": "6.3", "Description": "Ensure User suspended due to suspicious activity is configured", - "Checks": [], + "Checks": [ + "rules_suspicious_activity_suspension_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", @@ -1864,7 +1870,9 @@ { "Id": "6.4", "Description": "Ensure User granted Admin privilege is configured", - "Checks": [], + "Checks": [ + "rules_admin_privilege_granted_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", @@ -1885,7 +1893,9 @@ { "Id": "6.5", "Description": "Ensure Suspicious programmatic login is configured", - "Checks": [], + "Checks": [ + "rules_suspicious_programmatic_login_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", @@ -1906,7 +1916,9 @@ { "Id": "6.6", "Description": "Ensure Suspicious login is configured", - "Checks": [], + "Checks": [ + "rules_suspicious_login_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", @@ -1927,7 +1939,9 @@ { "Id": "6.7", "Description": "Ensure Leaked password is configured", - "Checks": [], + "Checks": [ + "rules_leaked_password_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", @@ -1948,7 +1962,9 @@ { "Id": "6.8", "Description": "Ensure Gmail potential employee spoofing is configured", - "Checks": [], + "Checks": [ + "rules_gmail_employee_spoofing_alert_configured" + ], "Attributes": [ { "Section": "6 Rules", diff --git a/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json b/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json index 0ce4d2ef6aa..0760d0f7ebd 100644 --- a/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json +++ b/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json @@ -402,7 +402,16 @@ { "Id": "GWS.COMMONCONTROLS.13.1", "Description": "All system-defined alerting rules SHALL be enabled with alerts sent to admin email addresses", - "Checks": [], + "Checks": [ + "rules_password_changed_alert_configured", + "rules_government_backed_attacks_alert_configured", + "rules_suspicious_activity_suspension_alert_configured", + "rules_admin_privilege_granted_alert_configured", + "rules_suspicious_programmatic_login_alert_configured", + "rules_suspicious_login_alert_configured", + "rules_leaked_password_alert_configured", + "rules_gmail_employee_spoofing_alert_configured" + ], "Attributes": [ { "Section": "Common Controls", diff --git a/prowler/providers/googleworkspace/services/rules/__init__.py b/prowler/providers/googleworkspace/services/rules/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json new file mode 100644 index 00000000000..c87b25083c5 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_admin_privilege_granted_alert_configured", + "CheckTitle": "User granted Admin privilege alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **User granted Admin privilege** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are notified when a user is given elevated admin privileges.", + "Risk": "Without this alert enabled, administrators will not be notified when users receive **elevated admin privileges**. Unauthorized privilege escalation could indicate account compromise or insider threats and requires immediate verification.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **User granted Admin privilege** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **Medium**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **User granted Admin privilege** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_admin_privilege_granted_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py new file mode 100644 index 00000000000..3dd509441bb --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "User granted Admin privilege" + + +class rules_admin_privilege_granted_alert_configured(Check): + """Check that the User granted Admin privilege system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/rules/rules_client.py b/prowler/providers/googleworkspace/services/rules/rules_client.py new file mode 100644 index 00000000000..8d1c8821a15 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_client.py @@ -0,0 +1,6 @@ +from prowler.providers.common.provider import Provider +from prowler.providers.googleworkspace.services.rules.rules_service import ( + Rules, +) + +rules_client = Rules(Provider.get_global_provider()) diff --git a/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json new file mode 100644 index 00000000000..510551c6349 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_gmail_employee_spoofing_alert_configured", + "CheckTitle": "Gmail potential employee spoofing alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **Gmail potential employee spoofing** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are notified when incoming messages have a sender name matching the directory but from an external domain.", + "Risk": "Without this alert enabled, administrators will not be notified of potential **employee spoofing via email**. Attackers may impersonate internal employees using external email addresses to conduct phishing attacks against the organization.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **Gmail potential employee spoofing** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **Medium**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **Gmail potential employee spoofing** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_gmail_employee_spoofing_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py new file mode 100644 index 00000000000..7203a08fc79 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "Gmail potential employee spoofing" + + +class rules_gmail_employee_spoofing_alert_configured(Check): + """Check that the Gmail potential employee spoofing system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json new file mode 100644 index 00000000000..22bc210b9ca --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_government_backed_attacks_alert_configured", + "CheckTitle": "Government-backed attacks alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **Government-backed attacks** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are notified when Google believes users are being targeted by a government-backed attacker.", + "Risk": "Without this alert enabled, administrators will not be notified of potential **government-backed attacks** targeting their users. These attacks are sophisticated and require immediate response to protect affected accounts and investigate the threat.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **Government-backed attacks** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **High**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **Government-backed attacks** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_government_backed_attacks_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py new file mode 100644 index 00000000000..635b012bfcc --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "Government-backed attacks" + + +class rules_government_backed_attacks_alert_configured(Check): + """Check that the Government-backed attacks system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json new file mode 100644 index 00000000000..a4bfb2ac10d --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_leaked_password_alert_configured", + "CheckTitle": "Leaked password alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **Leaked password** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are notified when Google detects compromised credentials requiring a password reset.", + "Risk": "Without this alert enabled, administrators will not be notified when Google detects that a user's **credentials have been compromised** in a publicized breach. The user likely reused their password at another site that was breached, and their account requires an immediate password change.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **Leaked password** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **Medium**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **Leaked password** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_leaked_password_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py new file mode 100644 index 00000000000..8b445803481 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "Leaked password" + + +class rules_leaked_password_alert_configured(Check): + """Check that the Leaked password system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json new file mode 100644 index 00000000000..cd1f6270407 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_password_changed_alert_configured", + "CheckTitle": "User's password changed alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **User's password changed** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are promptly notified when user passwords are changed.", + "Risk": "Without this alert enabled, administrators will not be notified when user passwords are changed. This could allow **credential compromise and account takeover** to go undetected, giving attackers time to establish persistence.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **User's password changed** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **Medium**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **User's password changed** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_password_changed_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py new file mode 100644 index 00000000000..c259d980832 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "User's password changed" + + +class rules_password_changed_alert_configured(Check): + """Check that the User's password changed system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/rules/rules_service.py b/prowler/providers/googleworkspace/services/rules/rules_service.py new file mode 100644 index 00000000000..76b0b0df7a5 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_service.py @@ -0,0 +1,143 @@ +from typing import Dict, List, Optional + +from pydantic import BaseModel + +from prowler.lib.logger import logger +from prowler.providers.googleworkspace.lib.service.service import GoogleWorkspaceService + +SYSTEM_RULE_DEFAULTS: Dict[str, str] = { + "User's password changed": "INACTIVE", + "Government-backed attacks": "ACTIVE", + "User suspended due to suspicious activity": "ACTIVE", + "User granted Admin privilege": "INACTIVE", + "Suspicious programmatic login": "ACTIVE", + "Suspicious login": "ACTIVE", + "Leaked password": "ACTIVE", + "Gmail potential employee spoofing": "ACTIVE", +} + + +class Rules(GoogleWorkspaceService): + """Google Workspace Rules service for auditing system-defined alert rules. + + Uses the Cloud Identity Policy API v1 to read system-defined alert rule + configurations from the Admin Console "Rules" section. + """ + + def __init__(self, provider): + super().__init__(provider) + self.system_defined_alerts: List[SystemDefinedAlert] = [] + self.policies_fetched = False + self._fetch_system_defined_alerts() + + def _fetch_system_defined_alerts(self): + """Fetch system-defined alert rules from the Cloud Identity Policy API v1.""" + logger.info("Rules - Fetching system-defined alert rules...") + + try: + service = self._build_service("cloudidentity", "v1") + + if not service: + logger.error("Failed to build Cloud Identity service") + return + + request = service.policies().list( + pageSize=100, + filter='setting.type.matches("rule.system_defined_alerts")', + ) + fetch_succeeded = True + found_rules: Dict[str, SystemDefinedAlert] = {} + + while request is not None: + try: + response = request.execute() + + for policy in response.get("policies", []): + if not self._is_customer_level_policy(policy): + continue + + setting = policy.get("setting", {}) + value = setting.get("value", {}) + display_name = value.get("displayName", "") + + if display_name not in SYSTEM_RULE_DEFAULTS: + continue + + alert = self._parse_alert(value) + found_rules[display_name] = alert + logger.debug( + f"System-defined alert rule: {display_name} " + f"state={alert.state} " + f"has_recipients={alert.email_notifications_enabled}" + ) + + request = service.policies().list_next(request, response) + + except Exception as error: + self._handle_api_error( + error, + "fetching system-defined alert rules", + self.provider.identity.customer_id, + ) + fetch_succeeded = False + break + + for rule_name, default_state in SYSTEM_RULE_DEFAULTS.items(): + if rule_name not in found_rules: + is_active_default = default_state == "ACTIVE" + found_rules[rule_name] = SystemDefinedAlert( + display_name=rule_name, + state=default_state, + email_notifications_enabled=is_active_default, + all_super_admins=is_active_default, + ) + logger.debug( + f"System-defined alert rule (default): {rule_name} " + f"state={default_state}" + ) + + self.system_defined_alerts = list(found_rules.values()) + self.policies_fetched = fetch_succeeded + + logger.info( + f"Rules policies fetched - " + f"{len(self.system_defined_alerts)} system-defined alert rules" + ) + + except Exception as error: + self._handle_api_error( + error, + "fetching system-defined alert rules", + self.provider.identity.customer_id, + ) + self.policies_fetched = False + + @staticmethod + def _parse_alert(value: dict) -> "SystemDefinedAlert": + """Parse a single system-defined alert rule from the API response.""" + display_name = value.get("displayName", "") + state = value.get("state", "INACTIVE") + + alert_center_action = value.get("action", {}).get("alertCenterAction", {}) + severity = alert_center_action.get("alertCenterConfig", {}).get("severity") + recipients = alert_center_action.get("recipients", []) + + all_super_admins = any(r.get("allSuperAdmins") is True for r in recipients) + + return SystemDefinedAlert( + display_name=display_name, + state=state, + severity=severity, + email_notifications_enabled=len(recipients) > 0, + all_super_admins=all_super_admins, + ) + + +class SystemDefinedAlert(BaseModel): + """Model for a system-defined alert rule.""" + + display_name: str + state: str = "INACTIVE" + severity: Optional[str] = None + email_notifications_enabled: bool = False + all_super_admins: bool = False diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json new file mode 100644 index 00000000000..cf723299912 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_suspicious_activity_suspension_alert_configured", + "CheckTitle": "User suspended due to suspicious activity alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **User suspended due to suspicious activity** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are notified when Google suspends an account due to a potential compromise.", + "Risk": "Without this alert enabled, administrators will not be promptly notified when Google **suspends a user account** due to detected compromise. The suspended user cannot work, and the underlying security incident requires immediate investigation.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **User suspended due to suspicious activity** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **High**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **User suspended due to suspicious activity** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_suspicious_activity_suspension_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py new file mode 100644 index 00000000000..56ba6474479 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "User suspended due to suspicious activity" + + +class rules_suspicious_activity_suspension_alert_configured(Check): + """Check that the User suspended due to suspicious activity system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json new file mode 100644 index 00000000000..a3b4d20bf30 --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_suspicious_login_alert_configured", + "CheckTitle": "Suspicious login alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "low", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **Suspicious login** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are notified when Google detects a sign-in attempt that does not match a user's normal behavior.", + "Risk": "Without this alert enabled, administrators will not be notified of **suspicious login attempts** such as sign-ins from unusual locations. This could indicate an active attack using previously obtained credentials.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **Suspicious login** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **Low**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **Suspicious login** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_suspicious_login_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py new file mode 100644 index 00000000000..1836dab12fb --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "Suspicious login" + + +class rules_suspicious_login_alert_configured(Check): + """Check that the Suspicious login system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/__init__.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json new file mode 100644 index 00000000000..a3afc9364fa --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "googleworkspace", + "CheckID": "rules_suspicious_programmatic_login_alert_configured", + "CheckTitle": "Suspicious programmatic login alert rule is configured", + "CheckType": [], + "ServiceName": "rules", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "low", + "ResourceType": "NotDefined", + "ResourceGroup": "monitoring", + "Description": "The **Suspicious programmatic login** system-defined alert rule should be enabled with alerts sent to the alert center, email notifications turned on, and recipients set to all super administrators. This ensures administrators are notified when Google detects suspicious login attempts from applications or programs.", + "Risk": "Without this alert enabled, administrators will not be notified of **suspicious programmatic login attempts**. This could indicate automated credential stuffing or unauthorized API access using compromised credentials.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Select **Rules**\n3. Under **Google protects you by default** select **View list**\n4. Scroll to **Suspicious programmatic login** and select it\n5. Within the Actions pane, click the edit pencil\n6. Select **Send to alert center** to set the alert to ON\n7. Set the alert severity to **Low**\n8. Select **Send email notifications**\n9. Ensure **All super administrators** is selected as recipients\n10. Click **Review** to confirm the values\n11. Click **Update Rule**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure the **Suspicious programmatic login** alert rule with alert center ON, email notifications ON, and recipients set to **all super administrators**.", + "Url": "https://hub.prowler.com/check/rules_suspicious_programmatic_login_alert_configured" + } + }, + "Categories": [ + "forensics-ready" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py new file mode 100644 index 00000000000..4d9239cdc5d --- /dev/null +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py @@ -0,0 +1,61 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.rules.rules_client import ( + rules_client, +) + +RULE_NAME = "Suspicious programmatic login" + + +class rules_suspicious_programmatic_login_alert_configured(Check): + """Check that the Suspicious programmatic login system-defined alert rule is fully configured.""" + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if rules_client.policies_fetched: + for alert in rules_client.system_defined_alerts: + if alert.display_name != RULE_NAME: + continue + + domain = rules_client.provider.identity.domain + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=alert, + resource_id="system_defined_alert", + resource_name=RULE_NAME, + customer_id=rules_client.provider.identity.customer_id, + ) + + is_active = alert.state == "ACTIVE" + has_recipients = alert.email_notifications_enabled + all_super_admins = alert.all_super_admins + + if is_active and has_recipients and all_super_admins: + report.status = "PASS" + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is properly " + f"configured in domain {domain}: alert is ON, email " + f"notifications are enabled, and recipients include " + f"all super administrators." + ) + else: + report.status = "FAIL" + issues = [] + if not is_active: + issues.append("alert is OFF") + if not has_recipients: + issues.append("email notifications are disabled") + elif not all_super_admins: + issues.append( + "email recipients do not include all super administrators" + ) + report.status_extended = ( + f"System-defined alert rule '{RULE_NAME}' is not properly " + f"configured in domain {domain}: {', '.join(issues)}." + ) + + findings.append(report) + + return findings diff --git a/tests/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured_test.py new file mode 100644 index 00000000000..cf1a6128c45 --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "User granted Admin privilege" + + +class TestRulesAdminPrivilegeGrantedAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured import ( + rules_admin_privilege_granted_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_admin_privilege_granted_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured import ( + rules_admin_privilege_granted_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_admin_privilege_granted_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured import ( + rules_admin_privilege_granted_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_admin_privilege_granted_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured import ( + rules_admin_privilege_granted_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_admin_privilege_granted_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_admin_privilege_granted_alert_configured.rules_admin_privilege_granted_alert_configured import ( + rules_admin_privilege_granted_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_admin_privilege_granted_alert_configured() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured_test.py new file mode 100644 index 00000000000..f907b2f89e6 --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "Gmail potential employee spoofing" + + +class TestRulesGmailEmployeeSpoofingAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured import ( + rules_gmail_employee_spoofing_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_gmail_employee_spoofing_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured import ( + rules_gmail_employee_spoofing_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_gmail_employee_spoofing_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured import ( + rules_gmail_employee_spoofing_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_gmail_employee_spoofing_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured import ( + rules_gmail_employee_spoofing_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_gmail_employee_spoofing_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_gmail_employee_spoofing_alert_configured.rules_gmail_employee_spoofing_alert_configured import ( + rules_gmail_employee_spoofing_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_gmail_employee_spoofing_alert_configured() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured_test.py new file mode 100644 index 00000000000..90ec8452582 --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "Government-backed attacks" + + +class TestRulesGovernmentBackedAttacksAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured import ( + rules_government_backed_attacks_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_government_backed_attacks_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured import ( + rules_government_backed_attacks_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_government_backed_attacks_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured import ( + rules_government_backed_attacks_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_government_backed_attacks_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured import ( + rules_government_backed_attacks_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_government_backed_attacks_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_government_backed_attacks_alert_configured.rules_government_backed_attacks_alert_configured import ( + rules_government_backed_attacks_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_government_backed_attacks_alert_configured() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured_test.py new file mode 100644 index 00000000000..ace6c29d137 --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "Leaked password" + + +class TestRulesLeakedPasswordAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured import ( + rules_leaked_password_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_leaked_password_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured import ( + rules_leaked_password_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_leaked_password_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured import ( + rules_leaked_password_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_leaked_password_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured import ( + rules_leaked_password_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_leaked_password_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_leaked_password_alert_configured.rules_leaked_password_alert_configured import ( + rules_leaked_password_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_leaked_password_alert_configured() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured_test.py new file mode 100644 index 00000000000..db1b8056e77 --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "User's password changed" + + +class TestRulesPasswordChangedAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured import ( + rules_password_changed_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_password_changed_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured import ( + rules_password_changed_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_password_changed_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured import ( + rules_password_changed_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_password_changed_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured import ( + rules_password_changed_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_password_changed_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_password_changed_alert_configured.rules_password_changed_alert_configured import ( + rules_password_changed_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_password_changed_alert_configured() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/rules/rules_service_test.py b/tests/providers/googleworkspace/services/rules/rules_service_test.py new file mode 100644 index 00000000000..6368df4bcba --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_service_test.py @@ -0,0 +1,323 @@ +from unittest.mock import MagicMock, patch + +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + set_mocked_googleworkspace_provider, +) + + +class TestRulesService: + def test_fetch_fully_configured_rule(self): + """Test fetching a system-defined alert rule with all 3 conditions met.""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_policies_list = MagicMock() + mock_policies_list.execute.return_value = { + "policies": [ + { + "setting": { + "type": "settings/rule.system_defined_alerts", + "value": { + "displayName": "Suspicious login", + "description": "Google detected a suspicious sign-in.", + "action": { + "alertCenterAction": { + "recipients": [{"allSuperAdmins": True}], + "alertCenterConfig": {"severity": "LOW"}, + } + }, + "state": "ACTIVE", + }, + } + }, + ] + } + mock_service.policies().list.return_value = mock_policies_list + mock_service.policies().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.rules.rules_service import ( + Rules, + ) + + rules = Rules(mock_provider) + + assert rules.policies_fetched is True + assert len(rules.system_defined_alerts) == 8 + + suspicious_login = next( + a + for a in rules.system_defined_alerts + if a.display_name == "Suspicious login" + ) + assert suspicious_login.state == "ACTIVE" + assert suspicious_login.email_notifications_enabled is True + assert suspicious_login.all_super_admins is True + assert suspicious_login.severity == "LOW" + + def test_fetch_rule_without_email_notifications(self): + """Test a rule that is ACTIVE but has no email recipients configured.""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_policies_list = MagicMock() + mock_policies_list.execute.return_value = { + "policies": [ + { + "setting": { + "type": "settings/rule.system_defined_alerts", + "value": { + "displayName": "Government-backed attacks", + "description": "Google believes a user is targeted.", + "action": { + "alertCenterAction": { + "alertCenterConfig": {"severity": "HIGH"} + } + }, + "state": "ACTIVE", + }, + } + }, + ] + } + mock_service.policies().list.return_value = mock_policies_list + mock_service.policies().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.rules.rules_service import ( + Rules, + ) + + rules = Rules(mock_provider) + + gov_attack = next( + a + for a in rules.system_defined_alerts + if a.display_name == "Government-backed attacks" + ) + assert gov_attack.state == "ACTIVE" + assert gov_attack.email_notifications_enabled is False + assert gov_attack.all_super_admins is False + assert gov_attack.severity == "HIGH" + + def test_empty_response_fills_defaults(self): + """Test that all 8 rules get default values when API returns nothing.""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_policies_list = MagicMock() + mock_policies_list.execute.return_value = {"policies": []} + mock_service.policies().list.return_value = mock_policies_list + mock_service.policies().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.rules.rules_service import ( + Rules, + ) + + rules = Rules(mock_provider) + + assert rules.policies_fetched is True + assert len(rules.system_defined_alerts) == 8 + + # All defaults should have no severity (not returned by API) + for alert in rules.system_defined_alerts: + assert alert.severity is None + + # INACTIVE defaults: no email notifications + password_changed = next( + a + for a in rules.system_defined_alerts + if a.display_name == "User's password changed" + ) + assert password_changed.state == "INACTIVE" + assert password_changed.email_notifications_enabled is False + assert password_changed.all_super_admins is False + + admin_privilege = next( + a + for a in rules.system_defined_alerts + if a.display_name == "User granted Admin privilege" + ) + assert admin_privilege.state == "INACTIVE" + assert admin_privilege.email_notifications_enabled is False + assert admin_privilege.all_super_admins is False + + # ACTIVE defaults: email notifications ON with all super admins + suspicious_login = next( + a + for a in rules.system_defined_alerts + if a.display_name == "Suspicious login" + ) + assert suspicious_login.state == "ACTIVE" + assert suspicious_login.email_notifications_enabled is True + assert suspicious_login.all_super_admins is True + + gov_attacks = next( + a + for a in rules.system_defined_alerts + if a.display_name == "Government-backed attacks" + ) + assert gov_attacks.state == "ACTIVE" + assert gov_attacks.email_notifications_enabled is True + assert gov_attacks.all_super_admins is True + + def test_api_error_sets_policies_fetched_false(self): + """Test that API errors result in policies_fetched being False.""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_service.policies().list.side_effect = Exception("API Error") + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.rules.rules_service import ( + Rules, + ) + + rules = Rules(mock_provider) + + assert rules.policies_fetched is False + + def test_build_service_returns_none(self): + """Test early return when _build_service fails.""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_service.GoogleWorkspaceService._build_service", + return_value=None, + ), + ): + from prowler.providers.googleworkspace.services.rules.rules_service import ( + Rules, + ) + + rules = Rules(mock_provider) + + assert rules.policies_fetched is False + assert len(rules.system_defined_alerts) == 0 + + def test_non_cis_rules_are_ignored(self): + """Test that system-defined rules not in the 8 CIS rules are ignored.""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_policies_list = MagicMock() + mock_policies_list.execute.return_value = { + "policies": [ + { + "setting": { + "type": "settings/rule.system_defined_alerts", + "value": { + "displayName": "Device compromised", + "description": "A device has been compromised.", + "state": "ACTIVE", + }, + } + }, + ] + } + mock_service.policies().list.return_value = mock_policies_list + mock_service.policies().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.rules.rules_service import ( + Rules, + ) + + rules = Rules(mock_provider) + + assert len(rules.system_defined_alerts) == 8 + names = {a.display_name for a in rules.system_defined_alerts} + assert "Device compromised" not in names + + def test_system_defined_alert_model(self): + """Test SystemDefinedAlert Pydantic model defaults.""" + from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, + ) + + alert = SystemDefinedAlert(display_name="Test rule") + assert alert.state == "INACTIVE" + assert alert.severity is None + assert alert.email_notifications_enabled is False + assert alert.all_super_admins is False diff --git a/tests/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured_test.py new file mode 100644 index 00000000000..acd60756f06 --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "User suspended due to suspicious activity" + + +class TestRulesSuspiciousActivitySuspensionAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured import ( + rules_suspicious_activity_suspension_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_suspicious_activity_suspension_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured import ( + rules_suspicious_activity_suspension_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_suspicious_activity_suspension_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured import ( + rules_suspicious_activity_suspension_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_suspicious_activity_suspension_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured import ( + rules_suspicious_activity_suspension_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_suspicious_activity_suspension_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_activity_suspension_alert_configured.rules_suspicious_activity_suspension_alert_configured import ( + rules_suspicious_activity_suspension_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_suspicious_activity_suspension_alert_configured() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured_test.py new file mode 100644 index 00000000000..aab70797dc8 --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "Suspicious login" + + +class TestRulesSuspiciousLoginAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured import ( + rules_suspicious_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_suspicious_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured import ( + rules_suspicious_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_suspicious_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured import ( + rules_suspicious_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_suspicious_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured import ( + rules_suspicious_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_suspicious_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_login_alert_configured.rules_suspicious_login_alert_configured import ( + rules_suspicious_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_suspicious_login_alert_configured() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured_test.py b/tests/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured_test.py new file mode 100644 index 00000000000..f1596ace67f --- /dev/null +++ b/tests/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured_test.py @@ -0,0 +1,183 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.rules.rules_service import ( + SystemDefinedAlert, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + set_mocked_googleworkspace_provider, +) + +RULE_NAME = "Suspicious programmatic login" + + +class TestRulesSuspiciousProgrammaticLoginAlertConfigured: + def test_pass_fully_configured(self): + """Test PASS when alert is ON, email notifications ON, recipients = all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured import ( + rules_suspicious_programmatic_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=True, + ) + ] + + check = rules_suspicious_programmatic_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "properly configured" in findings[0].status_extended + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_alert_off(self): + """Test FAIL when alert is OFF (INACTIVE state).""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured import ( + rules_suspicious_programmatic_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="INACTIVE", + ) + ] + + check = rules_suspicious_programmatic_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "alert is OFF" in findings[0].status_extended + + def test_fail_no_email_notifications(self): + """Test FAIL when alert is ON but email notifications are disabled.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured import ( + rules_suspicious_programmatic_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=False, + all_super_admins=False, + ) + ] + + check = rules_suspicious_programmatic_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "email notifications are disabled" in findings[0].status_extended + + def test_fail_recipients_not_all_super_admins(self): + """Test FAIL when email notifications ON but recipients do not include all super admins.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured import ( + rules_suspicious_programmatic_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = True + mock_rules_client.system_defined_alerts = [ + SystemDefinedAlert( + display_name=RULE_NAME, + state="ACTIVE", + severity="MEDIUM", + email_notifications_enabled=True, + all_super_admins=False, + ) + ] + + check = rules_suspicious_programmatic_login_alert_configured() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert ( + "do not include all super administrators" in findings[0].status_extended + ) + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed.""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured.rules_client" + ) as mock_rules_client, + ): + from prowler.providers.googleworkspace.services.rules.rules_suspicious_programmatic_login_alert_configured.rules_suspicious_programmatic_login_alert_configured import ( + rules_suspicious_programmatic_login_alert_configured, + ) + + mock_rules_client.provider = mock_provider + mock_rules_client.policies_fetched = False + mock_rules_client.system_defined_alerts = [] + + check = rules_suspicious_programmatic_login_alert_configured() + findings = check.execute() + + assert len(findings) == 0 From b860f8d042d9d8a4a521dae7bf66be249093f7ea Mon Sep 17 00:00:00 2001 From: Lydia Vilchez Date: Thu, 28 May 2026 10:14:34 +0200 Subject: [PATCH 2/3] docs: update CHANGELOG --- prowler/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 08a01233d79..86cb166ab76 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - `application` service for Okta provider with `application_admin_console_session_idle_timeout_15min`, `application_admin_console_mfa_required`, `application_admin_console_phishing_resistant_authentication`, `application_dashboard_mfa_required`, `application_dashboard_phishing_resistant_authentication`, and `application_authentication_policy_network_zone_enforced` checks [(#11358)](https://github.com/prowler-cloud/prowler/pull/11358) - AWS AI Security Framework compliance for AWS provider [(#11353)](https://github.com/prowler-cloud/prowler/pull/11353) - `storage_account_public_network_access_disabled` check for Azure provider and remapped the Azure CIS "Public Network Access is Disabled" requirements to it [(#11334)](https://github.com/prowler-cloud/prowler/pull/11334) +- 8 Rules service checks for Google Workspace provider using the Cloud Identity Policy API [(#11379)](https://github.com/prowler-cloud/prowler/pull/11379) ### 🐞 Fixed From 00b6d4f69e7e9694507dd541bc0ea2b6cf41f9e1 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero Date: Thu, 28 May 2026 11:12:30 +0200 Subject: [PATCH 3/3] chore: revision --- ...es_admin_privilege_granted_alert_configured.metadata.json | 5 +++-- .../rules_admin_privilege_granted_alert_configured.py | 2 +- ...es_gmail_employee_spoofing_alert_configured.metadata.json | 5 +++-- .../rules_gmail_employee_spoofing_alert_configured.py | 2 +- ..._government_backed_attacks_alert_configured.metadata.json | 4 ++-- .../rules_government_backed_attacks_alert_configured.py | 2 +- .../rules_leaked_password_alert_configured.metadata.json | 5 +++-- .../rules_leaked_password_alert_configured.py | 2 +- .../rules_password_changed_alert_configured.metadata.json | 5 +++-- .../rules_password_changed_alert_configured.py | 2 +- ...icious_activity_suspension_alert_configured.metadata.json | 5 +++-- .../rules_suspicious_activity_suspension_alert_configured.py | 2 +- .../rules_suspicious_login_alert_configured.metadata.json | 5 +++-- .../rules_suspicious_login_alert_configured.py | 2 +- ...picious_programmatic_login_alert_configured.metadata.json | 5 +++-- .../rules_suspicious_programmatic_login_alert_configured.py | 2 +- 16 files changed, 31 insertions(+), 24 deletions(-) diff --git a/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json index c87b25083c5..a93b4fb7374 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be notified when users receive **elevated admin privileges**. Unauthorized privilege escalation could indicate account compromise or insider threats and requires immediate verification.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "logging", + "identity-access" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py index 3dd509441bb..55b9a685bf2 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_admin_privilege_granted_alert_configured/rules_admin_privilege_granted_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, ) diff --git a/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json index 510551c6349..34885cb605b 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be notified of potential **employee spoofing via email**. Attackers may impersonate internal employees using external email addresses to conduct phishing attacks against the organization.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "logging", + "email-security" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py index 7203a08fc79..0993f72d3da 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_gmail_employee_spoofing_alert_configured/rules_gmail_employee_spoofing_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, ) diff --git a/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json index 22bc210b9ca..35fa25f2480 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be notified of potential **government-backed attacks** targeting their users. These attacks are sophisticated and require immediate response to protect affected accounts and investigate the threat.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,7 @@ } }, "Categories": [ - "forensics-ready" + "logging" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py index 635b012bfcc..b566d99e0ca 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_government_backed_attacks_alert_configured/rules_government_backed_attacks_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, ) diff --git a/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json index a4bfb2ac10d..870144a873e 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be notified when Google detects that a user's **credentials have been compromised** in a publicized breach. The user likely reused their password at another site that was breached, and their account requires an immediate password change.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "logging", + "identity-access" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py index 8b445803481..797bed4f710 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_leaked_password_alert_configured/rules_leaked_password_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, ) diff --git a/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json index cd1f6270407..f1f4fbc6226 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be notified when user passwords are changed. This could allow **credential compromise and account takeover** to go undetected, giving attackers time to establish persistence.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "logging", + "identity-access" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py index c259d980832..fb6382caf85 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_password_changed_alert_configured/rules_password_changed_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, ) diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json index cf723299912..b79120abde7 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be promptly notified when Google **suspends a user account** due to detected compromise. The suspended user cannot work, and the underlying security incident requires immediate investigation.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "logging", + "identity-access" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py index 56ba6474479..cd243be8e39 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_activity_suspension_alert_configured/rules_suspicious_activity_suspension_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, ) diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json index a3b4d20bf30..93af99b5654 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be notified of **suspicious login attempts** such as sign-ins from unusual locations. This could indicate an active attack using previously obtained credentials.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "logging", + "identity-access" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py index 1836dab12fb..eee7844c43d 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_login_alert_configured/rules_suspicious_login_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, ) diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json index a3afc9364fa..202df2adadc 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.metadata.json @@ -13,7 +13,7 @@ "Risk": "Without this alert enabled, administrators will not be notified of **suspicious programmatic login attempts**. This could indicate automated credential stuffing or unauthorized API access using compromised credentials.", "RelatedUrl": "", "AdditionalURLs": [ - "https://knowledge.workspace.google.com/admin/reports/view-and-edit-system-defined-rules", + "https://support.google.com/a/answer/3230421", "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" ], "Remediation": { @@ -29,7 +29,8 @@ } }, "Categories": [ - "forensics-ready" + "logging", + "identity-access" ], "DependsOn": [], "RelatedTo": [], diff --git a/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py index 4d9239cdc5d..0d609a99e15 100644 --- a/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py +++ b/prowler/providers/googleworkspace/services/rules/rules_suspicious_programmatic_login_alert_configured/rules_suspicious_programmatic_login_alert_configured.py @@ -23,7 +23,7 @@ def execute(self) -> List[CheckReportGoogleWorkspace]: report = CheckReportGoogleWorkspace( metadata=self.metadata(), resource=alert, - resource_id="system_defined_alert", + resource_id=f"systemDefinedAlert/{RULE_NAME}", resource_name=RULE_NAME, customer_id=rules_client.provider.identity.customer_id, )