mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
feat(azure): add defender_ensure_defender_cspm_is_on check (#11037)
Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
This commit is contained in:
@@ -19,6 +19,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `cosmosdb_account_public_network_access_disabled` check for Azure provider, verifying Cosmos DB accounts have public network access disabled so connectivity is restricted to private endpoints or VNet service endpoints [(#11034)](https://github.com/prowler-cloud/prowler/pull/11034)
|
||||
- `databricks_workspace_public_network_access_disabled` check for Azure provider, verifying Databricks workspaces have public network access disabled so connectivity is restricted to Azure Private Link private endpoints [(#11035)](https://github.com/prowler-cloud/prowler/pull/11035)
|
||||
- `databricks_workspace_no_public_ip_enabled` check for Azure provider, verifying Databricks workspaces use secure cluster connectivity (no public IP) so compute nodes are not assigned public IP addresses [(#11036)](https://github.com/prowler-cloud/prowler/pull/11036)
|
||||
- `defender_ensure_defender_cspm_is_on` check for Azure provider, verifying Microsoft Defender Cloud Security Posture Management (CSPM) is enabled on the Standard tier [(#11037)](https://github.com/prowler-cloud/prowler/pull/11037)
|
||||
- `aks_cluster_auto_upgrade_enabled` check for Azure provider [(#11027)](https://github.com/prowler-cloud/prowler/pull/11027)
|
||||
- Public `Provider.get_class()` method that resolves a provider class by name for both built-in and external (entry-point) providers [(#11398)](https://github.com/prowler-cloud/prowler/pull/11398)
|
||||
- Jira timeout preventing the calls from hanging indefinitely when the Jira endpoint is unreachable or slow [(#11602)](https://github.com/prowler-cloud/prowler/pull/11602)
|
||||
|
||||
@@ -2094,7 +2094,9 @@
|
||||
{
|
||||
"Id": "8.1.1.1",
|
||||
"Description": "Ensure Microsoft Defender CSPM is set to 'On'",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_ensure_defender_cspm_is_on"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "8 Security Services",
|
||||
|
||||
@@ -1340,6 +1340,7 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_ensure_defender_cspm_is_on",
|
||||
"monitor_alert_create_policy_assignment",
|
||||
"monitor_alert_create_update_nsg",
|
||||
"monitor_alert_create_update_public_ip_address_rule",
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "defender_ensure_defender_cspm_is_on",
|
||||
"CheckTitle": "Microsoft Defender CSPM is set to On",
|
||||
"CheckType": [],
|
||||
"ServiceName": "defender",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "microsoft.security/pricings",
|
||||
"ResourceGroup": "security",
|
||||
"Description": "**Microsoft Defender for Cloud** Cloud Security Posture Management (CSPM) plan is evaluated for **standard tier** activation. Defender CSPM provides advanced posture management capabilities including attack path analysis, cloud security explorer, agentless scanning, and governance rules.",
|
||||
"Risk": "Without Defender CSPM, the subscription relies on **foundational CSPM** (free tier) which lacks attack path analysis, agentless vulnerability scanning, and security governance. Advanced threats exploiting misconfiguration chains go undetected.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/azure/defender-for-cloud/concept-cloud-security-posture-management",
|
||||
"https://learn.microsoft.com/en-us/azure/defender-for-cloud/enable-enhanced-security"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az security pricing create -n CloudPosture --tier Standard",
|
||||
"NativeIaC": "```bicep\ntargetScope = 'subscription'\n\nresource defenderCSPM 'Microsoft.Security/pricings@2024-01-01' = {\n name: 'CloudPosture'\n properties: {\n pricingTier: 'Standard' // Critical: enables Defender CSPM\n }\n}\n```",
|
||||
"Other": "1. Sign in to Azure portal\n2. Go to Microsoft Defender for Cloud\n3. Select Environment Settings\n4. Click on the subscription\n5. Set Cloud Security Posture Management (CSPM) to On\n6. Click Save",
|
||||
"Terraform": "```hcl\nresource \"azurerm_security_center_subscription_pricing\" \"<example_resource_name>\" {\n tier = \"Standard\" # Critical: enables Defender CSPM\n resource_type = \"CloudPosture\"\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **Defender CSPM** standard tier for advanced cloud security posture management. Evaluate the cost against the security benefits \u2014 CSPM provides attack path analysis and agentless scanning.",
|
||||
"Url": "https://hub.prowler.com/check/defender_ensure_defender_cspm_is_on"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"threat-detection"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.defender.defender_client import defender_client
|
||||
|
||||
|
||||
class defender_ensure_defender_cspm_is_on(Check):
|
||||
"""
|
||||
Ensure Microsoft Defender Cloud Security Posture Management (CSPM) is set to On.
|
||||
|
||||
This check evaluates whether the Defender CSPM plan (CloudPosture pricing) is enabled with the Standard tier for each subscription.
|
||||
|
||||
- PASS: The CloudPosture pricing tier is "Standard" (Defender CSPM is on).
|
||||
- FAIL: The CloudPosture pricing tier is not "Standard" (Defender CSPM is off).
|
||||
"""
|
||||
|
||||
def execute(self) -> Check_Report_Azure:
|
||||
findings = []
|
||||
for subscription, pricings in defender_client.pricings.items():
|
||||
subscription_name = defender_client.subscriptions.get(
|
||||
subscription, subscription
|
||||
)
|
||||
if "CloudPosture" in pricings:
|
||||
report = Check_Report_Azure(
|
||||
metadata=self.metadata(),
|
||||
resource=pricings["CloudPosture"],
|
||||
)
|
||||
report.subscription = subscription
|
||||
report.resource_name = "Defender plan CSPM"
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Defender plan CSPM from subscription {subscription_name} ({subscription}) is set to ON (pricing tier standard)."
|
||||
if pricings["CloudPosture"].pricing_tier != "Standard":
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Defender plan CSPM from subscription {subscription_name} ({subscription}) is set to OFF (pricing tier not standard)."
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.defender.defender_service import Pricing
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_DISPLAY,
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
AZURE_SUBSCRIPTION_NAME,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_defender_ensure_defender_cspm_is_on:
|
||||
def test_defender_no_cspm(self):
|
||||
defender_client = mock.MagicMock
|
||||
defender_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
|
||||
defender_client.pricings = {}
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.defender.defender_ensure_defender_cspm_is_on.defender_ensure_defender_cspm_is_on.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.defender.defender_ensure_defender_cspm_is_on.defender_ensure_defender_cspm_is_on import (
|
||||
defender_ensure_defender_cspm_is_on,
|
||||
)
|
||||
|
||||
check = defender_ensure_defender_cspm_is_on()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_defender_cspm_pricing_tier_not_standard(self):
|
||||
resource_id = str(uuid4())
|
||||
defender_client = mock.MagicMock
|
||||
defender_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
|
||||
defender_client.pricings = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"CloudPosture": Pricing(
|
||||
resource_id=resource_id,
|
||||
resource_name="Defender plan CSPM",
|
||||
pricing_tier="Free",
|
||||
free_trial_remaining_time=0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.defender.defender_ensure_defender_cspm_is_on.defender_ensure_defender_cspm_is_on.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.defender.defender_ensure_defender_cspm_is_on.defender_ensure_defender_cspm_is_on import (
|
||||
defender_ensure_defender_cspm_is_on,
|
||||
)
|
||||
|
||||
check = defender_ensure_defender_cspm_is_on()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Defender plan CSPM from subscription {AZURE_SUBSCRIPTION_DISPLAY} is set to OFF (pricing tier not standard)."
|
||||
)
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].resource_name == "Defender plan CSPM"
|
||||
assert result[0].resource_id == resource_id
|
||||
|
||||
def test_defender_cspm_pricing_tier_standard(self):
|
||||
resource_id = str(uuid4())
|
||||
defender_client = mock.MagicMock
|
||||
defender_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
|
||||
defender_client.pricings = {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"CloudPosture": Pricing(
|
||||
resource_id=resource_id,
|
||||
resource_name="Defender plan CSPM",
|
||||
pricing_tier="Standard",
|
||||
free_trial_remaining_time=0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.azure.services.defender.defender_ensure_defender_cspm_is_on.defender_ensure_defender_cspm_is_on.defender_client",
|
||||
new=defender_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.defender.defender_ensure_defender_cspm_is_on.defender_ensure_defender_cspm_is_on import (
|
||||
defender_ensure_defender_cspm_is_on,
|
||||
)
|
||||
|
||||
check = defender_ensure_defender_cspm_is_on()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Defender plan CSPM from subscription {AZURE_SUBSCRIPTION_DISPLAY} is set to ON (pricing tier standard)."
|
||||
)
|
||||
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert result[0].resource_name == "Defender plan CSPM"
|
||||
assert result[0].resource_id == resource_id
|
||||
Reference in New Issue
Block a user