mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-10 21:44:16 +00:00
520 lines
22 KiB
Python
520 lines
22 KiB
Python
from unittest.mock import MagicMock, patch
|
|
|
|
from googleapiclient.errors import HttpError
|
|
from httplib2 import Response as HttpResponse
|
|
|
|
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
|
ROOT_ORG_UNIT_ID,
|
|
set_mocked_googleworkspace_provider,
|
|
)
|
|
|
|
|
|
class TestGmailService:
|
|
def test_gmail_fetch_policies_all_settings(self):
|
|
"""Test fetching all 10 Gmail policy settings from Cloud Identity API"""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_credentials = MagicMock()
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = mock_credentials
|
|
mock_provider.session = mock_session
|
|
|
|
mock_service = MagicMock()
|
|
mock_policies_list = MagicMock()
|
|
mock_policies_list.execute.return_value = {
|
|
"policies": [
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.mail_delegation",
|
|
"value": {"enableMailDelegation": False},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.email_attachment_safety",
|
|
"value": {
|
|
"enableEncryptedAttachmentProtection": True,
|
|
"encryptedAttachmentProtectionConsequence": "SPAM_FOLDER",
|
|
"enableAttachmentWithScriptsProtection": True,
|
|
"scriptAttachmentProtectionConsequence": "QUARANTINE",
|
|
"enableAnomalousAttachmentProtection": True,
|
|
"anomalousAttachmentProtectionConsequence": "WARNING",
|
|
},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.links_and_external_images",
|
|
"value": {
|
|
"enableShortenerScanning": True,
|
|
"enableExternalImageScanning": True,
|
|
"enableAggressiveWarningsOnUntrustedLinks": True,
|
|
},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.spoofing_and_authentication",
|
|
"value": {
|
|
"detectDomainNameSpoofing": True,
|
|
"domainSpoofingConsequence": "SPAM_FOLDER",
|
|
"detectEmployeeNameSpoofing": True,
|
|
"employeeNameSpoofingConsequence": "SPAM_FOLDER",
|
|
"detectDomainSpoofingFromUnauthenticatedSenders": True,
|
|
"inboundDomainSpoofingConsequence": "QUARANTINE",
|
|
"detectUnauthenticatedEmails": True,
|
|
"unauthenticatedEmailConsequence": "WARNING",
|
|
"detectGroupsSpoofing": True,
|
|
"groupsSpoofingConsequence": "SPAM_FOLDER",
|
|
},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.pop_access",
|
|
"value": {"enablePopAccess": False},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.imap_access",
|
|
"value": {"enableImapAccess": False},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.auto_forwarding",
|
|
"value": {"enableAutoForwarding": False},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.per_user_outbound_gateway",
|
|
"value": {"allowUsersToUseExternalSmtpServers": False},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.enhanced_pre_delivery_message_scanning",
|
|
"value": {"enableImprovedSuspiciousContentDetection": True},
|
|
}
|
|
},
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.comprehensive_mail_storage",
|
|
"value": {"ruleId": "rule-abc-123"},
|
|
}
|
|
},
|
|
]
|
|
}
|
|
mock_service.policies().list.return_value = mock_policies_list
|
|
mock_service.policies().list_next.return_value = None
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=mock_service,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
assert gmail.policies_fetched is True
|
|
assert gmail.policies.enable_mail_delegation is False
|
|
assert gmail.policies.enable_encrypted_attachment_protection is True
|
|
assert (
|
|
gmail.policies.encrypted_attachment_protection_consequence
|
|
== "SPAM_FOLDER"
|
|
)
|
|
assert gmail.policies.enable_script_attachment_protection is True
|
|
assert (
|
|
gmail.policies.script_attachment_protection_consequence == "QUARANTINE"
|
|
)
|
|
assert gmail.policies.enable_anomalous_attachment_protection is True
|
|
assert (
|
|
gmail.policies.anomalous_attachment_protection_consequence == "WARNING"
|
|
)
|
|
assert gmail.policies.enable_shortener_scanning is True
|
|
assert gmail.policies.enable_external_image_scanning is True
|
|
assert gmail.policies.enable_aggressive_warnings_on_untrusted_links is True
|
|
assert gmail.policies.detect_domain_name_spoofing is True
|
|
assert gmail.policies.domain_spoofing_consequence == "SPAM_FOLDER"
|
|
assert gmail.policies.detect_employee_name_spoofing is True
|
|
assert gmail.policies.employee_name_spoofing_consequence == "SPAM_FOLDER"
|
|
assert gmail.policies.detect_inbound_domain_spoofing is True
|
|
assert gmail.policies.inbound_domain_spoofing_consequence == "QUARANTINE"
|
|
assert gmail.policies.detect_unauthenticated_emails is True
|
|
assert gmail.policies.unauthenticated_email_consequence == "WARNING"
|
|
assert gmail.policies.detect_groups_spoofing is True
|
|
assert gmail.policies.groups_spoofing_consequence == "SPAM_FOLDER"
|
|
assert gmail.policies.enable_pop_access is False
|
|
assert gmail.policies.enable_imap_access is False
|
|
assert gmail.policies.enable_auto_forwarding is False
|
|
assert gmail.policies.allow_per_user_outbound_gateway is False
|
|
assert gmail.policies.enable_enhanced_pre_delivery_scanning is True
|
|
assert gmail.policies.comprehensive_mail_storage_enabled is True
|
|
|
|
def test_gmail_fetch_policies_empty_response(self):
|
|
"""Test handling empty policies response"""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = MagicMock()
|
|
mock_provider.session = mock_session
|
|
|
|
mock_service = MagicMock()
|
|
mock_policies_list = MagicMock()
|
|
mock_policies_list.execute.return_value = {"policies": []}
|
|
mock_service.policies().list.return_value = mock_policies_list
|
|
mock_service.policies().list_next.return_value = None
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=mock_service,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
assert gmail.policies_fetched is True
|
|
assert gmail.policies.enable_mail_delegation is None
|
|
assert gmail.policies.encrypted_attachment_protection_consequence is None
|
|
assert gmail.policies.enable_pop_access is None
|
|
assert gmail.policies.comprehensive_mail_storage_enabled is None
|
|
|
|
def test_gmail_fetch_policies_api_error(self):
|
|
"""Test handling of API errors during policy fetch"""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = MagicMock()
|
|
mock_provider.session = mock_session
|
|
|
|
mock_service = MagicMock()
|
|
mock_service.policies().list.side_effect = Exception("API Error")
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=mock_service,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
assert gmail.policies_fetched is False
|
|
assert gmail.policies.enable_mail_delegation is None
|
|
|
|
def test_gmail_fetch_policies_build_service_returns_none(self):
|
|
"""Test early return when _build_service fails to construct the client"""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = MagicMock()
|
|
mock_provider.session = mock_session
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=None,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
assert gmail.policies_fetched is False
|
|
assert gmail.policies.enable_mail_delegation is None
|
|
|
|
def test_gmail_fetch_policies_execute_raises(self):
|
|
"""Test inner except handler when request.execute() raises during pagination"""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = MagicMock()
|
|
mock_provider.session = mock_session
|
|
|
|
mock_service = MagicMock()
|
|
mock_request = MagicMock()
|
|
mock_request.execute.side_effect = Exception("Execute failed")
|
|
mock_service.policies().list.return_value = mock_request
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=mock_service,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
assert gmail.policies_fetched is False
|
|
assert gmail.policies.enable_mail_delegation is None
|
|
|
|
def test_gmail_fetch_policies_ignores_ou_and_group_level(self):
|
|
"""Test that OU-level and group-level policies are skipped, only customer-level used"""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = MagicMock()
|
|
mock_provider.session = mock_session
|
|
|
|
mock_service = MagicMock()
|
|
mock_policies_list = MagicMock()
|
|
mock_policies_list.execute.return_value = {
|
|
"policies": [
|
|
{
|
|
# Customer-level: no policyQuery → should be used
|
|
"setting": {
|
|
"type": "settings/gmail.mail_delegation",
|
|
"value": {"enableMailDelegation": False},
|
|
}
|
|
},
|
|
{
|
|
# OU-level: has policyQuery.orgUnit → should be skipped
|
|
"policyQuery": {"orgUnit": "orgUnits/sales_team"},
|
|
"setting": {
|
|
"type": "settings/gmail.mail_delegation",
|
|
"value": {"enableMailDelegation": True},
|
|
},
|
|
},
|
|
{
|
|
# Group-level: has policyQuery.group → should be skipped
|
|
"policyQuery": {"group": "groups/contractors"},
|
|
"setting": {
|
|
"type": "settings/gmail.auto_forwarding",
|
|
"value": {"enableAutoForwarding": True},
|
|
},
|
|
},
|
|
{
|
|
# Customer-level: no policyQuery → should be used
|
|
"setting": {
|
|
"type": "settings/gmail.auto_forwarding",
|
|
"value": {"enableAutoForwarding": False},
|
|
}
|
|
},
|
|
]
|
|
}
|
|
mock_service.policies().list.return_value = mock_policies_list
|
|
mock_service.policies().list_next.return_value = None
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=mock_service,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
assert gmail.policies_fetched is True
|
|
assert gmail.policies.enable_mail_delegation is False
|
|
assert gmail.policies.enable_auto_forwarding is False
|
|
|
|
def test_gmail_fetch_policies_accepts_root_ou(self):
|
|
"""Test that root-OU-scoped policies are accepted as customer-level"""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = MagicMock()
|
|
mock_provider.session = mock_session
|
|
|
|
mock_service = MagicMock()
|
|
mock_policies_list = MagicMock()
|
|
mock_policies_list.execute.return_value = {
|
|
"policies": [
|
|
{
|
|
# Root OU: matches provider's root_org_unit_id → should be accepted
|
|
"policyQuery": {"orgUnit": f"orgUnits/{ROOT_ORG_UNIT_ID}"},
|
|
"setting": {
|
|
"type": "settings/gmail.mail_delegation",
|
|
"value": {"enableMailDelegation": True},
|
|
},
|
|
},
|
|
{
|
|
# Sub-OU: different orgUnit → should be skipped
|
|
"policyQuery": {"orgUnit": "orgUnits/sub_ou_sales"},
|
|
"setting": {
|
|
"type": "settings/gmail.auto_forwarding",
|
|
"value": {"enableAutoForwarding": True},
|
|
},
|
|
},
|
|
]
|
|
}
|
|
mock_service.policies().list.return_value = mock_policies_list
|
|
mock_service.policies().list_next.return_value = None
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=mock_service,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
assert gmail.policies_fetched is True
|
|
# Root OU policy accepted
|
|
assert gmail.policies.enable_mail_delegation is True
|
|
# Sub-OU policy skipped
|
|
assert gmail.policies.enable_auto_forwarding is None
|
|
|
|
def test_gmail_partial_fetch_marks_policies_fetched_false(self):
|
|
"""Regression: if page 1 returns valid data but page 2 raises an error,
|
|
policies_fetched must be False even though some policy values were stored."""
|
|
mock_provider = set_mocked_googleworkspace_provider()
|
|
mock_provider.audit_config = {}
|
|
mock_provider.fixer_config = {}
|
|
mock_session = MagicMock()
|
|
mock_session.credentials = MagicMock()
|
|
mock_provider.session = mock_session
|
|
|
|
mock_service = MagicMock()
|
|
|
|
# Page 1: returns valid Gmail data
|
|
page1_response = {
|
|
"policies": [
|
|
{
|
|
"setting": {
|
|
"type": "settings/gmail.mail_delegation",
|
|
"value": {"enableMailDelegation": False},
|
|
}
|
|
},
|
|
]
|
|
}
|
|
|
|
# Page 2 request raises HttpError 429
|
|
page1_request = MagicMock()
|
|
page1_request.execute.return_value = page1_response
|
|
|
|
page2_request = MagicMock()
|
|
page2_request.execute.side_effect = HttpError(
|
|
HttpResponse({"status": "429"}), b"Rate limit exceeded"
|
|
)
|
|
|
|
mock_service.policies().list.return_value = page1_request
|
|
mock_service.policies().list_next.return_value = page2_request
|
|
|
|
with (
|
|
patch(
|
|
"prowler.providers.common.provider.Provider.get_global_provider",
|
|
return_value=mock_provider,
|
|
),
|
|
patch(
|
|
"prowler.providers.googleworkspace.services.gmail.gmail_service.GoogleWorkspaceService._build_service",
|
|
return_value=mock_service,
|
|
),
|
|
):
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
Gmail,
|
|
)
|
|
|
|
gmail = Gmail(mock_provider)
|
|
|
|
# Page 1 data was stored
|
|
assert gmail.policies.enable_mail_delegation is False
|
|
# But policies_fetched must be False because page 2 failed
|
|
assert gmail.policies_fetched is False
|
|
|
|
def test_gmail_policies_model(self):
|
|
"""Test GmailPolicies Pydantic model"""
|
|
from prowler.providers.googleworkspace.services.gmail.gmail_service import (
|
|
GmailPolicies,
|
|
)
|
|
|
|
policies = GmailPolicies(
|
|
enable_mail_delegation=False,
|
|
enable_encrypted_attachment_protection=True,
|
|
encrypted_attachment_protection_consequence="SPAM_FOLDER",
|
|
enable_script_attachment_protection=True,
|
|
script_attachment_protection_consequence="QUARANTINE",
|
|
enable_anomalous_attachment_protection=True,
|
|
anomalous_attachment_protection_consequence="WARNING",
|
|
enable_shortener_scanning=True,
|
|
enable_external_image_scanning=True,
|
|
enable_aggressive_warnings_on_untrusted_links=True,
|
|
detect_domain_name_spoofing=True,
|
|
domain_spoofing_consequence="SPAM_FOLDER",
|
|
detect_employee_name_spoofing=True,
|
|
employee_name_spoofing_consequence="SPAM_FOLDER",
|
|
detect_inbound_domain_spoofing=True,
|
|
inbound_domain_spoofing_consequence="QUARANTINE",
|
|
detect_unauthenticated_emails=True,
|
|
unauthenticated_email_consequence="WARNING",
|
|
detect_groups_spoofing=True,
|
|
groups_spoofing_consequence="SPAM_FOLDER",
|
|
enable_pop_access=False,
|
|
enable_imap_access=False,
|
|
enable_auto_forwarding=False,
|
|
allow_per_user_outbound_gateway=False,
|
|
enable_enhanced_pre_delivery_scanning=True,
|
|
comprehensive_mail_storage_enabled=True,
|
|
)
|
|
|
|
assert policies.enable_mail_delegation is False
|
|
assert policies.enable_encrypted_attachment_protection is True
|
|
assert policies.encrypted_attachment_protection_consequence == "SPAM_FOLDER"
|
|
assert policies.enable_shortener_scanning is True
|
|
assert policies.detect_domain_name_spoofing is True
|
|
assert policies.domain_spoofing_consequence == "SPAM_FOLDER"
|
|
assert policies.enable_pop_access is False
|
|
assert policies.enable_auto_forwarding is False
|
|
assert policies.enable_enhanced_pre_delivery_scanning is True
|
|
assert policies.comprehensive_mail_storage_enabled is True
|