mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-09 21:14:22 +00:00
feat(m365): add check for directory sync object takeover protection (#11098)
Co-authored-by: shadyfox <git@twink.energy> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com> Co-authored-by: omobolaji adeyan <omobolaji.adeyan@gmail.com>
This commit is contained in:
co-authored by
shadyfox
Claude Opus 4.6
Daniel Barranquero
omobolaji adeyan
parent
0463cd1559
commit
8d4ec561c2
@@ -19,6 +19,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `aks_cluster_auto_upgrade_enabled` check for Azure provider [(#11027)](https://github.com/prowler-cloud/prowler/pull/11027)
|
||||
- Jira timeout preventing the calls from hanging indefinitely when the Jira endpoint is unreachable or slow [(#11602)](https://github.com/prowler-cloud/prowler/pull/11602)
|
||||
- TLS certificate verification in the `codepipeline_project_repo_private` check, which previously used an unverified SSL context, leaving the repository-visibility probe open to MITM tampering [(#11603)](https://github.com/prowler-cloud/prowler/pull/11603)
|
||||
- `entra_directory_sync_object_takeover_blocked` check for the M365 provider, verifying that hybrid Entra tenants block cloud object takeover through both soft-match and hard-match directory synchronization [(#11098)](https://github.com/prowler-cloud/prowler/pull/11098)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"Provider": "m365",
|
||||
"CheckID": "entra_directory_sync_object_takeover_blocked",
|
||||
"CheckTitle": "Microsoft Entra directory sync must block object takeover (soft- and hard-matching)",
|
||||
"CheckType": [],
|
||||
"ServiceName": "entra",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "IAM",
|
||||
"Description": "When on-premises directory synchronization is enabled, both blockSoftMatchEnabled and blockCloudObjectTakeoverThroughHardMatchEnabled must be true. Without these blocks, an attacker who can write to on-premises AD can craft an object that matches a privileged cloud account and take it over.",
|
||||
"Risk": "An attacker with write access to on-premises Active Directory can create an object whose UPN, SMTP address, or ImmutableID matches an existing cloud-only account (e.g. Global Administrator). When the sync engine processes this object, it merges the on-premises identity into the cloud account, effectively granting the attacker full control of that privileged account.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/graph/api/resources/onpremisesdirectorysynchronization?view=graph-rest-1.0",
|
||||
"https://learn.microsoft.com/en-us/graph/api/resources/onpremisesdirectorysynchronizationfeature?view=graph-rest-1.0",
|
||||
"https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-syncservice-features"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Open Microsoft Entra admin center\n2. Navigate to Identity > Hybrid management > Microsoft Entra Connect > Connect Sync\n3. Enable 'Block soft match' and 'Block cloud object takeover through hard match'\n4. Alternatively, use Microsoft Graph API to set both features to true on the onPremisesDirectorySynchronization resource",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable both blockSoftMatchEnabled and blockCloudObjectTakeoverThroughHardMatchEnabled on the on-premises directory synchronization configuration. These should remain enabled permanently except during time-boxed migration windows.",
|
||||
"Url": "https://hub.prowler.com/check/entra_directory_sync_object_takeover_blocked"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"identity-access",
|
||||
"e3"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"entra_password_hash_sync_enabled",
|
||||
"entra_seamless_sso_disabled"
|
||||
],
|
||||
"Notes": "This check only applies to hybrid tenants with on-premises directory synchronization enabled. Cloud-only tenants receive a PASS since the attack path does not exist."
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.entra.entra_client import entra_client
|
||||
|
||||
|
||||
class entra_directory_sync_object_takeover_blocked(Check):
|
||||
"""Check that directory sync blocks object takeover via soft-match and hard-match.
|
||||
|
||||
When on-premises directory synchronization is enabled, an attacker who can
|
||||
write to on-premises AD can craft an object that matches a privileged cloud
|
||||
account and take it over. Both blockSoftMatchEnabled and
|
||||
blockCloudObjectTakeoverThroughHardMatchEnabled must be true to prevent this.
|
||||
|
||||
The attack path only exists on hybrid tenants, so the tenant's
|
||||
organization.onPremisesSyncEnabled is evaluated first. Microsoft Graph
|
||||
returns an onPremisesSynchronization object (with all features disabled) even
|
||||
for cloud-only tenants, so the directory sync features must not be evaluated
|
||||
unless on-premises synchronization is actually enabled.
|
||||
|
||||
- PASS: The tenant is cloud-only, or both block flags are enabled.
|
||||
- FAIL: On-premises sync is enabled and either block flag is disabled.
|
||||
- MANUAL: On-premises sync is enabled but the settings cannot be read
|
||||
(insufficient permissions) or were not returned by Microsoft Graph.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
findings = []
|
||||
|
||||
organizations = entra_client.organizations or []
|
||||
on_premises_sync_enabled = any(
|
||||
organization.on_premises_sync_enabled for organization in organizations
|
||||
)
|
||||
|
||||
# Cloud-only tenant: the object takeover attack path does not exist, so
|
||||
# the directory sync features are not evaluated even if Microsoft Graph
|
||||
# returns an (all-disabled) onPremisesSynchronization object.
|
||||
if organizations and not on_premises_sync_enabled:
|
||||
for organization in organizations:
|
||||
report = CheckReportM365(
|
||||
self.metadata(),
|
||||
resource=organization,
|
||||
resource_id=organization.id,
|
||||
resource_name=organization.name,
|
||||
)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Entra organization {organization.name} is cloud-only "
|
||||
"(no on-premises sync), object takeover protection is not "
|
||||
"applicable."
|
||||
)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
# Hybrid tenant but the directory sync settings could not be read.
|
||||
if entra_client.directory_sync_error:
|
||||
for organization in organizations:
|
||||
report = CheckReportM365(
|
||||
self.metadata(),
|
||||
resource=organization,
|
||||
resource_id=organization.id,
|
||||
resource_name=organization.name,
|
||||
)
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = (
|
||||
f"Cannot verify object takeover protection for "
|
||||
f"{organization.name}: {entra_client.directory_sync_error}."
|
||||
)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
for sync_settings in entra_client.directory_sync_settings:
|
||||
report = CheckReportM365(
|
||||
self.metadata(),
|
||||
resource=sync_settings,
|
||||
resource_id=sync_settings.id,
|
||||
resource_name=f"Directory Sync {sync_settings.id}",
|
||||
)
|
||||
|
||||
disabled_flags = []
|
||||
if not sync_settings.block_soft_match_enabled:
|
||||
disabled_flags.append("blockSoftMatchEnabled")
|
||||
if not sync_settings.block_cloud_object_takeover_through_hard_match_enabled:
|
||||
disabled_flags.append("blockCloudObjectTakeoverThroughHardMatchEnabled")
|
||||
|
||||
if not disabled_flags:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Entra directory sync {sync_settings.id} blocks both soft-match "
|
||||
"and hard-match object takeover."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Entra directory sync {sync_settings.id} does not block object "
|
||||
f"takeover: {', '.join(disabled_flags)} disabled."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
# Hybrid tenant that reported on-premises sync but returned no settings.
|
||||
if not entra_client.directory_sync_settings:
|
||||
for organization in organizations:
|
||||
report = CheckReportM365(
|
||||
self.metadata(),
|
||||
resource=organization,
|
||||
resource_id=organization.id,
|
||||
resource_name=organization.name,
|
||||
)
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = (
|
||||
f"Entra organization {organization.name} has on-premises sync "
|
||||
"enabled, but no directory sync settings were returned. Review "
|
||||
"the tenant configuration manually."
|
||||
)
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -791,6 +791,16 @@ class Entra(M365Service):
|
||||
features, "seamless_sso_enabled", False
|
||||
)
|
||||
or False,
|
||||
block_soft_match_enabled=getattr(
|
||||
features, "block_soft_match_enabled", False
|
||||
)
|
||||
or False,
|
||||
block_cloud_object_takeover_through_hard_match_enabled=getattr(
|
||||
features,
|
||||
"block_cloud_object_takeover_through_hard_match_enabled",
|
||||
False,
|
||||
)
|
||||
or False,
|
||||
)
|
||||
)
|
||||
except ODataError as error:
|
||||
@@ -1637,6 +1647,8 @@ class DirectorySyncSettings(BaseModel):
|
||||
id: str
|
||||
password_sync_enabled: bool = False
|
||||
seamless_sso_enabled: bool = False
|
||||
block_soft_match_enabled: bool = False
|
||||
block_cloud_object_takeover_through_hard_match_enabled: bool = False
|
||||
|
||||
|
||||
class AuthenticationMethodConfiguration(BaseModel):
|
||||
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.m365.services.entra.entra_service import (
|
||||
DirectorySyncSettings,
|
||||
Organization,
|
||||
)
|
||||
from tests.providers.m365.m365_fixtures import set_mocked_m365_provider
|
||||
|
||||
CHECK_MODULE = (
|
||||
"prowler.providers.m365.services.entra."
|
||||
"entra_directory_sync_object_takeover_blocked."
|
||||
"entra_directory_sync_object_takeover_blocked"
|
||||
)
|
||||
|
||||
|
||||
def _hybrid_org():
|
||||
return Organization(
|
||||
id="org-001",
|
||||
name="Hybrid Org",
|
||||
on_premises_sync_enabled=True,
|
||||
)
|
||||
|
||||
|
||||
def _cloud_only_org():
|
||||
return Organization(
|
||||
id="org-001",
|
||||
name="Cloud Only Org",
|
||||
on_premises_sync_enabled=False,
|
||||
)
|
||||
|
||||
|
||||
class Test_entra_directory_sync_object_takeover_blocked:
|
||||
def test_both_blocks_enabled(self):
|
||||
"""PASS when both soft-match and hard-match blocks are enabled."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = [
|
||||
DirectorySyncSettings(
|
||||
id="sync-001",
|
||||
block_soft_match_enabled=True,
|
||||
block_cloud_object_takeover_through_hard_match_enabled=True,
|
||||
)
|
||||
]
|
||||
entra_client.directory_sync_error = None
|
||||
entra_client.organizations = [_hybrid_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert "blocks both soft-match and hard-match" in result[0].status_extended
|
||||
|
||||
def test_soft_match_disabled(self):
|
||||
"""FAIL when soft-match block is disabled on a hybrid tenant."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = [
|
||||
DirectorySyncSettings(
|
||||
id="sync-001",
|
||||
block_soft_match_enabled=False,
|
||||
block_cloud_object_takeover_through_hard_match_enabled=True,
|
||||
)
|
||||
]
|
||||
entra_client.directory_sync_error = None
|
||||
entra_client.organizations = [_hybrid_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert "blockSoftMatchEnabled" in result[0].status_extended
|
||||
|
||||
def test_hard_match_disabled(self):
|
||||
"""FAIL when hard-match block is disabled on a hybrid tenant."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = [
|
||||
DirectorySyncSettings(
|
||||
id="sync-001",
|
||||
block_soft_match_enabled=True,
|
||||
block_cloud_object_takeover_through_hard_match_enabled=False,
|
||||
)
|
||||
]
|
||||
entra_client.directory_sync_error = None
|
||||
entra_client.organizations = [_hybrid_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
"blockCloudObjectTakeoverThroughHardMatchEnabled"
|
||||
in result[0].status_extended
|
||||
)
|
||||
|
||||
def test_both_blocks_disabled(self):
|
||||
"""FAIL when both blocks are disabled on a hybrid tenant."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = [
|
||||
DirectorySyncSettings(
|
||||
id="sync-001",
|
||||
block_soft_match_enabled=False,
|
||||
block_cloud_object_takeover_through_hard_match_enabled=False,
|
||||
)
|
||||
]
|
||||
entra_client.directory_sync_error = None
|
||||
entra_client.organizations = [_hybrid_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert "blockSoftMatchEnabled" in result[0].status_extended
|
||||
assert (
|
||||
"blockCloudObjectTakeoverThroughHardMatchEnabled"
|
||||
in result[0].status_extended
|
||||
)
|
||||
|
||||
def test_cloud_only_tenant(self):
|
||||
"""PASS when tenant is cloud-only and no sync object is returned."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = []
|
||||
entra_client.directory_sync_error = None
|
||||
entra_client.organizations = [_cloud_only_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert "cloud-only" in result[0].status_extended
|
||||
|
||||
def test_cloud_only_tenant_with_sync_object_returned(self):
|
||||
"""PASS for cloud-only tenants even when Graph returns a sync object.
|
||||
|
||||
Microsoft Graph returns an onPremisesSynchronization object (with all
|
||||
features disabled) for cloud-only tenants. The check must not treat the
|
||||
disabled flags as a FAIL when on-premises sync is not enabled.
|
||||
"""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = [
|
||||
DirectorySyncSettings(
|
||||
id="tenant-id",
|
||||
password_sync_enabled=False,
|
||||
seamless_sso_enabled=False,
|
||||
block_soft_match_enabled=False,
|
||||
block_cloud_object_takeover_through_hard_match_enabled=False,
|
||||
)
|
||||
]
|
||||
entra_client.directory_sync_error = None
|
||||
entra_client.organizations = [_cloud_only_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert "cloud-only" in result[0].status_extended
|
||||
|
||||
def test_permission_error_hybrid(self):
|
||||
"""MANUAL when permissions are insufficient for a hybrid tenant."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = []
|
||||
entra_client.directory_sync_error = "Insufficient privileges"
|
||||
entra_client.organizations = [_hybrid_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "MANUAL"
|
||||
assert "Cannot verify" in result[0].status_extended
|
||||
assert "Insufficient privileges" in result[0].status_extended
|
||||
|
||||
def test_permission_error_cloud_only(self):
|
||||
"""PASS when settings cannot be read but the tenant is cloud-only."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = []
|
||||
entra_client.directory_sync_error = "Insufficient privileges"
|
||||
entra_client.organizations = [_cloud_only_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert "cloud-only" in result[0].status_extended
|
||||
|
||||
def test_hybrid_no_settings_returned(self):
|
||||
"""MANUAL when a hybrid tenant returns no directory sync settings."""
|
||||
entra_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
f"{CHECK_MODULE}.entra_client",
|
||||
new=entra_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.entra.entra_directory_sync_object_takeover_blocked.entra_directory_sync_object_takeover_blocked import (
|
||||
entra_directory_sync_object_takeover_blocked,
|
||||
)
|
||||
|
||||
entra_client.directory_sync_settings = []
|
||||
entra_client.directory_sync_error = None
|
||||
entra_client.organizations = [_hybrid_org()]
|
||||
|
||||
check = entra_directory_sync_object_takeover_blocked()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "MANUAL"
|
||||
assert (
|
||||
"no directory sync settings were returned" in result[0].status_extended
|
||||
)
|
||||
Reference in New Issue
Block a user