From cbaddad358c95491e28b1ace483f8b663a71a376 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Tue, 22 Apr 2025 19:25:30 +0200 Subject: [PATCH] feat(teams): add new check `teams_unmanaged_communication_disabled` (#7561) Co-authored-by: MrCloudSec --- prowler/CHANGELOG.md | 1 + .../m365/services/teams/teams_service.py | 2 + .../__init__.py | 0 ...naged_communication_disabled.metadata.json | 30 +++++ .../teams_unmanaged_communication_disabled.py | 42 +++++++ .../m365/services/teams/teams_service_test.py | 5 +- ...s_unmanaged_communication_disabled_test.py | 112 ++++++++++++++++++ 7 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/__init__.py create mode 100644 prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.metadata.json create mode 100644 prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.py create mode 100644 tests/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled_test.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 8c4cad0e15..f903a3024d 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add check for Bypass Disable in every Mailbox for service Defender in M365 [(#7418)](https://github.com/prowler-cloud/prowler/pull/7418) - Add new check `teams_external_domains_restricted` [(#7557)](https://github.com/prowler-cloud/prowler/pull/7557) - Add new check `teams_email_sending_to_channel_disabled` [(#7533)](https://github.com/prowler-cloud/prowler/pull/7533) +- Add new check `teams_unmanaged_communication_disabled` [(#7561)](https://github.com/prowler-cloud/prowler/pull/7561) - Add new check for AllowList not used in the Connection Filter Policy from service Defender in M365 [(#7492)](https://github.com/prowler-cloud/prowler/pull/7492) - Add new check for SafeList not enabled in the Connection Filter Policy from service Defender in M365 [(#7492)](https://github.com/prowler-cloud/prowler/pull/7492) - Add new check for DKIM enabled for service Defender in M365 [(#7485)](https://github.com/prowler-cloud/prowler/pull/7485) diff --git a/prowler/providers/m365/services/teams/teams_service.py b/prowler/providers/m365/services/teams/teams_service.py index ef758e9677..c082bd4dc4 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -46,6 +46,7 @@ class Teams(M365Service): if settings: user_settings = UserSettings( allow_external_access=settings.get("AllowFederatedUsers", True), + allow_teams_consumer=settings.get("AllowTeamsConsumer", True), ) except Exception as error: logger.error( @@ -69,3 +70,4 @@ class TeamsSettings(BaseModel): class UserSettings(BaseModel): allow_external_access: bool = True + allow_teams_consumer: bool = True diff --git a/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/__init__.py b/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.metadata.json b/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.metadata.json new file mode 100644 index 0000000000..07233d8eac --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "teams_unmanaged_communication_disabled", + "CheckTitle": "Ensure unmanaged communication is disabled.", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "critical", + "ResourceType": "Teams Settings", + "Description": "Ensure unmanaged communication is disabled in Teams admin center.", + "Risk": "Allowing communication with unmanaged Microsoft Teams users increases the risk of targeted attacks such as phishing, malware distribution (e.g., DarkGate), and exploitation techniques like GIFShell and username enumeration. Unmanaged accounts are easier for threat actors to create and use as attack vectors.", + "RelatedUrl": "https://learn.microsoft.com/en-us/powershell/module/teams/set-cstenantfederationconfiguration?view=teams-ps", + "Remediation": { + "Code": { + "CLI": "Set-CsTenantFederationConfiguration -AllowTeamsConsumer $false", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft Teams admin center https://admin.teams.microsoft.com/. 2. Click to expand Users select External access. 3. Scroll to Teams accounts not managed by an organization. 4. Set People in my organization can communicate with Teams users whose accounts aren't managed by an organization to Off. 5. Click Save.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Disable communication with Teams users whose accounts aren't managed by an organization by setting 'People in my organization can communicate with Teams users whose accounts aren't managed by an organization' to Off. This helps prevent unauthorized or risky external interactions.", + "Url": "https://learn.microsoft.com/en-us/powershell/module/teams/set-cstenantfederationconfiguration?view=teams-ps" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.py b/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.py new file mode 100644 index 0000000000..9672cd88bb --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled.py @@ -0,0 +1,42 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportM365 +from prowler.providers.m365.services.teams.teams_client import teams_client + + +class teams_unmanaged_communication_disabled(Check): + """Check if unmanaged communication is disabled in Teams admin center. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for + + This method checks if unmanaged communication is disabled in Teams admin center. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + user_settings = teams_client.user_settings + if user_settings: + report = CheckReportM365( + metadata=self.metadata(), + resource=user_settings if user_settings else {}, + resource_name="Teams User Settings", + resource_id="userSettings", + ) + report.status = "FAIL" + report.status_extended = "Users can communicate with unmanaged users." + + if user_settings and not user_settings.allow_teams_consumer: + report.status = "PASS" + report.status_extended = ( + "Users can not communicate with unmanaged users." + ) + + findings.append(report) + + return findings diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index d20a696432..5cc8a59e89 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -26,6 +26,7 @@ def mock_get_teams_client_configuration(_): def mock_get_user_settings(_): return UserSettings( allow_external_access=False, + allow_teams_consumer=False, ) @@ -67,7 +68,8 @@ class Test_Teams_Service: allow_egnyte=False, allow_google_drive=False, allow_share_file=False, - ) + ), + allow_email_into_channel=True, ) teams_client.powershell.close() @@ -88,5 +90,6 @@ class Test_Teams_Service: ) assert teams_client.user_settings == UserSettings( allow_external_access=False, + allow_teams_consumer=False, ) teams_client.powershell.close() diff --git a/tests/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled_test.py b/tests/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled_test.py new file mode 100644 index 0000000000..cde835de41 --- /dev/null +++ b/tests/providers/m365/services/teams/teams_unmanaged_communication_disabled/teams_unmanaged_communication_disabled_test.py @@ -0,0 +1,112 @@ +from unittest import mock + +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_teams_unmanaged_communication_disabled: + def test_no_user_settings(self): + teams_client = mock.MagicMock() + teams_client.audited_tenant = "audited_tenant" + teams_client.audited_domain = DOMAIN + teams_client.user_settings = None + + 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_microsoft_teams" + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_unmanaged_communication_disabled.teams_unmanaged_communication_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_unmanaged_communication_disabled.teams_unmanaged_communication_disabled import ( + teams_unmanaged_communication_disabled, + ) + + check = teams_unmanaged_communication_disabled() + result = check.execute() + assert len(result) == 0 + + def test_unmanaged_communication_allowed(self): + teams_client = mock.MagicMock() + teams_client.audited_tenant = "audited_tenant" + teams_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_microsoft_teams" + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_unmanaged_communication_disabled.teams_unmanaged_communication_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_service import UserSettings + from prowler.providers.m365.services.teams.teams_unmanaged_communication_disabled.teams_unmanaged_communication_disabled import ( + teams_unmanaged_communication_disabled, + ) + + teams_client.user_settings = UserSettings( + allow_teams_consumer=True, + ) + + check = teams_unmanaged_communication_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Users can communicate with unmanaged users." + ) + assert result[0].resource == teams_client.user_settings.dict() + assert result[0].resource_name == "Teams User Settings" + assert result[0].resource_id == "userSettings" + assert result[0].location == "global" + + def test_unmanaged_communication_restricted(self): + teams_client = mock.MagicMock() + teams_client.audited_tenant = "audited_tenant" + teams_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_microsoft_teams" + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_unmanaged_communication_disabled.teams_unmanaged_communication_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_service import UserSettings + from prowler.providers.m365.services.teams.teams_unmanaged_communication_disabled.teams_unmanaged_communication_disabled import ( + teams_unmanaged_communication_disabled, + ) + + teams_client.user_settings = UserSettings( + allow_teams_consumer=False, + ) + + check = teams_unmanaged_communication_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Users can not communicate with unmanaged users." + ) + assert result[0].resource == teams_client.user_settings.dict() + assert result[0].resource_name == "Teams User Settings" + assert result[0].resource_id == "userSettings" + assert result[0].location == "global"