Skip to content

Commit c919287

Browse files
committed
fix(function): validate custom tool param keys before code interpolation (#4474)
* fix(function): validate custom tool param keys before code interpolation * fix(function): exclude JS reserved words from param key injection guard
1 parent 4b601ba commit c919287

1 file changed

Lines changed: 61 additions & 2 deletions

File tree

  • apps/sim/app/api/function/execute

apps/sim/app/api/function/execute/route.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,59 @@ const TAG_PATTERN = createReferencePattern()
3434
const E2B_JS_WRAPPER_LINES = 3
3535
const E2B_PYTHON_WRAPPER_LINES = 1
3636

37+
/** Matches valid JS identifier names (letters, digits, underscore; no leading digit). */
38+
const SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/
39+
40+
/** ES2023 reserved words — using these as `const` variable names produces a SyntaxError. */
41+
const JS_RESERVED_WORDS = new Set([
42+
'break',
43+
'case',
44+
'catch',
45+
'class',
46+
'const',
47+
'continue',
48+
'debugger',
49+
'default',
50+
'delete',
51+
'do',
52+
'else',
53+
'export',
54+
'extends',
55+
'false',
56+
'finally',
57+
'for',
58+
'function',
59+
'if',
60+
'import',
61+
'in',
62+
'instanceof',
63+
'let',
64+
'new',
65+
'null',
66+
'return',
67+
'static',
68+
'super',
69+
'switch',
70+
'this',
71+
'throw',
72+
'true',
73+
'try',
74+
'typeof',
75+
'var',
76+
'void',
77+
'while',
78+
'with',
79+
'yield',
80+
'enum',
81+
'await',
82+
'implements',
83+
'interface',
84+
'package',
85+
'private',
86+
'protected',
87+
'public',
88+
])
89+
3790
type TypeScriptModule = typeof import('typescript')
3891

3992
let typescriptModulePromise: Promise<TypeScriptModule> | null = null
@@ -1089,18 +1142,24 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
10891142

10901143
const executionMethod = 'isolated-vm'
10911144

1145+
const isSafeParamKey = (key: string) => SAFE_IDENTIFIER.test(key) && !JS_RESERVED_WORDS.has(key)
1146+
10921147
const wrapperLines = ['(async () => {', ' try {']
10931148
if (isCustomTool) {
10941149
Object.keys(executionParams).forEach((key) => {
1095-
wrapperLines.push(` const ${key} = params.${key};`)
1150+
if (isSafeParamKey(key)) {
1151+
wrapperLines.push(` const ${key} = params.${key};`)
1152+
} else {
1153+
logger.warn('Skipping param key — not a safe JS identifier', { key, requestId })
1154+
}
10961155
})
10971156
}
10981157
userCodeStartLine = wrapperLines.length + 1
10991158

11001159
let codeToExecute = resolvedCode
11011160
let prependedLineCount = 0
11021161
if (isCustomTool) {
1103-
const paramKeys = Object.keys(executionParams)
1162+
const paramKeys = Object.keys(executionParams).filter(isSafeParamKey)
11041163
const paramDestructuring = paramKeys.map((key) => `const ${key} = params.${key};`).join('\n')
11051164
codeToExecute = `${paramDestructuring}\n${resolvedCode}`
11061165
prependedLineCount = paramKeys.length

0 commit comments

Comments
 (0)