feat(providers/huaweicloud): add vpc_security_group_open_egress check (#12209)

Co-authored-by: tomitobio <tomitobio@users.noreply.github.com>
Co-authored-by: Hugo P.Brito <hugopbrit@gmail.com>
This commit is contained in:
Tomás Sebastián Tobio
2026-08-20 10:52:13 +01:00
committed by GitHub
co-authored by tomitobio Hugo P.Brito
parent ba564af4f4
commit acb6ff0425
7 changed files with 408 additions and 4 deletions
@@ -6,6 +6,7 @@ from prowler.providers.huaweicloud.services.vpc.vpc_service import (
SecurityGroupRule,
SecurityGroups,
VPCs,
rule_source_is_open,
)
from tests.providers.huaweicloud.huaweicloud_fixtures import (
set_mocked_huaweicloud_provider,
@@ -24,6 +25,18 @@ def _provider_with_client(regional_client):
class TestVPCService:
def test_rule_with_remote_address_group_is_not_open(self):
rule = SecurityGroupRule(
id="rule-1",
direction="ingress",
protocol="tcp",
ethertype="IPv4",
remote_ip_prefix="",
remote_address_group_id="address-group-1",
)
assert rule_source_is_open(rule) is False
def test_list_vpcs_and_security_groups_parses(self):
vpc = SimpleNamespace(
id="vpc-1",
@@ -36,12 +49,14 @@ class TestVPCService:
rule = SimpleNamespace(
id="rule-1",
direction="ingress",
action="deny",
protocol="tcp",
ethertype="IPv4",
port_range_min=22,
port_range_max=22,
remote_ip_prefix="0.0.0.0/0",
remote_group_id="",
remote_address_group_id="address-group-1",
description="ssh open",
)
sg = SimpleNamespace(
@@ -82,8 +97,10 @@ class TestVPCService:
parsed_rule = parsed_sg.rules[0]
assert isinstance(parsed_rule, SecurityGroupRule)
assert parsed_rule.direction == "ingress"
assert parsed_rule.action == "deny"
assert parsed_rule.protocol == "tcp"
assert parsed_rule.remote_ip_prefix == "0.0.0.0/0"
assert parsed_rule.remote_address_group_id == "address-group-1"
assert parsed_rule.port_range_min == 22
assert parsed_rule.port_range_max == 22
@@ -94,12 +111,14 @@ class TestVPCService:
rule = SimpleNamespace(
id="rule-1",
direction=None,
action=None,
protocol=None,
ethertype=None,
port_range_min=None,
port_range_max=None,
remote_ip_prefix=None,
remote_group_id=None,
remote_address_group_id=None,
description=None,
)
sg = SimpleNamespace(
@@ -134,8 +153,10 @@ class TestVPCService:
assert parsed_sg.name == "sg-1" # falls back to id
assert parsed_sg.vpc_id == ""
parsed_rule = parsed_sg.rules[0]
assert parsed_rule.action == "allow"
assert parsed_rule.protocol == ""
assert parsed_rule.remote_ip_prefix == ""
assert parsed_rule.remote_address_group_id == ""
assert parsed_rule.description == ""
assert parsed_rule.direction == ""
@@ -0,0 +1,277 @@
from unittest import mock
from tests.providers.huaweicloud.huaweicloud_fixtures import (
set_mocked_huaweicloud_provider,
)
class TestVpcSecurityGroupOpenEgress:
def test_no_open_egress_passes(self):
vpc_client = mock.MagicMock()
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_huaweicloud_provider(),
),
mock.patch(
"prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress.vpc_client",
new=vpc_client,
),
):
from prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress import (
vpc_security_group_open_egress,
)
from prowler.providers.huaweicloud.services.vpc.vpc_service import (
SecurityGroupRule,
SecurityGroups,
)
sg = SecurityGroups(
id="sg-1",
name="safe-sg",
region="la-south-2",
vpc_id="vpc-1",
rules=[
SecurityGroupRule(
id="rule-1",
direction="egress",
protocol="tcp",
ethertype="IPv4",
remote_ip_prefix="10.0.0.0/24",
port_range_min=443,
port_range_max=443,
),
SecurityGroupRule(
id="deny-rule",
direction="egress",
action="deny",
protocol="",
ethertype="IPv4",
remote_ip_prefix="",
),
SecurityGroupRule(
id="address-group-rule",
direction="egress",
action="allow",
protocol="",
ethertype="IPv4",
remote_ip_prefix="",
remote_address_group_id="address-group-1",
),
],
)
vpc_client.security_groups = {"sg-1": sg}
vpc_client.audited_account = "123456789012"
check = vpc_security_group_open_egress()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].status_extended == (
"Security group safe-sg (sg-1) does not allow open egress to "
"the internet."
)
assert result[0].resource_id == "sg-1"
assert result[0].resource_name == "safe-sg"
assert result[0].resource_arn == (
"huaweicloud:vpc:la-south-2:123456789012:security-group/sg-1"
)
assert result[0].region == "la-south-2"
def test_open_egress_ipv4_fails(self):
vpc_client = mock.MagicMock()
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_huaweicloud_provider(),
),
mock.patch(
"prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress.vpc_client",
new=vpc_client,
),
):
from prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress import (
vpc_security_group_open_egress,
)
from prowler.providers.huaweicloud.services.vpc.vpc_service import (
SecurityGroupRule,
SecurityGroups,
)
sg = SecurityGroups(
id="sg-1",
name="open-egress-sg",
region="la-south-2",
vpc_id="vpc-1",
rules=[
SecurityGroupRule(
id="rule-1",
direction="egress",
protocol="tcp",
ethertype="IPv4",
remote_ip_prefix="0.0.0.0/0",
port_range_min=80,
port_range_max=80,
),
],
)
vpc_client.security_groups = {"sg-1": sg}
vpc_client.audited_account = "123456789012"
check = vpc_security_group_open_egress()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].status_extended == (
"Security group open-egress-sg (sg-1) allows open egress "
"(0.0.0.0/0) to the internet."
)
assert result[0].resource_id == "sg-1"
assert result[0].resource_name == "open-egress-sg"
assert result[0].resource_arn == (
"huaweicloud:vpc:la-south-2:123456789012:security-group/sg-1"
)
assert result[0].region == "la-south-2"
def test_open_egress_ipv6_fails(self):
vpc_client = mock.MagicMock()
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_huaweicloud_provider(),
),
mock.patch(
"prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress.vpc_client",
new=vpc_client,
),
):
from prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress import (
vpc_security_group_open_egress,
)
from prowler.providers.huaweicloud.services.vpc.vpc_service import (
SecurityGroupRule,
SecurityGroups,
)
sg = SecurityGroups(
id="sg-1",
name="open-egress-sg-ipv6",
region="la-south-2",
vpc_id="vpc-1",
rules=[
SecurityGroupRule(
id="rule-1",
direction="egress",
protocol="tcp",
ethertype="IPv6",
remote_ip_prefix="::/0",
port_range_min=443,
port_range_max=443,
),
],
)
vpc_client.security_groups = {"sg-1": sg}
vpc_client.audited_account = "123456789012"
check = vpc_security_group_open_egress()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].status_extended == (
"Security group open-egress-sg-ipv6 (sg-1) allows open egress "
"(::/0) to the internet."
)
assert result[0].resource_id == "sg-1"
assert result[0].resource_name == "open-egress-sg-ipv6"
assert result[0].resource_arn == (
"huaweicloud:vpc:la-south-2:123456789012:security-group/sg-1"
)
assert result[0].region == "la-south-2"
def test_open_egress_with_empty_destination_fails(self):
vpc_client = mock.MagicMock()
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_huaweicloud_provider(),
),
mock.patch(
"prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress.vpc_client",
new=vpc_client,
),
):
from prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress import (
vpc_security_group_open_egress,
)
from prowler.providers.huaweicloud.services.vpc.vpc_service import (
SecurityGroupRule,
SecurityGroups,
)
sg = SecurityGroups(
id="sg-1",
name="open-egress-sg-empty-destination",
region="la-south-2",
vpc_id="vpc-1",
rules=[
SecurityGroupRule(
id="rule-1",
direction="egress",
action="allow",
protocol="",
ethertype="IPv4",
remote_ip_prefix="",
remote_address_group_id="",
),
],
)
vpc_client.security_groups = {"sg-1": sg}
vpc_client.audited_account = "123456789012"
check = vpc_security_group_open_egress()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].status_extended == (
"Security group open-egress-sg-empty-destination (sg-1) allows "
"open egress (all destinations) to the internet."
)
assert result[0].resource_id == "sg-1"
assert result[0].resource_name == "open-egress-sg-empty-destination"
assert result[0].resource_arn == (
"huaweicloud:vpc:la-south-2:123456789012:security-group/sg-1"
)
assert result[0].region == "la-south-2"
def test_no_security_groups(self):
vpc_client = mock.MagicMock()
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_huaweicloud_provider(),
),
mock.patch(
"prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress.vpc_client",
new=vpc_client,
),
):
from prowler.providers.huaweicloud.services.vpc.vpc_security_group_open_egress.vpc_security_group_open_egress import (
vpc_security_group_open_egress,
)
vpc_client.security_groups = {}
vpc_client.audited_account = "123456789012"
check = vpc_security_group_open_egress()
result = check.execute()
assert len(result) == 0