From c795d76fe98e36c7f80ea1141636863b89250ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Mon, 5 Feb 2024 13:51:56 +0100 Subject: [PATCH] feat(azure): Defender checks related to security contacts and notifications (#3344) --- .../__init__.py | 0 ...ured_with_a_security_contact.metadata.json | 30 +++ ...mail_configured_with_a_security_contact.py | 36 +++ .../__init__.py | 0 ...tify_alerts_severity_is_high.metadata.json | 30 +++ ...r_ensure_notify_alerts_severity_is_high.py | 27 +++ .../__init__.py | 0 ...sure_notify_emails_to_owners.metadata.json | 30 +++ ...defender_ensure_notify_emails_to_owners.py | 34 +++ .../services/defender/defender_service.py | 38 ++++ ...configured_with_a_security_contact_test.py | 210 ++++++++++++++++++ ...ure_notify_alerts_severity_is_high_test.py | 99 +++++++++ ...der_ensure_notify_emails_to_owners_test.py | 136 ++++++++++++ .../defender/defender_service_test.py | 58 +++++ 14 files changed, 728 insertions(+) create mode 100644 prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/__init__.py create mode 100644 prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.metadata.json create mode 100644 prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.py create mode 100644 prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/__init__.py create mode 100644 prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.metadata.json create mode 100644 prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.py create mode 100644 prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/__init__.py create mode 100644 prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.metadata.json create mode 100644 prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.py create mode 100644 tests/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact_test.py create mode 100644 tests/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high_test.py create mode 100644 tests/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners_test.py diff --git a/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/__init__.py b/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.metadata.json b/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.metadata.json new file mode 100644 index 00000000..1efa561a --- /dev/null +++ b/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "defender_additional_email_configured_with_a_security_contact", + "CheckTitle": "Ensure 'Additional email addresses' is Configured with a Security Contact Email", + "CheckType": [], + "ServiceName": "defender", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AzureEmailNotifications", + "Description": "Microsoft Defender for Cloud emails the subscription owners whenever a high-severity alert is triggered for their subscription. You should provide a security contact email address as an additional email address.", + "Risk": "Microsoft Defender for Cloud emails the Subscription Owner to notify them about security alerts. Adding your Security Contact's email address to the 'Additional email addresses' field ensures that your organization's Security Team is included in these alerts. This ensures that the proper people are aware of any potential compromise in order to mitigate the risk in a timely fashion.", + "RelatedUrl": "https://docs.microsoft.com/en-us/azure/security-center/security-center-provide-security-contact-details", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/SecurityCenter/security-contact-email.html", + "Terraform": "" + }, + "Recommendation": { + "Text": "1. From Azure Home select the Portal Menu 2. Select Microsoft Defender for Cloud 3. Click on Environment Settings 4. Click on the appropriate Management Group, Subscription, or Workspace 5. Click on Email notifications 6. Enter a valid security contact email address (or multiple addresses separated by commas) in the Additional email addresses field 7. Click Save", + "Url": "https://learn.microsoft.com/en-us/rest/api/defenderforcloud/security-contacts/list?view=rest-defenderforcloud-2020-01-01-preview&tabs=HTTP" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.py b/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.py new file mode 100644 index 00000000..98bcbb80 --- /dev/null +++ b/prowler/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact.py @@ -0,0 +1,36 @@ +import re + +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.defender.defender_client import defender_client + + +class defender_additional_email_configured_with_a_security_contact(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for ( + subscription_name, + security_contacts, + ) in defender_client.security_contacts.items(): + for contact_name, contact_info in security_contacts.items(): + report = Check_Report_Azure(self.metadata()) + report.status = "PASS" + report.subscription = subscription_name + report.resource_name = contact_name + report.resource_id = contact_info.resource_id + report.status_extended = f"There is another correct email configured for susbscription {subscription_name}." + + emails = contact_info.emails.split(";") + + for email in emails: + if re.fullmatch( + r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", email + ): + break + else: + report.status = "FAIL" + report.status_extended = f"There is not another correct email configured for susbscription {subscription_name}." + + findings.append(report) + + return findings diff --git a/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/__init__.py b/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.metadata.json b/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.metadata.json new file mode 100644 index 00000000..1896a27a --- /dev/null +++ b/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "defender_ensure_notify_alerts_severity_is_high", + "CheckTitle": "Ensure That 'Notify about alerts with the following severity' is Set to 'High'", + "CheckType": [], + "ServiceName": "defender", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "AzureEmailNotifications", + "Description": "Microsoft Defender for Cloud emails the subscription owners whenever a high-severity alert is triggered for their subscription. You should provide a security contact email address as an additional email address.", + "Risk": "Microsoft Defender for Cloud emails the Subscription Owner to notify them about security alerts. Adding your Security Contact's email address to the 'Additional email addresses' field ensures that your organization's Security Team is included in these alerts. This ensures that the proper people are aware of any potential compromise in order to mitigate the risk in a timely fashion.", + "RelatedUrl": "https://docs.microsoft.com/en-us/azure/security-center/security-center-provide-security-contact-details", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/SecurityCenter/enable-high-severity-email-notifications.html", + "Terraform": "" + }, + "Recommendation": { + "Text": "1. From Azure Home select the Portal Menu 2. Select Microsoft Defender for Cloud 3. Click on Environment Settings 4. Click on the appropriate Management Group, Subscription, or Workspace 5. Click on Email notifications 6. Enter a valid security contact email address (or multiple addresses separated by commas) in the Additional email addresses field 7. Click Save", + "Url": "https://docs.microsoft.com/en-us/rest/api/securitycenter/securitycontacts/list" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.py b/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.py new file mode 100644 index 00000000..ca793906 --- /dev/null +++ b/prowler/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high.py @@ -0,0 +1,27 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.defender.defender_client import defender_client + + +class defender_ensure_notify_alerts_severity_is_high(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for ( + subscription_name, + security_contacts, + ) in defender_client.security_contacts.items(): + for contact_name, contact_info in security_contacts.items(): + report = Check_Report_Azure(self.metadata()) + report.status = "PASS" + report.subscription = subscription_name + report.resource_name = contact_name + report.resource_id = contact_info.resource_id + report.status_extended = f"Notifiy alerts are enabled for severity high in susbscription {subscription_name}." + + if contact_info.alert_notifications_minimal_severity != "High": + report.status = "FAIL" + report.status_extended = f"Notifiy alerts are not enabled for severity high in susbscription {subscription_name}." + + findings.append(report) + + return findings diff --git a/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/__init__.py b/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.metadata.json b/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.metadata.json new file mode 100644 index 00000000..a0375f9e --- /dev/null +++ b/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "defender_ensure_notify_emails_to_owners", + "CheckTitle": "Ensure That 'All users with the following roles' is set to 'Owner'", + "CheckType": [], + "ServiceName": "defender", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AzureEmailNotifications", + "Description": "Enable security alert emails to subscription owners.", + "Risk": "Enabling security alert emails to subscription owners ensures that they receive security alert emails from Microsoft. This ensures that they are aware of any potential security issues and can mitigate the risk in a timely fashion.", + "RelatedUrl": "https://docs.microsoft.com/en-us/azure/security-center/security-center-provide-security-contact-details", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/azure/SecurityCenter/email-to-subscription-owners.html", + "Terraform": "" + }, + "Recommendation": { + "Text": "1. From Azure Home select the Portal Menu 2. Select Microsoft Defender for Cloud 3. Click on Environment Settings 4. Click on the appropriate Management Group, Subscription, or Workspace 5. Click on Email notifications 6. In the drop down of the All users with the following roles field select Owner 7. Click Save", + "Url": "https://docs.microsoft.com/en-us/rest/api/securitycenter/securitycontacts/list" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.py b/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.py new file mode 100644 index 00000000..c689b652 --- /dev/null +++ b/prowler/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners.py @@ -0,0 +1,34 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.defender.defender_client import defender_client + + +class defender_ensure_notify_emails_to_owners(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for ( + subscription_name, + security_contacts, + ) in defender_client.security_contacts.items(): + + for contact_name, contact_info in security_contacts.items(): + + report = Check_Report_Azure(self.metadata()) + report.status = "PASS" + report.subscription = subscription_name + report.resource_name = contact_name + report.resource_id = contact_info.resource_id + report.status_extended = ( + f"The Owner role is notified for subscription {subscription_name}." + ) + + if ( + contact_info.notified_roles_state != "On" + or "Owner" not in contact_info.notified_roles + ): + report.status = "FAIL" + report.status_extended = f"The Owner role is not notified for subscription {subscription_name}." + + findings.append(report) + + return findings diff --git a/prowler/providers/azure/services/defender/defender_service.py b/prowler/providers/azure/services/defender/defender_service.py index 0133dd28..085ada92 100644 --- a/prowler/providers/azure/services/defender/defender_service.py +++ b/prowler/providers/azure/services/defender/defender_service.py @@ -15,6 +15,7 @@ class Defender(AzureService): self.pricings = self.__get_pricings__() self.auto_provisioning_settings = self.__get_auto_provisioning_settings__() self.assessments = self.__get_assessments__() + self.security_contacts = self.__get_security_contacts__() def __get_pricings__(self): logger.info("Defender - Getting pricings...") @@ -89,6 +90,33 @@ class Defender(AzureService): ) return assessments + def __get_security_contacts__(self): + logger.info("Defender - Getting security contacts...") + security_contacts = {} + for subscription_name, client in self.clients.items(): + try: + # TODO: List all security contacts. For now, the list method is not working. + security_contact_default = client.security_contacts.get("default") + security_contacts.update({subscription_name: {}}) + security_contacts[subscription_name].update( + { + security_contact_default.name: Defender_Security_Contacts( + resource_id=security_contact_default.id, + emails=security_contact_default.emails, + phone=security_contact_default.phone, + alert_notifications_minimal_severity=security_contact_default.alert_notifications.minimal_severity, + alert_notifications_state=security_contact_default.alert_notifications.state, + notified_roles=security_contact_default.notifications_by_role.roles, + notified_roles_state=security_contact_default.notifications_by_role.state, + ) + } + ) + except Exception as error: + logger.error( + f"Subscription name: {subscription_name} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return security_contacts + class Defender_Pricing(BaseModel): resource_id: str @@ -107,3 +135,13 @@ class Defender_Assessments(BaseModel): resource_id: str resource_name: str status: str + + +class Defender_Security_Contacts(BaseModel): + resource_id: str + emails: str + phone: str + alert_notifications_minimal_severity: str + alert_notifications_state: str + notified_roles: list[str] + notified_roles_state: str diff --git a/tests/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact_test.py b/tests/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact_test.py new file mode 100644 index 00000000..e50e5273 --- /dev/null +++ b/tests/providers/azure/services/defender/defender_additional_email_configured_with_a_security_contact/defender_additional_email_configured_with_a_security_contact_test.py @@ -0,0 +1,210 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.azure.services.defender.defender_service import ( + Defender_Security_Contacts, +) +from tests.providers.azure.azure_fixtures import AZURE_SUSCRIPTION + + +class Test_defender_additional_email_configured_with_a_security_contact: + def test_defender_no_notify_emails(self): + defender_client = mock.MagicMock + defender_client.security_contacts = {} + + with mock.patch( + "prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact import ( + defender_additional_email_configured_with_a_security_contact, + ) + + check = defender_additional_email_configured_with_a_security_contact() + result = check.execute() + assert len(result) == 0 + + def test_defender_no_additional_emails(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact import ( + defender_additional_email_configured_with_a_security_contact, + ) + + check = defender_additional_email_configured_with_a_security_contact() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"There is not another correct email configured for susbscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id + + def test_defender_additional_email_bad_format(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="bad_email", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact import ( + defender_additional_email_configured_with_a_security_contact, + ) + + check = defender_additional_email_configured_with_a_security_contact() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"There is not another correct email configured for susbscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id + + def test_defender_additional_email_bad_separator(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="test@test.es, test@test.email.com", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact import ( + defender_additional_email_configured_with_a_security_contact, + ) + + check = defender_additional_email_configured_with_a_security_contact() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"There is not another correct email configured for susbscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id + + def test_defender_additional_email_good_format(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="test@test.com", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact import ( + defender_additional_email_configured_with_a_security_contact, + ) + + check = defender_additional_email_configured_with_a_security_contact() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"There is another correct email configured for susbscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id + + def test_defender_additional_email_good_format_multiple_subdomains(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="test@test.mail.es; bad_mail", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_additional_email_configured_with_a_security_contact.defender_additional_email_configured_with_a_security_contact import ( + defender_additional_email_configured_with_a_security_contact, + ) + + check = defender_additional_email_configured_with_a_security_contact() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"There is another correct email configured for susbscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id diff --git a/tests/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high_test.py b/tests/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high_test.py new file mode 100644 index 00000000..65182e5f --- /dev/null +++ b/tests/providers/azure/services/defender/defender_ensure_notify_alerts_severity_is_high/defender_ensure_notify_alerts_severity_is_high_test.py @@ -0,0 +1,99 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.azure.services.defender.defender_service import ( + Defender_Security_Contacts, +) +from tests.providers.azure.azure_fixtures import AZURE_SUSCRIPTION + + +class Test_defender_ensure_notify_alerts_severity_is_high: + def test_defender_no_severity_alerts(self): + defender_client = mock.MagicMock + defender_client.security_contacts = {} + + with mock.patch( + "prowler.providers.azure.services.defender.defender_ensure_notify_alerts_severity_is_high.defender_ensure_notify_alerts_severity_is_high.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_ensure_notify_alerts_severity_is_high.defender_ensure_notify_alerts_severity_is_high import ( + defender_ensure_notify_alerts_severity_is_high, + ) + + check = defender_ensure_notify_alerts_severity_is_high() + result = check.execute() + assert len(result) == 0 + + def test_defender_severity_alerts_low(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="", + phone="", + alert_notifications_minimal_severity="Low", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_ensure_notify_alerts_severity_is_high.defender_ensure_notify_alerts_severity_is_high.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_ensure_notify_alerts_severity_is_high.defender_ensure_notify_alerts_severity_is_high import ( + defender_ensure_notify_alerts_severity_is_high, + ) + + check = defender_ensure_notify_alerts_severity_is_high() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Notifiy alerts are not enabled for severity high in susbscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id + + def test_defender_severity_alerts_high(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_ensure_notify_alerts_severity_is_high.defender_ensure_notify_alerts_severity_is_high.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_ensure_notify_alerts_severity_is_high.defender_ensure_notify_alerts_severity_is_high import ( + defender_ensure_notify_alerts_severity_is_high, + ) + + check = defender_ensure_notify_alerts_severity_is_high() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Notifiy alerts are enabled for severity high in susbscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id diff --git a/tests/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners_test.py b/tests/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners_test.py new file mode 100644 index 00000000..344821a2 --- /dev/null +++ b/tests/providers/azure/services/defender/defender_ensure_notify_emails_to_owners/defender_ensure_notify_emails_to_owners_test.py @@ -0,0 +1,136 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.azure.services.defender.defender_service import ( + Defender_Security_Contacts, +) +from tests.providers.azure.azure_fixtures import AZURE_SUSCRIPTION + + +class Test_defender_ensure_notify_emails_to_owners: + def test_defender_no_notify_emails(self): + defender_client = mock.MagicMock + defender_client.security_contacts = {} + + with mock.patch( + "prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners import ( + defender_ensure_notify_emails_to_owners, + ) + + check = defender_ensure_notify_emails_to_owners() + result = check.execute() + assert len(result) == 0 + + def test_defender_no_notify_emails_to_owners(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners import ( + defender_ensure_notify_emails_to_owners, + ) + + check = defender_ensure_notify_emails_to_owners() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"The Owner role is not notified for subscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id + + def test_defender_notify_emails_to_owners_off(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Owner", "Contributor"], + notified_roles_state="Off", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners import ( + defender_ensure_notify_emails_to_owners, + ) + + check = defender_ensure_notify_emails_to_owners() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"The Owner role is not notified for subscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id + + def test_defender_notify_emails_to_owners(self): + resource_id = str(uuid4()) + defender_client = mock.MagicMock + defender_client.security_contacts = { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id=resource_id, + emails="test@test.es", + phone="", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Owner", "Contributor"], + notified_roles_state="On", + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners.defender_client", + new=defender_client, + ): + from prowler.providers.azure.services.defender.defender_ensure_notify_emails_to_owners.defender_ensure_notify_emails_to_owners import ( + defender_ensure_notify_emails_to_owners, + ) + + check = defender_ensure_notify_emails_to_owners() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"The Owner role is notified for subscription {AZURE_SUSCRIPTION}." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == "default" + assert result[0].resource_id == resource_id diff --git a/tests/providers/azure/services/defender/defender_service_test.py b/tests/providers/azure/services/defender/defender_service_test.py index b50668ef..2d440345 100644 --- a/tests/providers/azure/services/defender/defender_service_test.py +++ b/tests/providers/azure/services/defender/defender_service_test.py @@ -6,6 +6,7 @@ from prowler.providers.azure.services.defender.defender_service import ( Defender, Defender_Assessments, Defender_Pricing, + Defender_Security_Contacts, ) from tests.providers.azure.azure_fixtures import ( AZURE_SUSCRIPTION, @@ -50,6 +51,22 @@ def mock_defender_get_assessments(_): } +def mock_defender_get_security_contacts(_): + return { + AZURE_SUSCRIPTION: { + "default": Defender_Security_Contacts( + resource_id="/subscriptions/resource_id", + emails="user@user.com, test@test.es", + phone="666666666", + alert_notifications_minimal_severity="High", + alert_notifications_state="On", + notified_roles=["Owner", "Contributor"], + notified_roles_state="On", + ) + } + } + + @patch( "prowler.providers.azure.services.defender.defender_service.Defender.__get_pricings__", new=mock_defender_get_pricings, @@ -62,6 +79,10 @@ def mock_defender_get_assessments(_): "prowler.providers.azure.services.defender.defender_service.Defender.__get_assessments__", new=mock_defender_get_assessments, ) +@patch( + "prowler.providers.azure.services.defender.defender_service.Defender.__get_security_contacts__", + new=mock_defender_get_security_contacts, +) class Test_Defender_Service: def test__get_client__(self): defender = Defender(set_mocked_azure_audit_info()) @@ -129,3 +150,40 @@ class Test_Defender_Service: == "default" ) assert defender.assessments[AZURE_SUSCRIPTION]["default"].status == "Healthy" + + def test__get_security_contacts__(self): + defender = Defender(set_mocked_azure_audit_info()) + assert len(defender.security_contacts) == 1 + assert ( + defender.security_contacts[AZURE_SUSCRIPTION]["default"].resource_id + == "/subscriptions/resource_id" + ) + assert ( + defender.security_contacts[AZURE_SUSCRIPTION]["default"].emails + == "user@user.com, test@test.es" + ) + assert ( + defender.security_contacts[AZURE_SUSCRIPTION]["default"].phone + == "666666666" + ) + assert ( + defender.security_contacts[AZURE_SUSCRIPTION][ + "default" + ].alert_notifications_minimal_severity + == "High" + ) + assert ( + defender.security_contacts[AZURE_SUSCRIPTION][ + "default" + ].alert_notifications_state + == "On" + ) + assert defender.security_contacts[AZURE_SUSCRIPTION][ + "default" + ].notified_roles == ["Owner", "Contributor"] + assert ( + defender.security_contacts[AZURE_SUSCRIPTION][ + "default" + ].notified_roles_state + == "On" + )