Pull the error object out of isolated-vm when a user script throws an error.

This commit is contained in:
Sam Rose 2024-10-03 09:42:08 +01:00
parent b9a975694f
commit b004ef5448
No known key found for this signature in database
3 changed files with 25 additions and 2 deletions

View File

@ -44,7 +44,7 @@ describe("jsRunner (using isolated-vm)", () => {
const output = await processJS(
`return this.constructor.constructor("return process.env")()`
)
expect(output).toBe("Error while executing JS")
expect(output).toBe("ReferenceError: process is not defined")
})
describe("helpers", () => {

View File

@ -17,6 +17,15 @@ class ExecutionTimeoutError extends Error {
}
}
class UserScriptError extends Error {
constructor(readonly userScriptError: Error) {
super(
`error while running user-supplied JavaScript: ${userScriptError.message}`,
{ cause: userScriptError }
)
}
}
export class IsolatedVM implements VM {
private isolate: ivm.Isolate
private vm: ivm.Context
@ -29,6 +38,7 @@ export class IsolatedVM implements VM {
private readonly resultKey = "results"
private runResultKey: string
private runErrorKey: string
constructor({
memoryLimit,
@ -47,6 +57,7 @@ export class IsolatedVM implements VM {
this.jail.setSync("global", this.jail.derefInto())
this.runResultKey = crypto.randomUUID()
this.runErrorKey = crypto.randomUUID()
this.addToContext({
[this.resultKey]: { [this.runResultKey]: "" },
})
@ -216,7 +227,13 @@ export class IsolatedVM implements VM {
}
}
code = `results['${this.runResultKey}']=${this.codeWrapper(code)}`
code = `
try {
results['${this.runResultKey}']=${this.codeWrapper(code)}
} catch (e) {
results['${this.runErrorKey}']=e
}
`
const script = this.isolate.compileScriptSync(code)
@ -227,6 +244,9 @@ export class IsolatedVM implements VM {
// We can't rely on the script run result as it will not work for non-transferable values
const result = this.getFromContext(this.resultKey)
if (result[this.runErrorKey]) {
throw new UserScriptError(result[this.runErrorKey])
}
return result[this.runResultKey]
}

View File

@ -100,6 +100,9 @@ export function processJS(handlebars: string, context: any) {
if (error.name === "ExecutionTimeoutError") {
return "Request JS execution limit hit"
}
if ("userScriptError" in error) {
return error.userScriptError.toString()
}
return "Error while executing JS"
}
}