fix: align dashboard policy scopes - #6092
Conversation
🦋 Changeset detectedLatest commit: c5068b8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Running ultrareview automatically — This PR changes how policy scopes are computed and saved, introducing new CEL encoding/decoding logic and shifting the API payload from messageTypes to detectionScopes. A subtle bug here could cause policies to be misconfigured, leading to undetected security issues or over-blocking, and the.... I'll post findings when complete. |
There was a problem hiding this comment.
Ultrareview completed in 13m 39s
All reported issues were addressed across 5 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai review this PR |
@vishalg0wda I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
There was a problem hiding this comment.
2 issues found and verified against the latest diff
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="client/dashboard/src/pages/security/PolicyCenter.tsx">
<violation number="1" location="client/dashboard/src/pages/security/PolicyCenter.tsx:964">
P2: When a scope contains non-decodable CEL and also has decodable message kinds, the Applies To column shows the kinds plus `+ custom CEL` instead of `Custom scope`. Use the required `Custom scope` summary whenever `scope.custom` is true.</violation>
</file>
<file name="client/dashboard/src/pages/setup/components/steps/configure-policies-step.tsx">
<violation number="1" location="client/dashboard/src/pages/setup/components/steps/configure-policies-step.tsx:359">
P1: When the flag is enabled, the wizard hides the legacy narrowing that the scanner still enforces. According to linked Linear issue AIS-678, legacy `messageTypes` remains a hard prefilter until migration; intersect it with the category scope, including when no category override exists, before populating these controls.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| if (recommendedScopesEnabled) { | ||
| serverMessageTypes = | ||
| effectiveScope === null || effectiveScope.custom | ||
| ? new Set(next[cat].messageTypes) |
There was a problem hiding this comment.
P1: When the flag is enabled, the wizard hides the legacy narrowing that the scanner still enforces. According to linked Linear issue AIS-678, legacy messageTypes remains a hard prefilter until migration; intersect it with the category scope, including when no category override exists, before populating these controls.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/setup/components/steps/configure-policies-step.tsx, line 356:
<comment>When the flag is enabled, the wizard hides the legacy narrowing that the scanner still enforces. According to linked Linear issue AIS-678, legacy `messageTypes` remains a hard prefilter until migration; intersect it with the category scope, including when no category override exists, before populating these controls.</comment>
<file context>
@@ -327,13 +343,26 @@ export function ConfigurePoliciesStep({
+ if (recommendedScopesEnabled) {
+ serverMessageTypes =
+ effectiveScope === null || effectiveScope.custom
+ ? new Set(next[cat].messageTypes)
+ : new Set(effectiveScope.kinds);
+ } else if (existing.messageTypes?.length) {
</file context>
| } else { | ||
| summary = labels.join(", "); | ||
| } | ||
| if (scope.custom && labels.length > 0) summary += " + custom CEL"; |
There was a problem hiding this comment.
P2: When a scope contains non-decodable CEL and also has decodable message kinds, the Applies To column shows the kinds plus + custom CEL instead of Custom scope. Use the required Custom scope summary whenever scope.custom is true.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/security/PolicyCenter.tsx, line 964:
<comment>When a scope contains non-decodable CEL and also has decodable message kinds, the Applies To column shows the kinds plus `+ custom CEL` instead of `Custom scope`. Use the required `Custom scope` summary whenever `scope.custom` is true.</comment>
<file context>
@@ -895,31 +919,67 @@ function PolicyCenterContent() {
+ } else {
+ summary = labels.join(", ");
+ }
+ if (scope.custom && labels.length > 0) summary += " + custom CEL";
+
+ let tooltipSummary = labels.join(", ");
</file context>
| if (scope.custom && labels.length > 0) summary += " + custom CEL"; | |
| if (scope.custom) summary = "Custom scope"; |
There was a problem hiding this comment.
3 issues found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="client/dashboard/src/pages/security/PolicyCenter.tsx">
<violation number="1" location="client/dashboard/src/pages/security/PolicyCenter.tsx:922">
P2: When the category query fails after a prior successful fetch, `useQuery` keeps cached `categoriesData` while setting `categoriesError`, so this guard is bypassed and Applies To renders a scope from failed/stale definitions. Include `categoriesError` in the guard so the display shows `Scope unavailable` on every category-query error.
(Based on your team's feedback about holding Applies To when category definitions are loading or erroring.)</violation>
</file>
<file name="client/dashboard/src/pages/setup/components/steps/configure-policies-step.tsx">
<violation number="1" location="client/dashboard/src/pages/setup/components/steps/configure-policies-step.tsx:306">
P1: When the rollout flag is loading or unavailable, editing an existing scoped policy clears its `detectionScopes` because every non-enabled status selects the legacy persistence path. Hold persistence until the flag resolves, or distinguish resolved disabled from unavailable states instead of treating all non-enabled statuses as legacy mode.</violation>
</file>
<file name="client/dashboard/src/pages/security/policy-scope.ts">
<violation number="1" location="client/dashboard/src/pages/security/policy-scope.ts:136">
P2: When `scopeInclude` is a valid noncanonical `kind in` list, this fallback treats it as allowing prompt attachments. Preserve the legacy list parser so `Applies To` excludes `prompt_attachment` whenever the list does not contain it.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| disabledRules: existing.disabledRules, | ||
| customRuleIds: existing.customRuleIds ?? [], | ||
| messageTypes: [...nextCfg.messageTypes], | ||
| ...(recommendedScopesEnabled |
There was a problem hiding this comment.
P1: When the rollout flag is loading or unavailable, editing an existing scoped policy clears its detectionScopes because every non-enabled status selects the legacy persistence path. Hold persistence until the flag resolves, or distinguish resolved disabled from unavailable states instead of treating all non-enabled statuses as legacy mode.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/setup/components/steps/configure-policies-step.tsx, line 306:
<comment>When the rollout flag is loading or unavailable, editing an existing scoped policy clears its `detectionScopes` because every non-enabled status selects the legacy persistence path. Hold persistence until the flag resolves, or distinguish resolved disabled from unavailable states instead of treating all non-enabled statuses as legacy mode.</comment>
<file context>
@@ -297,14 +303,21 @@ export function ConfigurePoliciesStep({
- ...kindScopeForMessageTypes([...nextCfg.messageTypes]),
- },
- ),
+ ...(recommendedScopesEnabled
+ ? {
+ messageTypes: [],
</file context>
| typeSet.size === ALL_POLICY_MESSAGE_TYPES.length || | ||
| hasOnlyToolCallMessageTypes(typeSet) | ||
| ) { | ||
| if (recommendedScopesEnabled && !categoriesData) { |
There was a problem hiding this comment.
P2: When the category query fails after a prior successful fetch, useQuery keeps cached categoriesData while setting categoriesError, so this guard is bypassed and Applies To renders a scope from failed/stale definitions. Include categoriesError in the guard so the display shows Scope unavailable on every category-query error.
(Based on your team's feedback about holding Applies To when category definitions are loading or erroring.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/security/PolicyCenter.tsx, line 922:
<comment>When the category query fails after a prior successful fetch, `useQuery` keeps cached `categoriesData` while setting `categoriesError`, so this guard is bypassed and Applies To renders a scope from failed/stale definitions. Include `categoriesError` in the guard so the display shows `Scope unavailable` on every category-query error.
(Based on your team's feedback about holding Applies To when category definitions are loading or erroring.) </comment>
<file context>
@@ -915,6 +919,16 @@ function PolicyCenterContent() {
header: "Applies To",
width: "2.1fr",
render: (row) => {
+ if (recommendedScopesEnabled && !categoriesData) {
+ return (
+ <span className="text-muted-foreground text-sm">
</file context>
| if (recommendedScopesEnabled && !categoriesData) { | |
| if (recommendedScopesEnabled && (categoriesError || !categoriesData)) { |
|
|
||
| function promptAttachmentScopeMatch(cel: string): boolean | null { | ||
| const decoded = decodeEffectiveKindScope(cel); | ||
| return decoded ? decoded.includes("prompt_attachment") : null; |
There was a problem hiding this comment.
P2: When scopeInclude is a valid noncanonical kind in list, this fallback treats it as allowing prompt attachments. Preserve the legacy list parser so Applies To excludes prompt_attachment whenever the list does not contain it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/dashboard/src/pages/security/policy-scope.ts, line 136:
<comment>When `scopeInclude` is a valid noncanonical `kind in` list, this fallback treats it as allowing prompt attachments. Preserve the legacy list parser so `Applies To` excludes `prompt_attachment` whenever the list does not contain it.</comment>
<file context>
@@ -112,34 +126,14 @@ export function decodeKindScope(cel: string): PolicyMessageType[] | null {
- return null;
+function promptAttachmentScopeMatch(cel: string): boolean | null {
+ const decoded = decodeEffectiveKindScope(cel);
+ return decoded ? decoded.includes("prompt_attachment") : null;
}
</file context>
| return decoded ? decoded.includes("prompt_attachment") : null; | |
| if (decoded) return decoded.includes("prompt_attachment"); | |
| if (cel.startsWith("kind in ")) { | |
| try { | |
| const parsed: unknown = JSON.parse(cel.slice("kind in ".length)); | |
| if ( | |
| !Array.isArray(parsed) || | |
| !parsed.every((value) => typeof value === "string") | |
| ) { | |
| return null; | |
| } | |
| return parsed.includes("prompt_attachment"); | |
| } catch { | |
| return null; | |
| } | |
| } | |
| return null; |

https://linear.app/speakeasy/issue/AIS-678
Summary
Align policy scope persistence and display so the dashboard matches effective scanner behavior.
Motivation
The wizard wrote a legacy hard prefilter while the editor used per-category scopes, permanently narrowing new policies and making the list contradictory.
Summary by cubic
Fixes AIS-678 by making the Policy Center’s “Applies To” value match the scanner’s effective scope instead of reading only legacy
messageTypes. With therisk-recommended-scopesflag enabled, onboarding saves selections as per-categorydetectionScopes; legacy mode remains unchanged and prevents empty scopes.messageTypesand CEL narrowing, and shows “Nothing in scope” or “Custom scope” when needed.Written for commit c5068b8. Summary will update on new commits.