diff --git a/docker/images/n8n/n8n-task-runners.json b/docker/images/n8n/n8n-task-runners.json index c64d0ecdd0..1f38cf6d93 100644 --- a/docker/images/n8n/n8n-task-runners.json +++ b/docker/images/n8n/n8n-task-runners.json @@ -4,7 +4,10 @@ "runner-type": "javascript", "workdir": "/home/node", "command": "/usr/local/bin/node", - "args": ["/usr/local/lib/node_modules/n8n/node_modules/@n8n/task-runner/dist/start.js"], + "args": [ + "--disallow-code-generation-from-strings", + "/usr/local/lib/node_modules/n8n/node_modules/@n8n/task-runner/dist/start.js" + ], "allowed-env": [ "PATH", "GENERIC_TIMEZONE", diff --git a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts index 39b6ba7f45..6ef479239d 100644 --- a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts +++ b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts @@ -302,6 +302,7 @@ describe('JsTaskRunner', () => { ['typeof clearInterval', 'function'], ['typeof clearImmediate', 'function'], ], + eval: [['eval("1+2")', 3]], 'JS built-ins': [ ['typeof btoa', 'function'], ['typeof atob', 'function'], diff --git a/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts b/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts index ab2cc3a304..58750327d7 100644 --- a/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts +++ b/packages/@n8n/task-runner/src/js-task-runner/js-task-runner.ts @@ -19,7 +19,7 @@ import type { } from 'n8n-workflow'; import * as a from 'node:assert'; import { inspect } from 'node:util'; -import { runInNewContext, type Context } from 'node:vm'; +import { type Context, createContext, runInContext } from 'node:vm'; import type { MainConfig } from '@/config/main-config'; import { UnsupportedFunctionError } from '@/js-task-runner/errors/unsupported-function.error'; @@ -158,10 +158,8 @@ export class JsTaskRunner extends TaskRunner { private getNativeVariables() { return { - // Exposed Node.js globals in vm2 + // Exposed Node.js globals Buffer, - Function, - eval, setTimeout, setInterval, setImmediate, @@ -205,7 +203,7 @@ export class JsTaskRunner extends TaskRunner { signal.addEventListener('abort', abortHandler, { once: true }); - const taskResult = runInNewContext( + const taskResult = runInContext( `globalThis.global = globalThis; module.exports = async function VmCodeWrapper() {${settings.code}\n}()`, context, { timeout: this.taskTimeout * 1000 }, @@ -268,7 +266,7 @@ export class JsTaskRunner extends TaskRunner { signal.addEventListener('abort', abortHandler); - const taskResult = runInNewContext( + const taskResult = runInContext( `module.exports = async function VmCodeWrapper() {${settings.code}\n}()`, context, { timeout: this.taskTimeout * 1000 }, @@ -470,7 +468,7 @@ export class JsTaskRunner extends TaskRunner { dataProxy: IWorkflowDataProxyData, additionalProperties: Record = {}, ): Context { - const context: Context = { + return createContext({ [inspect.custom]: () => '[[ExecutionContext]]', require: this.requireResolver, module: {}, @@ -480,8 +478,6 @@ export class JsTaskRunner extends TaskRunner { ...dataProxy, ...this.buildRpcCallObject(taskId), ...additionalProperties, - }; - - return context; + }); } } diff --git a/packages/cli/src/task-runners/__tests__/task-runner-process.test.ts b/packages/cli/src/task-runners/__tests__/task-runner-process.test.ts index d00ce7b88f..a3ca65fe21 100644 --- a/packages/cli/src/task-runners/__tests__/task-runner-process.test.ts +++ b/packages/cli/src/task-runners/__tests__/task-runner-process.test.ts @@ -117,5 +117,16 @@ describe('TaskRunnerProcess', () => { const options = spawnMock.mock.calls[0][2] as SpawnOptions; expect(options.env).not.toHaveProperty('NODE_OPTIONS'); }); + + it('should use --disallow-code-generation-from-strings flag', async () => { + jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken'); + + await taskRunnerProcess.start(); + + expect(spawnMock.mock.calls[0].at(1)).toEqual([ + '--disallow-code-generation-from-strings', + expect.stringContaining('/packages/@n8n/task-runner/dist/start.js'), + ]); + }); }); }); diff --git a/packages/cli/src/task-runners/task-runner-process.ts b/packages/cli/src/task-runners/task-runner-process.ts index 5129ae98b1..7e83e56ca0 100644 --- a/packages/cli/src/task-runners/task-runner-process.ts +++ b/packages/cli/src/task-runners/task-runner-process.ts @@ -106,7 +106,7 @@ export class TaskRunnerProcess extends TypedEmitter { startNode(grantToken: string, taskBrokerUri: string) { const startScript = require.resolve('@n8n/task-runner/start'); - return spawn('node', [startScript], { + return spawn('node', ['--disallow-code-generation-from-strings', startScript], { env: this.getProcessEnvVars(grantToken, taskBrokerUri), }); }