feat(m365): ensure all forms of mail forwarding are blocked or disabled (#7658)

Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
This commit is contained in:
Hugo Pereira Brito
2025-05-05 17:21:14 +02:00
committed by GitHub
parent 9d788af932
commit 7eec60f4d9
14 changed files with 564 additions and 1 deletions
@@ -7,7 +7,7 @@
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "low",
"ResourceType": "Defender Anti-Spam Policy",
"ResourceType": "Defender Anti-Spam Outbound Policy",
"Description": "Ensure that outbound anti-spam policies are configured to notify administrators and copy suspicious outbound messages to designated recipients when a sender is blocked for sending spam emails.",
"Risk": "Without outbound spam notifications and message copies, compromised accounts may go undetected, increasing the risk of reputation damage or data leakage through unauthorized email activity.",
"RelatedUrl": "https://learn.microsoft.com/en-us/defender-office-365/outbound-spam-protection-about",
@@ -0,0 +1,30 @@
{
"Provider": "m365",
"CheckID": "defender_antispam_outbound_policy_forwarding_disabled",
"CheckTitle": "Ensure Defender Outbound Spam Policies are set to disable mail forwarding.",
"CheckType": [],
"ServiceName": "defender",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "Defender Anti-Spam Outbound Policy",
"Description": "Ensure Defender Outbound Spam Policies are set to disable mail forwarding.",
"Risk": "Enabling email auto-forwarding can be exploited by attackers or malicious insiders to exfiltrate sensitive data outside the organization, often without detection.",
"RelatedUrl": "https://learn.microsoft.com/en-us/defender-office-365/outbound-spam-protection-about",
"Remediation": {
"Code": {
"CLI": "Set-HostedOutboundSpamFilterPolicy -Identity {policyName} -AutoForwardingMode Off",
"NativeIaC": "",
"Other": "1. Navigate to Microsoft 365 Defender https://security.microsoft.com/. 2. Expand E-mail & collaboration then select Policies & rules. 3. Select Threat policies > Anti-spam. 4. Select Anti-spam outbound policy (default). 5. Click Edit protection settings. 6. Set Automatic forwarding rules dropdown to Off - Forwarding is disabled and click Save. 7. Repeat steps 4-6 for any additional higher priority, custom policies.",
"Terraform": ""
},
"Recommendation": {
"Text": "Block all forms of mail forwarding using Anti-spam outbound policies in Exchange Online. Apply exclusions only where justified by organizational policy.",
"Url": "https://learn.microsoft.com/en-us/defender-office-365/outbound-spam-protection-about"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Ensure settings are applied to the highest priority policy if custom policies exist."
}
@@ -0,0 +1,47 @@
from typing import List
from prowler.lib.check.models import Check, CheckReportM365
from prowler.providers.m365.services.defender.defender_client import defender_client
class defender_antispam_outbound_policy_forwarding_disabled(Check):
"""
Check if the Defender Outbound Spam Policies are configured to disable mail forwarding.
Attributes:
metadata: Metadata associated with the check (inherited from Check).
"""
def execute(self) -> List[CheckReportM365]:
"""
Execute the check to verify if the Defender Outbound Spam Policies disable mail forwarding.
Returns:
List[CheckReportM365]: A list of reports containing the result of the check.
"""
findings = []
for policy_name, policy in defender_client.outbound_spam_policies.items():
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name="Defender Outbound Spam Policy",
resource_id=policy_name,
)
report.status = "FAIL"
report.status_extended = (
f"Outbound Spam Policy {policy_name} does allow mail forwarding."
)
if (
not policy.default
and policy_name in defender_client.outbound_spam_rules
and defender_client.outbound_spam_rules[policy_name].state.lower()
== "enabled"
) or policy.default:
if not policy.auto_forwarding_mode:
report.status = "PASS"
report.status_extended = f"Outbound Spam Policy {policy_name} does not allow mail forwarding."
findings.append(report)
return findings
@@ -164,6 +164,7 @@ class Defender(M365Service):
notify_sender_blocked_addresses=policy.get(
"NotifyOutboundSpamRecipients", []
),
auto_forwarding_mode=policy.get("AutoForwardingMode", True),
default=policy.get("IsDefault", False),
)
except Exception as error:
@@ -255,6 +256,7 @@ class OutboundSpamPolicy(BaseModel):
notify_limit_exceeded: bool
notify_limit_exceeded_addresses: List[str]
notify_sender_blocked_addresses: List[str]
auto_forwarding_mode: bool
default: bool
@@ -128,6 +128,7 @@ class Exchange(M365Service):
name=rule.get("Name", ""),
scl=rule.get("SetSCL", None),
sender_domain_is=rule.get("SenderDomainIs", []),
redirect_message_to=rule.get("RedirectMessageTo", None),
)
)
except Exception as error:
@@ -260,6 +261,7 @@ class TransportRule(BaseModel):
name: str
scl: Optional[int]
sender_domain_is: list[str]
redirect_message_to: Optional[list[str]]
class TransportConfig(BaseModel):
@@ -0,0 +1,30 @@
{
"Provider": "m365",
"CheckID": "exchange_transport_rules_mail_forwarding_disabled",
"CheckTitle": "Ensure mail transport rules are set to disable mail forwarding.",
"CheckType": [],
"ServiceName": "exchange",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "Exchange Transport Rules",
"Description": "Ensure mail transport rules are set to disable mail forwarding.",
"Risk": "Enabling email auto-forwarding can be exploited by attackers or malicious insiders to exfiltrate sensitive data outside the organization, often without detection.",
"RelatedUrl": "https://learn.microsoft.com/en-us/exchange/security-and-compliance/mail-flow-rules/configuration-best-practices",
"Remediation": {
"Code": {
"CLI": "Remove-TransportRule -Identity <RuleName>",
"NativeIaC": "",
"Other": "1. Select Exchange to open the Exchange admin center. 2. Select Mail Flow then Rules. 3. For each rule that redirects email to external domains, select the rule and click the 'Delete' icon.",
"Terraform": ""
},
"Recommendation": {
"Text": "Block all forms of mail forwarding using Transport rules in Exchange Online. Apply exclusions only where justified by organizational policy.",
"Url": "https://learn.microsoft.com/en-us/exchange/security-and-compliance/mail-flow-rules/mail-flow-rules"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,43 @@
from typing import List
from prowler.lib.check.models import Check, CheckReportM365
from prowler.providers.m365.services.exchange.exchange_client import exchange_client
class exchange_transport_rules_mail_forwarding_disabled(Check):
"""
Check to ensure that no mail transport rules allow forwarding mail to external domains.
Attributes:
metadata: Metadata associated with the check (inherited from Check).
"""
def execute(self) -> List[CheckReportM365]:
"""
Execute the check to validate that no transport rules allow forwarding mail to external domains.
This method retrieves all transport rules from the Exchange service and evaluates
whether any of them allow forwarding mail to external domains. A report is generated for each
transport rule.
Returns:
List[CheckReportM365]: A list of findings with the status of each transport rule.
"""
findings = []
for rule in exchange_client.transport_rules:
report = CheckReportM365(
metadata=self.metadata(),
resource=rule,
resource_name=rule.name,
resource_id="ExchangeTransportRule",
)
report.status = "PASS"
report.status_extended = f"Transport rule {rule.name} does not allow forwarding mail to external domains."
if rule.redirect_message_to:
report.status = "FAIL"
report.status_extended = f"Transport rule {rule.name} allows forwarding mail to external domains: {', '.join(rule.redirect_message_to)}."
findings.append(report)
return findings
@@ -37,6 +37,7 @@ class Test_defender_antispam_outbound_policy_configured:
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
default=False,
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {
@@ -92,6 +93,7 @@ class Test_defender_antispam_outbound_policy_configured:
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
default=False,
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {
@@ -146,6 +148,7 @@ class Test_defender_antispam_outbound_policy_configured:
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
default=True,
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {}
@@ -198,6 +201,7 @@ class Test_defender_antispam_outbound_policy_configured:
notify_limit_exceeded_addresses=["admin@org.com"],
notify_sender_blocked_addresses=["admin@org.com"],
default=False,
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {}
@@ -0,0 +1,252 @@
from unittest import mock
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_defender_antispam_outbound_policy_forwarding_disabled:
def test_no_outbound_spam_policies(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
defender_client.outbound_spam_policies = {}
defender_client.outbound_spam_rules = {}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 0
def test_forwarding_disabled_custom_policy(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Policy1": OutboundSpamPolicy(
default=False,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {
"Policy1": OutboundSpamRule(state="Enabled")
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Outbound Spam Policy Policy1 does not allow mail forwarding."
)
assert (
result[0].resource
== defender_client.outbound_spam_policies["Policy1"].dict()
)
assert result[0].resource_name == "Defender Outbound Spam Policy"
assert result[0].resource_id == "Policy1"
assert result[0].location == "global"
def test_forwarding_enabled_policy(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
OutboundSpamRule,
)
defender_client.outbound_spam_policies = {
"Policy2": OutboundSpamPolicy(
default=False,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=True,
)
}
defender_client.outbound_spam_rules = {
"Policy2": OutboundSpamRule(state="Enabled")
}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Outbound Spam Policy Policy2 does allow mail forwarding."
)
assert (
result[0].resource
== defender_client.outbound_spam_policies["Policy2"].dict()
)
assert result[0].resource_name == "Defender Outbound Spam Policy"
assert result[0].resource_id == "Policy2"
assert result[0].location == "global"
def test_forwarding_disabled_default_policy(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
)
defender_client.outbound_spam_policies = {
"Default": OutboundSpamPolicy(
default=True,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Outbound Spam Policy Default does not allow mail forwarding."
)
assert (
result[0].resource
== defender_client.outbound_spam_policies["Default"].dict()
)
assert result[0].resource_name == "Defender Outbound Spam Policy"
assert result[0].resource_id == "Default"
assert result[0].location == "global"
def test_policy_without_rule(self):
defender_client = mock.MagicMock()
defender_client.audited_tenant = "audited_tenant"
defender_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled.defender_client",
new=defender_client,
),
):
from prowler.providers.m365.services.defender.defender_antispam_outbound_policy_forwarding_disabled.defender_antispam_outbound_policy_forwarding_disabled import (
defender_antispam_outbound_policy_forwarding_disabled,
)
from prowler.providers.m365.services.defender.defender_service import (
OutboundSpamPolicy,
)
defender_client.outbound_spam_policies = {
"PolicyX": OutboundSpamPolicy(
default=False,
notify_sender_blocked=True,
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["test@correo.com"],
notify_sender_blocked_addresses=["test@correo.com"],
auto_forwarding_mode=False,
)
}
defender_client.outbound_spam_rules = {}
check = defender_antispam_outbound_policy_forwarding_disabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Outbound Spam Policy PolicyX does allow mail forwarding."
)
assert (
result[0].resource
== defender_client.outbound_spam_policies["PolicyX"].dict()
)
assert result[0].resource_name == "Defender Outbound Spam Policy"
assert result[0].resource_id == "PolicyX"
assert result[0].location == "global"
@@ -106,6 +106,7 @@ def mock_defender_get_outbound_spam_filter_policy(_):
notify_limit_exceeded=True,
notify_limit_exceeded_addresses=["security@example.com"],
notify_sender_blocked_addresses=["security@example.com"],
auto_forwarding_mode=False,
default=False,
),
"Policy2": OutboundSpamPolicy(
@@ -113,6 +114,7 @@ def mock_defender_get_outbound_spam_filter_policy(_):
notify_limit_exceeded=False,
notify_limit_exceeded_addresses=[],
notify_sender_blocked_addresses=[],
auto_forwarding_mode=True,
default=True,
),
}
@@ -308,6 +310,7 @@ class Test_Defender_Service:
assert outbound_spam_policies[
"Policy1"
].notify_sender_blocked_addresses == ["security@example.com"]
assert outbound_spam_policies["Policy1"].auto_forwarding_mode is False
assert outbound_spam_policies["Policy1"].default is False
assert outbound_spam_policies["Policy2"].notify_sender_blocked is False
assert outbound_spam_policies["Policy2"].notify_limit_exceeded is False
@@ -317,6 +320,7 @@ class Test_Defender_Service:
assert (
outbound_spam_policies["Policy2"].notify_sender_blocked_addresses == []
)
assert outbound_spam_policies["Policy2"].auto_forwarding_mode is True
assert outbound_spam_policies["Policy2"].default is True
@patch(
@@ -55,11 +55,13 @@ def mock_exchange_get_transport_rules(_):
name="test",
scl=-1,
sender_domain_is=["example.com"],
redirect_message_to=None,
),
TransportRule(
name="test2",
scl=0,
sender_domain_is=["example.com"],
redirect_message_to=["test@example.com"],
),
]
@@ -261,9 +263,11 @@ class Test_Exchange_Service:
assert transport_rules[0].name == "test"
assert transport_rules[0].scl == -1
assert transport_rules[0].sender_domain_is == ["example.com"]
assert transport_rules[0].redirect_message_to is None
assert transport_rules[1].name == "test2"
assert transport_rules[1].scl == 0
assert transport_rules[1].sender_domain_is == ["example.com"]
assert transport_rules[1].redirect_message_to == ["test@example.com"]
exchange_client.powershell.close()
@@ -0,0 +1,145 @@
from unittest import mock
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
class Test_exchange_transport_rules_mail_forwarding_disabled:
def test_empty_rule_list(self):
exchange_client = mock.MagicMock()
exchange_client.audited_tenant = "audited_tenant"
exchange_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.exchange.exchange_transport_rules_mail_forwarding_disabled.exchange_transport_rules_mail_forwarding_disabled.exchange_client",
new=exchange_client,
),
):
from prowler.providers.m365.services.exchange.exchange_transport_rules_mail_forwarding_disabled.exchange_transport_rules_mail_forwarding_disabled import (
exchange_transport_rules_mail_forwarding_disabled,
)
exchange_client.transport_rules = []
check = exchange_transport_rules_mail_forwarding_disabled()
result = check.execute()
assert len(result) == 0
def test_forwarding_disabled(self):
exchange_client = mock.MagicMock()
exchange_client.audited_tenant = "audited_tenant"
exchange_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.exchange.exchange_transport_rules_mail_forwarding_disabled.exchange_transport_rules_mail_forwarding_disabled.exchange_client",
new=exchange_client,
),
):
from prowler.providers.m365.services.exchange.exchange_service import (
TransportRule,
)
from prowler.providers.m365.services.exchange.exchange_transport_rules_mail_forwarding_disabled.exchange_transport_rules_mail_forwarding_disabled import (
exchange_transport_rules_mail_forwarding_disabled,
)
exchange_client.transport_rules = [
TransportRule(
name="Rule1", redirect_message_to=[], sender_domain_is=[]
),
TransportRule(
name="Rule2", redirect_message_to=[], sender_domain_is=[]
),
]
check = exchange_transport_rules_mail_forwarding_disabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Transport rule Rule1 does not allow forwarding mail to external domains."
)
assert result[0].resource_name == "Rule1"
assert result[0].resource_id == "ExchangeTransportRule"
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Transport rule Rule2 does not allow forwarding mail to external domains."
)
assert result[1].resource_name == "Rule2"
def test_forwarding_enabled(self):
exchange_client = mock.MagicMock()
exchange_client.audited_tenant = "audited_tenant"
exchange_client.audited_domain = DOMAIN
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_m365_provider(),
),
mock.patch(
"prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online"
),
mock.patch(
"prowler.providers.m365.services.exchange.exchange_transport_rules_mail_forwarding_disabled.exchange_transport_rules_mail_forwarding_disabled.exchange_client",
new=exchange_client,
),
):
from prowler.providers.m365.services.exchange.exchange_service import (
TransportRule,
)
from prowler.providers.m365.services.exchange.exchange_transport_rules_mail_forwarding_disabled.exchange_transport_rules_mail_forwarding_disabled import (
exchange_transport_rules_mail_forwarding_disabled,
)
exchange_client.transport_rules = [
TransportRule(
name="ForwardingRule",
redirect_message_to=["external@example.com"],
sender_domain_is=[],
),
TransportRule(
name="NoForwardingRule",
redirect_message_to=[],
sender_domain_is=[],
),
]
check = exchange_transport_rules_mail_forwarding_disabled()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Transport rule ForwardingRule allows forwarding mail to external domains: external@example.com."
)
assert result[0].resource_name == "ForwardingRule"
assert result[0].resource_id == "ExchangeTransportRule"
assert result[0].location == "global"
assert result[1].status == "PASS"
assert (
result[1].status_extended
== "Transport rule NoForwardingRule does not allow forwarding mail to external domains."
)
assert result[1].resource_name == "NoForwardingRule"
assert result[1].resource_id == "ExchangeTransportRule"
assert result[1].location == "global"