From ed1f82828cc2fbb43ecba8e37acdca78cd72e3a1 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:02:24 +0200 Subject: [PATCH 01/19] feat(teams): add new check --- ...ng_external_control_disabled.metadata.json | 30 +++++ ...teams_meeting_external_control_disabled.py | 44 +++++++ .../m365/services/teams/teams_service.py | 4 + ..._meeting_external_control_disabled_test.py | 118 ++++++++++++++++++ .../m365/services/teams/teams_service_test.py | 1 + 5 files changed, 197 insertions(+) create mode 100644 prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.metadata.json create mode 100644 prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.py create mode 100644 tests/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled_test.py diff --git a/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.metadata.json b/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.metadata.json new file mode 100644 index 0000000000..342a26dbe1 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "teams_meeting_external_control_disabled", + "CheckTitle": "Ensure external participants can't give or request control", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "Teams Global Meeting Policy", + "Description": "Ensure external participants can't give or request control in Teams meetings.", + "Risk": "Allowing external participants to give or request control during meetings could lead to unauthorized content sharing or malicious actions by external users.", + "RelatedUrl": "https://learn.microsoft.com/en-us/powershell/module/teams/set-csteamsmeetingpolicy?view=teams-ps", + "Remediation": { + "Code": { + "CLI": "Set-CsTeamsMeetingPolicy -Identity Global -AllowExternalParticipantGiveRequestControl $false", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft Teams admin center https://admin.teams.microsoft.com. 2. Click to expand Meetings select Meeting policies. 3. Click Global (Org-wide default). 4. Under content sharing set External participants can give or request control to Off.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Disable the ability for external participants to give or request control during Teams meetings to prevent unauthorized content sharing and maintain meeting security.", + "Url": "https://learn.microsoft.com/en-us/powershell/module/teams/set-csteamsmeetingpolicy?view=teams-ps" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.py b/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.py new file mode 100644 index 0000000000..68da2b46f0 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled.py @@ -0,0 +1,44 @@ +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_meeting_external_control_disabled(Check): + """Check if external participants can't give or request control in meetings. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for external participants' control permissions in meetings. + + This method checks if external participants are prevented from giving or requesting control in meetings. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + global_meeting_policy = teams_client.global_meeting_policy + if global_meeting_policy: + report = CheckReportM365( + metadata=self.metadata(), + resource=global_meeting_policy if global_meeting_policy else {}, + resource_name="Teams Meetings Global (Org-wide default) Policy", + resource_id="teamsMeetingsGlobalPolicy", + ) + report.status = "FAIL" + report.status_extended = ( + "External participants can give or request control in Teams meetings." + ) + + if ( + not global_meeting_policy.allow_external_participant_give_request_control + ): + report.status = "PASS" + report.status_extended = "External participants cannot give or request control in Teams meetings." + + 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 7231176b9a..5c48469990 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -52,6 +52,9 @@ def _get_global_meeting_policy(self): allow_anonymous_users_to_start_meeting=global_meeting_policy.get( "AllowAnonymousUsersToStartMeeting", True ), + allow_external_participant_give_request_control=global_meeting_policy.get( + "AllowExternalParticipantGiveRequestControl", True + ), ) except Exception as error: logger.error( @@ -95,6 +98,7 @@ class TeamsSettings(BaseModel): class GlobalMeetingPolicy(BaseModel): allow_anonymous_users_to_join_meeting: bool = True allow_anonymous_users_to_start_meeting: bool = True + allow_external_participant_give_request_control: bool = True class UserSettings(BaseModel): diff --git a/tests/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled_test.py b/tests/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled_test.py new file mode 100644 index 0000000000..2216c9ad16 --- /dev/null +++ b/tests/providers/m365/services/teams/teams_meeting_external_control_disabled/teams_meeting_external_control_disabled_test.py @@ -0,0 +1,118 @@ +from unittest import mock + +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_teams_meeting_external_control_disabled: + def test_no_global_meeting_policy(self): + teams_client = mock.MagicMock() + teams_client.global_meeting_policy = 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_meeting_external_control_disabled.teams_meeting_external_control_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_external_control_disabled.teams_meeting_external_control_disabled import ( + teams_meeting_external_control_disabled, + ) + + check = teams_meeting_external_control_disabled() + result = check.execute() + assert len(result) == 0 + + def test_external_participants_can_control(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_meeting_external_control_disabled.teams_meeting_external_control_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_external_control_disabled.teams_meeting_external_control_disabled import ( + teams_meeting_external_control_disabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + allow_external_participant_give_request_control=True + ) + + check = teams_meeting_external_control_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "External participants can give or request control in Teams meetings." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" + + def test_external_participants_cannot_control(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_meeting_external_control_disabled.teams_meeting_external_control_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_external_control_disabled.teams_meeting_external_control_disabled import ( + teams_meeting_external_control_disabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + allow_external_participant_give_request_control=False + ) + + check = teams_meeting_external_control_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "External participants cannot give or request control in Teams meetings." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index a4b523eaf7..a4ce1de7a6 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -28,6 +28,7 @@ def mock_get_global_meeting_policy(_): return GlobalMeetingPolicy( allow_anonymous_users_to_join_meeting=False, allow_anonymous_users_to_start_meeting=False, + allow_external_participant_give_request_control=False, ) From e5e49da3cdfa3f48fe9e0733bee6e2da2733db2d Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:18:58 +0200 Subject: [PATCH 02/19] fix(teams): add missing field --- tests/providers/m365/services/teams/teams_service_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index a4ce1de7a6..f4306a6215 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -123,5 +123,6 @@ def test_get_global_meeting_policy(self): assert teams_client.global_meeting_policy == GlobalMeetingPolicy( allow_anonymous_users_to_join_meeting=False, allow_anonymous_users_to_start_meeting=False, + allow_external_participant_give_request_control=False, ) teams_client.powershell.close() From d0d5f11492ff18f4c947a5c5ce3d0a0aa17f5dd7 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:28:42 +0200 Subject: [PATCH 03/19] feat(teams): add new check `teams_meeting_external_chat_disabled` --- ...eting_external_chat_disabled.metadata.json | 30 +++++ .../teams_meeting_external_chat_disabled.py | 44 +++++++ .../m365/services/teams/teams_service.py | 4 + ...ams_meeting_external_chat_disabled_test.py | 118 ++++++++++++++++++ .../m365/services/teams/teams_service_test.py | 2 + 5 files changed, 198 insertions(+) create mode 100644 prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.metadata.json create mode 100644 prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.py create mode 100644 tests/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled_test.py diff --git a/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.metadata.json b/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.metadata.json new file mode 100644 index 0000000000..50c5d8d138 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "teams_meeting_external_chat_disabled", + "CheckTitle": "Ensure external meeting chat is off", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "Teams Global Meeting Policy", + "Description": "Ensure users can't read or write messages in external meeting chats with untrusted organizations.", + "Risk": "Allowing chat in external meetings increases the risk of exploits like GIFShell or DarkGate malware being delivered to users through untrusted organizations.", + "RelatedUrl": "https://learn.microsoft.com/en-us/powershell/module/teams/set-csteamsmeetingpolicy?view=teams-ps", + "Remediation": { + "Code": { + "CLI": "Set-CsTeamsMeetingPolicy -Identity Global -AllowExternalNonTrustedMeetingChat $false", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft Teams admin center https://admin.teams.microsoft.com. 2. Click to expand Meetings select Meeting policies. 3. Click Global (Org-wide default). 4. Under meeting engagement set External meeting chat to Off.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Disable external meeting chat to prevent potential security risks from untrusted organizations. This helps protect against exploits like GIFShell or DarkGate malware.", + "Url": "https://learn.microsoft.com/en-us/powershell/module/teams/set-csteamsmeetingpolicy?view=teams-ps" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.py b/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.py new file mode 100644 index 0000000000..ac4787f7e6 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled.py @@ -0,0 +1,44 @@ +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_meeting_external_chat_disabled(Check): + """Check if external meeting chat is disabled. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for external meeting chat settings. + + This method checks if external meeting chat is disabled for untrusted organizations. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + global_meeting_policy = teams_client.global_meeting_policy + if global_meeting_policy: + report = CheckReportM365( + metadata=self.metadata(), + resource=global_meeting_policy if global_meeting_policy else {}, + resource_name="Teams Meetings Global (Org-wide default) Policy", + resource_id="teamsMeetingsGlobalPolicy", + ) + report.status = "FAIL" + report.status_extended = ( + "External meeting chat is enabled for untrusted organizations." + ) + + if not global_meeting_policy.allow_external_non_trusted_meeting_chat: + report.status = "PASS" + report.status_extended = ( + "External meeting chat is disabled for untrusted organizations." + ) + + 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 5c48469990..c5e4e0da02 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -55,6 +55,9 @@ def _get_global_meeting_policy(self): allow_external_participant_give_request_control=global_meeting_policy.get( "AllowExternalParticipantGiveRequestControl", True ), + allow_external_non_trusted_meeting_chat=global_meeting_policy.get( + "AllowExternalNonTrustedMeetingChat", True + ), ) except Exception as error: logger.error( @@ -99,6 +102,7 @@ class GlobalMeetingPolicy(BaseModel): allow_anonymous_users_to_join_meeting: bool = True allow_anonymous_users_to_start_meeting: bool = True allow_external_participant_give_request_control: bool = True + allow_external_non_trusted_meeting_chat: bool = True class UserSettings(BaseModel): diff --git a/tests/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled_test.py b/tests/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled_test.py new file mode 100644 index 0000000000..69da25e0eb --- /dev/null +++ b/tests/providers/m365/services/teams/teams_meeting_external_chat_disabled/teams_meeting_external_chat_disabled_test.py @@ -0,0 +1,118 @@ +from unittest import mock + +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_teams_meeting_external_chat_disabled: + def test_no_global_meeting_policy(self): + teams_client = mock.MagicMock() + teams_client.global_meeting_policy = 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_meeting_external_chat_disabled.teams_meeting_external_chat_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_external_chat_disabled.teams_meeting_external_chat_disabled import ( + teams_meeting_external_chat_disabled, + ) + + check = teams_meeting_external_chat_disabled() + result = check.execute() + assert len(result) == 0 + + def test_external_chat_enabled(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_meeting_external_chat_disabled.teams_meeting_external_chat_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_external_chat_disabled.teams_meeting_external_chat_disabled import ( + teams_meeting_external_chat_disabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + allow_external_non_trusted_meeting_chat=True + ) + + check = teams_meeting_external_chat_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "External meeting chat is enabled for untrusted organizations." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" + + def test_external_chat_disabled(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_meeting_external_chat_disabled.teams_meeting_external_chat_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_external_chat_disabled.teams_meeting_external_chat_disabled import ( + teams_meeting_external_chat_disabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + allow_external_non_trusted_meeting_chat=False + ) + + check = teams_meeting_external_chat_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "External meeting chat is disabled for untrusted organizations." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index f4306a6215..0980c5020b 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -29,6 +29,7 @@ def mock_get_global_meeting_policy(_): allow_anonymous_users_to_join_meeting=False, allow_anonymous_users_to_start_meeting=False, allow_external_participant_give_request_control=False, + allow_external_non_trusted_meeting_chat=False, ) @@ -124,5 +125,6 @@ def test_get_global_meeting_policy(self): allow_anonymous_users_to_join_meeting=False, allow_anonymous_users_to_start_meeting=False, allow_external_participant_give_request_control=False, + allow_external_non_trusted_meeting_chat=False, ) teams_client.powershell.close() From 9b546838b6894ccf95042bee17908f8c3b868efe Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:48:27 +0200 Subject: [PATCH 04/19] feat(teams): add new check `teams_meeting_recording_disabled` --- ...s_meeting_recording_disabled.metadata.json | 30 +++++ .../teams_meeting_recording_disabled.py | 42 ++++++ .../m365/services/teams/teams_service.py | 4 + .../teams_meeting_recording_disabled_test.py | 120 ++++++++++++++++++ .../m365/services/teams/teams_service_test.py | 2 + 5 files changed, 198 insertions(+) create mode 100644 prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled.metadata.json create mode 100644 prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled.py create mode 100644 tests/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled_test.py diff --git a/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled.metadata.json b/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled.metadata.json new file mode 100644 index 0000000000..c6fb507e8f --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "teams_meeting_recording_disabled", + "CheckTitle": "Ensure meeting recording is off by default", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "Teams Global Meeting Policy", + "Description": "Ensures that only authorized users, such as organizers, co-organizers, and leads, can initiate a recording.", + "Risk": "Allowing meeting recordings by default increases the risk of unauthorized individuals capturing and potentially sharing sensitive meeting content.", + "RelatedUrl": "https://learn.microsoft.com/en-us/powershell/module/teams/set-csteamsmeetingpolicy?view=teams-ps", + "Remediation": { + "Code": { + "CLI": "Set-CsTeamsMeetingPolicy -Identity Global -AllowCloudRecording $false", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft Teams admin center https://admin.teams.microsoft.com. 2. Click to expand Meetings select Meeting policies. 3. Click Global (Org-wide default). 4. Under Recording & transcription set Meeting recording to Off.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Disable meeting recording in the Global meeting policy to ensure only authorized users can initiate recordings. Create separate policies for users or groups who need recording capabilities.", + "Url": "https://learn.microsoft.com/en-us/powershell/module/teams/set-csteamsmeetingpolicy?view=teams-ps" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled.py b/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled.py new file mode 100644 index 0000000000..7f2b4e2ec1 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_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_meeting_recording_disabled(Check): + """Check if meeting recording is disabled by default. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for meeting recording settings. + + This method checks if meeting recording is disabled in the Global meeting policy. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + global_meeting_policy = teams_client.global_meeting_policy + if global_meeting_policy: + report = CheckReportM365( + metadata=self.metadata(), + resource=global_meeting_policy if global_meeting_policy else {}, + resource_name="Teams Meetings Global (Org-wide default) Policy", + resource_id="teamsMeetingsGlobalPolicy", + ) + report.status = "FAIL" + report.status_extended = ( + "Meeting recording is enabled by default in the Global meeting policy." + ) + + if not global_meeting_policy.allow_cloud_recording: + report.status = "PASS" + report.status_extended = "Meeting recording is disabled by default in the Global meeting policy." + + 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 c5e4e0da02..f04af0d7b5 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -58,6 +58,9 @@ def _get_global_meeting_policy(self): allow_external_non_trusted_meeting_chat=global_meeting_policy.get( "AllowExternalNonTrustedMeetingChat", True ), + allow_cloud_recording=global_meeting_policy.get( + "AllowCloudRecording", True + ), ) except Exception as error: logger.error( @@ -103,6 +106,7 @@ class GlobalMeetingPolicy(BaseModel): allow_anonymous_users_to_start_meeting: bool = True allow_external_participant_give_request_control: bool = True allow_external_non_trusted_meeting_chat: bool = True + allow_cloud_recording: bool = True class UserSettings(BaseModel): diff --git a/tests/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled_test.py b/tests/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled_test.py new file mode 100644 index 0000000000..d331f78b6a --- /dev/null +++ b/tests/providers/m365/services/teams/teams_meeting_recording_disabled/teams_meeting_recording_disabled_test.py @@ -0,0 +1,120 @@ +from unittest import mock + +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_teams_meeting_recording_disabled: + def test_no_global_meeting_policy(self): + teams_client = mock.MagicMock() + teams_client.global_meeting_policy = None + 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_meeting_recording_disabled.teams_meeting_recording_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_recording_disabled.teams_meeting_recording_disabled import ( + teams_meeting_recording_disabled, + ) + + check = teams_meeting_recording_disabled() + result = check.execute() + assert len(result) == 0 + + def test_meeting_recording_enabled(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_meeting_recording_disabled.teams_meeting_recording_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_recording_disabled.teams_meeting_recording_disabled import ( + teams_meeting_recording_disabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + allow_cloud_recording=True + ) + + check = teams_meeting_recording_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Meeting recording is enabled by default in the Global meeting policy." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" + + def test_meeting_recording_disabled(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_meeting_recording_disabled.teams_meeting_recording_disabled.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_recording_disabled.teams_meeting_recording_disabled import ( + teams_meeting_recording_disabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + allow_cloud_recording=False + ) + + check = teams_meeting_recording_disabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Meeting recording is disabled by default in the Global meeting policy." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index 0980c5020b..31156f0d01 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -30,6 +30,7 @@ def mock_get_global_meeting_policy(_): allow_anonymous_users_to_start_meeting=False, allow_external_participant_give_request_control=False, allow_external_non_trusted_meeting_chat=False, + allow_cloud_recording=False, ) @@ -126,5 +127,6 @@ def test_get_global_meeting_policy(self): allow_anonymous_users_to_start_meeting=False, allow_external_participant_give_request_control=False, allow_external_non_trusted_meeting_chat=False, + allow_cloud_recording=False, ) teams_client.powershell.close() From f6cf35a6a6261c9d7b79aa3da0d8e3ac4398c4ad Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:40:49 +0200 Subject: [PATCH 05/19] chore: update changelog and add missing init --- prowler/CHANGELOG.md | 1 + .../teams/teams_meeting_external_control_disabled/__init__.py | 0 2 files changed, 1 insertion(+) create mode 100644 prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/__init__.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 93d065e206..f048e9214f 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -30,6 +30,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check `teams_meeting_anonymous_user_start_disabled` [(#7567)](https://github.com/prowler-cloud/prowler/pull/7567) - Add new check `teams_meeting_external_lobby_bypass_disabled` [(#7568)](https://github.com/prowler-cloud/prowler/pull/7568) - Add new check `teams_meeting_dial_in_lobby_bypass_disabled` [(#7571)](https://github.com/prowler-cloud/prowler/pull/7571) +- Add new check `teams_meeting_external_control_disabled` [(#7604)](https://github.com/prowler-cloud/prowler/pull/7604) ### Fixed diff --git a/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/__init__.py b/prowler/providers/m365/services/teams/teams_meeting_external_control_disabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 1e5cb4756bb4db8465c6f62d7bbc9be2dc5d97dd Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:51:28 +0200 Subject: [PATCH 06/19] chore: update changelog and add missing init --- prowler/CHANGELOG.md | 1 + .../teams/teams_meeting_external_chat_disabled/__init__.py | 0 2 files changed, 1 insertion(+) create mode 100644 prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/__init__.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index f048e9214f..0537e929b4 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -31,6 +31,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check `teams_meeting_external_lobby_bypass_disabled` [(#7568)](https://github.com/prowler-cloud/prowler/pull/7568) - Add new check `teams_meeting_dial_in_lobby_bypass_disabled` [(#7571)](https://github.com/prowler-cloud/prowler/pull/7571) - Add new check `teams_meeting_external_control_disabled` [(#7604)](https://github.com/prowler-cloud/prowler/pull/7604) +- Add new check `teams_meeting_external_chat_disabled` [(#7605)](https://github.com/prowler-cloud/prowler/pull/7605) ### Fixed diff --git a/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/__init__.py b/prowler/providers/m365/services/teams/teams_meeting_external_chat_disabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 3f65963d5fd7685f6a64ebdd463cc86172f9d855 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 09:05:59 +0200 Subject: [PATCH 07/19] chore: update changelog and add missing init --- prowler/CHANGELOG.md | 1 + .../services/teams/teams_meeting_recording_disabled/__init__.py | 0 2 files changed, 1 insertion(+) create mode 100644 prowler/providers/m365/services/teams/teams_meeting_recording_disabled/__init__.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 0537e929b4..a9070f556c 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -32,6 +32,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check `teams_meeting_dial_in_lobby_bypass_disabled` [(#7571)](https://github.com/prowler-cloud/prowler/pull/7571) - Add new check `teams_meeting_external_control_disabled` [(#7604)](https://github.com/prowler-cloud/prowler/pull/7604) - Add new check `teams_meeting_external_chat_disabled` [(#7605)](https://github.com/prowler-cloud/prowler/pull/7605) +- Add new check `teams_meeting_recording_disabled` [(#7607)](https://github.com/prowler-cloud/prowler/pull/7607) ### Fixed diff --git a/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/__init__.py b/prowler/providers/m365/services/teams/teams_meeting_recording_disabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 7e25414d54d2c433f0a5e338c8e54df93f8205fc Mon Sep 17 00:00:00 2001 From: Andoni A <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:24:11 +0200 Subject: [PATCH 08/19] feat(teams): add new check `teams_meeting_presenters_restricted` --- .../__init__.py | 0 ...eeting_presenters_restricted.metadata.json | 30 +++++ .../teams_meeting_presenters_restricted.py | 45 +++++++ .../m365/services/teams/teams_service.py | 4 + ...eams_meeting_presenters_restricted_test.py | 117 ++++++++++++++++++ .../m365/services/teams/teams_service_test.py | 2 + 6 files changed, 198 insertions(+) create mode 100644 prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/__init__.py create mode 100644 prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.metadata.json create mode 100644 prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.py create mode 100644 tests/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted_test.py diff --git a/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/__init__.py b/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.metadata.json b/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.metadata.json new file mode 100644 index 0000000000..d1117d0e2a --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "teams_meeting_presenters_restricted", + "CheckTitle": "Ensure only organizers and co-organizers can present", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "Teams Global Meeting Policy", + "Description": "Ensure only organizers and co-organizers can present in a Teams meeting. The recommended state is 'Only organizers and co-organizers'.", + "Risk": "Allowing everyone to present increases the risk that a malicious user can inadvertently show inappropriate content.", + "RelatedUrl": "https://learn.microsoft.com/en-us/microsoftteams/meeting-who-present-request-control", + "Remediation": { + "Code": { + "CLI": "Set-CsTeamsMeetingPolicy -Identity Global -DesignatedPresenterRoleMode \"OrganizerOnlyUserOverride\"", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft Teams admin center https://admin.teams.microsoft.com. 2. Click to expand Meetings select Meeting policies. 3. Click Global (Org-wide default). 4. Under content sharing set Who can present to Only organizers and co-organizers.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Restrict presentation capabilities to only organizers and co-organizers to reduce the risk of inappropriate content being shown.", + "Url": "https://learn.microsoft.com/en-us/microsoftteams/meeting-who-present-request-control" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.py b/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.py new file mode 100644 index 0000000000..234eb97adf --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted.py @@ -0,0 +1,45 @@ +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_meeting_presenters_restricted(Check): + """Check if only organizers and co-organizers can present in Teams meetings. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for meeting presenter settings. + + This method checks if only organizers and co-organizers can present in Teams meetings. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + global_meeting_policy = teams_client.global_meeting_policy + if global_meeting_policy: + report = CheckReportM365( + metadata=self.metadata(), + resource=global_meeting_policy if global_meeting_policy else {}, + resource_name="Teams Meetings Global (Org-wide default) Policy", + resource_id="teamsMeetingsGlobalPolicy", + ) + report.status = "FAIL" + report.status_extended = "Everyone can present in Teams meetings." + + if ( + global_meeting_policy.designated_presenter_role_mode + == "OrganizerOnlyUserOverride" + ): + report.status = "PASS" + report.status_extended = ( + "Only organizers and co-organizers can present in Teams meetings." + ) + + 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 bdd97d7390..0490c2fefd 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -67,6 +67,9 @@ def _get_global_meeting_policy(self): allow_cloud_recording=global_meeting_policy.get( "AllowCloudRecording", True ), + designated_presenter_role_mode=global_meeting_policy.get( + "DesignatedPresenterRoleMode", "EveryoneUserOverride" + ), ) except Exception as error: logger.error( @@ -113,6 +116,7 @@ class GlobalMeetingPolicy(BaseModel): allow_external_participant_give_request_control: bool = True allow_external_non_trusted_meeting_chat: bool = True allow_cloud_recording: bool = True + designated_presenter_role_mode: str = "EveryoneUserOverride" allow_external_users_to_bypass_lobby: str = "Everyone" allow_pstn_users_to_bypass_lobby: bool = True diff --git a/tests/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted_test.py b/tests/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted_test.py new file mode 100644 index 0000000000..7a11518da5 --- /dev/null +++ b/tests/providers/m365/services/teams/teams_meeting_presenters_restricted/teams_meeting_presenters_restricted_test.py @@ -0,0 +1,117 @@ +from unittest import mock + +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_teams_meeting_presenters_restricted: + def test_no_global_meeting_policy(self): + teams_client = mock.MagicMock() + teams_client.global_meeting_policy = 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_meeting_presenters_restricted.teams_meeting_presenters_restricted.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_presenters_restricted.teams_meeting_presenters_restricted import ( + teams_meeting_presenters_restricted, + ) + + check = teams_meeting_presenters_restricted() + result = check.execute() + assert len(result) == 0 + + def test_presenters_not_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_meeting_presenters_restricted.teams_meeting_presenters_restricted.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_presenters_restricted.teams_meeting_presenters_restricted import ( + teams_meeting_presenters_restricted, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + designated_presenter_role_mode="EveryoneUserOverride" + ) + + check = teams_meeting_presenters_restricted() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended == "Everyone can present in Teams meetings." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" + + def test_presenters_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_meeting_presenters_restricted.teams_meeting_presenters_restricted.teams_client", + new=teams_client, + ), + ): + from prowler.providers.m365.services.teams.teams_meeting_presenters_restricted.teams_meeting_presenters_restricted import ( + teams_meeting_presenters_restricted, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMeetingPolicy, + ) + + teams_client.global_meeting_policy = GlobalMeetingPolicy( + designated_presenter_role_mode="OrganizerOnlyUserOverride" + ) + + check = teams_meeting_presenters_restricted() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Only organizers and co-organizers can present in Teams meetings." + ) + assert result[0].resource == teams_client.global_meeting_policy.dict() + assert ( + result[0].resource_name + == "Teams Meetings Global (Org-wide default) Policy" + ) + assert result[0].resource_id == "teamsMeetingsGlobalPolicy" diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index f3ec8e2862..84f8e04b63 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -31,6 +31,7 @@ def mock_get_global_meeting_policy(_): allow_external_participant_give_request_control=False, allow_external_non_trusted_meeting_chat=False, allow_cloud_recording=False, + designated_presenter_role_mode="EveryoneUserOverride", allow_external_users_to_bypass_lobby="EveryoneInCompanyExcludingGuests", allow_pstn_users_to_bypass_lobby=False, ) @@ -130,6 +131,7 @@ def test_get_global_meeting_policy(self): allow_external_participant_give_request_control=False, allow_external_non_trusted_meeting_chat=False, allow_cloud_recording=False, + designated_presenter_role_mode="EveryoneUserOverride", allow_external_users_to_bypass_lobby="EveryoneInCompanyExcludingGuests", allow_pstn_users_to_bypass_lobby=False, ) From 636d50b6f90202ff0cdbf2477a86edacd1633326 Mon Sep 17 00:00:00 2001 From: Andoni A <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:27:37 +0200 Subject: [PATCH 09/19] chore: update changelog --- prowler/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index a9070f556c..f12c03ffce 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -33,6 +33,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check `teams_meeting_external_control_disabled` [(#7604)](https://github.com/prowler-cloud/prowler/pull/7604) - Add new check `teams_meeting_external_chat_disabled` [(#7605)](https://github.com/prowler-cloud/prowler/pull/7605) - Add new check `teams_meeting_recording_disabled` [(#7607)](https://github.com/prowler-cloud/prowler/pull/7607) +- Add new check `teams_meeting_presenters_restricted` [(#7613)](https://github.com/prowler-cloud/prowler/pull/7613) ### Fixed From 2ba2360557b2c7dc13b5e021b6d24ad7bbb4a29c Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:33:15 +0200 Subject: [PATCH 10/19] feat(defender): add report submission policy --- .../m365/lib/powershell/m365_powershell.py | 29 ++++++++++ .../services/defender/defender_service.py | 56 +++++++++++++++++++ .../defender/m365_defender_service_test.py | 36 ++++++++++++ 3 files changed, 121 insertions(+) diff --git a/prowler/providers/m365/lib/powershell/m365_powershell.py b/prowler/providers/m365/lib/powershell/m365_powershell.py index b3a7db9689..838ca43133 100644 --- a/prowler/providers/m365/lib/powershell/m365_powershell.py +++ b/prowler/providers/m365/lib/powershell/m365_powershell.py @@ -453,3 +453,32 @@ def get_inbound_spam_filter_policy(self) -> dict: } """ return self.execute("Get-HostedContentFilterPolicy | ConvertTo-Json") + + def get_report_submission_policy(self) -> dict: + """ + Get Exchange Online Report Submission Policy. + + Retrieves the current Exchange Online report submission policy settings. + + Returns: + dict: Report submission policy settings in JSON format. + + Example: + >>> get_report_submission_policy() + { + "Id": "DefaultReportSubmissionPolicy", + "Identity": "DefaultReportSubmissionPolicy", + "Name": "DefaultReportSubmissionPolicy", + "ReportChatMessageEnabled": true, + "ReportChatMessageToCustomizedAddressEnabled": true, + "ReportJunkAddresses": [], + "ReportJunkToCustomizedAddress": true, + "ReportNotJunkAddresses": [], + "ReportNotJunkToCustomizedAddress": true, + "ReportPhishAddresses": [], + "ReportPhishToCustomizedAddress": true, + "ThirdPartyReportAddresses": [], + ... + } + """ + return self.execute("Get-ReportSubmissionPolicy | ConvertTo-Json") diff --git a/prowler/providers/m365/services/defender/defender_service.py b/prowler/providers/m365/services/defender/defender_service.py index 4fc86aa51c..366fa852fe 100644 --- a/prowler/providers/m365/services/defender/defender_service.py +++ b/prowler/providers/m365/services/defender/defender_service.py @@ -17,6 +17,7 @@ def __init__(self, provider: M365Provider): self.antiphishing_policies = self._get_antiphising_policy() self.antiphising_rules = self._get_antiphising_rules() self.inbound_spam_policies = self._get_inbound_spam_filter_policy() + self.report_submission_policy = self._get_report_submission_policy() self.connection_filter_policy = self._get_connection_filter_policy() self.dkim_configurations = self._get_dkim_config() self.powershell.close() @@ -205,6 +206,47 @@ def _get_inbound_spam_filter_policy(self): ) return inbound_spam_policies + def _get_report_submission_policy(self): + logger.info("Microsoft365 - Getting Defender report submission policy...") + report_submission_policy = None + try: + report_submission_policy = self.powershell.get_report_submission_policy() + if report_submission_policy: + report_submission_policy = ReportSubmissionPolicy( + id=report_submission_policy.get("Id", ""), + identity=report_submission_policy.get("Identity", ""), + name=report_submission_policy.get("Name", ""), + report_junk_to_customized_address=report_submission_policy.get( + "ReportJunkToCustomizedAddress", True + ), + report_not_junk_to_customized_address=report_submission_policy.get( + "ReportNotJunkToCustomizedAddress", True + ), + report_phish_to_customized_address=report_submission_policy.get( + "ReportPhishToCustomizedAddress", True + ), + report_junk_addresses=report_submission_policy.get( + "ReportJunkAddresses", [] + ), + report_not_junk_addresses=report_submission_policy.get( + "ReportNotJunkAddresses", [] + ), + report_phish_addresses=report_submission_policy.get( + "ReportPhishAddresses", [] + ), + report_chat_message_enabled=report_submission_policy.get( + "ReportChatMessageEnabled", True + ), + report_chat_message_to_customized_address_enabled=report_submission_policy.get( + "ReportChatMessageToCustomizedAddressEnabled", True + ), + ) + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return report_submission_policy + class MalwarePolicy(BaseModel): enable_file_filter: bool @@ -255,3 +297,17 @@ class OutboundSpamRule(BaseModel): class DefenderInboundSpamPolicy(BaseModel): identity: str allowed_sender_domains: list[str] = [] + + +class ReportSubmissionPolicy(BaseModel): + id: str + identity: str + name: str + report_junk_to_customized_address: bool + report_not_junk_to_customized_address: bool + report_phish_to_customized_address: bool + report_junk_addresses: list[str] + report_not_junk_addresses: list[str] + report_phish_addresses: list[str] + report_chat_message_enabled: bool + report_chat_message_to_customized_address_enabled: bool diff --git a/tests/providers/m365/services/defender/m365_defender_service_test.py b/tests/providers/m365/services/defender/m365_defender_service_test.py index efce49f843..0fdb119d16 100644 --- a/tests/providers/m365/services/defender/m365_defender_service_test.py +++ b/tests/providers/m365/services/defender/m365_defender_service_test.py @@ -12,6 +12,7 @@ MalwarePolicy, OutboundSpamPolicy, OutboundSpamRule, + ReportSubmissionPolicy, ) from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider @@ -99,6 +100,22 @@ def mock_defender_get_dkim_config(_): ] +def mock_defender_get_report_submission_policy(_): + return ReportSubmissionPolicy( + id="DefaultReportSubmissionPolicy", + identity="DefaultReportSubmissionPolicy", + name="DefaultReportSubmissionPolicy", + report_junk_to_customized_address=True, + report_not_junk_to_customized_address=True, + report_phish_to_customized_address=True, + report_junk_addresses=[], + report_not_junk_addresses=[], + report_phish_addresses=[], + report_chat_message_enabled=True, + report_chat_message_to_customized_address_enabled=True, + ) + + def mock_defender_get_outbound_spam_filter_policy(_): return { "Policy1": OutboundSpamPolicy( @@ -357,3 +374,22 @@ def test__get_inbound_spam_filter_policy(self): assert inbound_spam_policies[0].allowed_sender_domains == [] assert inbound_spam_policies[1].allowed_sender_domains == ["example.com"] defender_client.powershell.close() + + @patch( + "prowler.providers.m365.services.defender.defender_service.Defender._get_report_submission_policy", + new=mock_defender_get_report_submission_policy, + ) + def test_get_report_submission_policy(self): + with ( + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + ): + defender_client = Defender( + set_mocked_m365_provider( + identity=M365IdentityInfo(tenant_domain=DOMAIN) + ) + ) + report_submission_policy = defender_client.report_submission_policy + assert report_submission_policy.id == "DefaultReportSubmissionPolicy" + assert report_submission_policy.identity == "DefaultReportSubmissionPolicy" From cdf5f8cdcd651a14449077693d039d9ef6d96658 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 13:10:44 +0200 Subject: [PATCH 11/19] feat(teams): add new check `teams_security_reporting_enabled` --- .../m365/lib/powershell/m365_powershell.py | 19 +++ .../services/defender/defender_service.py | 6 - .../__init__.py | 0 ...s_security_reporting_enabled.metadata.json | 30 ++++ .../teams_security_reporting_enabled.py | 63 +++++++ .../m365/services/teams/teams_service.py | 26 +++ .../teams_security_reporting_enabled_test.py | 159 ++++++++++++++++++ .../m365/services/teams/teams_service_test.py | 24 +++ 8 files changed, 321 insertions(+), 6 deletions(-) create mode 100644 prowler/providers/m365/services/teams/teams_security_reporting_enabled/__init__.py create mode 100644 prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json create mode 100644 prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py create mode 100644 tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py diff --git a/prowler/providers/m365/lib/powershell/m365_powershell.py b/prowler/providers/m365/lib/powershell/m365_powershell.py index 838ca43133..a40b677103 100644 --- a/prowler/providers/m365/lib/powershell/m365_powershell.py +++ b/prowler/providers/m365/lib/powershell/m365_powershell.py @@ -175,6 +175,25 @@ def get_global_meeting_policy(self) -> dict: "Get-CsTeamsMeetingPolicy -Identity Global | ConvertTo-Json" ) + def get_global_messaging_policy(self) -> dict: + """ + Get Teams Global Messaging Policy. + + Retrieves the current Microsoft Teams global messaging policy settings. + + Returns: + dict: Teams global messaging policy settings in JSON format. + + Example: + >>> get_global_meeting_policy() + { + "AllowAnonymousUsersToJoinMeeting": true + } + """ + return self.execute( + "Get-CsTeamsMessagingPolicy -Identity Global | ConvertTo-Json" + ) + def get_user_settings(self) -> dict: """ Get Teams User Settings. diff --git a/prowler/providers/m365/services/defender/defender_service.py b/prowler/providers/m365/services/defender/defender_service.py index 366fa852fe..75280fde89 100644 --- a/prowler/providers/m365/services/defender/defender_service.py +++ b/prowler/providers/m365/services/defender/defender_service.py @@ -213,9 +213,6 @@ def _get_report_submission_policy(self): report_submission_policy = self.powershell.get_report_submission_policy() if report_submission_policy: report_submission_policy = ReportSubmissionPolicy( - id=report_submission_policy.get("Id", ""), - identity=report_submission_policy.get("Identity", ""), - name=report_submission_policy.get("Name", ""), report_junk_to_customized_address=report_submission_policy.get( "ReportJunkToCustomizedAddress", True ), @@ -300,9 +297,6 @@ class DefenderInboundSpamPolicy(BaseModel): class ReportSubmissionPolicy(BaseModel): - id: str - identity: str - name: str report_junk_to_customized_address: bool report_not_junk_to_customized_address: bool report_phish_to_customized_address: bool diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/__init__.py b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json new file mode 100644 index 0000000000..25e27c8263 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "teams_security_reporting_enabled", + "CheckTitle": "Ensure users can report security concerns in Teams", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "Teams Global Messaging Policy and Defender Settings", + "Description": "Ensure users can report security concerns in Teams through proper configuration of Teams messaging policy and Defender settings.", + "Risk": "Without proper security reporting enabled, users cannot effectively report suspicious or malicious messages, potentially allowing security threats to go unnoticed.", + "RelatedUrl": "https://learn.microsoft.com/en-us/defender-office-365/submissions-teams?view=o365-worldwide", + "Remediation": { + "Code": { + "CLI": "Set-CsTeamsMessagingPolicy -Identity Global -AllowSecurityEndUserReporting $true\nSet-ReportSubmissionPolicy -Identity DefaultReportSubmissionPolicy -EnableReportToMicrosoft $false -ReportChatMessageEnabled $false -ReportChatMessageToCustomizedAddressEnabled $true -ReportJunkToCustomizedAddress $true -ReportNotJunkToCustomizedAddress $true -ReportPhishToCustomizedAddress $true -ReportJunkAddresses $usersub -ReportNotJunkAddresses $usersub -ReportPhishAddresses $usersub", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft Teams admin center (https://admin.teams.microsoft.com). 2. Click to expand Messaging and select Messaging policies. 3. Click Global (Org-wide default). 4. Ensure Report a security concern is On. 5. Navigate to Microsoft 365 Defender (https://security.microsoft.com/). 6. Click on Settings > Email & collaboration > User reported settings. 7. Scroll to Microsoft Teams section. 8. Ensure Monitor reported messages in Microsoft Teams is checked. 9. Ensure Send reported messages to: is set to My reporting mailbox only with report email addresses defined for authorized staff.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable security reporting in Teams and configure Defender to use organization-specific reporting addresses to ensure proper handling of security concerns.", + "Url": "https://learn.microsoft.com/en-us/defender-office-365/submissions-teams?view=o365-worldwide" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py new file mode 100644 index 0000000000..cea77f6c26 --- /dev/null +++ b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py @@ -0,0 +1,63 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportM365 +from prowler.providers.m365.services.defender.defender_client import defender_client +from prowler.providers.m365.services.teams.teams_client import teams_client + + +class teams_security_reporting_enabled(Check): + """Check if users can report security concerns in Teams. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for Teams security reporting settings. + + This method checks if security reporting is properly configured in Teams + and Defender settings. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + global_messaging_policy = teams_client.global_messaging_policy + report_submission_policy = defender_client.report_submission_policy + + if global_messaging_policy and report_submission_policy: + report = CheckReportM365( + metadata=self.metadata(), + resource={ + "teams_messaging_policy": global_messaging_policy.dict(), + "defender_policy": report_submission_policy.dict(), + }, + resource_name="Teams Security Reporting Settings", + resource_id="teamsSecurityReporting", + ) + + teams_reporting_enabled = ( + global_messaging_policy.allow_security_end_user_reporting + ) + + defender_settings_valid = ( + report_submission_policy.report_junk_to_customized_address + and report_submission_policy.report_not_junk_to_customized_address + and report_submission_policy.report_phish_to_customized_address + and report_submission_policy.report_junk_addresses + and report_submission_policy.report_not_junk_addresses + and report_submission_policy.report_phish_addresses + and not report_submission_policy.report_chat_message_enabled + and report_submission_policy.report_chat_message_to_customized_address_enabled + ) + + if teams_reporting_enabled and defender_settings_valid: + report.status = "PASS" + report.status_extended = "Security reporting is properly configured in Teams and Defender settings." + else: + report.status = "FAIL" + report.status_extended = "Security reporting is not properly configured in Teams and Defender settings." + + 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 0490c2fefd..7e7644dc90 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -11,6 +11,7 @@ def __init__(self, provider: M365Provider): self.powershell.connect_microsoft_teams() self.teams_settings = self._get_teams_client_configuration() self.global_meeting_policy = self._get_global_meeting_policy() + self.global_messaging_policy = self._get_global_messaging_policy() self.user_settings = self._get_user_settings() self.powershell.close() @@ -70,6 +71,9 @@ def _get_global_meeting_policy(self): designated_presenter_role_mode=global_meeting_policy.get( "DesignatedPresenterRoleMode", "EveryoneUserOverride" ), + allow_security_end_user_reporting=global_meeting_policy.get( + "AllowSecurityEndUserReporting", True + ), ) except Exception as error: logger.error( @@ -77,6 +81,23 @@ def _get_global_meeting_policy(self): ) return global_meeting_policy + def _get_global_messaging_policy(self): + logger.info("M365 - Getting Teams global messaging policy...") + global_messaging_policy = None + try: + global_messaging_policy = self.powershell.get_global_messaging_policy() + if global_messaging_policy: + global_messaging_policy = GlobalMessagingPolicy( + allow_security_end_user_reporting=global_messaging_policy.get( + "AllowSecurityEndUserReporting", True + ), + ) + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return global_messaging_policy + def _get_user_settings(self): logger.info("M365 - Getting Teams user settings...") user_settings = None @@ -119,6 +140,11 @@ class GlobalMeetingPolicy(BaseModel): designated_presenter_role_mode: str = "EveryoneUserOverride" allow_external_users_to_bypass_lobby: str = "Everyone" allow_pstn_users_to_bypass_lobby: bool = True + allow_security_end_user_reporting: bool = True + + +class GlobalMessagingPolicy(BaseModel): + allow_security_end_user_reporting: bool = True class UserSettings(BaseModel): diff --git a/tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py b/tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py new file mode 100644 index 0000000000..488c85f360 --- /dev/null +++ b/tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py @@ -0,0 +1,159 @@ +from unittest import mock + +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_teams_security_reporting_enabled: + def test_no_policies(self): + teams_client = mock.MagicMock() + teams_client.global_messaging_policy = None + defender_client = mock.MagicMock() + defender_client.report_submission_policy = 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.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.teams_client", + new=teams_client, + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled import ( + teams_security_reporting_enabled, + ) + + check = teams_security_reporting_enabled() + result = check.execute() + assert len(result) == 0 + + def test_security_reporting_properly_configured(self): + teams_client = mock.MagicMock() + teams_client.audited_tenant = "audited_tenant" + teams_client.audited_domain = DOMAIN + defender_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.lib.powershell.m365_powershell.M365PowerShell.connect_microsoft_teams" + ), + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.teams_client", + new=teams_client, + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_service import ( + ReportSubmissionPolicy, + ) + from prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled import ( + teams_security_reporting_enabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMessagingPolicy, + ) + + teams_client.global_messaging_policy = GlobalMessagingPolicy( + allow_security_end_user_reporting=True + ) + defender_client.report_submission_policy = ReportSubmissionPolicy( + report_junk_to_customized_address=True, + report_not_junk_to_customized_address=True, + report_phish_to_customized_address=True, + report_junk_addresses=["security@example.com"], + report_not_junk_addresses=["security@example.com"], + report_phish_addresses=["security@example.com"], + report_chat_message_enabled=False, + report_chat_message_to_customized_address_enabled=True, + ) + + check = teams_security_reporting_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Security reporting is properly configured in Teams and Defender settings." + ) + assert result[0].resource_name == "Teams Security Reporting Settings" + assert result[0].resource_id == "teamsSecurityReporting" + + def test_security_reporting_not_properly_configured(self): + teams_client = mock.MagicMock() + teams_client.audited_tenant = "audited_tenant" + teams_client.audited_domain = DOMAIN + defender_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.lib.powershell.m365_powershell.M365PowerShell.connect_microsoft_teams" + ), + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.teams_client", + new=teams_client, + ), + mock.patch( + "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_service import ( + ReportSubmissionPolicy, + ) + from prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled import ( + teams_security_reporting_enabled, + ) + from prowler.providers.m365.services.teams.teams_service import ( + GlobalMessagingPolicy, + ) + + teams_client.global_messaging_policy = GlobalMessagingPolicy( + allow_security_end_user_reporting=True + ) + defender_client.report_submission_policy = ReportSubmissionPolicy( + report_junk_to_customized_address=False, + report_not_junk_to_customized_address=False, + report_phish_to_customized_address=False, + report_junk_addresses=["security@example.com"], + report_not_junk_addresses=[], + report_phish_addresses=[], + report_chat_message_enabled=False, + report_chat_message_to_customized_address_enabled=True, + ) + + check = teams_security_reporting_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Security reporting is not properly configured in Teams and Defender settings." + ) diff --git a/tests/providers/m365/services/teams/teams_service_test.py b/tests/providers/m365/services/teams/teams_service_test.py index 84f8e04b63..1daba64382 100644 --- a/tests/providers/m365/services/teams/teams_service_test.py +++ b/tests/providers/m365/services/teams/teams_service_test.py @@ -5,6 +5,7 @@ from prowler.providers.m365.services.teams.teams_service import ( CloudStorageSettings, GlobalMeetingPolicy, + GlobalMessagingPolicy, Teams, TeamsSettings, UserSettings, @@ -37,6 +38,10 @@ def mock_get_global_meeting_policy(_): ) +def mock_get_global_messaging_policy(_): + return GlobalMessagingPolicy(allow_security_end_user_reporting=True) + + def mock_get_user_settings(_): return UserSettings( allow_external_access=False, @@ -136,3 +141,22 @@ def test_get_global_meeting_policy(self): allow_pstn_users_to_bypass_lobby=False, ) teams_client.powershell.close() + + @patch( + "prowler.providers.m365.services.teams.teams_service.Teams._get_global_messaging_policy", + new=mock_get_global_messaging_policy, + ) + def test_get_global_messaging_policy(self): + with ( + mock.patch( + "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_microsoft_teams" + ), + ): + teams_client = Teams( + set_mocked_m365_provider( + identity=M365IdentityInfo(tenant_domain=DOMAIN) + ) + ) + assert teams_client.global_messaging_policy == GlobalMessagingPolicy( + allow_security_end_user_reporting=True + ) From 5593a57dfcefa073e47672e7daef17aa72d2c6c8 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Fri, 25 Apr 2025 13:30:58 +0200 Subject: [PATCH 12/19] chore: update changelog --- prowler/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index f12c03ffce..c2bc62c1e3 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -34,6 +34,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check `teams_meeting_external_chat_disabled` [(#7605)](https://github.com/prowler-cloud/prowler/pull/7605) - Add new check `teams_meeting_recording_disabled` [(#7607)](https://github.com/prowler-cloud/prowler/pull/7607) - Add new check `teams_meeting_presenters_restricted` [(#7613)](https://github.com/prowler-cloud/prowler/pull/7613) +- Add new check `teams_security_reporting_enabled` [(#7614)](https://github.com/prowler-cloud/prowler/pull/7614) ### Fixed From c6b2f9cd27e9c92d3302f6bde3cb62c572325827 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:00:35 +0200 Subject: [PATCH 13/19] fix(defender): update report submission test case --- .../services/defender/m365_defender_service_test.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/providers/m365/services/defender/m365_defender_service_test.py b/tests/providers/m365/services/defender/m365_defender_service_test.py index 0fdb119d16..e32abdabc8 100644 --- a/tests/providers/m365/services/defender/m365_defender_service_test.py +++ b/tests/providers/m365/services/defender/m365_defender_service_test.py @@ -391,5 +391,12 @@ def test_get_report_submission_policy(self): ) ) report_submission_policy = defender_client.report_submission_policy - assert report_submission_policy.id == "DefaultReportSubmissionPolicy" - assert report_submission_policy.identity == "DefaultReportSubmissionPolicy" + assert report_submission_policy.report_junk_to_customized_address is True + assert ( + report_submission_policy.report_not_junk_to_customized_address is True + ) + assert report_submission_policy.report_phish_to_customized_address is True + assert report_submission_policy.report_junk_addresses == [] + assert report_submission_policy.report_not_junk_addresses == [] + assert report_submission_policy.report_phish_addresses == [] + assert report_submission_policy.report_chat_message_enabled is True From 051bb4f5aaaf25d7d16bd384e636912823eb2aea Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:20:25 +0200 Subject: [PATCH 14/19] fix: add missing json_parse --- prowler/providers/m365/lib/powershell/m365_powershell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prowler/providers/m365/lib/powershell/m365_powershell.py b/prowler/providers/m365/lib/powershell/m365_powershell.py index bb78958076..a4d1e33d46 100644 --- a/prowler/providers/m365/lib/powershell/m365_powershell.py +++ b/prowler/providers/m365/lib/powershell/m365_powershell.py @@ -192,7 +192,8 @@ def get_global_messaging_policy(self) -> dict: } """ return self.execute( - "Get-CsTeamsMessagingPolicy -Identity Global | ConvertTo-Json" + "Get-CsTeamsMessagingPolicy -Identity Global | ConvertTo-Json", + json_parse=True, ) def get_user_settings(self) -> dict: From df5c02c3d2c34c0ed63d31967eb316977ea9d390 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Mon, 5 May 2025 18:04:33 +0200 Subject: [PATCH 15/19] feat(m365): separate Defender settings checks in a new check --- .../__init__.py | 0 ...der_report_policy_configured.metadata.json | 30 ++++ .../defender_report_policy_configured.py | 55 ++++++++ ...s_security_reporting_enabled.metadata.json | 6 +- .../teams_security_reporting_enabled.py | 29 ++-- .../defender_report_policy_configured_test.py | 133 ++++++++++++++++++ .../teams_security_reporting_enabled_test.py | 85 +---------- 7 files changed, 231 insertions(+), 107 deletions(-) create mode 100644 prowler/providers/m365/services/defender/defender_report_policy_configured/__init__.py create mode 100644 prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.metadata.json create mode 100644 prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py create mode 100644 tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py diff --git a/prowler/providers/m365/services/defender/defender_report_policy_configured/__init__.py b/prowler/providers/m365/services/defender/defender_report_policy_configured/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.metadata.json b/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.metadata.json new file mode 100644 index 0000000000..61f37e02fd --- /dev/null +++ b/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "m365", + "CheckID": "defender_report_policy_configured", + "CheckTitle": "Ensure Defender report submission policy is properly configured", + "CheckType": [], + "ServiceName": "teams", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "Defender Report Submission Policy", + "Description": "Ensure Defender report submission policy is properly configured to use customized addresses and enable chat message reporting to customized addresses, while disabling report chat message to Microsoft.", + "Risk": "If Defender report submission policy is not properly configured, reported messages from Teams may not be handled or routed correctly, reducing the organization's ability to respond to threats.", + "RelatedUrl": "https://learn.microsoft.com/en-us/defender-office-365/submissions-teams?view=o365-worldwide", + "Remediation": { + "Code": { + "CLI": "Set-ReportSubmissionPolicy -Identity DefaultReportSubmissionPolicy -EnableReportToMicrosoft $false -ReportChatMessageEnabled $false -ReportChatMessageToCustomizedAddressEnabled $true -ReportJunkToCustomizedAddress $true -ReportNotJunkToCustomizedAddress $true -ReportPhishToCustomizedAddress $true -ReportJunkAddresses $usersub -ReportNotJunkAddresses $usersub -ReportPhishAddresses $usersub", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft 365 Defender (https://security.microsoft.com/). 2. Click on Settings > Email & collaboration > User reported settings. 3. Scroll to Microsoft Teams section. 4. Ensure Monitor reported messages in Microsoft Teams is checked. 5. Ensure Send reported messages to: is set to My reporting mailbox only with report email addresses defined for authorized staff.", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure Defender report submission policy to use customized addresses and enable chat message reporting to customized addresses, while disabling report chat message to Microsoft.", + "Url": "https://learn.microsoft.com/en-us/defender-office-365/submissions-teams?view=o365-worldwide" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py b/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py new file mode 100644 index 0000000000..086bb4c904 --- /dev/null +++ b/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py @@ -0,0 +1,55 @@ +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_report_policy_configured(Check): + """Check if Defender report submission policy is properly configured for Teams security reporting. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportM365]: + """Execute the check for Defender report submission policy settings. + + This method checks if Defender report submission policy is properly configured for Teams security reporting. + + Returns: + List[CheckReportM365]: A list of reports containing the result of the check. + """ + findings = [] + report_submission_policy = defender_client.report_submission_policy + + if report_submission_policy: + report = CheckReportM365( + metadata=self.metadata(), + resource={ + "defender_policy": report_submission_policy.dict(), + }, + resource_name="Defender Security Reporting Policy", + resource_id="defenderSecurityReportingPolicy", + ) + + defender_settings_valid = ( + report_submission_policy.report_junk_to_customized_address + and report_submission_policy.report_not_junk_to_customized_address + and report_submission_policy.report_phish_to_customized_address + and report_submission_policy.report_junk_addresses + and report_submission_policy.report_not_junk_addresses + and report_submission_policy.report_phish_addresses + and not report_submission_policy.report_chat_message_enabled + and report_submission_policy.report_chat_message_to_customized_address_enabled + ) + + if defender_settings_valid: + report.status = "PASS" + report.status_extended = "Defender report submission policy is properly configured for Teams security reporting." + else: + report.status = "FAIL" + report.status_extended = "Defender report submission policy is not properly configured for Teams security reporting." + + findings.append(report) + + return findings diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json index 25e27c8263..f2b830b3d6 100644 --- a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json +++ b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json @@ -7,13 +7,13 @@ "SubServiceName": "", "ResourceIdTemplate": "", "Severity": "medium", - "ResourceType": "Teams Global Messaging Policy and Defender Settings", - "Description": "Ensure users can report security concerns in Teams through proper configuration of Teams messaging policy and Defender settings.", + "ResourceType": "Teams Global Messaging Policy", + "Description": "Ensure Teams user reporting settings allow a user to report a message as malicious for further analysis", "Risk": "Without proper security reporting enabled, users cannot effectively report suspicious or malicious messages, potentially allowing security threats to go unnoticed.", "RelatedUrl": "https://learn.microsoft.com/en-us/defender-office-365/submissions-teams?view=o365-worldwide", "Remediation": { "Code": { - "CLI": "Set-CsTeamsMessagingPolicy -Identity Global -AllowSecurityEndUserReporting $true\nSet-ReportSubmissionPolicy -Identity DefaultReportSubmissionPolicy -EnableReportToMicrosoft $false -ReportChatMessageEnabled $false -ReportChatMessageToCustomizedAddressEnabled $true -ReportJunkToCustomizedAddress $true -ReportNotJunkToCustomizedAddress $true -ReportPhishToCustomizedAddress $true -ReportJunkAddresses $usersub -ReportNotJunkAddresses $usersub -ReportPhishAddresses $usersub", + "CLI": "Set-CsTeamsMessagingPolicy -Identity Global -AllowSecurityEndUserReporting $true", "NativeIaC": "", "Other": "1. Navigate to Microsoft Teams admin center (https://admin.teams.microsoft.com). 2. Click to expand Messaging and select Messaging policies. 3. Click Global (Org-wide default). 4. Ensure Report a security concern is On. 5. Navigate to Microsoft 365 Defender (https://security.microsoft.com/). 6. Click on Settings > Email & collaboration > User reported settings. 7. Scroll to Microsoft Teams section. 8. Ensure Monitor reported messages in Microsoft Teams is checked. 9. Ensure Send reported messages to: is set to My reporting mailbox only with report email addresses defined for authorized staff.", "Terraform": "" diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py index cea77f6c26..83b1893675 100644 --- a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py +++ b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py @@ -1,7 +1,6 @@ from typing import List from prowler.lib.check.models import Check, CheckReportM365 -from prowler.providers.m365.services.defender.defender_client import defender_client from prowler.providers.m365.services.teams.teams_client import teams_client @@ -15,22 +14,19 @@ class teams_security_reporting_enabled(Check): def execute(self) -> List[CheckReportM365]: """Execute the check for Teams security reporting settings. - This method checks if security reporting is properly configured in Teams - and Defender settings. + This method checks if security reporting is properly configured in Teams settings. Returns: List[CheckReportM365]: A list of reports containing the result of the check. """ findings = [] global_messaging_policy = teams_client.global_messaging_policy - report_submission_policy = defender_client.report_submission_policy - if global_messaging_policy and report_submission_policy: + if global_messaging_policy: report = CheckReportM365( metadata=self.metadata(), resource={ "teams_messaging_policy": global_messaging_policy.dict(), - "defender_policy": report_submission_policy.dict(), }, resource_name="Teams Security Reporting Settings", resource_id="teamsSecurityReporting", @@ -40,23 +36,16 @@ def execute(self) -> List[CheckReportM365]: global_messaging_policy.allow_security_end_user_reporting ) - defender_settings_valid = ( - report_submission_policy.report_junk_to_customized_address - and report_submission_policy.report_not_junk_to_customized_address - and report_submission_policy.report_phish_to_customized_address - and report_submission_policy.report_junk_addresses - and report_submission_policy.report_not_junk_addresses - and report_submission_policy.report_phish_addresses - and not report_submission_policy.report_chat_message_enabled - and report_submission_policy.report_chat_message_to_customized_address_enabled - ) - - if teams_reporting_enabled and defender_settings_valid: + if teams_reporting_enabled: report.status = "PASS" - report.status_extended = "Security reporting is properly configured in Teams and Defender settings." + report.status_extended = ( + "Security reporting is enabled in Teams messaging policy." + ) else: report.status = "FAIL" - report.status_extended = "Security reporting is not properly configured in Teams and Defender settings." + report.status_extended = ( + "Security reporting is not enabled in Teams messaging policy." + ) findings.append(report) diff --git a/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py b/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py new file mode 100644 index 0000000000..5c304e1eda --- /dev/null +++ b/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py @@ -0,0 +1,133 @@ +from unittest import mock + +from prowler.providers.m365.services.defender.defender_service import ( + ReportSubmissionPolicy, +) +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_defender_report_policy_configured: + def test_report_policy_configured_pass(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + defender_client.report_submission_policy = ReportSubmissionPolicy( + report_junk_to_customized_address=True, + report_not_junk_to_customized_address=True, + report_phish_to_customized_address=True, + report_junk_addresses=["address1"], + report_not_junk_addresses=["address2"], + report_phish_addresses=["address3"], + report_chat_message_enabled=False, + report_chat_message_to_customized_address_enabled=True, + ) + + 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_report_policy_configured.defender_report_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured import ( + defender_report_policy_configured, + ) + + check = defender_report_policy_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Defender report submission policy is properly configured for Teams security reporting." + ) + assert ( + result[0].resource["defender_policy"] + == defender_client.report_submission_policy.dict() + ) + assert result[0].resource_name == "Defender Security Reporting Policy" + assert result[0].resource_id == "defenderSecurityReportingPolicy" + assert result[0].location == "global" + + def test_report_policy_configured_fail(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + defender_client.report_submission_policy = ReportSubmissionPolicy( + report_junk_to_customized_address=False, + report_not_junk_to_customized_address=True, + report_phish_to_customized_address=True, + report_junk_addresses=[], + report_not_junk_addresses=[], + report_phish_addresses=[], + report_chat_message_enabled=True, + report_chat_message_to_customized_address_enabled=False, + ) + + 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_report_policy_configured.defender_report_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured import ( + defender_report_policy_configured, + ) + + check = defender_report_policy_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Defender report submission policy is not properly configured for Teams security reporting." + ) + assert ( + result[0].resource["defender_policy"] + == defender_client.report_submission_policy.dict() + ) + assert result[0].resource_name == "Defender Security Reporting Policy" + assert result[0].resource_id == "defenderSecurityReportingPolicy" + assert result[0].location == "global" + + def test_report_policy_configured_none(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + defender_client.report_submission_policy = 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_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured import ( + defender_report_policy_configured, + ) + + check = defender_report_policy_configured() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py b/tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py index 488c85f360..04bcaa0da7 100644 --- a/tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py +++ b/tests/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled_test.py @@ -7,8 +7,6 @@ class Test_teams_security_reporting_enabled: def test_no_policies(self): teams_client = mock.MagicMock() teams_client.global_messaging_policy = None - defender_client = mock.MagicMock() - defender_client.report_submission_policy = None with ( mock.patch( @@ -25,10 +23,6 @@ def test_no_policies(self): "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.teams_client", new=teams_client, ), - mock.patch( - "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.defender_client", - new=defender_client, - ), ): from prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled import ( teams_security_reporting_enabled, @@ -42,7 +36,6 @@ def test_security_reporting_properly_configured(self): teams_client = mock.MagicMock() teams_client.audited_tenant = "audited_tenant" teams_client.audited_domain = DOMAIN - defender_client = mock.MagicMock() with ( mock.patch( @@ -59,14 +52,7 @@ def test_security_reporting_properly_configured(self): "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.teams_client", new=teams_client, ), - mock.patch( - "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.defender_client", - new=defender_client, - ), ): - from prowler.providers.m365.services.defender.defender_service import ( - ReportSubmissionPolicy, - ) from prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled import ( teams_security_reporting_enabled, ) @@ -77,16 +63,6 @@ def test_security_reporting_properly_configured(self): teams_client.global_messaging_policy = GlobalMessagingPolicy( allow_security_end_user_reporting=True ) - defender_client.report_submission_policy = ReportSubmissionPolicy( - report_junk_to_customized_address=True, - report_not_junk_to_customized_address=True, - report_phish_to_customized_address=True, - report_junk_addresses=["security@example.com"], - report_not_junk_addresses=["security@example.com"], - report_phish_addresses=["security@example.com"], - report_chat_message_enabled=False, - report_chat_message_to_customized_address_enabled=True, - ) check = teams_security_reporting_enabled() result = check.execute() @@ -94,66 +70,7 @@ def test_security_reporting_properly_configured(self): assert result[0].status == "PASS" assert ( result[0].status_extended - == "Security reporting is properly configured in Teams and Defender settings." + == "Security reporting is enabled in Teams messaging policy." ) assert result[0].resource_name == "Teams Security Reporting Settings" assert result[0].resource_id == "teamsSecurityReporting" - - def test_security_reporting_not_properly_configured(self): - teams_client = mock.MagicMock() - teams_client.audited_tenant = "audited_tenant" - teams_client.audited_domain = DOMAIN - defender_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.lib.powershell.m365_powershell.M365PowerShell.connect_microsoft_teams" - ), - mock.patch( - "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" - ), - mock.patch( - "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.teams_client", - new=teams_client, - ), - mock.patch( - "prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled.defender_client", - new=defender_client, - ), - ): - from prowler.providers.m365.services.defender.defender_service import ( - ReportSubmissionPolicy, - ) - from prowler.providers.m365.services.teams.teams_security_reporting_enabled.teams_security_reporting_enabled import ( - teams_security_reporting_enabled, - ) - from prowler.providers.m365.services.teams.teams_service import ( - GlobalMessagingPolicy, - ) - - teams_client.global_messaging_policy = GlobalMessagingPolicy( - allow_security_end_user_reporting=True - ) - defender_client.report_submission_policy = ReportSubmissionPolicy( - report_junk_to_customized_address=False, - report_not_junk_to_customized_address=False, - report_phish_to_customized_address=False, - report_junk_addresses=["security@example.com"], - report_not_junk_addresses=[], - report_phish_addresses=[], - report_chat_message_enabled=False, - report_chat_message_to_customized_address_enabled=True, - ) - - check = teams_security_reporting_enabled() - result = check.execute() - assert len(result) == 1 - assert result[0].status == "FAIL" - assert ( - result[0].status_extended - == "Security reporting is not properly configured in Teams and Defender settings." - ) From ee7834f1aa4eb656cd786e4c1400662751e9f20a Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Mon, 5 May 2025 18:09:53 +0200 Subject: [PATCH 16/19] chore: update changelog --- prowler/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index fc39850fee..511e085691 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -35,6 +35,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check `teams_meeting_recording_disabled` [(#7607)](https://github.com/prowler-cloud/prowler/pull/7607) - Add new check `teams_meeting_presenters_restricted` [(#7613)](https://github.com/prowler-cloud/prowler/pull/7613) - Add new check `teams_security_reporting_enabled` [(#7614)](https://github.com/prowler-cloud/prowler/pull/7614) +- Add new check `defender_report_policy_configured` [(#7614)](https://github.com/prowler-cloud/prowler/pull/7614) - 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 documentation for M365 provider [(#7622)](https://github.com/prowler-cloud/prowler/pull/7622) From 18bc49fc22accf3616ab8b6f812140ea4c9fcf0e Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Mon, 5 May 2025 18:11:19 +0200 Subject: [PATCH 17/19] fix: update check metadata --- .../defender_report_policy_configured.py | 4 +--- .../teams_security_reporting_enabled.metadata.json | 4 ++-- .../teams_security_reporting_enabled.py | 4 +--- .../defender_report_policy_configured_test.py | 10 ++-------- 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py b/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py index 086bb4c904..3d155d67e9 100644 --- a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py +++ b/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py @@ -25,9 +25,7 @@ def execute(self) -> List[CheckReportM365]: if report_submission_policy: report = CheckReportM365( metadata=self.metadata(), - resource={ - "defender_policy": report_submission_policy.dict(), - }, + resource=report_submission_policy if report_submission_policy else {}, resource_name="Defender Security Reporting Policy", resource_id="defenderSecurityReportingPolicy", ) diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json index f2b830b3d6..01479ee608 100644 --- a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json +++ b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.metadata.json @@ -15,11 +15,11 @@ "Code": { "CLI": "Set-CsTeamsMessagingPolicy -Identity Global -AllowSecurityEndUserReporting $true", "NativeIaC": "", - "Other": "1. Navigate to Microsoft Teams admin center (https://admin.teams.microsoft.com). 2. Click to expand Messaging and select Messaging policies. 3. Click Global (Org-wide default). 4. Ensure Report a security concern is On. 5. Navigate to Microsoft 365 Defender (https://security.microsoft.com/). 6. Click on Settings > Email & collaboration > User reported settings. 7. Scroll to Microsoft Teams section. 8. Ensure Monitor reported messages in Microsoft Teams is checked. 9. Ensure Send reported messages to: is set to My reporting mailbox only with report email addresses defined for authorized staff.", + "Other": "1. Navigate to Microsoft Teams admin center (https://admin.teams.microsoft.com). 2. Click to expand Messaging and select Messaging policies. 3. Click Global (Org-wide default). 4. Ensure Report a security concern is On.", "Terraform": "" }, "Recommendation": { - "Text": "Enable security reporting in Teams and configure Defender to use organization-specific reporting addresses to ensure proper handling of security concerns.", + "Text": "Enable security reporting in Teams messaging policy.", "Url": "https://learn.microsoft.com/en-us/defender-office-365/submissions-teams?view=o365-worldwide" } }, diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py index 83b1893675..d5254352a5 100644 --- a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py +++ b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py @@ -25,9 +25,7 @@ def execute(self) -> List[CheckReportM365]: if global_messaging_policy: report = CheckReportM365( metadata=self.metadata(), - resource={ - "teams_messaging_policy": global_messaging_policy.dict(), - }, + resource=global_messaging_policy if global_messaging_policy else {}, resource_name="Teams Security Reporting Settings", resource_id="teamsSecurityReporting", ) diff --git a/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py b/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py index 5c304e1eda..7523e45bac 100644 --- a/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py +++ b/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py @@ -48,10 +48,7 @@ def test_report_policy_configured_pass(self): result[0].status_extended == "Defender report submission policy is properly configured for Teams security reporting." ) - assert ( - result[0].resource["defender_policy"] - == defender_client.report_submission_policy.dict() - ) + assert result[0].resource == defender_client.report_submission_policy.dict() assert result[0].resource_name == "Defender Security Reporting Policy" assert result[0].resource_id == "defenderSecurityReportingPolicy" assert result[0].location == "global" @@ -97,10 +94,7 @@ def test_report_policy_configured_fail(self): result[0].status_extended == "Defender report submission policy is not properly configured for Teams security reporting." ) - assert ( - result[0].resource["defender_policy"] - == defender_client.report_submission_policy.dict() - ) + assert result[0].resource == defender_client.report_submission_policy.dict() assert result[0].resource_name == "Defender Security Reporting Policy" assert result[0].resource_id == "defenderSecurityReportingPolicy" assert result[0].location == "global" From ae68e56edf54ad7bcea9a47139b5c0e60333039a Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Tue, 6 May 2025 09:14:22 +0200 Subject: [PATCH 18/19] fix: clarify checks objective --- prowler/CHANGELOG.md | 2 +- .../__init__.py | 0 ...at_report_policy_configured.metadata.json} | 4 +- ...defender_chat_report_policy_configured.py} | 2 +- ...nder_chat_report_policy_configured_test.py | 127 ++++++++++++++++++ .../defender_report_policy_configured_test.py | 26 ++-- 6 files changed, 144 insertions(+), 17 deletions(-) rename prowler/providers/m365/services/defender/{defender_report_policy_configured => defender_chat_report_policy_configured}/__init__.py (100%) rename prowler/providers/m365/services/defender/{defender_report_policy_configured/defender_report_policy_configured.metadata.json => defender_chat_report_policy_configured/defender_chat_report_policy_configured.metadata.json} (93%) rename prowler/providers/m365/services/defender/{defender_report_policy_configured/defender_report_policy_configured.py => defender_chat_report_policy_configured/defender_chat_report_policy_configured.py} (97%) create mode 100644 tests/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured_test.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 511e085691..da4a37dd1d 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -35,7 +35,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add new check `teams_meeting_recording_disabled` [(#7607)](https://github.com/prowler-cloud/prowler/pull/7607) - Add new check `teams_meeting_presenters_restricted` [(#7613)](https://github.com/prowler-cloud/prowler/pull/7613) - Add new check `teams_security_reporting_enabled` [(#7614)](https://github.com/prowler-cloud/prowler/pull/7614) -- Add new check `defender_report_policy_configured` [(#7614)](https://github.com/prowler-cloud/prowler/pull/7614) +- Add new check `defender_chat_report_policy_configured` [(#7614)](https://github.com/prowler-cloud/prowler/pull/7614) - 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 documentation for M365 provider [(#7622)](https://github.com/prowler-cloud/prowler/pull/7622) diff --git a/prowler/providers/m365/services/defender/defender_report_policy_configured/__init__.py b/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/__init__.py similarity index 100% rename from prowler/providers/m365/services/defender/defender_report_policy_configured/__init__.py rename to prowler/providers/m365/services/defender/defender_chat_report_policy_configured/__init__.py diff --git a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.metadata.json b/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.metadata.json similarity index 93% rename from prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.metadata.json rename to prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.metadata.json index 61f37e02fd..4e485694dd 100644 --- a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.metadata.json +++ b/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.metadata.json @@ -1,7 +1,7 @@ { "Provider": "m365", - "CheckID": "defender_report_policy_configured", - "CheckTitle": "Ensure Defender report submission policy is properly configured", + "CheckID": "defender_chat_report_policy_configured", + "CheckTitle": "Ensure chat report submission policy is properly configured in Defender", "CheckType": [], "ServiceName": "teams", "SubServiceName": "", diff --git a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py b/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.py similarity index 97% rename from prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py rename to prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.py index 3d155d67e9..d71cbe12a7 100644 --- a/prowler/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured.py +++ b/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.py @@ -4,7 +4,7 @@ from prowler.providers.m365.services.defender.defender_client import defender_client -class defender_report_policy_configured(Check): +class defender_chat_report_policy_configured(Check): """Check if Defender report submission policy is properly configured for Teams security reporting. Attributes: diff --git a/tests/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured_test.py b/tests/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured_test.py new file mode 100644 index 0000000000..54fabb5762 --- /dev/null +++ b/tests/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured_test.py @@ -0,0 +1,127 @@ +from unittest import mock + +from prowler.providers.m365.services.defender.defender_service import ( + ReportSubmissionPolicy, +) +from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider + + +class Test_defender_chat_report_policy_configured: + def test_report_policy_configured_pass(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + defender_client.report_submission_policy = ReportSubmissionPolicy( + report_junk_to_customized_address=True, + report_not_junk_to_customized_address=True, + report_phish_to_customized_address=True, + report_junk_addresses=["address1"], + report_not_junk_addresses=["address2"], + report_phish_addresses=["address3"], + report_chat_message_enabled=False, + report_chat_message_to_customized_address_enabled=True, + ) + + 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_chat_report_policy_configured.defender_chat_report_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured import ( + defender_chat_report_policy_configured, + ) + + check = defender_chat_report_policy_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Defender report submission policy is properly configured for Teams security reporting." + ) + assert result[0].resource == defender_client.report_submission_policy.dict() + assert result[0].resource_name == "Defender Security Reporting Policy" + assert result[0].resource_id == "defenderSecurityReportingPolicy" + assert result[0].location == "global" + + def test_report_policy_configured_fail(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + defender_client.report_submission_policy = ReportSubmissionPolicy( + report_junk_to_customized_address=False, + report_not_junk_to_customized_address=True, + report_phish_to_customized_address=True, + report_junk_addresses=[], + report_not_junk_addresses=[], + report_phish_addresses=[], + report_chat_message_enabled=True, + report_chat_message_to_customized_address_enabled=False, + ) + + 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_chat_report_policy_configured.defender_chat_report_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured import ( + defender_chat_report_policy_configured, + ) + + check = defender_chat_report_policy_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Defender report submission policy is not properly configured for Teams security reporting." + ) + assert result[0].resource == defender_client.report_submission_policy.dict() + assert result[0].resource_name == "Defender Security Reporting Policy" + assert result[0].resource_id == "defenderSecurityReportingPolicy" + assert result[0].location == "global" + + def test_report_policy_configured_none(self): + defender_client = mock.MagicMock() + defender_client.audited_tenant = "audited_tenant" + defender_client.audited_domain = DOMAIN + defender_client.report_submission_policy = 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_exchange_online" + ), + mock.patch( + "prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured.defender_client", + new=defender_client, + ), + ): + from prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured import ( + defender_chat_report_policy_configured, + ) + + check = defender_chat_report_policy_configured() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py b/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py index 7523e45bac..54fabb5762 100644 --- a/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py +++ b/tests/providers/m365/services/defender/defender_report_policy_configured/defender_report_policy_configured_test.py @@ -6,7 +6,7 @@ from tests.providers.m365.m365_fixtures import DOMAIN, set_mocked_m365_provider -class Test_defender_report_policy_configured: +class Test_defender_chat_report_policy_configured: def test_report_policy_configured_pass(self): defender_client = mock.MagicMock() defender_client.audited_tenant = "audited_tenant" @@ -31,15 +31,15 @@ def test_report_policy_configured_pass(self): "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" ), mock.patch( - "prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured.defender_client", + "prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured.defender_client", new=defender_client, ), ): - from prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured import ( - defender_report_policy_configured, + from prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured import ( + defender_chat_report_policy_configured, ) - check = defender_report_policy_configured() + check = defender_chat_report_policy_configured() result = check.execute() assert len(result) == 1 @@ -77,15 +77,15 @@ def test_report_policy_configured_fail(self): "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" ), mock.patch( - "prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured.defender_client", + "prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured.defender_client", new=defender_client, ), ): - from prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured import ( - defender_report_policy_configured, + from prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured import ( + defender_chat_report_policy_configured, ) - check = defender_report_policy_configured() + check = defender_chat_report_policy_configured() result = check.execute() assert len(result) == 1 @@ -114,14 +114,14 @@ def test_report_policy_configured_none(self): "prowler.providers.m365.lib.powershell.m365_powershell.M365PowerShell.connect_exchange_online" ), mock.patch( - "prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured.defender_client", + "prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured.defender_client", new=defender_client, ), ): - from prowler.providers.m365.services.defender.defender_report_policy_configured.defender_report_policy_configured import ( - defender_report_policy_configured, + from prowler.providers.m365.services.defender.defender_chat_report_policy_configured.defender_chat_report_policy_configured import ( + defender_chat_report_policy_configured, ) - check = defender_report_policy_configured() + check = defender_chat_report_policy_configured() result = check.execute() assert len(result) == 0 From d097fd3933cac310567c9f139ee5551cfba558d1 Mon Sep 17 00:00:00 2001 From: Andoni Alonso <14891798+andoniaf@users.noreply.github.com> Date: Tue, 6 May 2025 11:20:58 +0200 Subject: [PATCH 19/19] chore: apply suggestions from code review Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> --- .../defender_chat_report_policy_configured.py | 2 +- .../teams_security_reporting_enabled.py | 2 +- prowler/providers/m365/services/teams/teams_service.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.py b/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.py index d71cbe12a7..648070bf70 100644 --- a/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.py +++ b/prowler/providers/m365/services/defender/defender_chat_report_policy_configured/defender_chat_report_policy_configured.py @@ -25,7 +25,7 @@ def execute(self) -> List[CheckReportM365]: if report_submission_policy: report = CheckReportM365( metadata=self.metadata(), - resource=report_submission_policy if report_submission_policy else {}, + resource=report_submission_policy, resource_name="Defender Security Reporting Policy", resource_id="defenderSecurityReportingPolicy", ) diff --git a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py index d5254352a5..f90d41543a 100644 --- a/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py +++ b/prowler/providers/m365/services/teams/teams_security_reporting_enabled/teams_security_reporting_enabled.py @@ -25,7 +25,7 @@ def execute(self) -> List[CheckReportM365]: if global_messaging_policy: report = CheckReportM365( metadata=self.metadata(), - resource=global_messaging_policy if global_messaging_policy else {}, + resource=global_messaging_policy, resource_name="Teams Security Reporting Settings", resource_id="teamsSecurityReporting", ) diff --git a/prowler/providers/m365/services/teams/teams_service.py b/prowler/providers/m365/services/teams/teams_service.py index 8eb204a9b8..f17f22d327 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -78,7 +78,7 @@ def _get_global_meeting_policy(self): "DesignatedPresenterRoleMode", "EveryoneUserOverride" ), allow_security_end_user_reporting=global_meeting_policy.get( - "AllowSecurityEndUserReporting", True + "AllowSecurityEndUserReporting", False ), meeting_chat_enabled_type=global_meeting_policy.get( "MeetingChatEnabledType", "EnabledForEveryone" @@ -98,7 +98,7 @@ def _get_global_messaging_policy(self): if global_messaging_policy: global_messaging_policy = GlobalMessagingPolicy( allow_security_end_user_reporting=global_messaging_policy.get( - "AllowSecurityEndUserReporting", True + "AllowSecurityEndUserReporting", False ), ) except Exception as error: @@ -153,7 +153,7 @@ class GlobalMeetingPolicy(BaseModel): class GlobalMessagingPolicy(BaseModel): - allow_security_end_user_reporting: bool = True + allow_security_end_user_reporting: bool = False class UserSettings(BaseModel):