feat(sdk): check Supabase organization member MFA

This commit is contained in:
Hugo P.Brito committed 2026-08-19 16:52:09 +01:00
1 parent 36f74584a7
commit 4c748e8e88
11 files changed
+301

No files matched your search

@@ -0,0 +1,101 @@
from unittest import mock
from prowler.providers.supabase.services.organizations.organizations_service import (
SupabaseOrganizationMember,
)
from tests.providers.supabase.supabase_fixtures import (
ORGANIZATION_NAME,
ORGANIZATION_SLUG,
USER_ID,
set_mocked_supabase_provider,
)
class Test_organizations_member_mfa_enabled:
def _execute(self, members):
organizations_client = mock.MagicMock()
organizations_client.members = members
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_supabase_provider(),
),
mock.patch(
"prowler.providers.supabase.services.organizations.organizations_member_mfa_enabled.organizations_member_mfa_enabled.organizations_client",
new=organizations_client,
),
):
from prowler.providers.supabase.services.organizations.organizations_member_mfa_enabled.organizations_member_mfa_enabled import (
organizations_member_mfa_enabled,
)
return organizations_member_mfa_enabled().execute()
def test_no_members(self):
assert self._execute({}) == []
def test_member_with_mfa_passes_without_email(self):
member = SupabaseOrganizationMember(
id=USER_ID,
name=f"member {USER_ID}",
organization_slug=ORGANIZATION_SLUG,
organization_name=ORGANIZATION_NAME,
mfa_enabled=True,
)
result = self._execute({f"{ORGANIZATION_SLUG}:{USER_ID}": member})
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_id == USER_ID
assert result[0].organization_slug == ORGANIZATION_SLUG
assert result[0].status_extended == (
f"Supabase organization {ORGANIZATION_SLUG} member {USER_ID} has MFA enabled."
)
def test_member_without_mfa_fails(self):
member = SupabaseOrganizationMember(
id=USER_ID,
name=f"member {USER_ID}",
organization_slug=ORGANIZATION_SLUG,
organization_name=ORGANIZATION_NAME,
mfa_enabled=False,
)
result = self._execute({f"{ORGANIZATION_SLUG}:{USER_ID}": member})
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == USER_ID
assert result[0].status_extended == (
f"Supabase organization {ORGANIZATION_SLUG} member {USER_ID} does not have MFA enabled."
)
def test_multiple_members_return_independent_pass_and_fail_findings(self):
enabled_member = SupabaseOrganizationMember(
id="enabled-user",
name="member enabled-user",
organization_slug=ORGANIZATION_SLUG,
organization_name=ORGANIZATION_NAME,
mfa_enabled=True,
)
disabled_member = SupabaseOrganizationMember(
id="disabled-user",
name="member disabled-user",
organization_slug=ORGANIZATION_SLUG,
organization_name=ORGANIZATION_NAME,
mfa_enabled=False,
)
result = self._execute(
{
f"{ORGANIZATION_SLUG}:enabled-user": enabled_member,
f"{ORGANIZATION_SLUG}:disabled-user": disabled_member,
}
)
assert [(finding.resource_id, finding.status) for finding in result] == [
("enabled-user", "PASS"),
("disabled-user", "FAIL"),
]
@@ -1,11 +1,14 @@
import os
from argparse import Namespace
from pathlib import Path
from unittest import mock
import pytest
from prowler.config.config import Provider as ProviderName
from prowler.lib.check.models import CheckReportSupabase
from prowler.lib.cli.parser import ProwlerArgumentParser
from prowler.lib.outputs.finding import Finding
from prowler.lib.outputs.html.html import HTML
from prowler.providers.common.provider import Provider
from prowler.providers.supabase.exceptions.exceptions import (
@@ -15,12 +18,16 @@ from prowler.providers.supabase.exceptions.exceptions import (
SupabaseRateLimitError,
)
from prowler.providers.supabase.models import SupabaseOrganization, SupabaseSession
from prowler.providers.supabase.services.organizations.organizations_service import (
SupabaseOrganizationMember,
)
from prowler.providers.supabase.supabase_provider import SupabaseProvider
from tests.providers.supabase.supabase_fixtures import (
ACCESS_TOKEN,
ORGANIZATION_ID,
ORGANIZATION_NAME,
ORGANIZATION_SLUG,
USER_ID,
)
@@ -149,6 +156,37 @@ class TestSupabaseProviderOutputHooks:
"region": "global",
}
def test_finding_output_pipeline_uses_supabase_fields(self):
provider = SupabaseProvider.__new__(SupabaseProvider)
provider._identity = mock.MagicMock(organizations=[])
member = SupabaseOrganizationMember(
id=USER_ID,
name=f"member {USER_ID}",
organization_slug=ORGANIZATION_SLUG,
organization_name=ORGANIZATION_NAME,
mfa_enabled=False,
)
metadata = Path(
"prowler/providers/supabase/services/organizations/"
"organizations_member_mfa_enabled/"
"organizations_member_mfa_enabled.metadata.json"
).read_text()
check_output = CheckReportSupabase(metadata=metadata, resource=member)
check_output.status = "FAIL"
check_output.status_extended = "Member does not have MFA enabled."
finding = Finding.generate_output(
provider, check_output, Namespace(unix_timestamp=False)
)
assert finding.provider == "supabase"
assert finding.account_uid == ORGANIZATION_SLUG
assert finding.account_name == ORGANIZATION_NAME
assert finding.resource_name == f"member {USER_ID}"
assert finding.resource_uid == USER_ID
assert finding.region == "global"
assert finding.auth_method == "personal_access_token"
@pytest.mark.parametrize(
("output_filename", "expected"),
[