Use bson in isolated-vm

This commit is contained in:
Adria Navarro 2024-02-06 10:59:57 +01:00
parent 2c95920f65
commit a273276bd3
4 changed files with 49 additions and 9 deletions

View File

@ -14,7 +14,8 @@
"postbuild": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client && copyfiles -f ../../yarn.lock ./dist/", "postbuild": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client && copyfiles -f ../../yarn.lock ./dist/",
"check:types": "tsc -p tsconfig.json --noEmit --paths null", "check:types": "tsc -p tsconfig.json --noEmit --paths null",
"build:isolated-vm-lib:string-templates": "esbuild --minify --bundle src/jsRunner/bundles/index-helpers.ts --outfile=src/jsRunner/bundles/index-helpers.ivm.bundle.js --platform=node --format=esm --external:handlebars", "build:isolated-vm-lib:string-templates": "esbuild --minify --bundle src/jsRunner/bundles/index-helpers.ts --outfile=src/jsRunner/bundles/index-helpers.ivm.bundle.js --platform=node --format=esm --external:handlebars",
"build:isolated-vm-libs": "yarn build:isolated-vm-lib:string-templates", "build:isolated-vm-lib:bson": "esbuild --minify --bundle src/jsRunner/bundles/bsonPackage.ts --outfile=src/jsRunner/bundles/bson.ivm.bundle.js --platform=node --format=esm",
"build:isolated-vm-libs": "yarn build:isolated-vm-lib:string-templates && yarn build:isolated-vm-lib:bson",
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
"debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js", "debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js",
"jest": "NODE_OPTIONS=\"--no-node-snapshot $NODE_OPTIONS\" jest", "jest": "NODE_OPTIONS=\"--no-node-snapshot $NODE_OPTIONS\" jest",

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
export { deserialize } from "bson"

View File

@ -1,24 +1,27 @@
import ivm from "isolated-vm" import ivm from "isolated-vm"
import bson from "bson"
const JS_TIMEOUT_MS = 1000 const JS_TIMEOUT_MS = 1000
class ScriptRunner { class ScriptRunner {
vm: IsolatedVM vm: IsolatedVM
constructor(script: string, context: any) { constructor(script: string, context: any) {
const code = `let fn = () => {\n${script}\n}; results.out = fn();` this.vm = new IsolatedVM({ memoryLimit: 64 })
this.vm = new IsolatedVM({ memoryLimit: 8 })
this.vm.context = { this.vm.context = {
...context, ...context,
data: bson.BSON.serialize({ data: context.data }),
results: { out: "" }, results: { out: "" },
} }
const code = `let fn = () => {data=deserialize(data).data;\n${script}\n}; cb(JSON.parse(JSON.stringify(fn())));`
this.vm.code = code this.vm.code = code
} }
execute() { execute() {
this.vm.runScript() const result = this.vm.runScript()
const results = this.vm.getValue("results") return result
return results.out
} }
} }
@ -26,7 +29,8 @@ class IsolatedVM {
isolate: ivm.Isolate isolate: ivm.Isolate
vm: ivm.Context vm: ivm.Context
#jail: ivm.Reference #jail: ivm.Reference
script: any script: ivm.Module = undefined!
#bsonModule: ivm.Module = undefined!
constructor({ memoryLimit }: { memoryLimit: number }) { constructor({ memoryLimit }: { memoryLimit: number }) {
this.isolate = new ivm.Isolate({ memoryLimit }) this.isolate = new ivm.Isolate({ memoryLimit })
@ -49,11 +53,38 @@ class IsolatedVM {
} }
set code(code: string) { set code(code: string) {
this.script = this.isolate.compileScriptSync(code) const bsonSource = require("../jsRunner/bundles/bson.ivm.bundle.js")
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}`
)
} }
runScript() { runScript() {
this.script.runSync(this.vm, { timeout: JS_TIMEOUT_MS }) this.script.instantiateSync(this.vm, specifier => {
if (specifier === "compiled_module") {
return this.#bsonModule
}
throw new Error(`"${specifier}" import not allowed`)
})
let result
this.vm.global.setSync(
"cb",
new ivm.Callback((value: any) => {
result = value
})
)
this.script.evaluateSync({ timeout: JS_TIMEOUT_MS })
return result
} }
copyRefToVm(value: Object): ivm.Copy<Object> { copyRefToVm(value: Object): ivm.Copy<Object> {