fix(copilot): enforce the chat's delegated workspace scope across model-steerable server tools - #6995
Conversation
…_environment_variables A model-supplied workspaceId (or a workflowId in another workspace) could steer both tools to any workspace the acting principal can reach, bypassing the asserted-vs-context workspace comparison the rest of the Copilot tool surface enforces. Both now resolve through requireCopilotWorkspace — moved to a shared module — so an asserted workspace may only re-state the chat's execution workspace, and the default-workspace fallback is removed so a missing scope fails closed.
…rable copilot surface Extends requireCopilotWorkspace to the remaining copilot tools that resolved their target workspace from model-supplied arguments: get_credentials (a workflowId could steer the credential listing to any workspace the user can access) and publish_custom_block (a workflowId could deploy/undeploy custom blocks from another workspace's workflow). The handlers already protected downstream by the application adapter (create workflow, generate API key, list/create workspace MCP servers) now use the same guard so a mismatch is rejected uniformly at the surface, and the getDefaultWorkspaceId fallback is deleted entirely — no copilot path picks a workspace for the model anymore.
…rop call-site boilerplate
requireCopilotWorkspace now accepts an undefined context and throws a
classified OrchestrationError for the missing-workspace branch too, so every
caller drops the 'context ?? {}' and '|| undefined' coercions and one
instanceof covers the guard. query_logs inlines its now-one-line wrapper,
get_credentials drops the workspace-less special case (a workflow with no
workspace asserts nothing), and publish_custom_block handles the guard
locally instead of widening its catch-all — keeping its deliberate
assume-not-published guidance for unrelated failures.
…ope guard's TSDoc
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview
Reviewed by Cursor Bugbot for commit 2009fca. Configure here. |
Greptile SummaryThe PR centralizes Copilot workspace-scope validation and applies it across model-steerable tools so model-provided workspace or workflow identifiers cannot escape the chat’s delegated workspace.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/copilot/tools/server/workspace-scope.ts | Introduces the shared fail-closed workspace assertion guard used by Copilot server tools. |
| apps/sim/lib/copilot/tools/server/workflow/query-logs.ts | Restricts log queries to the workspace carried by the trusted Copilot execution context. |
| apps/sim/lib/copilot/tools/server/user/set-environment-variables.ts | Prevents workspace and workflow arguments from redirecting secret writes outside the delegated workspace. |
| apps/sim/lib/copilot/tools/server/user/get-credentials.ts | Validates a workflow-derived credential scope against the chat’s execution workspace. |
| apps/sim/lib/copilot/tools/server/workflow/query-logs.test.ts | Adds workspace-scope coverage and replaces the previously reported any casts in the new tests with typed arguments and context. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Model[Model tool arguments] --> Guard[requireCopilotWorkspace]
Context[Trusted chat workspace] --> Guard
Guard -->|matching or omitted assertion| Workspace[Delegated workspace]
Guard -->|mismatch or missing context| Reject[Validation error]
Workspace --> Tools[Protected Copilot tools]
Reviews (2): Last reviewed commit: "test(copilot): type the new query-logs s..." | Re-trigger Greptile
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2009fca. Configure here.
Summary
query_logsandset_environment_variablesresolved their target workspace from a model-suppliedworkspaceId(or aworkflowIdin another workspace) before falling back to the chat's context, so a Copilot chat opened in one workspace could read execution logs from, and write environment secrets into, any other workspace the acting principal can reach — bypassing the asserted-vs-context comparison the rest of the Copilot tool surface enforces through the application adapterrequireCopilotWorkspace(moved fromfiles/file-folder-application.tsto a sharedtools/server/workspace-scope.ts): a model-supplied workspace may only re-assert the chat's execution workspace, and a missing scope fails closedget_credentials(aworkflowIdcould scope the credential listing to another workspace) andpublish_custom_block(aworkflowIdcould publish/unpublish a custom block from another workspace's workflow)authorizeWorkspaceOperation(create workflow, generate API key, list/create workspace MCP servers) now use the same guard so a mismatch is rejected uniformly at the surface, andgetDefaultWorkspaceIdis deleted — no Copilot path picks a workspace for the model anymoreOrchestrationError('validation'), so callers need no coercion boilerplate and oneinstanceofcovers itType of Change
Testing
Added mismatch/re-assert/fail-closed tests for every guarded surface; full
lib/copilotsuite (1764 tests),type-check,bun run lint, andcheck:audits(32 audits) passChecklist