mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
feat(sharepoint): add new check related with OneDrive Sync (#7589)
Co-authored-by: Andoni A. <14891798+andoniaf@users.noreply.github.com>
This commit is contained in:
@@ -75,7 +75,7 @@ It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, Fe
|
||||
| GCP | 79 | 13 | 7 | 3 |
|
||||
| Azure | 140 | 18 | 8 | 3 |
|
||||
| Kubernetes | 83 | 7 | 4 | 7 |
|
||||
| M365 | 5 | 2 | 1 | 0 |
|
||||
| M365 | 44 | 2 | 1 | 0 |
|
||||
| NHN (Unofficial) | 6 | 2 | 1 | 0 |
|
||||
|
||||
> You can list the checks, services, compliance frameworks and categories with `prowler <provider> --list-checks`, `prowler <provider> --list-services`, `prowler <provider> --list-compliance` and `prowler <provider> --list-categories`.
|
||||
|
||||
@@ -36,6 +36,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Add new check `teams_meeting_presenters_restricted` [(#7613)](https://github.com/prowler-cloud/prowler/pull/7613)
|
||||
- Add new check `teams_meeting_chat_anonymous_users_disabled` [(#7579)](https://github.com/prowler-cloud/prowler/pull/7579)
|
||||
- Add Prowler Threat Score Compliance Framework [(#7603)](https://github.com/prowler-cloud/prowler/pull/7603)
|
||||
- Add new check `sharepoint_onedrive_sync_restricted_unmanaged_devices` [(#7589)](https://github.com/prowler-cloud/prowler/pull/7589)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "m365",
|
||||
"CheckID": "sharepoint_onedrive_sync_restricted_unmanaged_devices",
|
||||
"CheckTitle": "Ensure OneDrive sync is restricted for unmanaged devices.",
|
||||
"CheckType": [],
|
||||
"ServiceName": "sharepoint",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "Sharepoint Settings",
|
||||
"Description": "Microsoft OneDrive allows users to sign in their cloud tenant account and begin syncing select folders or the entire contents of OneDrive to a local computer. By default, this includes any computer with OneDrive already installed, whether it is Entra Joined, Entra Hybrid Joined or Active Directory Domain joined. The recommended state for this setting is Allow syncing only on computers joined to specific domains Enabled: Specify the AD domain GUID(s).",
|
||||
"Risk": "Unmanaged devices can pose a security risk by allowing users to sync sensitive data to unauthorized devices, potentially leading to data leakage or unauthorized access.",
|
||||
"RelatedUrl": "https://learn.microsoft.com/en-us/graph/api/resources/sharepoint?view=graph-rest-1.0",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "Set-SPOTenantSyncClientRestriction -Enable -DomainGuids '<domain_guid_1>; <domain_guid_2>; ...'",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Navigate to SharePoint admin center https://admin.microsoft.com/sharepoint 2. Click Settings then select OneDrive - Sync. 3. Check the Allow syncing only on computers joined to specific domains. 4. Use the Get-ADDomain PowerShell command on the on-premises server to obtain the GUID for each on-premises domain. 5. Click Save.",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Restrict OneDrive sync to managed devices to prevent unauthorized access to sensitive data.",
|
||||
"Url": "https://learn.microsoft.com/en-us/sharepoint/allow-syncing-only-on-specific-domains"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportM365
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_client import (
|
||||
sharepoint_client,
|
||||
)
|
||||
|
||||
|
||||
class sharepoint_onedrive_sync_restricted_unmanaged_devices(Check):
|
||||
"""
|
||||
Check if OneDrive sync is restricted for unmanaged devices.
|
||||
|
||||
This check verifies that OneDrive sync is restricted to managed devices only.
|
||||
Unmanaged devices can pose a security risk by allowing users to sync sensitive data to unauthorized devices,
|
||||
potentially leading to data leakage or unauthorized access.
|
||||
|
||||
The check fails if OneDrive sync is not restricted to managed devices (AllowedDomainGuidsForSyncApp is empty).
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportM365]:
|
||||
"""
|
||||
Execute the OneDrive sync restriction check.
|
||||
|
||||
Retrieves the OneDrive sync settings from the Microsoft 365 SharePoint client and
|
||||
generates a report indicating whether OneDrive sync is restricted to managed devices only.
|
||||
|
||||
Returns:
|
||||
List[CheckReportM365]: A list containing the report object with the result of the check.
|
||||
"""
|
||||
findings = []
|
||||
settings = sharepoint_client.settings
|
||||
if settings:
|
||||
report = CheckReportM365(
|
||||
self.metadata(),
|
||||
resource=settings if settings else {},
|
||||
resource_name="SharePoint Settings",
|
||||
resource_id=sharepoint_client.tenant_domain,
|
||||
)
|
||||
report.status = "PASS"
|
||||
report.status_extended = "Microsoft 365 SharePoint does not allow OneDrive sync to unmanaged devices."
|
||||
|
||||
if len(settings.allowedDomainGuidsForSyncApp) == 0:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Microsoft 365 SharePoint allows OneDrive sync to unmanaged devices."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from asyncio import gather, get_event_loop
|
||||
from typing import List, Optional
|
||||
|
||||
@@ -26,7 +27,6 @@ class SharePoint(M365Service):
|
||||
settings = None
|
||||
try:
|
||||
global_settings = await self.client.admin.sharepoint.settings.get()
|
||||
|
||||
settings = SharePointSettings(
|
||||
sharingCapability=(
|
||||
str(global_settings.sharing_capability).split(".")[-1]
|
||||
@@ -38,6 +38,7 @@ class SharePoint(M365Service):
|
||||
sharingDomainRestrictionMode=global_settings.sharing_domain_restriction_mode,
|
||||
legacyAuth=global_settings.is_legacy_auth_protocols_enabled,
|
||||
resharingEnabled=global_settings.is_resharing_by_external_users_enabled,
|
||||
allowedDomainGuidsForSyncApp=global_settings.allowed_domain_guids_for_sync_app,
|
||||
)
|
||||
|
||||
except ODataError as error:
|
||||
@@ -60,3 +61,4 @@ class SharePointSettings(BaseModel):
|
||||
sharingDomainRestrictionMode: str
|
||||
resharingEnabled: bool
|
||||
legacyAuth: bool
|
||||
allowedDomainGuidsForSyncApp: List[uuid.UUID]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_service import (
|
||||
@@ -35,6 +36,7 @@ class Test_sharepoint_external_sharing_managed:
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
sharingDomainRestrictionMode="none",
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
@@ -80,6 +82,7 @@ class Test_sharepoint_external_sharing_managed:
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
@@ -125,6 +128,7 @@ class Test_sharepoint_external_sharing_managed:
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
sharingDomainRestrictionMode="blockList",
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
@@ -170,6 +174,7 @@ class Test_sharepoint_external_sharing_managed:
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
@@ -215,6 +220,7 @@ class Test_sharepoint_external_sharing_managed:
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
sharingDomainRestrictionMode="blockList",
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_service import (
|
||||
@@ -35,6 +36,7 @@ class Test_sharepoint_external_sharing_restricted:
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
resharingEnabled=False,
|
||||
legacyAuth=True,
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
@@ -78,6 +80,7 @@ class Test_sharepoint_external_sharing_restricted:
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
resharingEnabled=False,
|
||||
legacyAuth=True,
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_service import (
|
||||
@@ -35,6 +36,7 @@ class Test_sharepoint_guest_sharing_restricted:
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
@@ -79,6 +81,7 @@ class Test_sharepoint_guest_sharing_restricted:
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
legacyAuth=True,
|
||||
resharingEnabled=True,
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from unittest import mock
|
||||
|
||||
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
|
||||
@@ -35,6 +36,7 @@ class Test_sharepoint_modern_authentication_required:
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
resharingEnabled=False,
|
||||
legacyAuth=False,
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
@@ -81,6 +83,7 @@ class Test_sharepoint_modern_authentication_required:
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
resharingEnabled=False,
|
||||
legacyAuth=True,
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import uuid
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_service import (
|
||||
SharePointSettings,
|
||||
)
|
||||
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
|
||||
|
||||
|
||||
class Test_sharepoint_onedrive_sync_restricted_unmanaged_devices:
|
||||
def test_no_allowed_domain_guids(self):
|
||||
"""
|
||||
Test when there are no allowed domain guids for OneDrive sync app
|
||||
|
||||
|
||||
"""
|
||||
sharepoint_client = mock.MagicMock
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.m365.services.sharepoint.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_client",
|
||||
new=sharepoint_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_onedrive_sync_restricted_unmanaged_devices import (
|
||||
sharepoint_onedrive_sync_restricted_unmanaged_devices,
|
||||
)
|
||||
|
||||
sharepoint_client.settings = SharePointSettings(
|
||||
sharingCapability="ExternalUserSharingOnly",
|
||||
sharingAllowedDomainList=["allowed-domain.com"],
|
||||
sharingBlockedDomainList=["blocked-domain.com"],
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
sharingDomainRestrictionMode="none",
|
||||
allowedDomainGuidsForSyncApp=[],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
check = sharepoint_onedrive_sync_restricted_unmanaged_devices()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Microsoft 365 SharePoint allows OneDrive sync to unmanaged devices."
|
||||
)
|
||||
assert result[0].resource_id == DOMAIN
|
||||
assert result[0].location == "global"
|
||||
assert result[0].resource_name == "SharePoint Settings"
|
||||
assert result[0].resource == sharepoint_client.settings.dict()
|
||||
|
||||
def test_allowed_domain_guids(self):
|
||||
"""
|
||||
Test when there are allowed domain guids for OneDrive sync app
|
||||
"""
|
||||
sharepoint_client = mock.MagicMock
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.m365.services.sharepoint.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_client",
|
||||
new=sharepoint_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_onedrive_sync_restricted_unmanaged_devices import (
|
||||
sharepoint_onedrive_sync_restricted_unmanaged_devices,
|
||||
)
|
||||
|
||||
sharepoint_client.settings = SharePointSettings(
|
||||
sharingCapability="ExternalUserSharingOnly",
|
||||
sharingAllowedDomainList=[],
|
||||
sharingBlockedDomainList=["blocked-domain.com"],
|
||||
legacyAuth=True,
|
||||
resharingEnabled=False,
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
allowedDomainGuidsForSyncApp=[uuid.uuid4()],
|
||||
)
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
check = sharepoint_onedrive_sync_restricted_unmanaged_devices()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Microsoft 365 SharePoint does not allow OneDrive sync to unmanaged devices."
|
||||
)
|
||||
assert result[0].resource_id == DOMAIN
|
||||
assert result[0].location == "global"
|
||||
assert result[0].resource_name == "SharePoint Settings"
|
||||
assert result[0].resource == sharepoint_client.settings.dict()
|
||||
|
||||
def test_empty_settings(self):
|
||||
"""
|
||||
Test when sharepoint_client.settings is empty:
|
||||
The check should return an empty list of findings.
|
||||
"""
|
||||
sharepoint_client = mock.MagicMock
|
||||
sharepoint_client.settings = {}
|
||||
sharepoint_client.tenant_domain = DOMAIN
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_m365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.m365.services.sharepoint.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_client",
|
||||
new=sharepoint_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.m365.services.sharepoint.sharepoint_onedrive_sync_restricted_unmanaged_devices.sharepoint_onedrive_sync_restricted_unmanaged_devices import (
|
||||
sharepoint_onedrive_sync_restricted_unmanaged_devices,
|
||||
)
|
||||
|
||||
check = sharepoint_onedrive_sync_restricted_unmanaged_devices()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.m365.models import M365IdentityInfo
|
||||
@@ -7,6 +8,8 @@ from prowler.providers.m365.services.sharepoint.sharepoint_service import (
|
||||
)
|
||||
from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider
|
||||
|
||||
uuid_value = uuid.uuid4()
|
||||
|
||||
|
||||
async def mock_sharepoint_get_settings(_):
|
||||
return SharePointSettings(
|
||||
@@ -16,6 +19,7 @@ async def mock_sharepoint_get_settings(_):
|
||||
sharingDomainRestrictionMode="allowList",
|
||||
resharingEnabled=False,
|
||||
legacyAuth=True,
|
||||
allowedDomainGuidsForSyncApp=[uuid_value],
|
||||
)
|
||||
|
||||
|
||||
@@ -39,3 +43,5 @@ class Test_SharePoint_Service:
|
||||
assert settings.sharingDomainRestrictionMode == "allowList"
|
||||
assert settings.resharingEnabled is False
|
||||
assert settings.legacyAuth is True
|
||||
assert settings.allowedDomainGuidsForSyncApp == [uuid_value]
|
||||
assert len(settings.allowedDomainGuidsForSyncApp) == 1
|
||||
|
||||
Reference in New Issue
Block a user