diff --git a/packages/server/src/api/controllers/script.ts b/packages/server/src/api/controllers/script.ts index bdca2d6e18..93e1ad7df9 100644 --- a/packages/server/src/api/controllers/script.ts +++ b/packages/server/src/api/controllers/script.ts @@ -3,9 +3,10 @@ import { IsolatedVM } from "../../jsRunner/vm" export async function execute(ctx: Ctx) { const { script, context } = ctx.request.body - const runner = new IsolatedVM().withContext(context) - - const result = runner.execute(`(function(){\n${script}\n})();`) + const vm = new IsolatedVM() + const result = vm.withContext(context, () => + vm.execute(`(function(){\n${script}\n})();`) + ) ctx.body = result } diff --git a/packages/server/src/jsRunner/index.ts b/packages/server/src/jsRunner/index.ts index 3c13aef1d4..1e8bee04c3 100644 --- a/packages/server/src/jsRunner/index.ts +++ b/packages/server/src/jsRunner/index.ts @@ -23,22 +23,17 @@ export function init() { try { const bbCtx = context.getCurrentContext()! - let { vm } = bbCtx - if (!vm) { - // Can't copy the native helpers into the isolate. We just ignore them as they are handled properly from the helpersSource - const { helpers, ...ctxToPass } = ctx - - vm = new IsolatedVM({ + if (!bbCtx.vm) { + const vm = new IsolatedVM({ memoryLimit: env.JS_RUNNER_MEMORY_LIMIT, invocationTimeout: env.JS_PER_INVOCATION_TIMEOUT_MS, isolateAccumulatedTimeout: env.JS_PER_REQUEST_TIMEOUT_MS, - }) - .withContext(ctxToPass) - .withHelpers() + }).withHelpers() bbCtx.vm = vm } - return vm.execute(js) + const { helpers, ...rest } = ctx + return bbCtx.vm.withContext(rest, () => bbCtx.vm!.execute(js)) } catch (error: any) { if (error.message === "Script execution timed out.") { throw new JsErrorTimeout() diff --git a/packages/server/src/jsRunner/vm/isolated-vm.ts b/packages/server/src/jsRunner/vm/isolated-vm.ts index f431ff644b..78250acdd2 100644 --- a/packages/server/src/jsRunner/vm/isolated-vm.ts +++ b/packages/server/src/jsRunner/vm/isolated-vm.ts @@ -97,10 +97,14 @@ export class IsolatedVM implements VM { return this } - withContext(context: Record) { + withContext(context: Record, f: () => T) { this.addToContext(context) - return this + try { + return f() + } finally { + this.removeFromContext(Object.keys(context)) + } } withParsingBson(data: any) { @@ -224,6 +228,12 @@ export class IsolatedVM implements VM { } } + private removeFromContext(keys: string[]) { + for (let key of keys) { + this.jail.deleteSync(key) + } + } + private getFromContext(key: string) { const ref = this.vm.global.getSync(key, { reference: true }) const result = ref.copySync() diff --git a/packages/server/src/jsRunner/vm/vm2.ts b/packages/server/src/jsRunner/vm/vm2.ts index 6d05943d25..75e3899064 100644 --- a/packages/server/src/jsRunner/vm/vm2.ts +++ b/packages/server/src/jsRunner/vm/vm2.ts @@ -7,16 +7,26 @@ export class VM2 implements VM { vm: vm2.VM results: { out: string } - constructor(context: any) { + constructor() { this.vm = new vm2.VM({ timeout: JS_TIMEOUT_MS, }) this.results = { out: "" } - this.vm.setGlobals(context) this.vm.setGlobal("fetch", fetch) this.vm.setGlobal("results", this.results) } + withContext(context: Record, fn: () => T): T { + this.vm.setGlobals(context) + try { + return fn() + } finally { + for (const key in context) { + this.vm.setGlobal(key, undefined) + } + } + } + execute(script: string) { const code = `let fn = () => {\n${script}\n}; results.out = fn();` const vmScript = new vm2.VMScript(code) diff --git a/packages/server/src/threads/query.ts b/packages/server/src/threads/query.ts index c159f24268..6cc07c1256 100644 --- a/packages/server/src/threads/query.ts +++ b/packages/server/src/threads/query.ts @@ -131,24 +131,21 @@ class QueryRunner { if (transformer) { let runner: VM if (!USE_ISOLATED_VM) { - runner = new VM2({ - data: rows, - params: enrichedParameters, - }) + runner = new VM2() } else { transformer = `(function(){\n${transformer}\n})();` - let isolatedVm = new IsolatedVM().withContext({ - data: rows, - params: enrichedParameters, - }) + let vm = new IsolatedVM() if (datasource.source === SourceName.MONGODB) { - isolatedVm = isolatedVm.withParsingBson(rows) + vm = vm.withParsingBson(rows) } - - runner = isolatedVm + runner = vm } - rows = runner.execute(transformer) + const ctx = { + data: rows, + params: enrichedParameters, + } + rows = runner.withContext(ctx, () => runner.execute(transformer)) } // if the request fails we retry once, invalidating the cached value diff --git a/packages/types/src/sdk/vm.ts b/packages/types/src/sdk/vm.ts index 43b7775d3b..314883e0c5 100644 --- a/packages/types/src/sdk/vm.ts +++ b/packages/types/src/sdk/vm.ts @@ -1,3 +1,4 @@ export interface VM { execute(code: string): any + withContext(context: Record, fn: () => T): T }