mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-05-14 16:25:13 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9894ac7bc3 |
@@ -18,7 +18,7 @@ Prowler requests the following read-only OAuth 2.0 scopes:
|
||||
| `https://www.googleapis.com/auth/admin.directory.domain.readonly` | Read access to domain information |
|
||||
| `https://www.googleapis.com/auth/admin.directory.customer.readonly` | Read access to customer information (Customer ID) |
|
||||
| `https://www.googleapis.com/auth/admin.directory.orgunit.readonly` | Read access to organizational unit hierarchy (identifies the root OU for policy filtering) |
|
||||
| `https://www.googleapis.com/auth/cloud-identity.policies.readonly` | Read access to domain-level application policies (required for Calendar service checks) |
|
||||
| `https://www.googleapis.com/auth/cloud-identity.policies.readonly` | Read access to domain-level application policies (required for Calendar, Gmail, Chat, and Drive service checks) |
|
||||
| `https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly` | Read access to admin roles and role assignments |
|
||||
|
||||
<Warning>
|
||||
@@ -40,7 +40,7 @@ In the [Google Cloud Console](https://console.cloud.google.com), select the targ
|
||||
| API | Required For |
|
||||
|-----|--------------|
|
||||
| **Admin SDK API** | Directory service checks (users, roles, domains) |
|
||||
| **Cloud Identity API** | Calendar service checks (domain-level sharing and invitation policies) |
|
||||
| **Cloud Identity API** | Calendar, Gmail, Chat, and Drive service checks (domain-level application policies) |
|
||||
|
||||
For each API:
|
||||
|
||||
@@ -49,7 +49,7 @@ For each API:
|
||||
3. Click **Enable**
|
||||
|
||||
<Note>
|
||||
Both APIs must be enabled in the same GCP project that hosts the Service Account. Calendar checks will return no findings if the Cloud Identity API is not enabled.
|
||||
Both APIs must be enabled in the same GCP project that hosts the Service Account. Calendar, Gmail, Chat, and Drive checks will return no findings if the Cloud Identity API is not enabled.
|
||||
</Note>
|
||||
|
||||
### Step 3: Create a Service Account
|
||||
@@ -176,9 +176,9 @@ If Prowler connects but returns empty results or permission errors for specific
|
||||
- Verify all scopes are authorized in the Admin Console
|
||||
- Ensure the delegated user is an active super administrator
|
||||
|
||||
### Calendar Checks Return No Findings
|
||||
### Policy API Checks Return No Findings
|
||||
|
||||
If the Directory checks run successfully but the Calendar checks (e.g., `calendar_external_sharing_primary_calendar`) return no findings, the Cloud Identity Policy API is not reachable for this Service Account. Verify:
|
||||
If the Directory checks run successfully but the Calendar, Gmail, Chat, or Drive checks return no findings, the Cloud Identity Policy API is not reachable for this Service Account. Verify:
|
||||
|
||||
- The **Cloud Identity API** is enabled in the GCP project hosting the Service Account (Step 2)
|
||||
- The scope `https://www.googleapis.com/auth/cloud-identity.policies.readonly` is included in the Domain-Wide Delegation OAuth scopes list in the Admin Console (Step 5)
|
||||
|
||||
@@ -6,6 +6,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- 6 Chat file sharing, external messaging, spaces, and apps access checks for Google Workspace provider using the Cloud Identity Policy API [(#11126)](https://github.com/prowler-cloud/prowler/pull/11126)
|
||||
- `entra_service_principal_no_secrets_for_permanent_tier0_roles` check for M365 provider [(#10788)](https://github.com/prowler-cloud/prowler/pull/10788)
|
||||
- `iam_user_access_not_stale_to_sagemaker` check for AWS provider with configurable `max_unused_sagemaker_access_days` (default 90) [(#11000)](https://github.com/prowler-cloud/prowler/pull/11000)
|
||||
- `cloudtrail_bedrock_logging_enabled` check for AWS provider [(#10858)](https://github.com/prowler-cloud/prowler/pull/10858)
|
||||
|
||||
@@ -1084,7 +1084,9 @@
|
||||
{
|
||||
"Id": "3.1.4.1.1",
|
||||
"Description": "Ensure external filesharing in Google Chat and Hangouts is disabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_external_file_sharing_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "3 Apps",
|
||||
@@ -1105,7 +1107,9 @@
|
||||
{
|
||||
"Id": "3.1.4.1.2",
|
||||
"Description": "Ensure internal filesharing in Google Chat and Hangouts is disabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_internal_file_sharing_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "3 Apps",
|
||||
@@ -1126,7 +1130,9 @@
|
||||
{
|
||||
"Id": "3.1.4.2.1",
|
||||
"Description": "Ensure Google Chat externally is restricted to allowed domains",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_external_messaging_restricted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "3 Apps",
|
||||
@@ -1147,7 +1153,9 @@
|
||||
{
|
||||
"Id": "3.1.4.3.1",
|
||||
"Description": "Ensure external spaces in Google Chat and Hangouts are restricted",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_external_spaces_restricted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "3 Apps",
|
||||
@@ -1168,7 +1176,9 @@
|
||||
{
|
||||
"Id": "3.1.4.4.1",
|
||||
"Description": "Ensure allow users to install Chat apps is disabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_apps_installation_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "3 Apps",
|
||||
@@ -1189,7 +1199,9 @@
|
||||
{
|
||||
"Id": "3.1.4.4.2",
|
||||
"Description": "Ensure allow users to add and use incoming webhooks is disabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_incoming_webhooks_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "3 Apps",
|
||||
|
||||
@@ -1466,7 +1466,9 @@
|
||||
{
|
||||
"Id": "GWS.CHAT.2.1",
|
||||
"Description": "External file sharing SHALL be disabled to protect sensitive information from unauthorized or accidental sharing",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_external_file_sharing_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Chat",
|
||||
@@ -1492,7 +1494,9 @@
|
||||
{
|
||||
"Id": "GWS.CHAT.4.1",
|
||||
"Description": "External chat messaging SHALL be restricted to allowlisted domains only",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"chat_external_messaging_restricted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Chat",
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"Provider": "googleworkspace",
|
||||
"CheckID": "chat_apps_installation_disabled",
|
||||
"CheckTitle": "Chat apps installation is disabled for users",
|
||||
"CheckType": [],
|
||||
"ServiceName": "chat",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "collaboration",
|
||||
"Description": "Google Chat apps connect to external services to look up information, schedule meetings, or complete tasks. Apps are accounts created by Google, users in the organization, or third parties that can access user data including **email addresses**, **conversation content**, and **organizational information**.",
|
||||
"Risk": "Unrestricted Chat app installation allows **unvetted third-party applications** to access user data including conversation content and organizational information. An attacker could distribute a malicious Chat app to **exfiltrate confidential data** or establish **persistent access** to internal communications.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/6089179",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Google Chat and classic Hangouts**\n3. Click **Chat apps**\n4. Under Chat apps access settings, set **Allow users to install Chat apps** to **OFF**\n5. Click **Save**",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable Chat apps installation to prevent **unvetted third-party applications** from accessing organizational data through the Chat platform.",
|
||||
"Url": "https://hub.prowler.com/check/chat_apps_installation_disabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"trust-boundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"chat_incoming_webhooks_disabled"
|
||||
],
|
||||
"Notes": ""
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportGoogleWorkspace
|
||||
from prowler.providers.googleworkspace.services.chat.chat_client import chat_client
|
||||
|
||||
|
||||
class chat_apps_installation_disabled(Check):
|
||||
"""Check that users cannot install Chat apps.
|
||||
|
||||
This check verifies that the domain-level Chat policy prevents users
|
||||
from installing Chat apps, reducing the risk of data exposure through
|
||||
third-party or unvetted applications.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportGoogleWorkspace]:
|
||||
findings = []
|
||||
|
||||
if chat_client.policies_fetched:
|
||||
report = CheckReportGoogleWorkspace(
|
||||
metadata=self.metadata(),
|
||||
resource=chat_client.policies,
|
||||
resource_id="chatPolicies",
|
||||
resource_name="Chat Policies",
|
||||
customer_id=chat_client.provider.identity.customer_id,
|
||||
)
|
||||
|
||||
apps_enabled = chat_client.policies.enable_apps
|
||||
|
||||
if apps_enabled is False:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Chat apps installation is disabled "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
elif apps_enabled is None:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Chat apps installation uses Google's secure default "
|
||||
f"configuration (disabled) "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Chat apps installation is enabled "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"Chat apps installation should be disabled to prevent unvetted apps."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,4 @@
|
||||
from prowler.providers.common.provider import Provider
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import Chat
|
||||
|
||||
chat_client = Chat(Provider.get_global_provider())
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"Provider": "googleworkspace",
|
||||
"CheckID": "chat_external_file_sharing_disabled",
|
||||
"CheckTitle": "External file sharing in Chat is set to no files",
|
||||
"CheckType": [],
|
||||
"ServiceName": "chat",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "collaboration",
|
||||
"Description": "Google Chat **external file sharing** controls whether users can share files with people outside the organization via Chat conversations. Files often contain **confidential information**, and organizations in regulated industries need to control the flow of this information outside their boundaries.",
|
||||
"Risk": "Enabled external file sharing allows users to send files containing **confidential information** to external parties through Chat. This creates a **data leakage** channel that bypasses DLP controls, particularly dangerous for organizations handling **regulated data** such as PII, PHI, or financial records.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Google Chat and classic Hangouts**\n3. Click **Chat File Sharing**\n4. Under Setting, set **External filesharing** to **No files**\n5. Click **Save**",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable **external file sharing** in Chat to prevent users from sharing files with people outside the organization through Chat conversations.",
|
||||
"Url": "https://hub.prowler.com/check/chat_external_file_sharing_disabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"trust-boundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"chat_internal_file_sharing_disabled",
|
||||
"drive_sharing_allowlisted_domains"
|
||||
],
|
||||
"Notes": ""
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportGoogleWorkspace
|
||||
from prowler.providers.googleworkspace.services.chat.chat_client import chat_client
|
||||
|
||||
|
||||
class chat_external_file_sharing_disabled(Check):
|
||||
"""Check that external file sharing in Google Chat is disabled.
|
||||
|
||||
This check verifies that the domain-level Chat policy prevents users
|
||||
from sharing files with people outside the organization via Chat,
|
||||
protecting sensitive information from unauthorized external access.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportGoogleWorkspace]:
|
||||
findings = []
|
||||
|
||||
if chat_client.policies_fetched:
|
||||
report = CheckReportGoogleWorkspace(
|
||||
metadata=self.metadata(),
|
||||
resource=chat_client.policies,
|
||||
resource_id="chatPolicies",
|
||||
resource_name="Chat Policies",
|
||||
customer_id=chat_client.provider.identity.customer_id,
|
||||
)
|
||||
|
||||
external_sharing = chat_client.policies.external_file_sharing
|
||||
|
||||
if external_sharing == "NO_FILES":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"External file sharing in Chat is disabled "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
if external_sharing is None:
|
||||
report.status_extended = (
|
||||
f"External file sharing in Chat is not explicitly configured "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"External file sharing should be set to No files."
|
||||
)
|
||||
else:
|
||||
report.status_extended = (
|
||||
f"External file sharing in Chat is set to {external_sharing} "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"External file sharing should be set to No files."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"Provider": "googleworkspace",
|
||||
"CheckID": "chat_external_messaging_restricted",
|
||||
"CheckTitle": "External Chat messaging is restricted to allowed domains",
|
||||
"CheckType": [],
|
||||
"ServiceName": "chat",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "collaboration",
|
||||
"Description": "Google Chat **external messaging** controls whether users can send messages to people outside the organization. If external messaging is allowed, it can optionally be restricted to only **allowlisted domains** to limit the scope of external communication.",
|
||||
"Risk": "Unrestricted external messaging allows users to communicate freely with **any external party**, increasing the risk of **data exfiltration** through conversation content and **social engineering attacks** from untrusted domains targeting internal users.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Google Chat and classic Hangouts**\n3. Click **External Chat Settings**\n4. Select **Chat externally**\n5. Set **Allow users to send messages outside the organization** to **ON**\n6. Check **Only allow this for allowlisted domains**\n7. Click **Save**",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Restrict **external Chat messaging** to **allowlisted domains** only to limit information flow to trusted parties and reduce exposure to external threats.",
|
||||
"Url": "https://hub.prowler.com/check/chat_external_messaging_restricted"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"trust-boundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"chat_external_spaces_restricted",
|
||||
"drive_sharing_allowlisted_domains"
|
||||
],
|
||||
"Notes": ""
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportGoogleWorkspace
|
||||
from prowler.providers.googleworkspace.services.chat.chat_client import chat_client
|
||||
|
||||
|
||||
class chat_external_messaging_restricted(Check):
|
||||
"""Check that external Chat messaging is restricted to allowed domains.
|
||||
|
||||
This check verifies that external Chat messaging is either disabled
|
||||
entirely or restricted to allowlisted domains only, preventing
|
||||
unrestricted communication with external users.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportGoogleWorkspace]:
|
||||
findings = []
|
||||
|
||||
if chat_client.policies_fetched:
|
||||
report = CheckReportGoogleWorkspace(
|
||||
metadata=self.metadata(),
|
||||
resource=chat_client.policies,
|
||||
resource_id="chatPolicies",
|
||||
resource_name="Chat Policies",
|
||||
customer_id=chat_client.provider.identity.customer_id,
|
||||
)
|
||||
|
||||
allow_external = chat_client.policies.allow_external_chat
|
||||
restriction = chat_client.policies.external_chat_restriction
|
||||
|
||||
if allow_external is False:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"External Chat messaging is disabled "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
elif allow_external is None and restriction is None:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"External Chat messaging uses Google's secure default "
|
||||
f"configuration (disabled) "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
elif restriction == "TRUSTED_DOMAINS":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"External Chat messaging is restricted to allowed domains "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"External Chat messaging is not restricted to allowed domains "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"External messaging should be restricted to allowed domains only."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"Provider": "googleworkspace",
|
||||
"CheckID": "chat_external_spaces_restricted",
|
||||
"CheckTitle": "External spaces in Chat are restricted to allowed domains",
|
||||
"CheckType": [],
|
||||
"ServiceName": "chat",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "collaboration",
|
||||
"Description": "Google Chat **external spaces** allow users to create or join collaborative spaces that include people outside the organization. If external spaces are allowed, they can optionally be restricted to only **allowlisted domains** to limit external participation.",
|
||||
"Risk": "Unrestricted external spaces allow users to add **anyone from any domain** to persistent group conversations. This increases the risk of **confidential information exposure** in shared spaces and enables **unauthorized external access** to ongoing organizational discussions.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Google Chat and classic Hangouts**\n3. Click **External Spaces**\n4. Set **Allow users to create and join spaces with people outside their organization** to **ON**\n5. Check **Only allow users to add people from allowlisted domains**\n6. Click **Save**",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Restrict **external spaces** to **allowlisted domains** only to control which external parties can participate in organizational Chat spaces.",
|
||||
"Url": "https://hub.prowler.com/check/chat_external_spaces_restricted"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"trust-boundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"chat_external_messaging_restricted",
|
||||
"drive_sharing_allowlisted_domains"
|
||||
],
|
||||
"Notes": ""
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportGoogleWorkspace
|
||||
from prowler.providers.googleworkspace.services.chat.chat_client import chat_client
|
||||
|
||||
|
||||
class chat_external_spaces_restricted(Check):
|
||||
"""Check that external spaces in Google Chat are restricted.
|
||||
|
||||
This check verifies that external spaces are either disabled entirely
|
||||
or restricted to allowlisted domains only, preventing users from
|
||||
creating or joining spaces with unrestricted external participants.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportGoogleWorkspace]:
|
||||
findings = []
|
||||
|
||||
if chat_client.policies_fetched:
|
||||
report = CheckReportGoogleWorkspace(
|
||||
metadata=self.metadata(),
|
||||
resource=chat_client.policies,
|
||||
resource_id="chatPolicies",
|
||||
resource_name="Chat Policies",
|
||||
customer_id=chat_client.provider.identity.customer_id,
|
||||
)
|
||||
|
||||
spaces_enabled = chat_client.policies.external_spaces_enabled
|
||||
allowlist_mode = chat_client.policies.external_spaces_domain_allowlist_mode
|
||||
|
||||
if spaces_enabled is False:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"External spaces are disabled "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
elif allowlist_mode == "TRUSTED_DOMAINS":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"External spaces are restricted to allowed domains "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
if spaces_enabled is None and allowlist_mode is None:
|
||||
report.status_extended = (
|
||||
f"External spaces restriction is not explicitly configured "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"External spaces should be restricted to allowed domains only."
|
||||
)
|
||||
else:
|
||||
report.status_extended = (
|
||||
f"External spaces are not restricted to allowed domains "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"External spaces should be restricted to allowed domains only."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"Provider": "googleworkspace",
|
||||
"CheckID": "chat_incoming_webhooks_disabled",
|
||||
"CheckTitle": "Incoming webhooks in Chat are disabled for users",
|
||||
"CheckType": [],
|
||||
"ServiceName": "chat",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "collaboration",
|
||||
"Description": "**Incoming webhooks** let external applications post asynchronous messages into Google Chat spaces without being a Chat app. When enabled, users can configure webhooks and developers can call them to send content from **external applications**.",
|
||||
"Risk": "Exposed webhook URLs allow **unauthorized content injection** into Chat spaces. Attackers can send **fraudulent or misleading messages** that appear to come from trusted services, creating a vector for **social engineering** and **phishing** within internal communications.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/6089179",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Google Chat and classic Hangouts**\n3. Click **Chat apps**\n4. Under Chat apps access settings, set **Allow users to add and use incoming webhooks** to **OFF**\n5. Click **Save**",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable **incoming webhooks** to prevent unauthenticated external applications from **injecting content** into internal Chat spaces.",
|
||||
"Url": "https://hub.prowler.com/check/chat_incoming_webhooks_disabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"trust-boundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"chat_apps_installation_disabled"
|
||||
],
|
||||
"Notes": ""
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportGoogleWorkspace
|
||||
from prowler.providers.googleworkspace.services.chat.chat_client import chat_client
|
||||
|
||||
|
||||
class chat_incoming_webhooks_disabled(Check):
|
||||
"""Check that incoming webhooks are disabled in Google Chat.
|
||||
|
||||
This check verifies that the domain-level Chat policy prevents users
|
||||
from adding and using incoming webhooks, reducing the risk of
|
||||
unauthorized content being posted into Chat spaces.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportGoogleWorkspace]:
|
||||
findings = []
|
||||
|
||||
if chat_client.policies_fetched:
|
||||
report = CheckReportGoogleWorkspace(
|
||||
metadata=self.metadata(),
|
||||
resource=chat_client.policies,
|
||||
resource_id="chatPolicies",
|
||||
resource_name="Chat Policies",
|
||||
customer_id=chat_client.provider.identity.customer_id,
|
||||
)
|
||||
|
||||
webhooks_enabled = chat_client.policies.enable_webhooks
|
||||
|
||||
if webhooks_enabled is False:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Incoming webhooks are disabled "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
elif webhooks_enabled is None:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Incoming webhooks use Google's secure default "
|
||||
f"configuration (disabled) "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Incoming webhooks are enabled "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"Incoming webhooks should be disabled to prevent unauthorized content."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"Provider": "googleworkspace",
|
||||
"CheckID": "chat_internal_file_sharing_disabled",
|
||||
"CheckTitle": "Internal file sharing in Chat is set to no files",
|
||||
"CheckType": [],
|
||||
"ServiceName": "chat",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "low",
|
||||
"ResourceType": "NotDefined",
|
||||
"ResourceGroup": "collaboration",
|
||||
"Description": "Google Chat **internal file sharing** controls whether users can share files with other people inside the organization via Chat conversations. Organizations in regulated industries may need to **control and audit** all file sharing, even between internal users.",
|
||||
"Risk": "Unrestricted internal file sharing in Chat allows files with **sensitive information** to be distributed freely without passing through approved channels. This undermines **data governance** and **audit trail** requirements, making it harder to track data movement within the organization.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Google Chat and classic Hangouts**\n3. Click **Chat File Sharing**\n4. Under Setting, set **Internal filesharing** to **No files**\n5. Click **Save**",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable **internal file sharing** in Chat to enforce file distribution through **approved channels** with proper audit trails and governance controls.",
|
||||
"Url": "https://hub.prowler.com/check/chat_internal_file_sharing_disabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"trust-boundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [
|
||||
"chat_external_file_sharing_disabled"
|
||||
],
|
||||
"Notes": ""
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, CheckReportGoogleWorkspace
|
||||
from prowler.providers.googleworkspace.services.chat.chat_client import chat_client
|
||||
|
||||
|
||||
class chat_internal_file_sharing_disabled(Check):
|
||||
"""Check that internal file sharing in Google Chat is disabled.
|
||||
|
||||
This check verifies that the domain-level Chat policy prevents users
|
||||
from sharing files internally via Chat, providing maximum control over
|
||||
file distribution within the organization.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[CheckReportGoogleWorkspace]:
|
||||
findings = []
|
||||
|
||||
if chat_client.policies_fetched:
|
||||
report = CheckReportGoogleWorkspace(
|
||||
metadata=self.metadata(),
|
||||
resource=chat_client.policies,
|
||||
resource_id="chatPolicies",
|
||||
resource_name="Chat Policies",
|
||||
customer_id=chat_client.provider.identity.customer_id,
|
||||
)
|
||||
|
||||
internal_sharing = chat_client.policies.internal_file_sharing
|
||||
|
||||
if internal_sharing == "NO_FILES":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Internal file sharing in Chat is disabled "
|
||||
f"in domain {chat_client.provider.identity.domain}."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
if internal_sharing is None:
|
||||
report.status_extended = (
|
||||
f"Internal file sharing in Chat is not explicitly configured "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"Internal file sharing should be set to No files."
|
||||
)
|
||||
else:
|
||||
report.status_extended = (
|
||||
f"Internal file sharing in Chat is set to {internal_sharing} "
|
||||
f"in domain {chat_client.provider.identity.domain}. "
|
||||
f"Internal file sharing should be set to No files."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,125 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.googleworkspace.lib.service.service import GoogleWorkspaceService
|
||||
|
||||
|
||||
class Chat(GoogleWorkspaceService):
|
||||
"""Google Workspace Chat service for auditing domain-level Chat policies.
|
||||
|
||||
Uses the Cloud Identity Policy API v1 to read Chat file sharing, external
|
||||
messaging, spaces, and apps access settings configured in the Admin Console.
|
||||
"""
|
||||
|
||||
def __init__(self, provider):
|
||||
super().__init__(provider)
|
||||
self.policies = ChatPolicies()
|
||||
self.policies_fetched = False
|
||||
self._fetch_chat_policies()
|
||||
|
||||
def _fetch_chat_policies(self):
|
||||
"""Fetch Chat policies from the Cloud Identity Policy API v1."""
|
||||
logger.info("Chat - Fetching Chat policies...")
|
||||
|
||||
try:
|
||||
service = self._build_service("cloudidentity", "v1")
|
||||
|
||||
if not service:
|
||||
logger.error("Failed to build Cloud Identity service")
|
||||
return
|
||||
|
||||
request = service.policies().list(
|
||||
pageSize=100,
|
||||
filter='setting.type.matches("chat.*")',
|
||||
)
|
||||
fetch_succeeded = True
|
||||
|
||||
while request is not None:
|
||||
try:
|
||||
response = request.execute()
|
||||
|
||||
for policy in response.get("policies", []):
|
||||
if not self._is_customer_level_policy(policy):
|
||||
continue
|
||||
|
||||
setting = policy.get("setting", {})
|
||||
setting_type = setting.get("type", "").removeprefix("settings/")
|
||||
logger.debug(f"Processing setting type: {setting_type}")
|
||||
|
||||
value = setting.get("value", {})
|
||||
|
||||
if setting_type == "chat.chat_file_sharing":
|
||||
self.policies.external_file_sharing = value.get(
|
||||
"externalFileSharing"
|
||||
)
|
||||
self.policies.internal_file_sharing = value.get(
|
||||
"internalFileSharing"
|
||||
)
|
||||
logger.debug("Chat file sharing settings fetched.")
|
||||
|
||||
elif setting_type == "chat.external_chat_restriction":
|
||||
self.policies.allow_external_chat = value.get(
|
||||
"allowExternalChat"
|
||||
)
|
||||
self.policies.external_chat_restriction = value.get(
|
||||
"externalChatRestriction"
|
||||
)
|
||||
logger.debug(
|
||||
"Chat external chat restriction settings fetched."
|
||||
)
|
||||
|
||||
elif setting_type == "chat.chat_external_spaces":
|
||||
self.policies.external_spaces_enabled = value.get("enabled")
|
||||
self.policies.external_spaces_domain_allowlist_mode = (
|
||||
value.get("domainAllowlistMode")
|
||||
)
|
||||
logger.debug("Chat external spaces settings fetched.")
|
||||
|
||||
elif setting_type == "chat.chat_apps_access":
|
||||
self.policies.enable_apps = value.get("enableApps")
|
||||
self.policies.enable_webhooks = value.get("enableWebhooks")
|
||||
logger.debug("Chat apps access settings fetched.")
|
||||
|
||||
request = service.policies().list_next(request, response)
|
||||
|
||||
except Exception as error:
|
||||
self._handle_api_error(
|
||||
error,
|
||||
"fetching Chat policies",
|
||||
self.provider.identity.customer_id,
|
||||
)
|
||||
fetch_succeeded = False
|
||||
break
|
||||
|
||||
self.policies_fetched = fetch_succeeded
|
||||
logger.info("Chat policies fetched successfully.")
|
||||
|
||||
except Exception as error:
|
||||
self._handle_api_error(
|
||||
error,
|
||||
"fetching Chat policies",
|
||||
self.provider.identity.customer_id,
|
||||
)
|
||||
self.policies_fetched = False
|
||||
|
||||
|
||||
class ChatPolicies(BaseModel):
|
||||
"""Model for domain-level Chat policy settings."""
|
||||
|
||||
# chat.chat_file_sharing
|
||||
external_file_sharing: Optional[str] = None
|
||||
internal_file_sharing: Optional[str] = None
|
||||
|
||||
# chat.external_chat_restriction
|
||||
allow_external_chat: Optional[bool] = None
|
||||
external_chat_restriction: Optional[str] = None
|
||||
|
||||
# chat.chat_external_spaces
|
||||
external_spaces_enabled: Optional[bool] = None
|
||||
external_spaces_domain_allowlist_mode: Optional[str] = None
|
||||
|
||||
# chat.chat_apps_access
|
||||
enable_apps: Optional[bool] = None
|
||||
enable_webhooks: Optional[bool] = None
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import ChatPolicies
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
CUSTOMER_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestChatAppsInstallationDisabled:
|
||||
def test_pass(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled import (
|
||||
chat_apps_installation_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(enable_apps=False)
|
||||
|
||||
check = chat_apps_installation_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "disabled" in findings[0].status_extended
|
||||
assert findings[0].resource_name == "Chat Policies"
|
||||
assert findings[0].resource_id == "chatPolicies"
|
||||
assert findings[0].customer_id == CUSTOMER_ID
|
||||
assert findings[0].resource == ChatPolicies(enable_apps=False).dict()
|
||||
|
||||
def test_fail_enabled(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled import (
|
||||
chat_apps_installation_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(enable_apps=True)
|
||||
|
||||
check = chat_apps_installation_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "enabled" in findings[0].status_extended
|
||||
|
||||
def test_pass_no_policy_set(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled import (
|
||||
chat_apps_installation_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(enable_apps=None)
|
||||
|
||||
check = chat_apps_installation_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "secure default" in findings[0].status_extended
|
||||
|
||||
def test_no_findings_when_fetch_failed(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_apps_installation_disabled.chat_apps_installation_disabled import (
|
||||
chat_apps_installation_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = False
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_apps_installation_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 0
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import ChatPolicies
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
CUSTOMER_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestChatExternalFileSharingDisabled:
|
||||
def test_pass(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled import (
|
||||
chat_external_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(external_file_sharing="NO_FILES")
|
||||
|
||||
check = chat_external_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "disabled" in findings[0].status_extended
|
||||
assert findings[0].resource_name == "Chat Policies"
|
||||
assert findings[0].resource_id == "chatPolicies"
|
||||
assert findings[0].customer_id == CUSTOMER_ID
|
||||
assert (
|
||||
findings[0].resource
|
||||
== ChatPolicies(external_file_sharing="NO_FILES").dict()
|
||||
)
|
||||
|
||||
def test_fail_all_files(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled import (
|
||||
chat_external_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(external_file_sharing="ALL_FILES")
|
||||
|
||||
check = chat_external_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "ALL_FILES" in findings[0].status_extended
|
||||
|
||||
def test_fail_images_only(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled import (
|
||||
chat_external_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(external_file_sharing="IMAGES_ONLY")
|
||||
|
||||
check = chat_external_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "IMAGES_ONLY" in findings[0].status_extended
|
||||
|
||||
def test_fail_no_policy_set(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled import (
|
||||
chat_external_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(external_file_sharing=None)
|
||||
|
||||
check = chat_external_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "not explicitly configured" in findings[0].status_extended
|
||||
|
||||
def test_no_findings_when_fetch_failed(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_file_sharing_disabled.chat_external_file_sharing_disabled import (
|
||||
chat_external_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = False
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_external_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 0
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import ChatPolicies
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
CUSTOMER_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestChatExternalMessagingRestricted:
|
||||
def test_pass_external_chat_disabled(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted import (
|
||||
chat_external_messaging_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(allow_external_chat=False)
|
||||
|
||||
check = chat_external_messaging_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "disabled" in findings[0].status_extended
|
||||
assert findings[0].resource_name == "Chat Policies"
|
||||
assert findings[0].resource_id == "chatPolicies"
|
||||
assert findings[0].customer_id == CUSTOMER_ID
|
||||
assert (
|
||||
findings[0].resource == ChatPolicies(allow_external_chat=False).dict()
|
||||
)
|
||||
|
||||
def test_pass_trusted_domains(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted import (
|
||||
chat_external_messaging_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(
|
||||
allow_external_chat=True,
|
||||
external_chat_restriction="TRUSTED_DOMAINS",
|
||||
)
|
||||
|
||||
check = chat_external_messaging_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "restricted to allowed domains" in findings[0].status_extended
|
||||
|
||||
def test_fail_no_restriction(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted import (
|
||||
chat_external_messaging_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(
|
||||
allow_external_chat=True,
|
||||
external_chat_restriction="NO_RESTRICTION",
|
||||
)
|
||||
|
||||
check = chat_external_messaging_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "not restricted" in findings[0].status_extended
|
||||
|
||||
def test_pass_no_policy_set(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted import (
|
||||
chat_external_messaging_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_external_messaging_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "secure default" in findings[0].status_extended
|
||||
|
||||
def test_no_findings_when_fetch_failed(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_messaging_restricted.chat_external_messaging_restricted import (
|
||||
chat_external_messaging_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = False
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_external_messaging_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 0
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import ChatPolicies
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
CUSTOMER_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestChatExternalSpacesRestricted:
|
||||
def test_pass_spaces_disabled(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted import (
|
||||
chat_external_spaces_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(external_spaces_enabled=False)
|
||||
|
||||
check = chat_external_spaces_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "disabled" in findings[0].status_extended
|
||||
assert findings[0].resource_name == "Chat Policies"
|
||||
assert findings[0].resource_id == "chatPolicies"
|
||||
assert findings[0].customer_id == CUSTOMER_ID
|
||||
assert (
|
||||
findings[0].resource
|
||||
== ChatPolicies(external_spaces_enabled=False).dict()
|
||||
)
|
||||
|
||||
def test_pass_trusted_domains(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted import (
|
||||
chat_external_spaces_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(
|
||||
external_spaces_enabled=True,
|
||||
external_spaces_domain_allowlist_mode="TRUSTED_DOMAINS",
|
||||
)
|
||||
|
||||
check = chat_external_spaces_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "restricted to allowed domains" in findings[0].status_extended
|
||||
|
||||
def test_fail_all_domains(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted import (
|
||||
chat_external_spaces_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(
|
||||
external_spaces_enabled=True,
|
||||
external_spaces_domain_allowlist_mode="ALL_DOMAINS",
|
||||
)
|
||||
|
||||
check = chat_external_spaces_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "not restricted" in findings[0].status_extended
|
||||
|
||||
def test_fail_no_policy_set(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted import (
|
||||
chat_external_spaces_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_external_spaces_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "not explicitly configured" in findings[0].status_extended
|
||||
|
||||
def test_no_findings_when_fetch_failed(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_external_spaces_restricted.chat_external_spaces_restricted import (
|
||||
chat_external_spaces_restricted,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = False
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_external_spaces_restricted()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 0
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import ChatPolicies
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
CUSTOMER_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestChatIncomingWebhooksDisabled:
|
||||
def test_pass(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled import (
|
||||
chat_incoming_webhooks_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(enable_webhooks=False)
|
||||
|
||||
check = chat_incoming_webhooks_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "disabled" in findings[0].status_extended
|
||||
assert findings[0].resource_name == "Chat Policies"
|
||||
assert findings[0].resource_id == "chatPolicies"
|
||||
assert findings[0].customer_id == CUSTOMER_ID
|
||||
assert findings[0].resource == ChatPolicies(enable_webhooks=False).dict()
|
||||
|
||||
def test_fail_enabled(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled import (
|
||||
chat_incoming_webhooks_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(enable_webhooks=True)
|
||||
|
||||
check = chat_incoming_webhooks_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "enabled" in findings[0].status_extended
|
||||
|
||||
def test_pass_no_policy_set(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled import (
|
||||
chat_incoming_webhooks_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(enable_webhooks=None)
|
||||
|
||||
check = chat_incoming_webhooks_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "secure default" in findings[0].status_extended
|
||||
|
||||
def test_no_findings_when_fetch_failed(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_incoming_webhooks_disabled.chat_incoming_webhooks_disabled import (
|
||||
chat_incoming_webhooks_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = False
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_incoming_webhooks_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 0
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import ChatPolicies
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
CUSTOMER_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestChatInternalFileSharingDisabled:
|
||||
def test_pass(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled import (
|
||||
chat_internal_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(internal_file_sharing="NO_FILES")
|
||||
|
||||
check = chat_internal_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "PASS"
|
||||
assert "disabled" in findings[0].status_extended
|
||||
assert findings[0].resource_name == "Chat Policies"
|
||||
assert findings[0].resource_id == "chatPolicies"
|
||||
assert findings[0].customer_id == CUSTOMER_ID
|
||||
assert (
|
||||
findings[0].resource
|
||||
== ChatPolicies(internal_file_sharing="NO_FILES").dict()
|
||||
)
|
||||
|
||||
def test_fail_all_files(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled import (
|
||||
chat_internal_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(internal_file_sharing="ALL_FILES")
|
||||
|
||||
check = chat_internal_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "ALL_FILES" in findings[0].status_extended
|
||||
|
||||
def test_fail_no_policy_set(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled import (
|
||||
chat_internal_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = True
|
||||
mock_client.policies = ChatPolicies(internal_file_sharing=None)
|
||||
|
||||
check = chat_internal_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 1
|
||||
assert findings[0].status == "FAIL"
|
||||
assert "not explicitly configured" in findings[0].status_extended
|
||||
|
||||
def test_no_findings_when_fetch_failed(self):
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled.chat_client"
|
||||
) as mock_client,
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_internal_file_sharing_disabled.chat_internal_file_sharing_disabled import (
|
||||
chat_internal_file_sharing_disabled,
|
||||
)
|
||||
|
||||
mock_client.provider = mock_provider
|
||||
mock_client.policies_fetched = False
|
||||
mock_client.policies = ChatPolicies()
|
||||
|
||||
check = chat_internal_file_sharing_disabled()
|
||||
findings = check.execute()
|
||||
|
||||
assert len(findings) == 0
|
||||
@@ -0,0 +1,440 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from googleapiclient.errors import HttpError
|
||||
from httplib2 import Response as HttpResponse
|
||||
|
||||
from tests.providers.googleworkspace.googleworkspace_fixtures import (
|
||||
ROOT_ORG_UNIT_ID,
|
||||
set_mocked_googleworkspace_provider,
|
||||
)
|
||||
|
||||
|
||||
class TestChatService:
|
||||
def test_chat_fetch_policies_all_settings(self):
|
||||
"""Test fetching all 4 Chat policy settings from Cloud Identity API"""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_credentials = MagicMock()
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = mock_credentials
|
||||
mock_provider.session = mock_session
|
||||
|
||||
mock_service = MagicMock()
|
||||
mock_policies_list = MagicMock()
|
||||
mock_policies_list.execute.return_value = {
|
||||
"policies": [
|
||||
{
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_file_sharing",
|
||||
"value": {
|
||||
"externalFileSharing": "NO_FILES",
|
||||
"internalFileSharing": "IMAGES_ONLY",
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"setting": {
|
||||
"type": "settings/chat.external_chat_restriction",
|
||||
"value": {
|
||||
"allowExternalChat": True,
|
||||
"externalChatRestriction": "TRUSTED_DOMAINS",
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_external_spaces",
|
||||
"value": {
|
||||
"enabled": True,
|
||||
"domainAllowlistMode": "TRUSTED_DOMAINS",
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_apps_access",
|
||||
"value": {
|
||||
"enableApps": False,
|
||||
"enableWebhooks": False,
|
||||
},
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
mock_service.policies().list.return_value = mock_policies_list
|
||||
mock_service.policies().list_next.return_value = None
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=mock_service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
assert chat.policies_fetched is True
|
||||
assert chat.policies.external_file_sharing == "NO_FILES"
|
||||
assert chat.policies.internal_file_sharing == "IMAGES_ONLY"
|
||||
assert chat.policies.allow_external_chat is True
|
||||
assert chat.policies.external_chat_restriction == "TRUSTED_DOMAINS"
|
||||
assert chat.policies.external_spaces_enabled is True
|
||||
assert (
|
||||
chat.policies.external_spaces_domain_allowlist_mode == "TRUSTED_DOMAINS"
|
||||
)
|
||||
assert chat.policies.enable_apps is False
|
||||
assert chat.policies.enable_webhooks is False
|
||||
|
||||
def test_chat_fetch_policies_empty_response(self):
|
||||
"""Test handling empty policies response"""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = MagicMock()
|
||||
mock_provider.session = mock_session
|
||||
|
||||
mock_service = MagicMock()
|
||||
mock_policies_list = MagicMock()
|
||||
mock_policies_list.execute.return_value = {"policies": []}
|
||||
mock_service.policies().list.return_value = mock_policies_list
|
||||
mock_service.policies().list_next.return_value = None
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=mock_service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
assert chat.policies_fetched is True
|
||||
assert chat.policies.external_file_sharing is None
|
||||
assert chat.policies.allow_external_chat is None
|
||||
assert chat.policies.enable_apps is None
|
||||
assert chat.policies.enable_webhooks is None
|
||||
|
||||
def test_chat_fetch_policies_api_error(self):
|
||||
"""Test handling of API errors during policy fetch"""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = MagicMock()
|
||||
mock_provider.session = mock_session
|
||||
|
||||
mock_service = MagicMock()
|
||||
mock_service.policies().list.side_effect = Exception("API Error")
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=mock_service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
assert chat.policies_fetched is False
|
||||
assert chat.policies.external_file_sharing is None
|
||||
|
||||
def test_chat_fetch_policies_build_service_returns_none(self):
|
||||
"""Test early return when _build_service fails to construct the client"""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = MagicMock()
|
||||
mock_provider.session = mock_session
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=None,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
assert chat.policies_fetched is False
|
||||
assert chat.policies.external_file_sharing is None
|
||||
|
||||
def test_chat_fetch_policies_execute_raises(self):
|
||||
"""Test inner except handler when request.execute() raises during pagination"""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = MagicMock()
|
||||
mock_provider.session = mock_session
|
||||
|
||||
mock_service = MagicMock()
|
||||
mock_request = MagicMock()
|
||||
mock_request.execute.side_effect = Exception("Execute failed")
|
||||
mock_service.policies().list.return_value = mock_request
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=mock_service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
assert chat.policies_fetched is False
|
||||
assert chat.policies.external_file_sharing is None
|
||||
|
||||
def test_chat_fetch_policies_ignores_ou_and_group_level(self):
|
||||
"""Test that OU-level and group-level policies are skipped, only customer-level used"""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = MagicMock()
|
||||
mock_provider.session = mock_session
|
||||
|
||||
mock_service = MagicMock()
|
||||
mock_policies_list = MagicMock()
|
||||
mock_policies_list.execute.return_value = {
|
||||
"policies": [
|
||||
{
|
||||
# Customer-level: no policyQuery → should be used
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_apps_access",
|
||||
"value": {"enableApps": False, "enableWebhooks": False},
|
||||
}
|
||||
},
|
||||
{
|
||||
# OU-level: has policyQuery.orgUnit → should be skipped
|
||||
"policyQuery": {"orgUnit": "orgUnits/sales_team"},
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_apps_access",
|
||||
"value": {"enableApps": True, "enableWebhooks": True},
|
||||
},
|
||||
},
|
||||
{
|
||||
# Group-level: has policyQuery.group → should be skipped
|
||||
"policyQuery": {"group": "groups/contractors"},
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_file_sharing",
|
||||
"value": {
|
||||
"externalFileSharing": "ALL_FILES",
|
||||
"internalFileSharing": "ALL_FILES",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
# Customer-level: no policyQuery → should be used
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_file_sharing",
|
||||
"value": {
|
||||
"externalFileSharing": "NO_FILES",
|
||||
"internalFileSharing": "NO_FILES",
|
||||
},
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
mock_service.policies().list.return_value = mock_policies_list
|
||||
mock_service.policies().list_next.return_value = None
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=mock_service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
assert chat.policies_fetched is True
|
||||
assert chat.policies.enable_apps is False
|
||||
assert chat.policies.external_file_sharing == "NO_FILES"
|
||||
|
||||
def test_chat_fetch_policies_accepts_root_ou(self):
|
||||
"""Test that root-OU-scoped policies are accepted as customer-level"""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = MagicMock()
|
||||
mock_provider.session = mock_session
|
||||
|
||||
mock_service = MagicMock()
|
||||
mock_policies_list = MagicMock()
|
||||
mock_policies_list.execute.return_value = {
|
||||
"policies": [
|
||||
{
|
||||
# Root OU: matches provider's root_org_unit_id → should be accepted
|
||||
"policyQuery": {"orgUnit": f"orgUnits/{ROOT_ORG_UNIT_ID}"},
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_apps_access",
|
||||
"value": {"enableApps": False, "enableWebhooks": True},
|
||||
},
|
||||
},
|
||||
{
|
||||
# Sub-OU: different orgUnit → should be skipped
|
||||
"policyQuery": {"orgUnit": "orgUnits/sub_ou_sales"},
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_file_sharing",
|
||||
"value": {
|
||||
"externalFileSharing": "ALL_FILES",
|
||||
"internalFileSharing": "ALL_FILES",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
mock_service.policies().list.return_value = mock_policies_list
|
||||
mock_service.policies().list_next.return_value = None
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=mock_service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
assert chat.policies_fetched is True
|
||||
# Root OU policy accepted
|
||||
assert chat.policies.enable_apps is False
|
||||
assert chat.policies.enable_webhooks is True
|
||||
# Sub-OU policy skipped
|
||||
assert chat.policies.external_file_sharing is None
|
||||
|
||||
def test_chat_partial_fetch_marks_policies_fetched_false(self):
|
||||
"""Regression: if page 1 returns valid data but page 2 raises an error,
|
||||
policies_fetched must be False even though some policy values were stored."""
|
||||
mock_provider = set_mocked_googleworkspace_provider()
|
||||
mock_provider.audit_config = {}
|
||||
mock_provider.fixer_config = {}
|
||||
mock_session = MagicMock()
|
||||
mock_session.credentials = MagicMock()
|
||||
mock_provider.session = mock_session
|
||||
|
||||
mock_service = MagicMock()
|
||||
|
||||
# Page 1: returns valid Chat data
|
||||
page1_response = {
|
||||
"policies": [
|
||||
{
|
||||
"setting": {
|
||||
"type": "settings/chat.chat_apps_access",
|
||||
"value": {"enableApps": False, "enableWebhooks": False},
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
# Page 2 request raises HttpError 429
|
||||
page1_request = MagicMock()
|
||||
page1_request.execute.return_value = page1_response
|
||||
|
||||
page2_request = MagicMock()
|
||||
page2_request.execute.side_effect = HttpError(
|
||||
HttpResponse({"status": "429"}), b"Rate limit exceeded"
|
||||
)
|
||||
|
||||
mock_service.policies().list.return_value = page1_request
|
||||
mock_service.policies().list_next.return_value = page2_request
|
||||
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=mock_provider,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.googleworkspace.services.chat.chat_service.GoogleWorkspaceService._build_service",
|
||||
return_value=mock_service,
|
||||
),
|
||||
):
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
Chat,
|
||||
)
|
||||
|
||||
chat = Chat(mock_provider)
|
||||
|
||||
# Page 1 data was stored
|
||||
assert chat.policies.enable_apps is False
|
||||
# But policies_fetched must be False because page 2 failed
|
||||
assert chat.policies_fetched is False
|
||||
|
||||
def test_chat_policies_model(self):
|
||||
"""Test ChatPolicies Pydantic model"""
|
||||
from prowler.providers.googleworkspace.services.chat.chat_service import (
|
||||
ChatPolicies,
|
||||
)
|
||||
|
||||
policies = ChatPolicies(
|
||||
external_file_sharing="NO_FILES",
|
||||
internal_file_sharing="IMAGES_ONLY",
|
||||
allow_external_chat=True,
|
||||
external_chat_restriction="TRUSTED_DOMAINS",
|
||||
external_spaces_enabled=True,
|
||||
external_spaces_domain_allowlist_mode="TRUSTED_DOMAINS",
|
||||
enable_apps=False,
|
||||
enable_webhooks=False,
|
||||
)
|
||||
|
||||
assert policies.external_file_sharing == "NO_FILES"
|
||||
assert policies.internal_file_sharing == "IMAGES_ONLY"
|
||||
assert policies.allow_external_chat is True
|
||||
assert policies.external_chat_restriction == "TRUSTED_DOMAINS"
|
||||
assert policies.external_spaces_enabled is True
|
||||
assert policies.external_spaces_domain_allowlist_mode == "TRUSTED_DOMAINS"
|
||||
assert policies.enable_apps is False
|
||||
assert policies.enable_webhooks is False
|
||||
Reference in New Issue
Block a user