From 3b8b60aa037be0af9d3ca670ae0fb5a8b631f03d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Feb 2024 18:08:15 +0100 Subject: [PATCH] Use wrapper --- packages/backend-core/src/context/types.ts | 6 - packages/server/src/jsRunner/index.ts | 133 ++------------------- 2 files changed, 11 insertions(+), 128 deletions(-) diff --git a/packages/backend-core/src/context/types.ts b/packages/backend-core/src/context/types.ts index 75f5234651..0f4c2106d0 100644 --- a/packages/backend-core/src/context/types.ts +++ b/packages/backend-core/src/context/types.ts @@ -1,5 +1,4 @@ import { IdentityContext, VM } from "@budibase/types" -import { Isolate, Context, Module } from "isolated-vm" // keep this out of Budibase types, don't want to expose context info export type ContextMap = { @@ -10,10 +9,5 @@ export type ContextMap = { isScim?: boolean automationId?: string isMigrating?: boolean - isolateRefs?: { - jsIsolate: Isolate - jsContext: Context - helpersModule: Module - } vm?: VM } diff --git a/packages/server/src/jsRunner/index.ts b/packages/server/src/jsRunner/index.ts index 9c54779567..140881aa21 100644 --- a/packages/server/src/jsRunner/index.ts +++ b/packages/server/src/jsRunner/index.ts @@ -1,12 +1,9 @@ -import ivm from "isolated-vm" import env from "../environment" import { setJSRunner, JsErrorTimeout } from "@budibase/string-templates" -import { context } from "@budibase/backend-core" import tracer from "dd-trace" -import url from "url" -import crypto from "crypto" -import querystring from "querystring" -import { BundleType, loadBundle } from "./bundles" + +import { IsolatedVM } from "./vm" +import { context } from "@budibase/backend-core" class ExecutionTimeoutError extends Error { constructor(message: string) { @@ -16,109 +13,24 @@ class ExecutionTimeoutError extends Error { } export function init() { - const helpersSource = loadBundle(BundleType.HELPERS) setJSRunner((js: string, ctx: Record) => { return tracer.trace("runJS", {}, span => { try { const bbCtx = context.getCurrentContext()! - const isolateRefs = bbCtx.isolateRefs - if (!isolateRefs) { - const jsIsolate = new ivm.Isolate({ + let { vm } = bbCtx + if (!vm) { + vm = new IsolatedVM({ memoryLimit: env.JS_RUNNER_MEMORY_LIMIT, - }) - const jsContext = jsIsolate.createContextSync() + timeout: env.JS_PER_EXECUTION_TIME_LIMIT_MS, + }).withHelpers() - const injectedRequire = ` - const require = function(val){ - switch (val) { - case "url": - return { - resolve: (...params) => urlResolveCb(...params), - parse: (...params) => urlParseCb(...params), - } - case "querystring": - return { - escape: (...params) => querystringEscapeCb(...params), - } - } - };` - - const global = jsContext.global - global.setSync( - "urlResolveCb", - new ivm.Callback((...params: Parameters) => - url.resolve(...params) - ) - ) - - global.setSync( - "urlParseCb", - new ivm.Callback((...params: Parameters) => - url.parse(...params) - ) - ) - - global.setSync( - "querystringEscapeCb", - new ivm.Callback( - (...params: Parameters) => - querystring.escape(...params) - ) - ) - - global.setSync( - "helpersStripProtocol", - new ivm.Callback((str: string) => { - var parsed = url.parse(str) as any - parsed.protocol = "" - return parsed.format() - }) - ) - - const helpersModule = jsIsolate.compileModuleSync( - `${injectedRequire};${helpersSource}` - ) - - const cryptoModule = jsIsolate.compileModuleSync( - `export default { randomUUID: cryptoRandomUUIDCb }` - ) - cryptoModule.instantiateSync(jsContext, specifier => { - throw new Error(`No imports allowed. Required: ${specifier}`) - }) - - global.setSync( - "cryptoRandomUUIDCb", - new ivm.Callback( - (...params: Parameters) => { - return crypto.randomUUID(...params) - } - ) - ) - - helpersModule.instantiateSync(jsContext, specifier => { - if (specifier === "crypto") { - return cryptoModule - } - throw new Error(`No imports allowed. Required: ${specifier}`) - }) - - for (const [key, value] of Object.entries(ctx)) { - if (key === "helpers") { - // Can't copy the native helpers into the isolate. We just ignore them as they are handled properly from the helpersSource - continue - } - global.setSync(key, value) - } - - bbCtx.isolateRefs = { jsContext, jsIsolate, helpersModule } + bbCtx.vm = vm } - let { jsIsolate, jsContext, helpersModule } = bbCtx.isolateRefs! - const perRequestLimit = env.JS_PER_REQUEST_TIME_LIMIT_MS if (perRequestLimit) { - const cpuMs = Number(jsIsolate.cpuTime) / 1e6 + const cpuMs = Number(vm.cpuTime) / 1e6 if (cpuMs > perRequestLimit) { throw new ExecutionTimeoutError( `CPU time limit exceeded (${cpuMs}ms > ${perRequestLimit}ms)` @@ -126,30 +38,7 @@ export function init() { } } - const script = jsIsolate.compileModuleSync( - `import helpers from "compiled_module";const result=${js};cb(result)`, - {} - ) - - script.instantiateSync(jsContext, specifier => { - if (specifier === "compiled_module") { - return helpersModule - } - - throw new Error(`"${specifier}" import not allowed`) - }) - - let result - jsContext.global.setSync( - "cb", - new ivm.Callback((value: any) => { - result = value - }) - ) - - script.evaluateSync({ - timeout: env.JS_PER_EXECUTION_TIME_LIMIT_MS, - }) + const result = vm.execute(js) return result } catch (error: any) {