Skip to content

Commit 2e52cf9

Browse files
authored
Network - 27028 - A web content filtering policy governs Copilot Studio agent traffic through the baseline profile (#1508)
2 parents 480e41e + 210f347 commit 2e52cf9

2 files changed

Lines changed: 160 additions & 0 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Forwarding Copilot Studio agent traffic to Global Secure Access provides visibility but doesn't restrict the web destinations agents can reach unless a web content filtering policy is linked to the baseline profile. The baseline profile is the supported enforcement path for agent traffic; security profiles linked to Conditional Access policies aren't supported for agents. Without an enabled, administrator-configured web content filtering policy on the baseline profile, an agent's HTTP node action or connector can reach web categories and URLs that the organization intended to block, giving a compromised or manipulated agent an unrestricted path to retrieve payloads, contact command-and-control infrastructure, or transmit data to an external destination. Linking the Copilot Studio web content filtering policy to the enabled baseline profile applies those restrictions tenant-wide to forwarded agent traffic.
2+
3+
**Remediation action**
4+
5+
- [Configure Secure Web and AI Gateway for Microsoft Copilot Studio agents](https://learn.microsoft.com/entra/global-secure-access/how-to-secure-web-ai-gateway-agents?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci) - Create a web content filtering policy for agent requirements and link it to the baseline profile.
6+
- [Global Secure Access for Copilot Studio agents](https://learn.microsoft.com/power-platform/admin/security/secure-web-ai-gateway-agents?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci) - Follow the Power Platform guidance for creating the Copilot Studio agent web repositories policy and linking it to the baseline profile.
7+
- Review the linked policy in the Microsoft Entra admin center under **Global Secure Access** > **Secure** > **Security profiles** > **Baseline profile**.
8+
9+
<!--- Results --->
10+
%TestResult%
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<#
2+
.SYNOPSIS
3+
A web content filtering policy governs Copilot Studio agent traffic through the baseline profile
4+
5+
.DESCRIPTION
6+
Evaluates whether the Global Secure Access baseline profile is enabled and linked to an enabled,
7+
administrator-configured web content filtering policy. The baseline profile is the only supported
8+
enforcement path for Copilot Studio agent traffic, so without such a policy an agent's HTTP node
9+
action or connector can reach web destinations the organization intended to block.
10+
11+
.NOTES
12+
Test ID: 27028
13+
Pillar: Network
14+
Risk Level: High
15+
SFI Pillar: Protect networks
16+
Required API: networkAccess/filteringProfiles (beta)
17+
#>
18+
19+
function Test-Assessment-27028 {
20+
[ZtTest(
21+
Category = 'AI Gateway',
22+
ImplementationCost = 'Medium',
23+
Service = ('Graph'),
24+
CompatibleLicense = ('Entra_Premium_Internet_Access'),
25+
Pillar = 'Network',
26+
RiskLevel = 'High',
27+
SfiPillar = 'Protect networks',
28+
TenantType = ('Workforce'),
29+
TestId = 27028,
30+
Title = 'A web content filtering policy governs Copilot Studio agent traffic through the baseline profile',
31+
UserImpact = 'Medium'
32+
)]
33+
[CmdletBinding()]
34+
param()
35+
36+
# The baseline profile is identified by its fixed priority and is the only profile supported for agent traffic.
37+
[int]$baselineProfilePriority = 65000
38+
# Allow-all placeholder policy that ships with every tenant; it isn't an administrator-configured restriction.
39+
[string]$defaultPolicyName = 'All websites'
40+
41+
#region Data Collection
42+
Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose
43+
44+
$activity = 'Evaluating web content filtering on the Global Secure Access baseline profile'
45+
Write-ZtProgress -Activity $activity -Status 'Querying filtering profiles'
46+
47+
# Q1: Get the baseline profile and its linked policies
48+
$filteringProfiles = @()
49+
$errorMsg = $null
50+
$httpStatusCode = $null
51+
52+
try {
53+
$filteringProfiles = Invoke-ZtGraphRequest -RelativeUri 'networkAccess/filteringProfiles' -QueryParameters @{
54+
'$filter' = "priority eq $baselineProfilePriority"
55+
'$select' = 'id,name,state,priority'
56+
'$expand' = 'policies($select=id,state;$expand=policy($select=id,name,version))'
57+
} -ApiVersion beta -ErrorAction Stop
58+
}
59+
catch {
60+
$errorMsg = $_
61+
$httpStatusCode = Get-ZtHttpStatusCode -ErrorRecord $_
62+
Write-PSFMessage "Failed to retrieve filtering profiles (HTTP $httpStatusCode): $errorMsg" -Tag Test -Level Warning
63+
}
64+
#endregion Data Collection
65+
66+
#region Assessment Logic
67+
$passed = $false
68+
$customStatus = $null
69+
$testResultMarkdown = ''
70+
$baselineState = 'Not found'
71+
$enabledPolicyNames = @()
72+
73+
if ($httpStatusCode -eq 404) {
74+
# The filtering profile resource is unavailable, so the required enforcement path is absent: same outcome as an empty result.
75+
Write-PSFMessage 'Global Secure Access filtering profiles are not available in this tenant.' -Tag Test -Level Verbose
76+
}
77+
78+
if ($errorMsg -and $httpStatusCode -ne 404) {
79+
$customStatus = 'Investigate'
80+
$testResultMarkdown = if ($httpStatusCode -in 401, 403) {
81+
'⚠️ Unable to read the Global Secure Access baseline profile due to insufficient permissions. Grant the **NetworkAccess.Read.All** Microsoft Graph permission and assign the **Global Secure Access Administrator** or **Security Reader** role, then rerun the assessment.'
82+
}
83+
else {
84+
'⚠️ Unable to retrieve the Global Secure Access filtering profiles due to an API error. Please rerun the assessment.'
85+
}
86+
}
87+
else {
88+
$baselineProfile = $filteringProfiles | Where-Object { $_.priority -eq $baselineProfilePriority } | Select-Object -First 1
89+
90+
if ($baselineProfile) {
91+
$baselineState = $baselineProfile.state
92+
$enabledPolicyNames = @($baselineProfile.policies | Where-Object {
93+
$_.'@odata.type' -eq '#microsoft.graph.networkaccess.filteringPolicyLink' -and
94+
$_.state -eq 'enabled' -and
95+
$_.policy.name -and
96+
$_.policy.name -ne $defaultPolicyName
97+
} | ForEach-Object { $_.policy.name })
98+
}
99+
100+
$passed = $baselineState -eq 'enabled' -and $enabledPolicyNames.Count -gt 0
101+
102+
if ($passed) {
103+
$testResultMarkdown = "✅ The Global Secure Access baseline profile is enabled and linked to an administrator-configured web content filtering policy that governs Copilot Studio agent traffic.`n`n%TestResult%"
104+
}
105+
else {
106+
$testResultMarkdown = "❌ The Global Secure Access baseline profile isn't enabled or lacks an enabled administrator-configured web content filtering policy, leaving Copilot Studio agent web traffic unrestricted by that policy.`n`n%TestResult%"
107+
}
108+
}
109+
#endregion Assessment Logic
110+
111+
#region Report Generation
112+
$mdInfo = ''
113+
114+
if (-not $customStatus) {
115+
$baselineStateDisplay = if ($baselineState -eq 'enabled') { '✅ Enabled' } else { "$baselineState" }
116+
$policyNamesDisplay = if ($enabledPolicyNames.Count -gt 0) {
117+
($enabledPolicyNames | Sort-Object -Unique | ForEach-Object { Get-SafeMarkdown $_ }) -join ', '
118+
}
119+
else {
120+
'None'
121+
}
122+
$statusDisplay = if ($passed) { '✅ Pass' } else { '❌ Fail' }
123+
124+
$formatTemplate = @'
125+
## [Global Secure Access Security Profiles]({0})
126+
127+
| Baseline profile state | Enabled web content filtering policy name(s) | Status |
128+
| :--------------------- | :------------------------------------------- | :----- |
129+
{1}
130+
'@
131+
132+
$portalLink = 'https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/FilteringPolicyProfiles.ReactView'
133+
$tableRows = "| $baselineStateDisplay | $policyNamesDisplay | $statusDisplay |`n"
134+
$mdInfo = $formatTemplate -f $portalLink, $tableRows
135+
}
136+
137+
$testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo
138+
#endregion Report Generation
139+
140+
$params = @{
141+
TestId = '27028'
142+
Title = 'A web content filtering policy governs Copilot Studio agent traffic through the baseline profile'
143+
Status = $passed
144+
Result = $testResultMarkdown
145+
}
146+
if ($customStatus) {
147+
$params.CustomStatus = $customStatus
148+
}
149+
Add-ZtTestResultDetail @params
150+
}

0 commit comments

Comments
 (0)