Replace # for privates
This commit is contained in:
parent
72c122105f
commit
57952131ac
|
@ -15,44 +15,44 @@ class ExecutionTimeoutError extends Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModuleHandler {
|
class ModuleHandler {
|
||||||
#modules: {
|
private modules: {
|
||||||
import: string
|
import: string
|
||||||
moduleKey: string
|
moduleKey: string
|
||||||
module: ivm.Module
|
module: ivm.Module
|
||||||
}[] = []
|
}[] = []
|
||||||
|
|
||||||
#generateRandomKey = () => `i${crypto.randomUUID().replace(/-/g, "")}`
|
private generateRandomKey = () => `i${crypto.randomUUID().replace(/-/g, "")}`
|
||||||
|
|
||||||
registerModule(module: ivm.Module, imports: string) {
|
registerModule(module: ivm.Module, imports: string) {
|
||||||
this.#modules.push({
|
this.modules.push({
|
||||||
moduleKey: this.#generateRandomKey(),
|
moduleKey: this.generateRandomKey(),
|
||||||
import: imports,
|
import: imports,
|
||||||
module: module,
|
module: module,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
generateImports() {
|
generateImports() {
|
||||||
return this.#modules
|
return this.modules
|
||||||
.map(m => `import ${m.import} from "${m.moduleKey}"`)
|
.map(m => `import ${m.import} from "${m.moduleKey}"`)
|
||||||
.join(";")
|
.join(";")
|
||||||
}
|
}
|
||||||
|
|
||||||
getModule(key: string) {
|
getModule(key: string) {
|
||||||
const module = this.#modules.find(m => m.moduleKey === key)
|
const module = this.modules.find(m => m.moduleKey === key)
|
||||||
return module?.module
|
return module?.module
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IsolatedVM implements VM {
|
export class IsolatedVM implements VM {
|
||||||
#isolate: ivm.Isolate
|
private isolate: ivm.Isolate
|
||||||
#vm: ivm.Context
|
private vm: ivm.Context
|
||||||
#jail: ivm.Reference
|
private jail: ivm.Reference
|
||||||
#timeout: number
|
private timeout: number
|
||||||
#perRequestLimit?: number
|
private perRequestLimit?: number
|
||||||
|
|
||||||
#moduleHandler = new ModuleHandler()
|
private moduleHandler = new ModuleHandler()
|
||||||
|
|
||||||
readonly #resultKey = "results"
|
private readonly resultKey = "results"
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
memoryLimit,
|
memoryLimit,
|
||||||
|
@ -63,30 +63,30 @@ export class IsolatedVM implements VM {
|
||||||
timeout: number
|
timeout: number
|
||||||
perRequestLimit?: number
|
perRequestLimit?: number
|
||||||
}) {
|
}) {
|
||||||
this.#isolate = new ivm.Isolate({ memoryLimit })
|
this.isolate = new ivm.Isolate({ memoryLimit })
|
||||||
this.#vm = this.#isolate.createContextSync()
|
this.vm = this.isolate.createContextSync()
|
||||||
this.#jail = this.#vm.global
|
this.jail = this.vm.global
|
||||||
this.#jail.setSync("global", this.#jail.derefInto())
|
this.jail.setSync("global", this.jail.derefInto())
|
||||||
|
|
||||||
this.#addToContext({
|
this.addToContext({
|
||||||
[this.#resultKey]: { out: "" },
|
[this.resultKey]: { out: "" },
|
||||||
})
|
})
|
||||||
|
|
||||||
this.#timeout = timeout
|
this.timeout = timeout
|
||||||
this.#perRequestLimit = perRequestLimit
|
this.perRequestLimit = perRequestLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
withHelpers() {
|
withHelpers() {
|
||||||
const urlModule = this.#registerCallbacks({
|
const urlModule = this.registerCallbacks({
|
||||||
resolve: url.resolve,
|
resolve: url.resolve,
|
||||||
parse: url.parse,
|
parse: url.parse,
|
||||||
})
|
})
|
||||||
|
|
||||||
const querystringModule = this.#registerCallbacks({
|
const querystringModule = this.registerCallbacks({
|
||||||
escape: querystring.escape,
|
escape: querystring.escape,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.#addToContext({
|
this.addToContext({
|
||||||
helpersStripProtocol: new ivm.Callback((str: string) => {
|
helpersStripProtocol: new ivm.Callback((str: string) => {
|
||||||
var parsed = url.parse(str) as any
|
var parsed = url.parse(str) as any
|
||||||
parsed.protocol = ""
|
parsed.protocol = ""
|
||||||
|
@ -101,19 +101,19 @@ export class IsolatedVM implements VM {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
const helpersSource = loadBundle(BundleType.HELPERS)
|
const helpersSource = loadBundle(BundleType.HELPERS)
|
||||||
const helpersModule = this.#isolate.compileModuleSync(
|
const helpersModule = this.isolate.compileModuleSync(
|
||||||
`${injectedRequire};${helpersSource}`
|
`${injectedRequire};${helpersSource}`
|
||||||
)
|
)
|
||||||
|
|
||||||
helpersModule.instantiateSync(this.#vm, specifier => {
|
helpersModule.instantiateSync(this.vm, specifier => {
|
||||||
if (specifier === "crypto") {
|
if (specifier === "crypto") {
|
||||||
const cryptoModule = this.#registerCallbacks({
|
const cryptoModule = this.registerCallbacks({
|
||||||
randomUUID: crypto.randomUUID,
|
randomUUID: crypto.randomUUID,
|
||||||
})
|
})
|
||||||
const module = this.#isolate.compileModuleSync(
|
const module = this.isolate.compileModuleSync(
|
||||||
`export default ${cryptoModule}`
|
`export default ${cryptoModule}`
|
||||||
)
|
)
|
||||||
module.instantiateSync(this.#vm, specifier => {
|
module.instantiateSync(this.vm, specifier => {
|
||||||
throw new Error(`No imports allowed. Required: ${specifier}`)
|
throw new Error(`No imports allowed. Required: ${specifier}`)
|
||||||
})
|
})
|
||||||
return module
|
return module
|
||||||
|
@ -121,20 +121,21 @@ export class IsolatedVM implements VM {
|
||||||
throw new Error(`No imports allowed. Required: ${specifier}`)
|
throw new Error(`No imports allowed. Required: ${specifier}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.#moduleHandler.registerModule(helpersModule, "helpers")
|
this.moduleHandler.registerModule(helpersModule, "helpers")
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
withContext(context: Record<string, any>) {
|
withContext(context: Record<string, any>) {
|
||||||
this.#addToContext(context)
|
debugger
|
||||||
|
this.addToContext(context)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(code: string): string {
|
execute(code: string): string {
|
||||||
const perRequestLimit = this.#perRequestLimit
|
const perRequestLimit = this.perRequestLimit
|
||||||
|
|
||||||
if (perRequestLimit) {
|
if (perRequestLimit) {
|
||||||
const cpuMs = Number(this.#isolate.cpuTime) / 1e6
|
const cpuMs = Number(this.isolate.cpuTime) / 1e6
|
||||||
if (cpuMs > perRequestLimit) {
|
if (cpuMs > perRequestLimit) {
|
||||||
throw new ExecutionTimeoutError(
|
throw new ExecutionTimeoutError(
|
||||||
`CPU time limit exceeded (${cpuMs}ms > ${perRequestLimit}ms)`
|
`CPU time limit exceeded (${cpuMs}ms > ${perRequestLimit}ms)`
|
||||||
|
@ -142,12 +143,12 @@ export class IsolatedVM implements VM {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code = `${this.#moduleHandler.generateImports()};results.out=${code};`
|
code = `${this.moduleHandler.generateImports()};results.out=${code};`
|
||||||
|
|
||||||
const script = this.#isolate.compileModuleSync(code)
|
const script = this.isolate.compileModuleSync(code)
|
||||||
|
|
||||||
script.instantiateSync(this.#vm, specifier => {
|
script.instantiateSync(this.vm, specifier => {
|
||||||
const module = this.#moduleHandler.getModule(specifier)
|
const module = this.moduleHandler.getModule(specifier)
|
||||||
if (module) {
|
if (module) {
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
@ -155,13 +156,13 @@ export class IsolatedVM implements VM {
|
||||||
throw new Error(`"${specifier}" import not allowed`)
|
throw new Error(`"${specifier}" import not allowed`)
|
||||||
})
|
})
|
||||||
|
|
||||||
script.evaluateSync({ timeout: this.#timeout })
|
script.evaluateSync({ timeout: this.timeout })
|
||||||
|
|
||||||
const result = this.#getResult()
|
const result = this.getResult()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
#registerCallbacks(functions: Record<string, any>) {
|
private registerCallbacks(functions: Record<string, any>) {
|
||||||
const libId = crypto.randomUUID().replace(/-/g, "")
|
const libId = crypto.randomUUID().replace(/-/g, "")
|
||||||
|
|
||||||
const x: Record<string, string> = {}
|
const x: Record<string, string> = {}
|
||||||
|
@ -169,7 +170,7 @@ export class IsolatedVM implements VM {
|
||||||
const key = `f${libId}${funcName}cb`
|
const key = `f${libId}${funcName}cb`
|
||||||
x[funcName] = key
|
x[funcName] = key
|
||||||
|
|
||||||
this.#addToContext({
|
this.addToContext({
|
||||||
[key]: new ivm.Callback((...params: any[]) => (func as any)(...params)),
|
[key]: new ivm.Callback((...params: any[]) => (func as any)(...params)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -183,17 +184,18 @@ export class IsolatedVM implements VM {
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
#addToContext(context: Record<string, any>) {
|
private addToContext(context: Record<string, any>) {
|
||||||
for (let key in context) {
|
for (let key in context) {
|
||||||
this.#jail.setSync(
|
this.jail.setSync(
|
||||||
key,
|
key,
|
||||||
new ivm.ExternalCopy(context[key]).copyInto({ release: true })
|
context[key]
|
||||||
|
//new ivm.ExternalCopy(context[key]).copyInto({ release: true })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#getResult() {
|
private getResult() {
|
||||||
const ref = this.#vm.global.getSync(this.#resultKey, { reference: true })
|
const ref = this.vm.global.getSync(this.resultKey, { reference: true })
|
||||||
const result = ref.copySync()
|
const result = ref.copySync()
|
||||||
ref.release()
|
ref.release()
|
||||||
return result.out
|
return result.out
|
||||||
|
|
|
@ -3,19 +3,19 @@ import { IsolatedVM } from "../jsRunner/vm"
|
||||||
|
|
||||||
const JS_TIMEOUT_MS = 1000
|
const JS_TIMEOUT_MS = 1000
|
||||||
class ScriptRunner {
|
class ScriptRunner {
|
||||||
#code
|
private code
|
||||||
#vm
|
private vm
|
||||||
|
|
||||||
constructor(script: string, context: any) {
|
constructor(script: string, context: any) {
|
||||||
this.#code = `(() => {${script}})();`
|
this.code = `(() => {${script}})();`
|
||||||
this.#vm = new IsolatedVM({
|
this.vm = new IsolatedVM({
|
||||||
memoryLimit: env.JS_RUNNER_MEMORY_LIMIT,
|
memoryLimit: env.JS_RUNNER_MEMORY_LIMIT,
|
||||||
timeout: JS_TIMEOUT_MS,
|
timeout: JS_TIMEOUT_MS,
|
||||||
}).withContext(context)
|
}).withContext(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
execute() {
|
execute() {
|
||||||
const result = this.#vm.execute(this.#code)
|
const result = this.vm.execute(this.code)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue