diff --git a/apps/sim/tools/index.test.ts b/apps/sim/tools/index.test.ts index 32d04a9d868..996770c5d4d 100644 --- a/apps/sim/tools/index.test.ts +++ b/apps/sim/tools/index.test.ts @@ -1550,6 +1550,29 @@ describe('executeTool Function', () => { ) }) + it('copies the env map so a child run cannot corrupt the parent context', async () => { + mockRunWorkflowTool.mockResolvedValueOnce({ success: true, output: { ok: true } }) + const executionContext = createToolExecutionContext({ + environmentVariables: { MY_API_KEY: 'parent-secret' }, + }) + + await executeTool( + 'workflow_executor_child-workflow', + { workflowId: 'child-workflow' }, + { executionContext } + ) + + const forwarded = (mockRunWorkflowTool.mock.calls[0]?.[1] as Record) + .environmentVariables as Record + expect(forwarded).toEqual({ MY_API_KEY: 'parent-secret' }) + expect(forwarded).not.toBe(executionContext.environmentVariables) + + forwarded.MY_API_KEY = 'mutated-by-child' + forwarded.INJECTED = 'added-by-child' + + expect(executionContext.environmentVariables).toEqual({ MY_API_KEY: 'parent-secret' }) + }) + it('leaves the custom-block runner without the consumer redaction policy', async () => { mockRunCustomBlockTool.mockResolvedValueOnce({ success: true, output: { ok: true } }) diff --git a/apps/sim/tools/index.ts b/apps/sim/tools/index.ts index f033b4e845e..b32c17f98fe 100644 --- a/apps/sim/tools/index.ts +++ b/apps/sim/tools/index.ts @@ -1792,7 +1792,11 @@ async function executeToolImplementation( // Trusted `executionContext`, never `_context` — that bag spreads // model-reachable `contextParams._context` first, so a model could otherwise // inject its own env map or disable redaction. - environmentVariables: executionContext?.environmentVariables ?? {}, + // Copied, not aliased: the child holds this map for its whole run, and a + // write through it would corrupt the parent's env and every later sibling + // tool call. Every other consumer of `ctx.environmentVariables` already + // copies (`normalizeStringRecord`); this boundary is the longest-lived one. + environmentVariables: { ...executionContext?.environmentVariables }, piiBlockOutputRedaction: executionContext?.piiBlockOutputRedaction, } )