From a6d782eb546f99046cb260e356371d9fb456bcd2 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 2 Nov 2026 12:16:06 -0800 Subject: [PATCH] fix(copilot): require approval for run_block and run_from_block All four workflow run tools reach the same executor, but the catalog only gated run_workflow and run_workflow_until_block. The approval gate is a pure catalog lookup, so an agent steered into run_from_block (which runs every block downstream of its start) or run_block executed the workflow with no approval card. Flag both siblings and assert every tool in WORKFLOW_TOOL_NAMES carries requiresApproval. --- .../lib/mothership/generated/tool-catalog-v1.ts | 2 ++ .../mothership/request/tools/permission.test.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/apps/sim/lib/mothership/generated/tool-catalog-v1.ts b/apps/sim/lib/mothership/generated/tool-catalog-v1.ts index 2ece49b3f3c..fce64c58f62 100644 --- a/apps/sim/lib/mothership/generated/tool-catalog-v1.ts +++ b/apps/sim/lib/mothership/generated/tool-catalog-v1.ts @@ -5440,6 +5440,7 @@ export const RunBlock: ToolCatalogEntry = { required: ['workflowId', 'blockId'], }, clientExecutable: true, + requiresApproval: true, } export const RunCode: ToolCatalogEntry = { @@ -5618,6 +5619,7 @@ export const RunFromBlock: ToolCatalogEntry = { required: ['workflowId', 'startBlockId'], }, clientExecutable: true, + requiresApproval: true, } export const RunFunction: ToolCatalogEntry = { diff --git a/apps/sim/lib/mothership/request/tools/permission.test.ts b/apps/sim/lib/mothership/request/tools/permission.test.ts index e5d74997645..9f13511ee10 100644 --- a/apps/sim/lib/mothership/request/tools/permission.test.ts +++ b/apps/sim/lib/mothership/request/tools/permission.test.ts @@ -199,6 +199,22 @@ describe('gated tools are askable', () => { expect(ungatable).toEqual([]) }) + + it('gates every tool that runs a workflow', async () => { + // They all reach the same executor: run_from_block runs everything + // downstream of its start block, so an ungated sibling would run what + // run_workflow asks about without asking. + const { TOOL_CATALOG } = await vi.importActual< + typeof import('@/lib/mothership/generated/tool-catalog-v1') + >('@/lib/mothership/generated/tool-catalog-v1') + const { WORKFLOW_TOOL_NAMES } = await vi.importActual< + typeof import('@/lib/mothership/tools/client-executed-tools') + >('@/lib/mothership/tools/client-executed-tools') + + const ungated = WORKFLOW_TOOL_NAMES.filter((name) => !TOOL_CATALOG[name]?.requiresApproval) + + expect(ungated).toEqual([]) + }) }) describe('runGatedToolExecution', () => {