fix(selectors): rebind Copilot principals for nested domain use cases - #8397
Conversation
Selectors backed by another domain (knowledge documents, table columns, workflows, sandboxes, MCP tools) forwarded the Copilot principal admitted under the selector audience into use cases that accept only their own audience. The refusal is a DelegatedWorkspaceAuthorizationError, which the v2 surface conceals as 404, so Chat saw "Workspace not found" on workspaces the user administers. Fork sync previews hit it whenever a saved dependent value (a table conflict column, a knowledge document) needed validation. Selectors now rebind through bindCopilotWorkspaceOperation, which can also project the single resource a nested call reaches, as the executor and Chat MCP paths do when they mint. A grant already narrowed to one resource is never moved to another. Managed MCP connections now reach their credential-group rule and return its 403 instead of the concealed 404. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
Tables honor resourceScope.tableId, so the nested read names the table it reads, as the MCP selector names its server or connection. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@cubic-dev-ai review this PR |
@icecrasher321 I have started the AI code review. It will take a few minutes to complete. |
Summary
Chat reported
404 "Workspace not found"when previewing a fork push or pull (sim workspaces push-preview/pull-preview), even though the user was an admin of both workspaces and direct reads of each succeeded.Root cause: the fork sync preview re-validates saved dependent values (for example a Table block's conflict column or a Knowledge block's document) through selectors. Selectors backed by another domain (
knowledge.documents,table.columns,table.outputColumns,sim.workflows,workspace.sandboxes,mcp.tools) forwarded the Copilot principal, admitted under thesim:selectorsaudience, straight into use cases that only accept their own audience (sim:knowledge,sim:tables, ...). The refusal is aDelegatedWorkspaceAuthorizationError, which the v2 surface conceals as a 404, so Chat saw a missing workspace. Session callers (the in-app sync view) were unaffected. The same bug broke those pickers for Chat everywhere (sim selectors list), not just in fork previews.Fix:
bindCopilotWorkspaceOperation(the existing nested-operation helper used by workflow lint and tool inspection) can now also project the single resource a nested call reaches onto the derived principal. This matches how the executor and Chat MCP paths already mint ({ credentialId }for a managed connection,{ mcpServerId }for a shared server). It only adds scope: a grant already narrowed to one resource is never moved to another.nestedSelectorPrincipalinlib/selectors/server/types.tswraps it with the selector source audience (SELECTOR_DELEGATION_AUDIENCE, now a named constant). Every internal and MCP selector that calls another domain's use case goes through it. Session-only selectors are unchanged.mcp.toolsmanaged branch now names the connection, so Chat reaches the credential-group rule. That rule deliberately denies Chat on credentials without an OAuth binding (pinned incredential-groups/application/authorization.test.ts), so the result is now its honest403 "Credential Group credential access denied"instead of the concealed 404. This PR does not change that policy.Type of Change
Testing
fork-sync.integration.ts: new scenario drives a push preview through the same in-process CLI transport Chat uses (createScopedCliTransport+withWorkspaceInvocationScope), with a mapped table and a saved conflict-column value. Before the fix it returns{"error":{"code":"NOT_FOUND","message":"Workspace not found"}}; with the fix it returns 200 with the field validated against the destination.selectors/__integration__/copilot-nested-selectors.integration.ts:POST /api/v2/selectors/listformcp.toolson a real managed MCP connection through the Chat transport. Before the fix it returns404 "Selector scope not found"; with the fix it returns the credential-group 403.copilot-workspace-invocation.test.ts: a grant scoped to one credential cannot be re-bound to another (security boundary).bun run type-check,bun run check:audits, Biome, and unit tests acrosslib/core/application,lib/selectors,lib/workflows,lib/mcp,lib/credentials,lib/credential-groups,ee/workspace-forkingandlib/mothership/agent-cliall pass. (Oneagent-cli/services.test.tscase timed out at 10s under full parallel load and passes in isolation. It coverslist_workspaces, not selectors.)Reviewers: the security-relevant piece is
narrowResourceScopeinlib/core/application/copilot-workspace-invocation.ts.Checklist
🤖 Generated with Claude Code