@@ -37,6 +37,7 @@ import {
3737import { fileGetContentTool } from '@/tools/file/get'
3838import { memoryAddTool } from '@/tools/memory/add'
3939import { tableBatchInsertRowsTool } from '@/tools/table/batch_insert_rows'
40+ import { customBlockExecutorTool } from '@/tools/workflow/custom-block-executor'
4041import { workflowExecutorTool } from '@/tools/workflow/executor'
4142
4243// Hoisted mock state - these are available to vi.mock factories
@@ -47,6 +48,7 @@ const {
4748 mockGetCustomToolById,
4849 mockListCustomTools,
4950 mockMarkWorkspaceFileSecretProvenanceUnknown,
51+ mockRunCustomBlockTool,
5052 mockRunWorkflowTool,
5153 mockGetCustomToolByIdOrTitle,
5254 mockGenerateInternalDelegationToken,
@@ -63,6 +65,7 @@ const {
6365 mockGetCustomToolById : vi . fn ( ) ,
6466 mockListCustomTools : vi . fn ( ) ,
6567 mockMarkWorkspaceFileSecretProvenanceUnknown : vi . fn ( ) ,
68+ mockRunCustomBlockTool : vi . fn ( ) ,
6669 mockRunWorkflowTool : vi . fn ( ) ,
6770 mockGetCustomToolByIdOrTitle : vi . fn ( ) ,
6871 mockGenerateInternalDelegationToken : vi . fn ( ) ,
@@ -130,9 +133,14 @@ vi.mock('@/executor/handlers/workflow/workflow-tool-runner', () => ({
130133 runWorkflowTool : ( ...args : unknown [ ] ) => mockRunWorkflowTool ( ...args ) ,
131134} ) )
132135
136+ vi . mock ( '@/executor/handlers/workflow/custom-block-tool-runner' , ( ) => ( {
137+ runCustomBlockTool : ( ...args : unknown [ ] ) => mockRunCustomBlockTool ( ...args ) ,
138+ } ) )
139+
133140// Mock the tools registry to avoid loading the full 4500+ line registry file.
134141// Only the tools actually exercised in tests are provided.
135142const mockRegistryTools : Record < string , any > = {
143+ deployed_block_executor : customBlockExecutorTool ,
136144 workflow_executor : workflowExecutorTool ,
137145 file_get_content : fileGetContentTool ,
138146 memory_add : memoryAddTool ,
@@ -1511,6 +1519,55 @@ describe('executeTool Function', () => {
15111519 expect ( global . fetch ) . not . toHaveBeenCalled ( )
15121520 } )
15131521
1522+ it ( 'overwrites custom-block tool context with the trusted workflow scope' , async ( ) => {
1523+ const executionContext = createToolExecutionContext ( {
1524+ userId : 'trusted-user' ,
1525+ workflowId : 'trusted-workflow' ,
1526+ workspaceId : 'trusted-workspace' ,
1527+ executionId : 'trusted-execution' ,
1528+ callChain : [ 'trusted-parent' ] ,
1529+ isDeployedContext : true ,
1530+ } )
1531+ mockRunCustomBlockTool . mockResolvedValueOnce ( { success : true , output : { ok : true } } )
1532+
1533+ await executeTool (
1534+ 'deployed_block_executor_custom_block_123' ,
1535+ {
1536+ blockType : 'custom_block_123' ,
1537+ _context : {
1538+ userId : 'forged-user' ,
1539+ workflowId : 'forged-workflow' ,
1540+ workspaceId : 'forged-workspace' ,
1541+ executionId : 'forged-execution' ,
1542+ callChain : [ 'forged-parent' ] ,
1543+ isDeployedContext : false ,
1544+ billingAttribution : { forged : true } ,
1545+ } ,
1546+ } ,
1547+ { executionContext }
1548+ )
1549+
1550+ expect ( mockRunCustomBlockTool ) . toHaveBeenCalledWith (
1551+ expect . objectContaining ( {
1552+ blockType : 'custom_block_123' ,
1553+ _context : expect . objectContaining ( {
1554+ userId : 'trusted-user' ,
1555+ workflowId : 'trusted-workflow' ,
1556+ workspaceId : 'trusted-workspace' ,
1557+ executionId : 'trusted-execution' ,
1558+ callChain : [ 'trusted-parent' ] ,
1559+ isDeployedContext : true ,
1560+ billingAttribution : TEST_BILLING_ATTRIBUTION ,
1561+ requestId : expect . any ( String ) ,
1562+ } ) ,
1563+ } ) ,
1564+ expect . objectContaining ( {
1565+ resolvedSecretTraceRegistry : undefined ,
1566+ } )
1567+ )
1568+ expect ( global . fetch ) . not . toHaveBeenCalled ( )
1569+ } )
1570+
15141571 it ( 'filters cross-scope workflow provenance to literals present in the unchanged result' , async ( ) => {
15151572 const registry = new ResolvedSecretTraceRegistry ( [ ] , {
15161573 userId : 'parent-user' ,
0 commit comments