tests/Test-Assessment.35040.ps1
|
<#
.SYNOPSIS Validates that Communication Compliance rules are configured to monitor enterprise AI app interactions. .DESCRIPTION This test verifies that Collection Policies are configured for data ingestion, Communication Compliance rules targeting enterprise AI apps (ConnectedAIApp and/or UnifiedGenAIWorkloads) are properly configured, and at least one enabled policy with ReviewMailbox exists for alert processing. .NOTES Test ID: 35040 Category: Data Security Posture Management Pillar: Data Required Module: ExchangeOnlineManagement Required Connection: Security & Compliance PowerShell #> function Test-Assessment-35040 { [ZtTest( Category = 'Data Security Posture Management', ImplementationCost = 'Medium', MinimumLicense = ('Microsoft 365 E5'), Pillar = 'Data', RiskLevel = 'High', SfiPillar = 'Protect tenants and production systems', TenantType = ('Workforce'), TestId = 35040, Title = 'Communication compliance monitoring is configured for enterprise AI tools', UserImpact = 'Medium' )] [CmdletBinding()] param() #region Data Collection Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose $activity = 'Checking Communication Compliance for Enterprise AI Apps' # Q1: Verify Collection Policies are configured for enterprise AI app data ingestion Write-ZtProgress -Activity $activity -Status 'Checking Collection Policies' $collectionPolicies = @() $errorMsg = $null try { $featureConfig = Get-FeatureConfiguration -FeatureScenario KnowYourData -ErrorAction Stop if ($featureConfig) { foreach ($config in $featureConfig) { $activities = @() $enforcementPlanes = @() if ($config.ScenarioConfig) { try { $scenarioData = $config.ScenarioConfig | ConvertFrom-Json -ErrorAction Stop if ($scenarioData.Activities) { $activities = $scenarioData.Activities } if ($scenarioData.EnforcementPlanes) { $enforcementPlanes = $scenarioData.EnforcementPlanes } } catch { Write-PSFMessage "Error parsing ScenarioConfig JSON: $_" -Level Warning } } $collectionPolicies += [PSCustomObject]@{ PolicyName = $config.Name Enabled = $config.Enabled Mode = $config.Mode Workload = $config.Workload Activities = $activities EnforcementPlanes = $enforcementPlanes CreatedBy = $config.CreatedBy ModifiedTime = $config.ModificationTimeUtc.ToString("yyyy-MM-ddTHH:mm:ss") PolicyCategory = "ApplicableToAI" } } } } catch { $errorMsg = $_ Write-PSFMessage "Failed to retrieve Collection Policies: $_" -Tag Test -Level Warning } # Q2: Get all Communication Compliance rules and identify those targeting enterprise AI apps Write-ZtProgress -Activity $activity -Status 'Analyzing Communication Compliance rules' $enterpriseAIRules = @() if ($collectionPolicies -and -not $errorMsg) { try { $allRules = Get-SupervisoryReviewRule -IncludeRuleXml -ErrorAction Stop $allReviewPolicy = Get-SupervisoryReviewPolicyV2 -ErrorAction Stop $policyMap = @{} if ($allReviewPolicy) { $allReviewPolicy | ForEach-Object { $policyMap[$_.Guid] = $_.Name } } foreach ($rule in $allRules) { if (-not [string]::IsNullOrWhiteSpace($rule.RuleXml)) { try { # Wrap RuleXml in a root element to handle multiple rule elements $wrappedXml = "<root>$($rule.RuleXml)</root>" $ruleXml = [xml]$wrappedXml $hasEnterpriseAIConfig = $false $detectedWorkloads = @() $detectedUnifiedGenAIWorkloads = @() # Check for ConnectedAIApp and UnifiedGenAIWorkloads in JSON value elements if ($ruleXml.root) { $valueElements = $ruleXml.root.GetElementsByTagName('value') foreach ($valueElement in $valueElements) { $rawValue = $valueElement.'#text' if (-not [string]::IsNullOrWhiteSpace($rawValue)) { try { $jsonText = $rawValue.Trim() # We only need object/array JSON payloads if ($jsonText -notmatch '^[\{\[]') { continue } $jsonData = $jsonText | ConvertFrom-Json -ErrorAction Stop # Check for ConnectedAIApp in Workloads if ($jsonData.Workloads -and $jsonData.Workloads -contains 'ConnectedAIApp') { $hasEnterpriseAIConfig = $true $detectedWorkloads = $jsonData.Workloads } # Check for "ChatGPT.Enterprise", "EntraApp", "AzureAI" keywords in UnifiedGenAIWorkloads $targetWorkloads = @('ChatGPT.Enterprise', 'EntraApp', 'AzureAI') if ($jsonData.UnifiedGenAIWorkloads -and ($jsonData.UnifiedGenAIWorkloads | Where-Object { $targetWorkloads -contains $_ })) { $hasEnterpriseAIConfig = $true $detectedUnifiedGenAIWorkloads = $jsonData.UnifiedGenAIWorkloads } if ($hasEnterpriseAIConfig) { break } } catch { # Skip if JSON parsing fails } } } } if ($hasEnterpriseAIConfig) { # Extract rule names from RuleXml structure $ruleNames = @() $ruleElements = $ruleXml.root.GetElementsByTagName('rule') foreach ($ruleElement in $ruleElements) { if ($ruleElement.name) { $ruleNames += $ruleElement.name } } $ruleNameDisplay = if ($ruleNames.Count -gt 0) { $ruleNames -join ', ' } else { $rule.Name } # Lookup policy name from $allReviewPolicy using Policy ID $policyId = $rule.Policy $policyName = if ($policyMap.ContainsKey($policyId)) { $policyMap[$policyId] } else { 'Unknown' } $enterpriseAIRules += [PSCustomObject]@{ RuleName = $ruleNameDisplay PolicyId = $policyId PolicyName = $policyName Workloads = $detectedWorkloads UnifiedGenAIWorkloads = $detectedUnifiedGenAIWorkloads } } } catch { Write-PSFMessage "Error parsing RuleXml for rule '$($rule.Name)': $_" -Level Warning } } } } catch { Write-PSFMessage "Failed to retrieve supervisory review rules: $_" -Tag Test -Level Warning } } # Q3: Get enabled Communication Compliance policies with ReviewMailbox configured Write-ZtProgress -Activity $activity -Status 'Checking enabled policies' $enabledPoliciesWithReviewMailbox = @() if ($enterpriseAIRules -and -not $errorMsg) { $enabledPoliciesWithReviewMailbox = $allReviewPolicy | Where-Object { $_.Enabled -eq $true -and $null -ne $_.ReviewMailbox } } #endregion Data Collection #region Assessment Logic $passed = $false $customStatus = $null # Evaluation logic: # - Investigate if there was an error during data collection # - Fail if no Collection Policies are configured or policies are disabled # - Fail if no Communication Compliance rules target enterprise AI apps # - Fail if no enabled policies with ReviewMailbox exist # - Pass if Collection Policies are configured, rules target enterprise AI apps, and at least one enabled policy with ReviewMailbox exists if ($errorMsg) { $customStatus = 'Investigate' $testResultMarkdown = "⚠️ Unable to determine enterprise AI monitoring status due to error:`n`n$errorMsg`n`n%TestResult%" } else { $hasActiveCollectionPolicies = ($collectionPolicies | Where-Object { $_.Enabled -eq $true -and $_.Activities.Count -ge 1 }).Count -ge 1 $hasEnterpriseAIRules = $enterpriseAIRules.Count -ge 1 $hasEnabledPoliciesWithReviewMailbox = $enabledPoliciesWithReviewMailbox.Count -ge 1 if (-not $hasActiveCollectionPolicies) { $passed = $false $testResultMarkdown = "❌ No enabled collection policies found for data ingestion.`n`n%TestResult%" } elseif (-not $hasEnterpriseAIRules) { $passed = $false $testResultMarkdown = "❌ No Communication Compliance rules targeting enterprise AI apps were found.`n`n%TestResult%" } elseif (-not $hasEnabledPoliciesWithReviewMailbox) { $passed = $false $testResultMarkdown = "❌ No Communication Compliance policies are enabled with ReviewMailbox configured, creating a gap where enterprise AI data exposure and policy violations cannot be detected and investigated.`n`n%TestResult%" } else { $passed = $true $testResultMarkdown = "✅ Collection Policies are configured for data ingestion, Communication Compliance rules are configured to target enterprise AI apps (ConnectedAIApp and/or UnifiedGenAIWorkloads identified in RuleXml), AND at least one Communication Compliance policy is ENABLED with a ReviewMailbox configured, enabling the organization to detect and investigate unauthorized data sharing and policy violations through enterprise AI interactions.`n`n%TestResult%" } } #endregion Assessment Logic #region Report Generation $mdInfo = '' if(-not $errorMsg){ # Portal Links $tenantId = (Get-MgContext).TenantId $collectionPoliciesLink = "https://purview.microsoft.com/cc/dataclassification/dataandactivitydiscovery?tid=$tenantId" $compliancePoliciesLink = "https://purview.microsoft.com/cc/policies?tid=$tenantId" # Build Collection Policies table rows $collectionTableRows = '' if ($collectionPolicies.Count -gt 0) { foreach ($policy in $collectionPolicies | Sort-Object PolicyName) { $enabledStatus = if ($policy.Enabled) { '✅ True' } else { '❌ False' } $modeStatus = if ($policy.Mode -eq 'Enable') { '✅ Enable' } else { '❌ Disable' } $activitiesDisplay = if ($policy.Activities.Count -gt 0) { ($policy.Activities -join ', ') } else { 'None' } $enforcementDisplay = if ($policy.EnforcementPlanes.Count -gt 0) { ($policy.EnforcementPlanes -join ', ') } else { 'None' } $modifiedDisplay = Get-FormattedDate -DateString $policy.ModifiedTime $collectionTableRows += "| $($policy.PolicyName) | $enabledStatus | $modeStatus | $($policy.Workload) | $activitiesDisplay | $enforcementDisplay | $($policy.CreatedBy) | $modifiedDisplay | $($policy.PolicyCategory) |`n" } } # Build Enterprise AI Rules table rows $rulesTableRows = '' if ($enterpriseAIRules.Count -gt 0) { foreach ($rule in $enterpriseAIRules | Sort-Object RuleName) { $workloadsDisplay = if ($rule.Workloads.Count -gt 0) { $rule.Workloads -join ', ' } else { 'None' } $unifiedGenAIDisplay = if ($rule.UnifiedGenAIWorkloads.Count -gt 0) { $rule.UnifiedGenAIWorkloads -join ', ' } else { 'None' } $rulesTableRows += "| $($rule.RuleName) | $($rule.PolicyName) | $workloadsDisplay | $unifiedGenAIDisplay |`n" } } # Build Enabled Policies table rows $policiesTableRows = '' if ($enabledPoliciesWithReviewMailbox.Count -gt 0) { foreach ($policy in $enabledPoliciesWithReviewMailbox | Sort-Object Name) { $enabledStatus = if ($policy.Enabled -eq $true) { '✅ True' } else { '❌ False' } $reviewMailbox = if ($policy.ReviewMailbox) { "✅ $($policy.ReviewMailbox)" } else { '❌ Not configured' } $policiesTableRows += "| $($policy.Name) | $enabledStatus | $reviewMailbox |`n" } } # Build Collection Policies section $collectionSection = '' if ($collectionPolicies.Count -gt 0) { $collectionSection = @" ### [Data ingestion layer (Collection policies)]($collectionPoliciesLink) | Policy name | Enabled | Mode | Workload | Activities | Enforcement planes | Created by | Last modified | Policy category | | :---------- | :------ | :--- | :------- | :--------- | :----------------- | :--------- | :------------ | :-------------- | $collectionTableRows "@ } # Build Enterprise AI Rules section # Always show this section when Q2 ran (no error and collection policies exist) $rulesSection = '' if ($collectionPolicies.Count -gt 0) { if ($enterpriseAIRules.Count -gt 0) { $rulesSection = @" ### [Communication Compliance rules targeting Enterprise AI Apps]($compliancePoliciesLink) | Rule name | Associated policy | Workloads | UnifiedGenAIWorkloads | | :-------- | :---------------- | :-------- | :-------------------- | $rulesTableRows "@ } else { $rulesSection = @" ### [Communication Compliance rules targeting Enterprise AI Apps]($compliancePoliciesLink) No Enterprise AI rules found. "@ } } # Build Enabled Policies section $policiesSection = '' if ($enabledPoliciesWithReviewMailbox.Count -gt 0) { $policiesSection = @" ### [Enabled policies with review mailbox]($compliancePoliciesLink) | Policy name | Enabled | Review mailbox | | :---------- | :------ | :------------- | $policiesTableRows "@ } else { $policiesSection = @" ### [Enabled policies with review mailbox]($compliancePoliciesLink) No enabled policies with review mailbox configured were found. "@ } $formatTemplate = @' {0}{1}{2} **Summary:** - Collection Policies Configured: {3} - Enterprise AI Rules Detected (with ConnectedAIApp or UnifiedGenAIWorkloads): {4} - Policies Enabled with ReviewMailbox: {5} '@ $mdInfo = $formatTemplate -f $collectionSection, $rulesSection, $policiesSection, $collectionPolicies.Count, $enterpriseAIRules.Count, $enabledPoliciesWithReviewMailbox.Count } # Replace placeholder with generated markdown $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo #endregion Report Generation $params = @{ TestId = '35040' Title = 'Communication compliance monitoring is configured for enterprise AI tools' Status = $passed Result = $testResultMarkdown } if ($null -ne $customStatus) { $params.CustomStatus = $customStatus } Add-ZtTestResultDetail @params } |