mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-11-08 20:33:41 +00:00
feat(azure): add Application Gateway WAF and HTTP/2 checks (#13160)
Co-authored-by: pedrooot <pedromarting3@gmail.com>
This commit is contained in:
1 parent
ca6ed48664
commit
8e83de67bc
13 files changed
+1395
-6
No files matched your search
+101
@@ -0,0 +1,101 @@
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.azure.services.network.network_service import (
|
||||
ApplicationGateway,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_DISPLAY,
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
AZURE_SUBSCRIPTION_NAME,
|
||||
RESOURCE_GROUP,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
CHECK = "network_application_gateway_http2_enabled"
|
||||
CHECK_PATH = f"prowler.providers.azure.services.network.{CHECK}.{CHECK}"
|
||||
GATEWAY_ID = f"/subscriptions/{AZURE_SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Network/applicationGateways/agw1"
|
||||
PREFIX = f"Application Gateway agw1 in subscription {AZURE_SUBSCRIPTION_DISPLAY}"
|
||||
|
||||
|
||||
def run_check(gateways, lookup_failed=()):
|
||||
network_client = mock.MagicMock()
|
||||
network_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
|
||||
network_client.application_gateways = gateways
|
||||
network_client.application_gateways_lookup_failed = set(lookup_failed)
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(f"{CHECK_PATH}.network_client", new=network_client),
|
||||
):
|
||||
from prowler.providers.azure.services.network.network_application_gateway_http2_enabled.network_application_gateway_http2_enabled import (
|
||||
network_application_gateway_http2_enabled,
|
||||
)
|
||||
|
||||
return network_application_gateway_http2_enabled().execute()
|
||||
|
||||
|
||||
def single(http2_enabled: bool):
|
||||
gateway = ApplicationGateway(
|
||||
id=GATEWAY_ID, name="agw1", location="westeurope", http2_enabled=http2_enabled
|
||||
)
|
||||
return run_check({AZURE_SUBSCRIPTION_ID: {GATEWAY_ID: gateway}})
|
||||
|
||||
|
||||
UNLISTED = f"Cannot evaluate HTTP/2 on the Application Gateways in subscription {AZURE_SUBSCRIPTION_DISPLAY}: they could not be listed completely. Check the scan log for the error, and verify that Microsoft.Network/applicationGateways/read (included in Reader) is granted to the scanning identity."
|
||||
|
||||
|
||||
def assert_gateway_finding(finding, status, status_extended):
|
||||
assert finding.status == status
|
||||
assert finding.status_extended == status_extended
|
||||
assert finding.subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert finding.resource_id == GATEWAY_ID
|
||||
assert finding.resource_name == "agw1"
|
||||
assert finding.location == "westeurope"
|
||||
|
||||
|
||||
def assert_subscription_finding(finding):
|
||||
assert finding.status == "MANUAL"
|
||||
assert finding.status_extended == UNLISTED
|
||||
assert finding.subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert finding.resource_id == f"/subscriptions/{AZURE_SUBSCRIPTION_ID}"
|
||||
assert finding.resource_name == AZURE_SUBSCRIPTION_ID
|
||||
assert finding.location == "global"
|
||||
|
||||
|
||||
class Test_network_application_gateway_http2_enabled:
|
||||
def test_no_gateways(self):
|
||||
assert run_check({}) == []
|
||||
assert run_check({AZURE_SUBSCRIPTION_ID: {}}) == []
|
||||
|
||||
def test_http2_enabled(self):
|
||||
result = single(True)
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(result[0], "PASS", f"{PREFIX} has HTTP/2 enabled.")
|
||||
|
||||
def test_http2_disabled(self):
|
||||
result = single(False)
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(
|
||||
result[0], "FAIL", f"{PREFIX} does not have HTTP/2 enabled."
|
||||
)
|
||||
|
||||
def test_gateways_lookup_failed_is_manual(self):
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: {}}, lookup_failed={AZURE_SUBSCRIPTION_ID}
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_subscription_finding(result[0])
|
||||
|
||||
def test_partial_lookup_still_reports_listed_gateways(self):
|
||||
gateway = ApplicationGateway(
|
||||
id=GATEWAY_ID, name="agw1", location="westeurope", http2_enabled=True
|
||||
)
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: {GATEWAY_ID: gateway}},
|
||||
lookup_failed={AZURE_SUBSCRIPTION_ID},
|
||||
)
|
||||
assert len(result) == 2
|
||||
assert_subscription_finding(result[0])
|
||||
assert_gateway_finding(result[1], "PASS", f"{PREFIX} has HTTP/2 enabled.")
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.azure.services.network.network_service import (
|
||||
ApplicationGateway,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_DISPLAY,
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
AZURE_SUBSCRIPTION_NAME,
|
||||
RESOURCE_GROUP,
|
||||
set_mocked_azure_provider,
|
||||
)
|
||||
|
||||
CHECK = "network_application_gateway_waf_enabled"
|
||||
CHECK_PATH = f"prowler.providers.azure.services.network.{CHECK}.{CHECK}"
|
||||
GATEWAY_ID = f"/subscriptions/{AZURE_SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Network/applicationGateways/agw1"
|
||||
POLICY_ID = f"/subscriptions/{AZURE_SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/waf1"
|
||||
PREFIX = f"Application Gateway agw1 in subscription {AZURE_SUBSCRIPTION_DISPLAY}"
|
||||
GATEWAYS_UNLISTED = f"Cannot evaluate the WAF policy association of the Application Gateways in subscription {AZURE_SUBSCRIPTION_DISPLAY}: they could not be listed completely. Check the scan log for the error, and verify that Microsoft.Network/applicationGateways/read (included in Reader) is granted to the scanning identity."
|
||||
POLICIES_UNLISTED = f"Cannot evaluate the WAF policy association of the Application Gateways in subscription {AZURE_SUBSCRIPTION_DISPLAY}: the WAF policies associated with them could not be listed or read completely. Check the scan log for the error, and verify that Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/read (included in Reader) is granted to the scanning identity."
|
||||
BOTH_UNLISTED = f"Cannot evaluate the WAF policy association of the Application Gateways in subscription {AZURE_SUBSCRIPTION_DISPLAY}: they could not be listed completely, and the WAF policies associated with them could not be listed or read completely. Check the scan log for the errors, and verify that Microsoft.Network/applicationGateways/read and Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/read (both included in Reader) are granted to the scanning identity."
|
||||
|
||||
|
||||
def build_gateway(**settings) -> ApplicationGateway:
|
||||
return ApplicationGateway(
|
||||
id=GATEWAY_ID, name="agw1", location="westeurope", **settings
|
||||
)
|
||||
|
||||
|
||||
def run_check(
|
||||
gateways,
|
||||
gateways_lookup_failed=(),
|
||||
policies_lookup_failed=(),
|
||||
policy_reads_failed=(),
|
||||
):
|
||||
network_client = mock.MagicMock()
|
||||
network_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME}
|
||||
network_client.application_gateways = gateways
|
||||
network_client.application_gateways_lookup_failed = set(gateways_lookup_failed)
|
||||
network_client.waf_policies_lookup_failed = set(policies_lookup_failed)
|
||||
network_client.waf_policy_reads_failed = set(policy_reads_failed)
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_azure_provider(),
|
||||
),
|
||||
mock.patch(f"{CHECK_PATH}.network_client", new=network_client),
|
||||
):
|
||||
from prowler.providers.azure.services.network.network_application_gateway_waf_enabled.network_application_gateway_waf_enabled import (
|
||||
network_application_gateway_waf_enabled,
|
||||
)
|
||||
|
||||
return network_application_gateway_waf_enabled().execute()
|
||||
|
||||
|
||||
def single(gateway):
|
||||
return run_check({AZURE_SUBSCRIPTION_ID: {gateway.id: gateway}})
|
||||
|
||||
|
||||
def assert_gateway_finding(finding, status, status_extended):
|
||||
assert finding.status == status
|
||||
assert finding.status_extended == status_extended
|
||||
assert finding.subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert finding.resource_id == GATEWAY_ID
|
||||
assert finding.resource_name == "agw1"
|
||||
assert finding.location == "westeurope"
|
||||
|
||||
|
||||
def assert_subscription_finding(finding, status_extended):
|
||||
assert finding.status == "MANUAL"
|
||||
assert finding.status_extended == status_extended
|
||||
assert finding.subscription == AZURE_SUBSCRIPTION_ID
|
||||
assert finding.resource_id == f"/subscriptions/{AZURE_SUBSCRIPTION_ID}"
|
||||
assert finding.resource_name == AZURE_SUBSCRIPTION_ID
|
||||
assert finding.location == "global"
|
||||
|
||||
|
||||
class Test_network_application_gateway_waf_enabled:
|
||||
def test_no_gateways(self):
|
||||
assert run_check({}) == []
|
||||
assert run_check({AZURE_SUBSCRIPTION_ID: {}}) == []
|
||||
|
||||
def test_enabled_waf_policy_associated(self):
|
||||
result = single(
|
||||
build_gateway(firewall_policy_id=POLICY_ID, firewall_policy_enabled=True)
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(
|
||||
result[0], "PASS", f"{PREFIX} has an enabled WAF policy associated."
|
||||
)
|
||||
|
||||
def test_disabled_waf_policy_associated(self):
|
||||
result = single(
|
||||
build_gateway(firewall_policy_id=POLICY_ID, firewall_policy_enabled=False)
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(
|
||||
result[0],
|
||||
"FAIL",
|
||||
f"{PREFIX} has a WAF policy associated, but the policy is disabled.",
|
||||
)
|
||||
|
||||
def test_waf_policy_state_unknown(self):
|
||||
result = single(build_gateway(firewall_policy_id=POLICY_ID))
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(
|
||||
result[0],
|
||||
"MANUAL",
|
||||
f"{PREFIX} has a WAF policy associated, but the state of that policy could not be determined. Check the scan log for the error.",
|
||||
)
|
||||
|
||||
def test_legacy_waf_configuration(self):
|
||||
result = single(build_gateway(waf_configuration_enabled=True))
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(
|
||||
result[0],
|
||||
"FAIL",
|
||||
f"{PREFIX} uses the legacy WAF configuration instead of a WAF policy.",
|
||||
)
|
||||
|
||||
def test_no_waf(self):
|
||||
result = single(build_gateway())
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(
|
||||
result[0], "FAIL", f"{PREFIX} has no gateway-level WAF policy associated."
|
||||
)
|
||||
|
||||
def test_gateways_lookup_failed_is_manual(self):
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: {}}, gateways_lookup_failed={AZURE_SUBSCRIPTION_ID}
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_subscription_finding(result[0], GATEWAYS_UNLISTED)
|
||||
|
||||
def test_partial_gateways_lookup_still_reports_listed_gateways(self):
|
||||
gateway = build_gateway(
|
||||
firewall_policy_id=POLICY_ID, firewall_policy_enabled=True
|
||||
)
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: {GATEWAY_ID: gateway}},
|
||||
gateways_lookup_failed={AZURE_SUBSCRIPTION_ID},
|
||||
)
|
||||
assert len(result) == 2
|
||||
assert_subscription_finding(result[0], GATEWAYS_UNLISTED)
|
||||
assert_gateway_finding(
|
||||
result[1], "PASS", f"{PREFIX} has an enabled WAF policy associated."
|
||||
)
|
||||
|
||||
def test_policies_lookup_failed_reports_unknown_states_once(self):
|
||||
other_id = GATEWAY_ID.replace("agw1", "agw2")
|
||||
gateways = {
|
||||
GATEWAY_ID: build_gateway(firewall_policy_id=POLICY_ID),
|
||||
other_id: ApplicationGateway(
|
||||
id=other_id,
|
||||
name="agw2",
|
||||
location="westeurope",
|
||||
firewall_policy_id=POLICY_ID,
|
||||
),
|
||||
}
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: gateways},
|
||||
policies_lookup_failed={AZURE_SUBSCRIPTION_ID},
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_subscription_finding(result[0], POLICIES_UNLISTED)
|
||||
|
||||
def test_policies_lookup_failed_keeps_gateways_with_known_states(self):
|
||||
other_id = GATEWAY_ID.replace("agw1", "agw2")
|
||||
gateways = {
|
||||
GATEWAY_ID: build_gateway(
|
||||
firewall_policy_id=POLICY_ID, firewall_policy_enabled=False
|
||||
),
|
||||
other_id: ApplicationGateway(
|
||||
id=other_id,
|
||||
name="agw2",
|
||||
location="westeurope",
|
||||
firewall_policy_id=POLICY_ID.replace("waf1", "waf2"),
|
||||
),
|
||||
}
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: gateways},
|
||||
policies_lookup_failed={AZURE_SUBSCRIPTION_ID},
|
||||
)
|
||||
assert len(result) == 2
|
||||
assert_subscription_finding(result[0], POLICIES_UNLISTED)
|
||||
assert_gateway_finding(
|
||||
result[1],
|
||||
"FAIL",
|
||||
f"{PREFIX} has a WAF policy associated, but the policy is disabled.",
|
||||
)
|
||||
|
||||
def test_policies_lookup_failed_without_unknown_states_adds_nothing(self):
|
||||
result = run_check(
|
||||
{
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
GATEWAY_ID: build_gateway(
|
||||
firewall_policy_id=POLICY_ID, firewall_policy_enabled=True
|
||||
)
|
||||
}
|
||||
},
|
||||
policies_lookup_failed={AZURE_SUBSCRIPTION_ID},
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_gateway_finding(
|
||||
result[0], "PASS", f"{PREFIX} has an enabled WAF policy associated."
|
||||
)
|
||||
|
||||
def test_failed_policy_read_reports_unknown_states_once(self):
|
||||
other_id = GATEWAY_ID.replace("agw1", "agw2")
|
||||
gateways = {
|
||||
GATEWAY_ID: build_gateway(firewall_policy_id=POLICY_ID),
|
||||
other_id: ApplicationGateway(
|
||||
id=other_id,
|
||||
name="agw2",
|
||||
location="westeurope",
|
||||
firewall_policy_id=POLICY_ID,
|
||||
),
|
||||
}
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: gateways},
|
||||
policy_reads_failed={POLICY_ID.casefold()},
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_subscription_finding(result[0], POLICIES_UNLISTED)
|
||||
|
||||
def test_failed_policy_read_keeps_gateway_with_missing_policy(self):
|
||||
other_id = GATEWAY_ID.replace("agw1", "agw2")
|
||||
gateways = {
|
||||
GATEWAY_ID: build_gateway(firewall_policy_id=POLICY_ID),
|
||||
other_id: ApplicationGateway(
|
||||
id=other_id,
|
||||
name="agw2",
|
||||
location="westeurope",
|
||||
firewall_policy_id=POLICY_ID.replace("waf1", "waf2"),
|
||||
),
|
||||
}
|
||||
result = run_check(
|
||||
{AZURE_SUBSCRIPTION_ID: gateways},
|
||||
policy_reads_failed={POLICY_ID.upper().casefold()},
|
||||
)
|
||||
assert len(result) == 2
|
||||
assert_subscription_finding(result[0], POLICIES_UNLISTED)
|
||||
assert result[1].resource_id == other_id
|
||||
assert result[1].status == "MANUAL"
|
||||
assert result[1].status_extended == (
|
||||
f"Application Gateway agw2 in subscription {AZURE_SUBSCRIPTION_DISPLAY} has a WAF policy associated, but the state of that policy could not be determined. Check the scan log for the error."
|
||||
)
|
||||
|
||||
def test_unlisted_gateways_and_policies_give_one_finding(self):
|
||||
result = run_check(
|
||||
{
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
GATEWAY_ID: build_gateway(firewall_policy_id=POLICY_ID)
|
||||
}
|
||||
},
|
||||
gateways_lookup_failed={AZURE_SUBSCRIPTION_ID},
|
||||
policies_lookup_failed={AZURE_SUBSCRIPTION_ID},
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert_subscription_finding(result[0], BOTH_UNLISTED)
|
||||
@@ -1,14 +1,23 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from azure.mgmt.network.models import FlowLog
|
||||
from azure.core.exceptions import ResourceNotFoundError
|
||||
from azure.mgmt.network import models as azure_network_models
|
||||
from azure.mgmt.network.models import (
|
||||
ApplicationGatewaySku,
|
||||
ApplicationGatewayWebApplicationFirewallConfiguration,
|
||||
FlowLog,
|
||||
SubResource,
|
||||
)
|
||||
|
||||
from prowler.providers.azure.services.network.network_service import (
|
||||
ApplicationGateway,
|
||||
BastionHost,
|
||||
Network,
|
||||
NetworkWatcher,
|
||||
PublicIp,
|
||||
SecurityGroup,
|
||||
VirtualNetwork,
|
||||
WafPolicy,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
@@ -83,6 +92,30 @@ def mock_network_get_virtual_networks(_):
|
||||
}
|
||||
|
||||
|
||||
def mock_network_get_waf_policies(_):
|
||||
return {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"waf_id": WafPolicy(
|
||||
id="waf_id", name="waf_name", location="location", state="Enabled"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def mock_network_get_application_gateways(_):
|
||||
return {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"id": ApplicationGateway(
|
||||
id="id",
|
||||
name="name",
|
||||
location="location",
|
||||
firewall_policy_id="policy_id",
|
||||
http2_enabled=True,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_security_groups",
|
||||
new=mock_network_get_security_groups,
|
||||
@@ -103,6 +136,14 @@ def mock_network_get_virtual_networks(_):
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_virtual_networks",
|
||||
new=mock_network_get_virtual_networks,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_Service:
|
||||
def test_get_client(self):
|
||||
network = Network(set_mocked_azure_provider())
|
||||
@@ -122,6 +163,21 @@ class Test_Network_Service:
|
||||
assert network.security_groups[AZURE_SUBSCRIPTION_ID][0].location == "location"
|
||||
assert network.security_groups[AZURE_SUBSCRIPTION_ID][0].security_rules == []
|
||||
|
||||
def test_get_waf_policies(self):
|
||||
network = Network(set_mocked_azure_provider())
|
||||
policy = network.waf_policies[AZURE_SUBSCRIPTION_ID]["waf_id"]
|
||||
assert policy.__class__.__name__ == "WafPolicy"
|
||||
assert policy.name == "waf_name"
|
||||
assert policy.state == "Enabled"
|
||||
|
||||
def test_get_application_gateways(self):
|
||||
network = Network(set_mocked_azure_provider())
|
||||
gateway = network.application_gateways[AZURE_SUBSCRIPTION_ID]["id"]
|
||||
assert gateway.__class__.__name__ == "ApplicationGateway"
|
||||
assert gateway.name == "name"
|
||||
assert gateway.firewall_policy_id == "policy_id"
|
||||
assert gateway.http2_enabled is True
|
||||
|
||||
def test_get_network_watchers(self):
|
||||
network = Network(set_mocked_azure_provider())
|
||||
assert (
|
||||
@@ -185,6 +241,14 @@ class Test_Network_Service:
|
||||
)
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_security_groups:
|
||||
def test_get_security_groups_no_resource_groups(self):
|
||||
mock_client = MagicMock()
|
||||
@@ -354,6 +418,14 @@ class Test_Network_get_security_groups:
|
||||
)
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_network_watchers:
|
||||
def test_get_network_watchers_no_resource_groups(self):
|
||||
mock_client = MagicMock()
|
||||
@@ -458,6 +530,14 @@ class Test_Network_get_network_watchers:
|
||||
assert AZURE_SUBSCRIPTION_ID in result
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_bastion_hosts:
|
||||
def test_get_bastion_hosts_no_resource_groups(self):
|
||||
mock_client = MagicMock()
|
||||
@@ -563,6 +643,14 @@ class Test_Network_get_bastion_hosts:
|
||||
assert result[AZURE_SUBSCRIPTION_ID] == []
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_public_ip_addresses:
|
||||
def test_get_public_ip_addresses_no_resource_groups(self):
|
||||
mock_client = MagicMock()
|
||||
@@ -668,6 +756,14 @@ class Test_Network_get_public_ip_addresses:
|
||||
assert result[AZURE_SUBSCRIPTION_ID] == []
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_network_watchers_extra:
|
||||
def test_get_network_watchers_with_multiple_resource_groups(self):
|
||||
mock_client = MagicMock()
|
||||
@@ -737,6 +833,14 @@ class Test_Network_get_network_watchers_extra:
|
||||
mock_client.network_watchers.list.assert_not_called()
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_bastion_hosts_extra:
|
||||
def test_get_bastion_hosts_with_multiple_resource_groups(self):
|
||||
mock_client = MagicMock()
|
||||
@@ -806,6 +910,14 @@ class Test_Network_get_bastion_hosts_extra:
|
||||
)
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_public_ip_addresses_extra:
|
||||
def test_get_public_ip_addresses_with_multiple_resource_groups(self):
|
||||
mock_client = MagicMock()
|
||||
@@ -875,6 +987,14 @@ class Test_Network_get_public_ip_addresses_extra:
|
||||
)
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_waf_policies",
|
||||
new=mock_network_get_waf_policies,
|
||||
)
|
||||
@patch(
|
||||
"prowler.providers.azure.services.network.network_service.Network._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
)
|
||||
class Test_Network_get_virtual_networks_extra:
|
||||
def _ctx(self):
|
||||
return (
|
||||
@@ -1085,3 +1205,395 @@ class Test_Network_get_virtual_networks_extra:
|
||||
mock_client.virtual_networks.list.assert_called_once_with(
|
||||
resource_group_name="RG"
|
||||
)
|
||||
|
||||
|
||||
NETWORK = "prowler.providers.azure.services.network.network_service.Network"
|
||||
GATEWAY_ID = f"/subscriptions/{AZURE_SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Network/applicationGateways/agw1"
|
||||
GATEWAY_POLICY_ID = f"/subscriptions/{AZURE_SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/waf1"
|
||||
|
||||
|
||||
def build_network_with_client(mock_client, resource_groups=None):
|
||||
with (
|
||||
patch(f"{NETWORK}._get_security_groups", new=mock_network_get_security_groups),
|
||||
patch(f"{NETWORK}._get_bastion_hosts", new=mock_network_get_bastion_hosts),
|
||||
patch(
|
||||
f"{NETWORK}._get_network_watchers", new=mock_network_get_network_watchers
|
||||
),
|
||||
patch(
|
||||
f"{NETWORK}._get_public_ip_addresses",
|
||||
new=mock_network_get_public_ip_addresses,
|
||||
),
|
||||
patch(
|
||||
f"{NETWORK}._get_virtual_networks", new=mock_network_get_virtual_networks
|
||||
),
|
||||
patch(f"{NETWORK}._get_waf_policies", new=mock_network_get_waf_policies),
|
||||
patch(
|
||||
f"{NETWORK}._get_application_gateways",
|
||||
new=mock_network_get_application_gateways,
|
||||
),
|
||||
):
|
||||
network = Network(set_mocked_azure_provider())
|
||||
network.clients = {AZURE_SUBSCRIPTION_ID: mock_client}
|
||||
network.resource_groups = resource_groups
|
||||
return network
|
||||
|
||||
|
||||
def build_sdk_waf_policy(state, policy_id=GATEWAY_POLICY_ID, name="waf1"):
|
||||
waf_policy = azure_network_models.WebApplicationFirewallPolicy(
|
||||
id=policy_id,
|
||||
location="westeurope",
|
||||
policy_settings=azure_network_models.PolicySettings(state=state),
|
||||
)
|
||||
waf_policy.name = name
|
||||
return waf_policy
|
||||
|
||||
|
||||
def build_waf_policy_model(state, policy_id=GATEWAY_POLICY_ID):
|
||||
return WafPolicy(id=policy_id, name="waf1", location="westeurope", state=state)
|
||||
|
||||
|
||||
def build_sdk_application_gateway(name="agw1", policy_id=None):
|
||||
gateway = azure_network_models.ApplicationGateway(
|
||||
id=GATEWAY_ID.replace("agw1", name),
|
||||
location="westeurope",
|
||||
sku=ApplicationGatewaySku(name="WAF_v2", tier="WAF_v2"),
|
||||
firewall_policy=SubResource(id=policy_id) if policy_id else None,
|
||||
)
|
||||
gateway.name = name
|
||||
return gateway
|
||||
|
||||
|
||||
def get_gateways(mock_client, listed_policies=(), resource_groups=None):
|
||||
"""Collect gateways with the given WAF policies already listed."""
|
||||
network = build_network_with_client(mock_client, resource_groups)
|
||||
network.waf_policies = {
|
||||
AZURE_SUBSCRIPTION_ID: {policy.id: policy for policy in listed_policies}
|
||||
}
|
||||
return network, network._get_application_gateways()
|
||||
|
||||
|
||||
class Test_Network_get_waf_policies:
|
||||
def test_maps_policy_state(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.web_application_firewall_policies.list_all.return_value = [
|
||||
build_sdk_waf_policy("Enabled")
|
||||
]
|
||||
|
||||
network = build_network_with_client(mock_client)
|
||||
result = network._get_waf_policies()
|
||||
|
||||
assert network.waf_policies_lookup_failed == set()
|
||||
policy = result[AZURE_SUBSCRIPTION_ID][GATEWAY_POLICY_ID]
|
||||
assert policy.id == GATEWAY_POLICY_ID
|
||||
assert policy.name == "waf1"
|
||||
assert policy.location == "westeurope"
|
||||
assert policy.state == "Enabled"
|
||||
|
||||
def test_uses_resource_group_scope(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.web_application_firewall_policies.list.return_value = []
|
||||
|
||||
network = build_network_with_client(
|
||||
mock_client, {AZURE_SUBSCRIPTION_ID: [RESOURCE_GROUP]}
|
||||
)
|
||||
result = network._get_waf_policies()
|
||||
|
||||
mock_client.web_application_firewall_policies.list.assert_called_once_with(
|
||||
resource_group_name=RESOURCE_GROUP
|
||||
)
|
||||
mock_client.web_application_firewall_policies.list_all.assert_not_called()
|
||||
assert result == {AZURE_SUBSCRIPTION_ID: {}}
|
||||
|
||||
def test_api_error_marks_subscription_lookup_failed(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.web_application_firewall_policies.list_all.side_effect = Exception(
|
||||
"boom"
|
||||
)
|
||||
|
||||
network = build_network_with_client(mock_client)
|
||||
result = network._get_waf_policies()
|
||||
|
||||
assert result == {AZURE_SUBSCRIPTION_ID: {}}
|
||||
assert network.waf_policies_lookup_failed == {AZURE_SUBSCRIPTION_ID}
|
||||
|
||||
def test_resource_group_error_marks_subscription_lookup_failed(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.web_application_firewall_policies.list.side_effect = Exception(
|
||||
"boom"
|
||||
)
|
||||
|
||||
network = build_network_with_client(
|
||||
mock_client, {AZURE_SUBSCRIPTION_ID: [RESOURCE_GROUP]}
|
||||
)
|
||||
result = network._get_waf_policies()
|
||||
|
||||
assert result == {AZURE_SUBSCRIPTION_ID: {}}
|
||||
assert network.waf_policies_lookup_failed == {AZURE_SUBSCRIPTION_ID}
|
||||
|
||||
def test_missing_resource_group_is_not_a_failed_lookup(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.web_application_firewall_policies.list.side_effect = (
|
||||
ResourceNotFoundError("gone")
|
||||
)
|
||||
|
||||
network = build_network_with_client(
|
||||
mock_client, {AZURE_SUBSCRIPTION_ID: [RESOURCE_GROUP]}
|
||||
)
|
||||
result = network._get_waf_policies()
|
||||
|
||||
assert result == {AZURE_SUBSCRIPTION_ID: {}}
|
||||
assert network.waf_policies_lookup_failed == set()
|
||||
|
||||
def test_unmappable_policy_is_skipped_and_marks_subscription(self):
|
||||
broken = azure_network_models.WebApplicationFirewallPolicy(
|
||||
location="westeurope"
|
||||
)
|
||||
mock_client = MagicMock()
|
||||
mock_client.web_application_firewall_policies.list_all.return_value = [
|
||||
broken,
|
||||
build_sdk_waf_policy("Enabled"),
|
||||
]
|
||||
|
||||
network = build_network_with_client(mock_client)
|
||||
result = network._get_waf_policies()
|
||||
|
||||
assert list(result[AZURE_SUBSCRIPTION_ID]) == [GATEWAY_POLICY_ID]
|
||||
assert network.waf_policies_lookup_failed == {AZURE_SUBSCRIPTION_ID}
|
||||
|
||||
|
||||
class Test_Network_get_application_gateways:
|
||||
def test_maps_gateway_settings(self):
|
||||
gateway = azure_network_models.ApplicationGateway(
|
||||
id=GATEWAY_ID,
|
||||
location="westeurope",
|
||||
sku=ApplicationGatewaySku(name="WAF_v2", tier="WAF_v2"),
|
||||
firewall_policy=SubResource(id=GATEWAY_POLICY_ID),
|
||||
enable_http2=True,
|
||||
)
|
||||
gateway.name = "agw1"
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [gateway]
|
||||
|
||||
network, result = get_gateways(mock_client, [build_waf_policy_model("Enabled")])
|
||||
|
||||
assert network.application_gateways_lookup_failed == set()
|
||||
mock_client.web_application_firewall_policies.get.assert_not_called()
|
||||
mapped = result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID]
|
||||
assert mapped.name == "agw1"
|
||||
assert mapped.location == "westeurope"
|
||||
assert mapped.firewall_policy_id == GATEWAY_POLICY_ID
|
||||
assert mapped.firewall_policy_enabled is True
|
||||
assert mapped.waf_configuration_enabled is False
|
||||
assert mapped.http2_enabled is True
|
||||
|
||||
def test_maps_legacy_waf_configuration(self):
|
||||
waf_configuration = ApplicationGatewayWebApplicationFirewallConfiguration(
|
||||
enabled=True,
|
||||
firewall_mode="Prevention",
|
||||
rule_set_type="OWASP",
|
||||
rule_set_version="3.2",
|
||||
)
|
||||
gateway = azure_network_models.ApplicationGateway(
|
||||
id=GATEWAY_ID,
|
||||
location="westeurope",
|
||||
sku=ApplicationGatewaySku(name="WAF_v2", tier="WAF_v2"),
|
||||
web_application_firewall_configuration=waf_configuration,
|
||||
)
|
||||
gateway.name = "agw1"
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [gateway]
|
||||
|
||||
_, result = get_gateways(mock_client)
|
||||
|
||||
mapped = result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID]
|
||||
assert mapped.firewall_policy_id is None
|
||||
assert mapped.firewall_policy_enabled is None
|
||||
assert mapped.waf_configuration_enabled is True
|
||||
assert mapped.http2_enabled is False
|
||||
|
||||
def test_uses_resource_group_scope(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list.return_value = []
|
||||
|
||||
_, result = get_gateways(
|
||||
mock_client, resource_groups={AZURE_SUBSCRIPTION_ID: [RESOURCE_GROUP]}
|
||||
)
|
||||
|
||||
mock_client.application_gateways.list.assert_called_once_with(
|
||||
resource_group_name=RESOURCE_GROUP
|
||||
)
|
||||
mock_client.application_gateways.list_all.assert_not_called()
|
||||
assert result[AZURE_SUBSCRIPTION_ID] == {}
|
||||
|
||||
def test_api_error_marks_subscription_lookup_failed(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.side_effect = Exception("boom")
|
||||
|
||||
network, result = get_gateways(mock_client)
|
||||
|
||||
assert result == {AZURE_SUBSCRIPTION_ID: {}}
|
||||
assert network.application_gateways_lookup_failed == {AZURE_SUBSCRIPTION_ID}
|
||||
|
||||
def test_resource_group_error_marks_subscription_lookup_failed(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list.side_effect = Exception("boom")
|
||||
|
||||
network, result = get_gateways(
|
||||
mock_client, resource_groups={AZURE_SUBSCRIPTION_ID: [RESOURCE_GROUP]}
|
||||
)
|
||||
|
||||
assert result == {AZURE_SUBSCRIPTION_ID: {}}
|
||||
assert network.application_gateways_lookup_failed == {AZURE_SUBSCRIPTION_ID}
|
||||
|
||||
def test_maps_disabled_waf_policy_whatever_its_casing(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=GATEWAY_POLICY_ID.upper())
|
||||
]
|
||||
|
||||
_, result = get_gateways(mock_client, [build_waf_policy_model("disabled")])
|
||||
|
||||
assert (
|
||||
result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID].firewall_policy_enabled is False
|
||||
)
|
||||
|
||||
def test_unlisted_waf_policy_is_read_by_id(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=GATEWAY_POLICY_ID)
|
||||
]
|
||||
mock_client.web_application_firewall_policies.get.return_value = (
|
||||
build_sdk_waf_policy("Enabled")
|
||||
)
|
||||
|
||||
_, result = get_gateways(mock_client)
|
||||
|
||||
mock_client.web_application_firewall_policies.get.assert_called_once_with(
|
||||
resource_group_name=RESOURCE_GROUP, policy_name="waf1"
|
||||
)
|
||||
assert result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID].firewall_policy_enabled is True
|
||||
|
||||
def test_waf_policy_read_error_is_recorded_and_leaves_state_unknown(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=GATEWAY_POLICY_ID)
|
||||
]
|
||||
mock_client.web_application_firewall_policies.get.side_effect = Exception(
|
||||
"boom"
|
||||
)
|
||||
|
||||
network, result = get_gateways(mock_client)
|
||||
|
||||
assert result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID].firewall_policy_enabled is None
|
||||
assert network.waf_policy_reads_failed == {GATEWAY_POLICY_ID.casefold()}
|
||||
assert network.application_gateways_lookup_failed == set()
|
||||
|
||||
def test_missing_waf_policy_is_not_a_failed_read(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=GATEWAY_POLICY_ID)
|
||||
]
|
||||
mock_client.web_application_firewall_policies.get.side_effect = (
|
||||
ResourceNotFoundError("gone")
|
||||
)
|
||||
|
||||
network, result = get_gateways(mock_client)
|
||||
|
||||
assert result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID].firewall_policy_enabled is None
|
||||
assert network.waf_policy_reads_failed == set()
|
||||
|
||||
def test_waf_policy_in_another_subscription_is_not_read(self):
|
||||
other_subscription_policy_id = GATEWAY_POLICY_ID.replace(
|
||||
AZURE_SUBSCRIPTION_ID, "00000000-0000-0000-0000-000000000000"
|
||||
)
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=other_subscription_policy_id)
|
||||
]
|
||||
|
||||
network, result = get_gateways(mock_client)
|
||||
|
||||
mock_client.web_application_firewall_policies.get.assert_not_called()
|
||||
assert result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID].firewall_policy_enabled is None
|
||||
assert network.waf_policy_reads_failed == set()
|
||||
|
||||
def test_waf_policy_without_known_state_leaves_state_unknown(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=GATEWAY_POLICY_ID)
|
||||
]
|
||||
|
||||
_, result = get_gateways(mock_client, [build_waf_policy_model(None)])
|
||||
|
||||
mock_client.web_application_firewall_policies.get.assert_not_called()
|
||||
assert result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID].firewall_policy_enabled is None
|
||||
|
||||
def test_policy_reference_that_is_not_a_waf_policy_id_is_not_read(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=f"{GATEWAY_POLICY_ID}/extra")
|
||||
]
|
||||
|
||||
network, result = get_gateways(mock_client)
|
||||
|
||||
mock_client.web_application_firewall_policies.get.assert_not_called()
|
||||
assert result[AZURE_SUBSCRIPTION_ID][GATEWAY_ID].firewall_policy_enabled is None
|
||||
assert network.waf_policy_reads_failed == set()
|
||||
|
||||
def test_shared_unlisted_waf_policy_is_read_once(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
build_sdk_application_gateway(policy_id=GATEWAY_POLICY_ID),
|
||||
build_sdk_application_gateway(
|
||||
name="agw2", policy_id=GATEWAY_POLICY_ID.upper()
|
||||
),
|
||||
]
|
||||
mock_client.web_application_firewall_policies.get.return_value = (
|
||||
build_sdk_waf_policy("Enabled")
|
||||
)
|
||||
|
||||
_, result = get_gateways(mock_client)
|
||||
|
||||
assert len(result[AZURE_SUBSCRIPTION_ID]) == 2
|
||||
mock_client.web_application_firewall_policies.get.assert_called_once()
|
||||
|
||||
def test_unmappable_gateway_is_skipped_and_marks_subscription(self):
|
||||
broken = azure_network_models.ApplicationGateway(location="westeurope")
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list_all.return_value = [
|
||||
broken,
|
||||
build_sdk_application_gateway(),
|
||||
]
|
||||
|
||||
network, result = get_gateways(mock_client)
|
||||
|
||||
assert list(result[AZURE_SUBSCRIPTION_ID]) == [GATEWAY_ID]
|
||||
assert network.application_gateways_lookup_failed == {AZURE_SUBSCRIPTION_ID}
|
||||
|
||||
def test_failed_resource_group_keeps_gateways_from_the_others(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list.side_effect = [
|
||||
[build_sdk_application_gateway()],
|
||||
Exception("boom"),
|
||||
]
|
||||
|
||||
network, result = get_gateways(
|
||||
mock_client,
|
||||
resource_groups={AZURE_SUBSCRIPTION_ID: [RESOURCE_GROUP, "rg2"]},
|
||||
)
|
||||
|
||||
assert list(result[AZURE_SUBSCRIPTION_ID]) == [GATEWAY_ID]
|
||||
assert network.application_gateways_lookup_failed == {AZURE_SUBSCRIPTION_ID}
|
||||
|
||||
def test_missing_resource_group_is_not_a_failed_lookup(self):
|
||||
mock_client = MagicMock()
|
||||
mock_client.application_gateways.list.side_effect = ResourceNotFoundError(
|
||||
"gone"
|
||||
)
|
||||
|
||||
network, result = get_gateways(
|
||||
mock_client, resource_groups={AZURE_SUBSCRIPTION_ID: [RESOURCE_GROUP]}
|
||||
)
|
||||
|
||||
assert result == {AZURE_SUBSCRIPTION_ID: {}}
|
||||
assert network.application_gateways_lookup_failed == set()
|
||||
Reference in new issue
Block a user