diff --git a/packages/server/src/threads/query.ts b/packages/server/src/threads/query.ts index 97906317fc..9577245aa0 100644 --- a/packages/server/src/threads/query.ts +++ b/packages/server/src/threads/query.ts @@ -14,7 +14,7 @@ import { context, cache, auth } from "@budibase/backend-core" import { getGlobalIDFromUserMetadataID } from "../db/utils" import sdk from "../sdk" import { cloneDeep } from "lodash/fp" -import { Datasource, Query } from "@budibase/types" +import { Datasource, Query, SourceName } from "@budibase/types" import { isSQL } from "../integrations/utils" import { interpolateSQL } from "../integrations/queries/sql" @@ -127,10 +127,16 @@ class QueryRunner { // transform as required if (transformer) { - const runner = new ScriptRunner(transformer, { - data: rows, - params: enrichedParameters, - }) + const runner = new ScriptRunner( + transformer, + { + data: rows, + params: enrichedParameters, + }, + { + parseBson: datasource.source === SourceName.MONGODB, + } + ) rows = runner.execute() } diff --git a/packages/server/src/utilities/scriptRunner.ts b/packages/server/src/utilities/scriptRunner.ts index 79f819b9b1..19cbaae2fa 100644 --- a/packages/server/src/utilities/scriptRunner.ts +++ b/packages/server/src/utilities/scriptRunner.ts @@ -9,15 +9,24 @@ const JS_TIMEOUT_MS = 1000 class ScriptRunner { vm: IsolatedVM - constructor(script: string, context: any) { - this.vm = new IsolatedVM({ memoryLimit: env.JS_RUNNER_MEMORY_LIMIT }) + constructor(script: string, context: any, { parseBson = false } = {}) { + this.vm = new IsolatedVM({ + memoryLimit: env.JS_RUNNER_MEMORY_LIMIT, + parseBson, + }) this.vm.context = { ...context, - data: bson.BSON.serialize({ data: context.data }), + data: parseBson + ? bson.BSON.serialize({ data: context.data }) + : context.data, results: { out: "" }, } - const code = `let fn = () => {data=deserialize(data).data;\n${script}\n}; cb(JSON.parse(JSON.stringify(fn())));` + if (parseBson) { + script = `return JSON.parse(JSON.stringify((function(){data=deserialize(data).data;${script}})()));` + } + + const code = `const fn=function(){${script}};cb(fn());` this.vm.code = code } @@ -32,13 +41,28 @@ class IsolatedVM { vm: ivm.Context #jail: ivm.Reference script: ivm.Module = undefined! - #bsonModule: ivm.Module = undefined! + #bsonModule?: ivm.Module - constructor({ memoryLimit }: { memoryLimit: number }) { + constructor({ + memoryLimit, + parseBson, + }: { + memoryLimit: number + parseBson: boolean + }) { this.isolate = new ivm.Isolate({ memoryLimit }) this.vm = this.isolate.createContextSync() this.#jail = this.vm.global this.#jail.setSync("global", this.#jail.derefInto()) + // this.#parseBson = parseBson + + if (parseBson) { + const bsonSource = loadBundle(BundleType.BSON) + this.#bsonModule = this.isolate.compileModuleSync(bsonSource) + this.#bsonModule.instantiateSync(this.vm, specifier => { + throw new Error(`No imports allowed. Required: ${specifier}`) + }) + } } getValue(key: string) { @@ -55,26 +79,22 @@ class IsolatedVM { } set code(code: string) { - const bsonSource = loadBundle(BundleType.BSON) - - this.#bsonModule = this.isolate.compileModuleSync(bsonSource) - this.#bsonModule.instantiateSync(this.vm, specifier => { - throw new Error(`No imports allowed. Required: ${specifier}`) - }) - - this.script = this.isolate.compileModuleSync( - `import {deserialize} from "compiled_module";${code}` - ) + if (this.#bsonModule) { + code = `import {deserialize} from "compiled_module";${code}` + } + this.script = this.isolate.compileModuleSync(code) } runScript() { - this.script.instantiateSync(this.vm, specifier => { - if (specifier === "compiled_module") { - return this.#bsonModule - } + if (this.#bsonModule) { + this.script.instantiateSync(this.vm, specifier => { + if (specifier === "compiled_module") { + return this.#bsonModule! + } - throw new Error(`"${specifier}" import not allowed`) - }) + throw new Error(`"${specifier}" import not allowed`) + }) + } let result this.vm.global.setSync(