tests/Test-Assessment.27000.ps1
|
<#
.SYNOPSIS Checks if high-risk web content filtering categories (Criminal activity, Hacking, Illegal software) are blocked. .DESCRIPTION This check evaluates whether Global Secure Access web content filtering policies block three high-risk Liability categories: Criminal activity, Hacking, and Illegal software. It validates that the blocking is effective through either the baseline profile or security profiles with active Conditional Access enforcement. .NOTES Test ID: 27000 Category: Global Secure Access Risk Level: High #> function Test-Assessment-27000 { [ZtTest( Category = 'Global Secure Access', ImplementationCost = 'Low', Service = ('Graph'), MinimumLicense = ('Entra_Premium_Internet_Access'), Pillar = 'Network', RiskLevel = 'High', SfiPillar = 'Protect networks', TenantType = ('Workforce'), TestId = 27000, Title = 'Web content filtering blocks high-risk categories', UserImpact = 'Low' )] [CmdletBinding()] param() #region Helper Functions function New-FailedCategoryResults { <# .SYNOPSIS Creates failed category results when policies or profiles are missing. #> param( [Parameter(Mandatory)] [array]$RequiredCategories, [Parameter(Mandatory)] [hashtable]$CategoryDisplayNames ) $results = @() foreach ($catName in $RequiredCategories) { $results += [PSCustomObject]@{ Category = $CategoryDisplayNames[$catName] EnforcedBy = 'None' CAEnforced = 'N/A' Status = 'Not Blocked' } } return $results } function Get-CategoryBlockStatus { <# .SYNOPSIS Evaluates whether a specific WCF category is blocked through an effective profile. .DESCRIPTION Finds policies covering the category, identifies linked profiles, and determines the effective profile based on priority and CA enforcement criteria. #> param( [Parameter(Mandatory)] [string]$CategoryName, [Parameter(Mandatory)] [string]$CategoryDisplayName, [Parameter(Mandatory)] [array]$FilteringPolicies, [Parameter(Mandatory)] [array]$FilteringProfiles, [Parameter(Mandatory)] [AllowNull()] [array]$CAPolicies, [Parameter(Mandatory)] [int]$BaselinePriority ) # Find all policies that cover this category $policiesCoveringCategory = @($FilteringPolicies | Where-Object { $policy = $_ $webCatRules = @($policy.policyRules | Where-Object { $_.ruleType -eq 'webCategory' }) $webCatRules | Where-Object { $_.destinations | Where-Object { $_.name -eq $CategoryName } } }) # Collect profile candidates from all matching policies $profileCandidates = @() foreach ($policy in $policiesCoveringCategory) { $findParams = @{ PolicyId = $policy.id FilteringProfiles = $FilteringProfiles CAPolicies = $CAPolicies BaselinePriority = $BaselinePriority PolicyLinkType = 'filteringPolicyLink' PolicyRules = @($policy.policyRules) } $linkedProfiles = Find-ZtProfilesLinkedToPolicy @findParams foreach ($linkedProfile in $linkedProfiles) { # Skip disabled profiles if ($linkedProfile.ProfileState -ne 'enabled') { Write-PSFMessage "Skipping disabled profile '$($linkedProfile.ProfileName)'" -Level Verbose continue } # Get the profile object to access policies collection $filteringProfile = $FilteringProfiles | Where-Object { $_.id -eq $linkedProfile.ProfileId } if (-not $filteringProfile) { Write-PSFMessage "Profile '$($linkedProfile.ProfileName)' not found in filteringProfiles collection" -Level Warning continue } # Find the policy link to get priority and action foreach ($policyLink in $filteringProfile.policies) { if ($policyLink.policy.id -ne $policy.id) { continue } # Skip disabled policy links if ($policyLink.state -ne 'enabled') { Write-PSFMessage "Skipping disabled policy link in profile '$($linkedProfile.ProfileName)' for policy '$($policy.name)'" -Level Verbose continue } $linkPriority = try { [int]$policyLink.priority } catch { [int]::MaxValue } # Use policy action directly (not overridden at profile level) $linkAction = if ($policyLink.policy.action) { $policyLink.policy.action.ToString().ToLower() } else { Write-PSFMessage "Policy action is null for policy '$($policy.name)' - defaulting to 'unknown'" -Level Warning 'unknown' } $profileCandidates += [PSCustomObject]@{ ProfileId = $linkedProfile.ProfileId ProfileName = $linkedProfile.ProfileName ProfilePriority= $linkedProfile.ProfilePriority IsBaseline = ($linkedProfile.ProfileType -eq 'Baseline Profile') PolicyAction = $linkAction PolicyPriority = $linkPriority PassesCriteria = $linkedProfile.PassesCriteria } } } } # Sort by profile priority, then policy priority $profileCandidates = @($profileCandidates | Sort-Object ProfilePriority, PolicyPriority) # Find effective profile per spec logic $effectiveProfileName = 'None' $caEnforced = 'N/A' $status = 'Not blocked' foreach ($pc in $profileCandidates) { if ($pc.IsBaseline) { # Baseline profile is always effective $effectiveProfileName = $pc.ProfileName $caEnforced = 'N/A' $status = if ($pc.PolicyAction -eq 'block') { 'Blocked' } else { 'Not blocked' } break } else { # Security profile - check if it passes CA enforcement criteria if ($pc.PassesCriteria) { $effectiveProfileName = $pc.ProfileName $caEnforced = 'Yes' $status = if ($pc.PolicyAction -eq 'block') { 'Blocked' } else { 'Not blocked' } break } } } return [PSCustomObject]@{ Category = $CategoryDisplayName EnforcedBy = $effectiveProfileName CAEnforced = $caEnforced Status = $status } } #endregion Helper Functions #region Data Collection Write-PSFMessage '🟦 Start high-risk WCF category block evaluation' -Tag Test -Level VeryVerbose $activity = 'Checking high-risk WCF categories are blocked' $filteringPolicies = $null $filteringProfiles = $null $caPolicies = $null $errorMsg = $null try { # Q1: Get all filtering policies with policy rules Write-ZtProgress -Activity $activity -Status 'Getting filtering policies' $filteringPolicies = Invoke-ZtGraphRequest -RelativeUri 'networkAccess/filteringPolicies' -QueryParameters @{ '$expand' = 'policyRules' } -ApiVersion beta -ErrorAction Stop Write-PSFMessage "Found $($filteringPolicies.Count) filtering policies" -Level Verbose # Q2: Get all filtering profiles with expanded policies (following 25407 pattern) if ($filteringPolicies -and $filteringPolicies.Count -gt 0) { Write-ZtProgress -Activity $activity -Status 'Getting filtering profiles' $filteringProfiles = Invoke-ZtGraphRequest -RelativeUri 'networkAccess/filteringProfiles' -QueryParameters @{ '$expand' = 'policies($expand=policy)' } -ApiVersion beta -ErrorAction Stop Write-PSFMessage "Found $($filteringProfiles.Count) filtering profiles" -Level Verbose } # Q3: Get CA policies (following 25407 pattern) if ($filteringPolicies -and $filteringPolicies.Count -gt 0 -and $filteringProfiles -and $filteringProfiles.Count -gt 0) { Write-ZtProgress -Activity $activity -Status 'Getting Conditional Access policies' $caPolicies = Get-ZtConditionalAccessPolicy } } catch { $errorMsg = $_ Write-PSFMessage "Failed to retrieve data: $_" -Level Error } #endregion Data Collection #region Assessment Logic # Required categories to check $requiredCategories = @('CriminalActivity', 'Hacking', 'IllegalSoftware') $categoryDisplayNames = @{ 'CriminalActivity' = 'Criminal activity' 'Hacking' = 'Hacking' 'IllegalSoftware' = 'Illegal software' } # Early validation: Check if we have the required data to proceed $passed = $false $categoryResults = @() if($errorMsg) { # Error occurred during data collection, cannot proceed with assessment -> Fail Write-PSFMessage "Error during data collection: $errorMsg" -Level Error $testResultMarkdown = "❌ Failed to retrieve necessary data for assessment.`n`nError: $errorMsg" } elseif(-not $filteringPolicies -or $filteringPolicies.Count -eq 0 ){ Write-PSFMessage "No WCF policies found -> Fail" -Level Warning $categoryResults = New-FailedCategoryResults -RequiredCategories $requiredCategories -CategoryDisplayNames $categoryDisplayNames $blockedCount = 0 $notBlockedCount = $requiredCategories.Count } elseif ($filteringPolicies -and $filteringPolicies.count -eq 1 -and $filteringPolicies[0].name -eq 'All Websites'){ Write-PSFMessage "Only default 'All Websites' policy exists -> Fail" -Level Warning $categoryResults = New-FailedCategoryResults -RequiredCategories $requiredCategories -CategoryDisplayNames $categoryDisplayNames $blockedCount = 0 $notBlockedCount = $requiredCategories.Count } elseif (-not $filteringProfiles -or $filteringProfiles.Count -eq 0) { Write-PSFMessage "No filtering profiles found -> Fail" -Level Warning $categoryResults = New-FailedCategoryResults -RequiredCategories $requiredCategories -CategoryDisplayNames $categoryDisplayNames $blockedCount = 0 $notBlockedCount = $requiredCategories.Count } else { [int]$BASELINE_PROFILE_PRIORITY = 65000 # Evaluate each category using the helper function foreach ($catName in $requiredCategories) { $catDisplay = $categoryDisplayNames[$catName] $getCategoryParams = @{ CategoryName = $catName CategoryDisplayName = $catDisplay FilteringPolicies = $filteringPolicies FilteringProfiles = $filteringProfiles CAPolicies = $caPolicies BaselinePriority = $BASELINE_PROFILE_PRIORITY } $categoryResult = Get-CategoryBlockStatus @getCategoryParams $categoryResults += $categoryResult } # Determine pass/fail $blockedCount = @($categoryResults | Where-Object { $_.Status -eq 'Blocked' }).Count $notBlockedCount = $requiredCategories.Count - $blockedCount $passed = $blockedCount -eq $requiredCategories.Count } #endregion Assessment Logic #region Report Generation # Only generate full report if we have category results if ($errorMsg) { # Error message already set, no table needed } else { if ($passed) { $testResultMarkdown = "✅ High-risk web content filtering categories (Criminal activity, Hacking, Illegal software) are blocked across enabled security profiles, protecting users from liability risks and malicious content.`n`n%TestResult%" } else { $testResultMarkdown = "❌ One or more high-risk web content filtering categories (Criminal activity, Hacking, Illegal software) are not blocked. Configure web content filtering policies to block these Liability categories to protect against security risks and policy violations.`n`n%TestResult%" } $reportTitle = 'Web Content Filtering – Category block status' $portalLink = 'https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/WebFilteringPolicy.ReactView' # Build table with exactly 4 columns as per spec $tableRows = '' foreach ($row in $categoryResults) { $categoryName = Get-SafeMarkdown -Text $row.Category $enforcedBy = Get-SafeMarkdown -Text $row.EnforcedBy $statusIcon = if ($row.Status -eq 'Blocked') { '✅ Blocked' } else { '❌ Not blocked' } $tableRows += "| $categoryName | $enforcedBy | $($row.CAEnforced) | $statusIcon |`n" } $formatTemplate = @' ## [{0}]({1}) | Category | Enforced by | CA enforced | Status | | :------- | :---------- | :---------- | :----- | {2} **Summary:** - Total required categories: {3} - Categories blocked: {4} - Categories not blocked: {5} '@ $mdInfo = $formatTemplate -f $reportTitle, $portalLink, $tableRows, $requiredCategories.Count, $blockedCount, $notBlockedCount $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo } #endregion Report Generation $params = @{ TestId = '27000' Title = 'Web content filtering blocks high-risk categories' Status = $passed Result = $testResultMarkdown } Add-ZtTestResultDetail @params } |