diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index f903a3024d..8426aa3e17 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - 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 `teams_external_users_cannot_start_conversations` [(#7562)](https://github.com/prowler-cloud/prowler/pull/7562) - 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_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled.py b/prowler/providers/m365/services/teams/teams_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled.py index 915840ff1c..6a473daf96 100644 --- a/prowler/providers/m365/services/teams/teams_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled.py +++ b/prowler/providers/m365/services/teams/teams_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled.py @@ -29,12 +29,14 @@ class teams_email_sending_to_channel_disabled(Check): resource_id="teamsSettings", ) report.status = "FAIL" - report.status_extended = "Users can send emails to channel email addresses." + report.status_extended = ( + "Teams users can send emails to channel email addresses." + ) if teams_settings and not teams_settings.allow_email_into_channel: report.status = "PASS" report.status_extended = ( - "Users can not send emails to channel email addresses." + "Teams users cannot send emails to channel email addresses." ) findings.append(report) diff --git a/prowler/providers/m365/services/teams/teams_external_users_cannot_start_conversations/__init__.py b/prowler/providers/m365/services/teams/teams_external_users_cannot_start_conversations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations.metadata.json b/prowler/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations.metadata.json new file mode 100644 index 0000000000..8f95019c66 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "teams_external_users_cannot_start_conversations", + "CheckTitle": "Ensure external users cannot start conversations.", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "critical", + "ResourceType": "Teams Settings", + "Description": "Ensure external users cannot initiate conversations.", + "Risk": "Allowing unmanaged external Teams users to initiate conversations increases the risk of phishing, malware distribution such as DarkGate, social engineering attacks like those by Midnight Blizzard, GIFShell exploitation, and username enumeration.", + "RelatedUrl": "https://learn.microsoft.com/en-us/powershell/module/teams/set-cstenantfederationconfiguration?view=teams-ps", + "Remediation": { + "Code": { + "CLI": "Set-CsTenantFederationConfiguration -AllowTeamsConsumerInbound $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. Uncheck External users with Teams accounts not managed by an organization can contact users in my organization. 5. Click Save.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Disable the ability for external Teams users not managed by an organization to initiate conversations by unchecking the option that permits them to contact users in your organization. This provides an added layer of protection, especially if exceptions are made to allow limited communication with unmanaged users.", + "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_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations.py b/prowler/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations.py new file mode 100644 index 0000000000..863c026383 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations.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_external_users_cannot_start_conversations(Check): + """Check if external users cannot start conversations. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for external users cannot start conversations. + + This method checks if external users cannot start conversations. + + 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 = "External Teams users can initiate conversations." + + if user_settings and not user_settings.allow_teams_consumer_inbound: + report.status = "PASS" + report.status_extended = ( + "External Teams users cannot initiate conversations." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/m365/services/teams/teams_service.py b/prowler/providers/m365/services/teams/teams_service.py index c082bd4dc4..9d1810e437 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -47,6 +47,9 @@ class Teams(M365Service): user_settings = UserSettings( allow_external_access=settings.get("AllowFederatedUsers", True), allow_teams_consumer=settings.get("AllowTeamsConsumer", True), + allow_teams_consumer_inbound=settings.get( + "AllowTeamsConsumerInbound", True + ), ) except Exception as error: logger.error( @@ -71,3 +74,4 @@ class TeamsSettings(BaseModel): class UserSettings(BaseModel): allow_external_access: bool = True allow_teams_consumer: bool = True + allow_teams_consumer_inbound: bool = True 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 index 9672cd88bb..dc8e377ecf 100644 --- 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 @@ -12,7 +12,7 @@ class teams_unmanaged_communication_disabled(Check): """ def execute(self) -> List[CheckReportM365]: - """Execute the check for + """Execute the check for unmanaged communication disabled. This method checks if unmanaged communication is disabled in Teams admin center. @@ -29,12 +29,12 @@ class teams_unmanaged_communication_disabled(Check): resource_id="userSettings", ) report.status = "FAIL" - report.status_extended = "Users can communicate with unmanaged users." + report.status_extended = "Teams 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." + "Teams users cannot communicate with unmanaged users." ) findings.append(report) diff --git a/tests/providers/m365/services/teams/teams_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled_test.py b/tests/providers/m365/services/teams/teams_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled_test.py index 7ee0a6a61d..3d1702e5ae 100644 --- a/tests/providers/m365/services/teams/teams_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled_test.py +++ b/tests/providers/m365/services/teams/teams_email_sending_to_channel_disabled/teams_email_sending_to_channel_disabled_test.py @@ -47,7 +47,7 @@ class Test_teams_email_sending_to_channel_disabled: assert result[0].status == "FAIL" assert ( result[0].status_extended - == "Users can send emails to channel email addresses." + == "Teams users can send emails to channel email addresses." ) assert result[0].resource == teams_client.teams_settings.dict() assert result[0].resource_name == "Teams Settings" @@ -97,7 +97,7 @@ class Test_teams_email_sending_to_channel_disabled: assert result[0].status == "PASS" assert ( result[0].status_extended - == "Users can not send emails to channel email addresses." + == "Teams users cannot send emails to channel email addresses." ) assert result[0].resource == teams_client.teams_settings.dict() assert result[0].resource_name == "Teams Settings" diff --git a/tests/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations_test.py b/tests/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations_test.py new file mode 100644 index 0000000000..6b5dd1dff0 --- /dev/null +++ b/tests/providers/m365/services/teams/teams_external_users_cannot_start_conversations/teams_external_users_cannot_start_conversations_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_external_users_cannot_start_conversations: + 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_external_users_cannot_start_conversations.teams_external_users_cannot_start_conversations.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_external_users_cannot_start_conversations.teams_external_users_cannot_start_conversations import ( + teams_external_users_cannot_start_conversations, + ) + + check = teams_external_users_cannot_start_conversations() + 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_external_users_cannot_start_conversations.teams_external_users_cannot_start_conversations.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_external_users_cannot_start_conversations.teams_external_users_cannot_start_conversations import ( + teams_external_users_cannot_start_conversations, + ) + from prowler.providers.m365.services.teams.teams_service import UserSettings + + teams_client.user_settings = UserSettings( + allow_teams_consumer_inbound=True, + ) + + check = teams_external_users_cannot_start_conversations() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "External Teams users can initiate conversations." + ) + 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_external_users_cannot_start_conversations.teams_external_users_cannot_start_conversations.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_external_users_cannot_start_conversations.teams_external_users_cannot_start_conversations import ( + teams_external_users_cannot_start_conversations, + ) + from prowler.providers.m365.services.teams.teams_service import UserSettings + + teams_client.user_settings = UserSettings( + allow_teams_consumer_inbound=False, + ) + + check = teams_external_users_cannot_start_conversations() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "External Teams users cannot initiate conversations." + ) + 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" diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index 5cc8a59e89..41874155d5 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -27,6 +27,7 @@ def mock_get_user_settings(_): return UserSettings( allow_external_access=False, allow_teams_consumer=False, + allow_teams_consumer_inbound=False, ) @@ -91,5 +92,6 @@ class Test_Teams_Service: assert teams_client.user_settings == UserSettings( allow_external_access=False, allow_teams_consumer=False, + allow_teams_consumer_inbound=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 index cde835de41..2e46ac8efc 100644 --- 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 @@ -64,7 +64,7 @@ class Test_teams_unmanaged_communication_disabled: assert result[0].status == "FAIL" assert ( result[0].status_extended - == "Users can communicate with unmanaged users." + == "Teams users can communicate with unmanaged users." ) assert result[0].resource == teams_client.user_settings.dict() assert result[0].resource_name == "Teams User Settings" @@ -104,7 +104,7 @@ class Test_teams_unmanaged_communication_disabled: assert result[0].status == "PASS" assert ( result[0].status_extended - == "Users can not communicate with unmanaged users." + == "Teams users cannot communicate with unmanaged users." ) assert result[0].resource == teams_client.user_settings.dict() assert result[0].resource_name == "Teams User Settings"