2
0

🔒 Use vm instead of Function in Node.js (#1509)

This commit is contained in:
Baptiste Arnaud
2024-05-14 14:17:40 +02:00
committed by GitHub
parent 1afa25a015
commit 75c44d61d5
6 changed files with 42 additions and 24 deletions

View File

@@ -4,6 +4,7 @@ import { parseGuessedValueType } from './parseGuessedValueType'
import { isDefined } from '@typebot.io/lib'
import { safeStringify } from '@typebot.io/lib/safeStringify'
import { Variable } from './types'
import vm from 'vm'
const defaultTimeout = 10
@@ -18,7 +19,9 @@ export const executeFunction = async ({
body,
args: initialArgs,
}: Props) => {
const parsedBody = parseVariables(variables, { fieldToParse: 'id' })(body)
const parsedBody = `(async function() {${parseVariables(variables, {
fieldToParse: 'id',
})(body)}})()`
const args = (
extractVariablesFromText(variables)(body).map((variable) => ({
@@ -30,22 +33,25 @@ export const executeFunction = async ({
? Object.entries(initialArgs).map(([id, value]) => ({ id, value }))
: []
)
const func = AsyncFunction(
...args.map(({ id }) => id),
'setVariable',
parsedBody
)
let updatedVariables: Record<string, any> = {}
const setVariable = (key: string, value: any) => {
updatedVariables[key] = value
}
const context = vm.createContext({
...Object.fromEntries(args.map(({ id, value }) => [id, value])),
setVariable,
fetch,
setTimeout,
})
const timeout = new Timeout()
try {
const output: unknown = await timeout.wrap(
func(...args.map(({ value }) => value), setVariable),
await vm.runInContext(parsedBody, context),
defaultTimeout * 1000
)
timeout.clear()
@@ -81,8 +87,6 @@ export const executeFunction = async ({
}
}
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
class Timeout {
private ids: NodeJS.Timeout[]

View File

@@ -2,6 +2,7 @@ import { safeStringify } from '@typebot.io/lib/safeStringify'
import { isDefined, isNotDefined } from '@typebot.io/lib/utils'
import { parseGuessedValueType } from './parseGuessedValueType'
import { Variable, VariableWithValue } from './types'
import vm from 'vm'
export type ParseVariablesOptions = {
fieldToParse?: 'value' | 'id'
@@ -73,11 +74,17 @@ const evaluateInlineCode = (
{ variables }: { variables: Variable[] }
) => {
const evaluating = parseVariables(variables, { fieldToParse: 'id' })(
code.includes('return ') ? code : `return ${code}`
`(async function() {${
code.includes('return ') ? code : 'return ' + code
}})()`
)
try {
const func = Function(...variables.map((v) => v.id), evaluating)
return func(...variables.map((v) => parseGuessedValueType(v.value)))
const sandbox = vm.createContext({
...Object.fromEntries(
variables.map((v) => [v.id, parseGuessedValueType(v.value)])
),
})
return vm.runInContext(evaluating, sandbox)
} catch (err) {
return parseVariables(variables)(code)
}