Make processStringSync's throw behaviour opt-in.
This commit is contained in:
parent
2e4607edb6
commit
4fb870e449
|
@ -145,10 +145,16 @@
|
||||||
const debouncedEval = Utils.debounce((expression, context, snippets) => {
|
const debouncedEval = Utils.debounce((expression, context, snippets) => {
|
||||||
try {
|
try {
|
||||||
expressionError = null
|
expressionError = null
|
||||||
expressionResult = processStringSync(expression || "", {
|
expressionResult = processStringSync(
|
||||||
...context,
|
expression || "",
|
||||||
snippets,
|
{
|
||||||
})
|
...context,
|
||||||
|
snippets,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
noThrow: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expressionResult = null
|
expressionResult = null
|
||||||
expressionError = err
|
expressionError = err
|
||||||
|
|
|
@ -41,11 +41,11 @@ describe("jsRunner (using isolated-vm)", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should prevent sandbox escape", async () => {
|
it("should prevent sandbox escape", async () => {
|
||||||
await expect(
|
expect(
|
||||||
processJS(`return this.constructor.constructor("return process.env")()`)
|
await processJS(
|
||||||
).rejects.toThrow(
|
`return this.constructor.constructor("return process.env")()`
|
||||||
"error while running user-supplied JavaScript: ReferenceError: process is not defined"
|
)
|
||||||
)
|
).toEqual("ReferenceError: process is not defined")
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("helpers", () => {
|
describe("helpers", () => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { AutoFieldDefaultNames } from "../../constants"
|
import { AutoFieldDefaultNames } from "../../constants"
|
||||||
import { processStringSync, UserScriptError } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
import {
|
import {
|
||||||
AutoColumnFieldMetadata,
|
AutoColumnFieldMetadata,
|
||||||
FieldSchema,
|
FieldSchema,
|
||||||
|
@ -81,14 +81,7 @@ export async function processFormulas<T extends Row | Row[]>(
|
||||||
...row,
|
...row,
|
||||||
[column]: tracer.trace("processStringSync", {}, span => {
|
[column]: tracer.trace("processStringSync", {}, span => {
|
||||||
span?.addTags({ table_id: table._id, column, static: isStatic })
|
span?.addTags({ table_id: table._id, column, static: isStatic })
|
||||||
try {
|
return processStringSync(formula, context)
|
||||||
return processStringSync(formula, context)
|
|
||||||
} catch (err: any) {
|
|
||||||
if (err.code === UserScriptError.code) {
|
|
||||||
return err.userScriptError.toString()
|
|
||||||
}
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,8 @@ export function processJS(handlebars: string, context: any) {
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
onErrorLog && onErrorLog(error)
|
onErrorLog && onErrorLog(error)
|
||||||
|
|
||||||
|
const { noThrow = true } = context.__opts || {}
|
||||||
|
|
||||||
// The error handling below is quite messy, because it has fallen to
|
// The error handling below is quite messy, because it has fallen to
|
||||||
// string-templates to handle a variety of types of error specific to usages
|
// string-templates to handle a variety of types of error specific to usages
|
||||||
// above it in the stack. It would be nice some day to refactor this to
|
// above it in the stack. It would be nice some day to refactor this to
|
||||||
|
@ -123,11 +125,17 @@ export function processJS(handlebars: string, context: any) {
|
||||||
// This is to catch the error that happens if a user-supplied JS script
|
// This is to catch the error that happens if a user-supplied JS script
|
||||||
// throws for reasons introduced by the user.
|
// throws for reasons introduced by the user.
|
||||||
if (error.code === UserScriptError.code) {
|
if (error.code === UserScriptError.code) {
|
||||||
|
if (noThrow) {
|
||||||
|
return error.userScriptError.toString()
|
||||||
|
}
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.name === "SyntaxError") {
|
if (error.name === "SyntaxError") {
|
||||||
return error.toString()
|
if (noThrow) {
|
||||||
|
return error.toString()
|
||||||
|
}
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Error while executing JS"
|
return "Error while executing JS"
|
||||||
|
|
|
@ -123,7 +123,7 @@ function createTemplate(
|
||||||
export async function processObject<T extends Record<string, any>>(
|
export async function processObject<T extends Record<string, any>>(
|
||||||
object: T,
|
object: T,
|
||||||
context: object,
|
context: object,
|
||||||
opts?: { noHelpers?: boolean; escapeNewlines?: boolean; onlyFound?: boolean }
|
opts?: ProcessOptions
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
testObject(object)
|
testObject(object)
|
||||||
|
|
||||||
|
@ -173,17 +173,13 @@ export async function processString(
|
||||||
export function processObjectSync(
|
export function processObjectSync(
|
||||||
object: { [x: string]: any },
|
object: { [x: string]: any },
|
||||||
context: any,
|
context: any,
|
||||||
opts: any
|
opts?: ProcessOptions
|
||||||
): object | Array<any> {
|
): object | Array<any> {
|
||||||
testObject(object)
|
testObject(object)
|
||||||
for (let key of Object.keys(object || {})) {
|
for (let key of Object.keys(object || {})) {
|
||||||
let val = object[key]
|
let val = object[key]
|
||||||
if (typeof val === "string") {
|
if (typeof val === "string") {
|
||||||
try {
|
object[key] = processStringSync(object[key], context, opts)
|
||||||
object[key] = processStringSync(object[key], context, opts)
|
|
||||||
} catch (error: any) {
|
|
||||||
object[key] = error.toString()
|
|
||||||
}
|
|
||||||
} else if (typeof val === "object") {
|
} else if (typeof val === "object") {
|
||||||
object[key] = processObjectSync(object[key], context, opts)
|
object[key] = processObjectSync(object[key], context, opts)
|
||||||
}
|
}
|
||||||
|
@ -235,9 +231,6 @@ export function processStringSync(
|
||||||
return process(string)
|
return process(string)
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.code === UserScriptError.code) {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ export interface ProcessOptions {
|
||||||
noEscaping?: boolean
|
noEscaping?: boolean
|
||||||
noHelpers?: boolean
|
noHelpers?: boolean
|
||||||
noFinalise?: boolean
|
noFinalise?: boolean
|
||||||
|
noThrow?: boolean
|
||||||
escapeNewlines?: boolean
|
escapeNewlines?: boolean
|
||||||
onlyFound?: boolean
|
onlyFound?: boolean
|
||||||
disabledHelpers?: string[]
|
disabledHelpers?: string[]
|
||||||
|
|
Loading…
Reference in New Issue