Security advisory GHSA-gjx9-wg9x-7gvp: Flowise Vulnerable to SQL Injection via `tableName` Parameter
Package: flowise-components (npm), vulnerable version: 2.2.3, fixed in: 2.2.4
CWE: CWE-89
Files changed by the fix:
packages/components/nodes/documentloaders/CustomDocumentLoader/CustomDocumentLoader.ts
packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts
packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts
packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts
packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts
packages/components/nodes/recordmanager/PostgresRecordManager/PostgresRecordManager.ts
packages/components/nodes/recordmanager/SQLiteRecordManager/SQLiteRecordManager.ts
packages/components/nodes/sequentialagents/State/State.ts
packages/components/nodes/sequentialagents/commonUtils.ts
packages/components/nodes/tools/ChatflowTool/ChatflowTool.ts
packages/components/nodes/tools/CustomTool/core.ts
packages/components/nodes/tools/OpenAPIToolkit/core.ts
packages/components/nodes/utilities/CustomFunction/CustomFunction.ts
packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts
packages/components/nodes/vectorstores/Postgres/driver/Base.ts
packages/components/src/storageUtils.ts

Fix diff:
diff --git a/packages/components/nodes/documentloaders/CustomDocumentLoader/CustomDocumentLoader.ts b/packages/components/nodes/documentloaders/CustomDocumentLoader/CustomDocumentLoader.ts
index 6afac5637d3..ed19a01e731 100644
--- a/packages/components/nodes/documentloaders/CustomDocumentLoader/CustomDocumentLoader.ts
+++ b/packages/components/nodes/documentloaders/CustomDocumentLoader/CustomDocumentLoader.ts
@@ -106,7 +106,14 @@ class CustomDocumentLoader_DocumentLoaders implements INode {
             }
         }
 
-        let sandbox: any = { $input: input }
+        let sandbox: any = {
+            $input: input,
+            util: undefined,
+            Symbol: undefined,
+            child_process: undefined,
+            fs: undefined,
+            process: undefined
+        }
         sandbox['$vars'] = prepareSandboxVars(variables)
         sandbox['$flow'] = flow
 
@@ -128,7 +135,10 @@ class CustomDocumentLoader_DocumentLoaders implements INode {
             require: {
                 external: { modules: deps },
                 builtin: builtinDeps
-            }
+            },
+            eval: false,
+            wasm: false,
+            timeout: 10000
         } as any
 
         const vm = new NodeVM(nodeVMOptions)
diff --git a/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts
index 4214734f53c..5b41b08874b 100644
--- a/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts
+++ b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts
@@ -19,6 +19,18 @@ export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods {
         this.threadId = threadId
     }
 
+    sanitizeTableName(tableName: string): string {
+        // Trim and normalize case, turn whitespace into underscores
+        tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_')
+
+        // Validate using a regex (alphanumeric and underscores only)
+        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
+            throw new Error('Invalid table name')
+        }
+
+        return tableName
+    }
+
     private async getDataSource(): Promise<DataSource> {
         const { datasourceOptions } = this.config
         if (!datasourceOptions) {
@@ -38,8 +50,9 @@ export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods {
 
         try {
             const queryRunner = dataSource.createQueryRunner()
+            const tableName = this.sanitizeTableName(this.tableName)
             await queryRunner.manager.query(`
-                CREATE TABLE IF NOT EXISTS ${this.tableName} (
+                CREATE TABLE IF NOT EXISTS ${tableName} (
                     thread_id VARCHAR(255) NOT NULL,
                     checkpoint_id VARCHAR(255) NOT NULL,
                     parent_id VARCHAR(255),
@@ -62,12 +75,13 @@ export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods {
 
         const thread_id = config.configurable?.thread_id || this.threadId
         const checkpoint_id = config.configurable?.checkpoint_id
+        const tableName = this.sanitizeTableName(this.tableName)
 
         try {
             const queryRunner = dataSource.createQueryRunner()
             const sql = checkpoint_id
-                ? `SELECT checkpoint, parent_id, metadata FROM ${this.tableName} WHERE thread_id = ? AND checkpoint_id = ?`
-                : `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${this.tableName} WHERE thread_id = ? ORDER BY checkpoint_id DESC LIMIT 1`
+                ? `SELECT checkpoint, parent_id, metadata FROM ${tableName} WHERE thread_id = ? AND checkpoint_id = ?`
+                : `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = ? ORDER BY checkpoint_id DESC LIMIT 1`
 
             const rows = await queryRunner.manager.query(sql, checkpoint_id ? [thread_id, checkpoint_id] : [thread_id])
             await queryRunner.release()
@@ -108,7 +122,8 @@ export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods {
         const queryRunner = dataSource.createQueryRunner()
         try {
             const threadId = config.configurable?.thread_id || this.threadId
-            let sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${this.tableName} WHERE thread_id = ? ${
+            const tableName = this.sanitizeTableName(this.tableName)
+            let sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = ? ${
                 before ? 'AND checkpoint_id < ?' : ''
             } ORDER BY checkpoint_id DESC`
             if (limit) {
@@ -163,8 +178,9 @@ export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods {
                 Buffer.from(this.serde.stringify(checkpoint)), // Encode to binary
                 Buffer.from(this.serde.stringify(metadata)) // Encode to binary
             ]
+            const tableName = this.sanitizeTableName(this.tableName)
 
-            const query = `INSERT INTO ${this.tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata)
+            const query = `INSERT INTO ${tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata)
                            VALUES (?, ?, ?, ?, ?)
                            ON DUPLICATE KEY UPDATE checkpoint = VALUES(checkpoint), metadata = VALUES(metadata)`
 
@@ -190,10 +206,11 @@ export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods {
 
         const dataSource = await this.getDataSource()
         await this.setup(dataSource)
+        const tableName = this.sanitizeTableName(this.tableName)
 
         try {
             const queryRunner = dataSource.createQueryRunner()
-            const query = `DELETE FROM ${this.tableName} WHERE thread_id = ?;`
+            const query = `DELETE FROM ${tableName} WHERE thread_id = ?;`
             await queryRunner.manager.query(query, [threadId])
             await queryRunner.release()
         } catch (error) {
diff --git a/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts
index 702c8cfad56..5bc69f663b9 100644
--- a/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts
+++ b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts
@@ -19,6 +19,18 @@ export class PostgresSaver extends BaseCheckpointSaver implements MemoryMethods
         this.threadId = threadId
     }
 
+    sanitizeTableName(tableName: string): string {
+        // Trim and normalize case, turn whitespace into underscores
+        tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_')
+
+        // Validate using a regex (alphanumeric and underscores only)
+        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
+            throw new Error('Invalid table name')
+        }
+
+        return tableName
+    }
+
     private async getDataSource(): Promise<DataSource> {
         const { datasourceOptions } = this.config
         if (!datasourceOptions) {
@@ -40,8 +52,9 @@ export class PostgresSaver extends BaseCheckpointSaver implements MemoryMethods
 
         try {
             const queryRunner = dataSource.createQueryRunner()
+            const tableName = this.sanitizeTableName(this.tableName)
             await queryRunner.manager.query(`
-CREATE TABLE IF NOT EXISTS ${this.tableName} (
+CREATE TABLE IF NOT EXISTS ${tableName} (
     thread_id TEXT NOT NULL,
     checkpoint_id TEXT NOT NULL,
     parent_id TEXT,
@@ -63,12 +76,13 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
 
         const thread_id = config.configurable?.thread_id || this.threadId
         const checkpoint_id = config.configurable?.checkpoint_id
+        const tableName = this.sanitizeTableName(this.tableName)
 
         if (checkpoint_id) {
             try {
                 const queryRunner = dataSource.createQueryRunner()
                 const keys = [thread_id, checkpoint_id]
-                const sql = `SELECT checkpoint, parent_id, metadata FROM ${this.tableName} WHERE thread_id = $1 AND checkpoint_id = $2`
+                const sql = `SELECT checkpoint, parent_id, metadata FROM ${tableName} WHERE thread_id = $1 AND checkpoint_id = $2`
 
                 const rows = await queryRunner.manager.query(sql, keys)
                 await queryRunner.release()
@@ -89,8 +103,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                     }
                 }
             } catch (error) {
-                console.error(`Error retrieving ${this.tableName}`, error)
-                throw new Error(`Error retrieving ${this.tableName}`)
+                console.error(`Error retrieving ${tableName}`, error)
+                throw new Error(`Error retrieving ${tableName}`)
             } finally {
                 await dataSource.destroy()
             }
@@ -98,7 +112,7 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
             try {
                 const queryRunner = dataSource.createQueryRunner()
                 const keys = [thread_id]
-                const sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${this.tableName} WHERE thread_id = $1 ORDER BY checkpoint_id DESC LIMIT 1`
+                const sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = $1 ORDER BY checkpoint_id DESC LIMIT 1`
 
                 const rows = await queryRunner.manager.query(sql, keys)
                 await queryRunner.release()
@@ -124,8 +138,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                     }
                 }
             } catch (error) {
-                console.error(`Error retrieving ${this.tableName}`, error)
-                throw new Error(`Error retrieving ${this.tableName}`)
+                console.error(`Error retrieving ${tableName}`, error)
+                throw new Error(`Error retrieving ${tableName}`)
             } finally {
                 await dataSource.destroy()
             }
@@ -139,7 +153,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
 
         const queryRunner = dataSource.createQueryRunner()
         const thread_id = config.configurable?.thread_id || this.threadId
-        let sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${this.tableName} WHERE thread_id = $1`
+        const tableName = this.sanitizeTableName(this.tableName)
+        let sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = $1`
         const args = [thread_id]
 
         if (before?.configurable?.checkpoint_id) {
@@ -179,8 +194,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                 }
             }
         } catch (error) {
-            console.error(`Error listing ${this.tableName}`, error)
-            throw new Error(`Error listing ${this.tableName}`)
+            console.error(`Error listing ${tableName}`, error)
+            throw new Error(`Error listing ${tableName}`)
         } finally {
             await dataSource.destroy()
         }
@@ -200,8 +215,9 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                 Buffer.from(this.serde.stringify(checkpoint)), // Encode to binary
                 Buffer.from(this.serde.stringify(metadata)) // Encode to binary
             ]
+            const tableName = this.sanitizeTableName(this.tableName)
 
-            const query = `INSERT INTO ${this.tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata)
+            const query = `INSERT INTO ${tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata)
                            VALUES ($1, $2, $3, $4, $5)
                            ON CONFLICT (thread_id, checkpoint_id)
                            DO UPDATE SET checkpoint = EXCLUDED.checkpoint, metadata = EXCLUDED.metadata`
@@ -230,8 +246,9 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
 
         const dataSource = await this.getDataSource()
         await this.setup(dataSource)
+        const tableName = this.sanitizeTableName(this.tableName)
 
-        const query = `DELETE FROM "${this.tableName}" WHERE thread_id = $1;`
+        const query = `DELETE FROM "${tableName}" WHERE thread_id = $1;`
 
         try {
             const queryRunner = dataSource.createQueryRunner()
diff --git a/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts
index e38b5bbb3a3..351c7896185 100644
--- a/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts
+++ b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts
@@ -19,6 +19,18 @@ export class SqliteSaver extends BaseCheckpointSaver implements MemoryMethods {
         this.threadId = threadId
     }
 
+    sanitizeTableName(tableName: string): string {
+        // Trim and normalize case, turn whitespace into underscores
+        tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_')
+
+        // Validate using a regex (alphanumeric and underscores only)
+        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
+            throw new Error('Invalid table name')
+        }
+
+        return tableName
+    }
+
     private async getDataSource(): Promise<DataSource> {
         const { datasourceOptions } = this.config
         const dataSource = new DataSource(datasourceOptions)
@@ -33,8 +45,9 @@ export class SqliteSaver extends BaseCheckpointSaver implements MemoryMethods {
 
         try {
             const queryRunner = dataSource.createQueryRunner()
+            const tableName = this.sanitizeTableName(this.tableName)
             await queryRunner.manager.query(`
-CREATE TABLE IF NOT EXISTS ${this.tableName} (
+CREATE TABLE IF NOT EXISTS ${tableName} (
     thread_id TEXT NOT NULL,
     checkpoint_id TEXT NOT NULL,
     parent_id TEXT,
@@ -56,12 +69,13 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
 
         const thread_id = config.configurable?.thread_id || this.threadId
         const checkpoint_id = config.configurable?.checkpoint_id
+        const tableName = this.sanitizeTableName(this.tableName)
 
         if (checkpoint_id) {
             try {
                 const queryRunner = dataSource.createQueryRunner()
                 const keys = [thread_id, checkpoint_id]
-                const sql = `SELECT checkpoint, parent_id, metadata FROM ${this.tableName} WHERE thread_id = ? AND checkpoint_id = ?`
+                const sql = `SELECT checkpoint, parent_id, metadata FROM ${tableName} WHERE thread_id = ? AND checkpoint_id = ?`
 
                 const rows = await queryRunner.manager.query(sql, [...keys])
                 await queryRunner.release()
@@ -82,8 +96,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                     }
                 }
             } catch (error) {
-                console.error(`Error retrieving ${this.tableName}`, error)
-                throw new Error(`Error retrieving ${this.tableName}`)
+                console.error(`Error retrieving ${tableName}`, error)
+                throw new Error(`Error retrieving ${tableName}`)
             } finally {
                 await dataSource.destroy()
             }
@@ -91,7 +105,7 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
             try {
                 const queryRunner = dataSource.createQueryRunner()
                 const keys = [thread_id]
-                const sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${this.tableName} WHERE thread_id = ? ORDER BY checkpoint_id DESC LIMIT 1`
+                const sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = ? ORDER BY checkpoint_id DESC LIMIT 1`
 
                 const rows = await queryRunner.manager.query(sql, [...keys])
                 await queryRunner.release()
@@ -117,8 +131,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                     }
                 }
             } catch (error) {
-                console.error(`Error retrieving ${this.tableName}`, error)
-                throw new Error(`Error retrieving ${this.tableName}`)
+                console.error(`Error retrieving ${tableName}`, error)
+                throw new Error(`Error retrieving ${tableName}`)
             } finally {
                 await dataSource.destroy()
             }
@@ -132,7 +146,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
 
         const queryRunner = dataSource.createQueryRunner()
         const thread_id = config.configurable?.thread_id || this.threadId
-        let sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${this.tableName} WHERE thread_id = ? ${
+        const tableName = this.sanitizeTableName(this.tableName)
+        let sql = `SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM ${tableName} WHERE thread_id = ? ${
             before ? 'AND checkpoint_id < ?' : ''
         } ORDER BY checkpoint_id DESC`
         if (limit) {
@@ -167,8 +182,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                 }
             }
         } catch (error) {
-            console.error(`Error listing ${this.tableName}`, error)
-            throw new Error(`Error listing ${this.tableName}`)
+            console.error(`Error listing ${tableName}`, error)
+            throw new Error(`Error listing ${tableName}`)
         } finally {
             await dataSource.destroy()
         }
@@ -188,8 +203,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
                 this.serde.stringify(checkpoint),
                 this.serde.stringify(metadata)
             ]
-
-            const query = `INSERT OR REPLACE INTO ${this.tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata) VALUES (?, ?, ?, ?, ?)`
+            const tableName = this.sanitizeTableName(this.tableName)
+            const query = `INSERT OR REPLACE INTO ${tableName} (thread_id, checkpoint_id, parent_id, checkpoint, metadata) VALUES (?, ?, ?, ?, ?)`
 
             await queryRunner.manager.query(query, row)
             await queryRunner.release()
@@ -215,8 +230,8 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} (
 
         const dataSource = await this.getDataSource()
         await this.setup(dataSource)
-
-        const query = `DELETE FROM "${this.tableName}" WHERE thread_id = ?;`
+        const tableName = this.sanitizeTableName(this.tableName)
+        const query = `DELETE FROM "${tableName}" WHERE thread_id = ?;`
 
         try {
             const queryRunner = dataSource.createQueryRunner()
diff --git a/packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts b/packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts
index 193c13ba82a..a1571b028c1 100644
--- a/packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts
+++ b/packages/components/nodes/recordmanager/MySQLRecordManager/MySQLrecordManager.ts
@@ -178,6 +178,18 @@ class MySQLRecordManager implements RecordManagerInterface {
         this.config = config
     }
 
+    sanitizeTableName(tableName: string): string {
+        // Trim and normalize case, turn whitespace into underscores
+        tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_')
+
+        // Validate using a regex (alphanumeric and underscores only)
+        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
+            throw new Error('Invalid table name')
+        }
+
+        return tableName
+    }
+
     private async getDataSource(): Promise<DataSource> {
         const { mysqlOptions } = this.config
         if (!mysqlOptions) {
@@ -196,8 +208,9 @@ class MySQLRecordManager implements RecordManagerInterface {
         try {
             const dataSource = await this.getDataSource()
             const queryRunner = dataSource.createQueryRunner()
+            const tableName = this.sanitizeTableName(this.tableName)
 
-            await queryRunner.manager.query(`create table if not exists \`${this.tableName}\` (
+            await queryRunner.manager.query(`create table if not exists \`${this.sanitizeTableName(tableName)}\` (
                 \`uuid\` varchar(36) primary key default (UUID()),
                 \`key\` varchar(255) not null,
                 \`namespace\` varchar(255) not null,
@@ -211,11 +224,11 @@ class MySQLRecordManager implements RecordManagerInterface {
                 // MySQL does not support 'IF NOT EXISTS' function for Index
                 const Check = await queryRunner.manager.query(
                     `SELECT COUNT(1) IndexIsThere FROM INFORMATION_SCHEMA.STATISTICS 
-                        WHERE table_schema=DATABASE() AND table_name='${this.tableName}' AND index_name='${column}_index';`
+                        WHERE table_schema=DATABASE() AND table_name='${tableName}' AND index_name='${column}_index';`
                 )
                 if (Check[0].IndexIsThere === 0)
                     await queryRunner.manager.query(`CREATE INDEX \`${column}_index\`
-        ON \`${this.tableName}\` (\`${column}\`);`)
+        ON \`${tableName}\` (\`${column}\`);`)
             }
 
             await queryRunner.release()
@@ -253,6 +266,7 @@ class MySQLRecordManager implements RecordManagerInterface {
 
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         const updatedAt = await this.getTime()
         const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {}
@@ -275,7 +289,7 @@ class MySQLRecordManager implements RecordManagerInterface {
         ])
 
         const query = `
-            INSERT INTO \`${this.tableName}\` (\`key\`, \`namespace\`, \`updated_at\`, \`group_id\`)
+            INSERT INTO \`${tableName}\` (\`key\`, \`namespace\`, \`updated_at\`, \`group_id\`)
             VALUES (?, ?, ?, ?)
             ON DUPLICATE KEY UPDATE \`updated_at\` = VALUES(\`updated_at\`)`
 
@@ -302,12 +316,13 @@ class MySQLRecordManager implements RecordManagerInterface {
 
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         // Prepare the placeholders and the query
         const placeholders = keys.map(() => `?`).join(', ')
         const query = `
     SELECT \`key\`
-    FROM \`${this.tableName}\`
+    FROM \`${tableName}\`
     WHERE \`namespace\` = ? AND \`key\` IN (${placeholders})`
 
         // Initialize an array to fill with the existence checks
@@ -335,10 +350,11 @@ class MySQLRecordManager implements RecordManagerInterface {
     async listKeys(options?: ListKeyOptions): Promise<string[]> {
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         try {
             const { before, after, limit, groupIds } = options ?? {}
-            let query = `SELECT \`key\` FROM \`${this.tableName}\` WHERE \`namespace\` = ?`
+            let query = `SELECT \`key\` FROM \`${tableName}\` WHERE \`namespace\` = ?`
             const values: (string | number | string[])[] = [this.namespace]
 
             if (before) {
@@ -385,9 +401,10 @@ class MySQLRecordManager implements RecordManagerInterface {
 
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         const placeholders = keys.map(() => '?').join(', ')
-        const query = `DELETE FROM \`${this.tableName}\` WHERE \`namespace\` = ? AND \`key\` IN (${placeholders});`
+        const query = `DELETE FROM \`${tableName}\` WHERE \`namespace\` = ? AND \`key\` IN (${placeholders});`
         const values = [this.namespace, ...keys].map((v) => (typeof v !== 'string' ? `${v}` : v))
 
         // Directly using try/catch with async/await for cleaner flow
diff --git a/packages/components/nodes/recordmanager/PostgresRecordManager/PostgresRecordManager.ts b/packages/components/nodes/recordmanager/PostgresRecordManager/PostgresRecordManager.ts
index 94d154da2c0..63beee92793 100644
--- a/packages/components/nodes/recordmanager/PostgresRecordManager/PostgresRecordManager.ts
+++ b/packages/components/nodes/recordmanager/PostgresRecordManager/PostgresRecordManager.ts
@@ -186,6 +186,18 @@ class PostgresRecordManager implements RecordManagerInterface {
         this.config = config
     }
 
+    sanitizeTableName(tableName: string): string {
+        // Trim and normalize case, turn whitespace into underscores
+        tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_')
+
+        // Validate using a regex (alphanumeric and underscores only)
+        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
+            throw new Error('Invalid table name')
+        }
+
+        return tableName
+    }
+
     private async getDataSource(): Promise<DataSource> {
         const { postgresConnectionOptions } = this.config
         if (!postgresConnectionOptions) {
@@ -204,9 +216,10 @@ class PostgresRecordManager implements RecordManagerInterface {
         try {
             const dataSource = await this.getDataSource()
             const queryRunner = dataSource.createQueryRunner()
+            const tableName = this.sanitizeTableName(this.tableName)
 
             await queryRunner.manager.query(`
-  CREATE TABLE IF NOT EXISTS "${this.tableName}" (
+  CREATE TABLE IF NOT EXISTS "${tableName}" (
     uuid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
     key TEXT NOT NULL,
     namespace TEXT NOT NULL,
@@ -214,10 +227,10 @@ class PostgresRecordManager implements RecordManagerInterface {
     group_id TEXT,
     UNIQUE (key, namespace)
   );
-  CREATE INDEX IF NOT EXISTS updated_at_index ON "${this.tableName}" (updated_at);
-  CREATE INDEX IF NOT EXISTS key_index ON "${this.tableName}" (key);
-  CREATE INDEX IF NOT EXISTS namespace_index ON "${this.tableName}" (namespace);
-  CREATE INDEX IF NOT EXISTS group_id_index ON "${this.tableName}" (group_id);`)
+  CREATE INDEX IF NOT EXISTS updated_at_index ON "${tableName}" (updated_at);
+  CREATE INDEX IF NOT EXISTS key_index ON "${tableName}" (key);
+  CREATE INDEX IF NOT EXISTS namespace_index ON "${tableName}" (namespace);
+  CREATE INDEX IF NOT EXISTS group_id_index ON "${tableName}" (group_id);`)
 
             await queryRunner.release()
         } catch (e: any) {
@@ -269,6 +282,7 @@ class PostgresRecordManager implements RecordManagerInterface {
 
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         const updatedAt = await this.getTime()
         const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {}
@@ -287,7 +301,7 @@ class PostgresRecordManager implements RecordManagerInterface {
 
         const valuesPlaceholders = recordsToUpsert.map((_, j) => this.generatePlaceholderForRowAt(j, recordsToUpsert[0].length)).join(', ')
 
-        const query = `INSERT INTO "${this.tableName}" (key, namespace, updated_at, group_id) VALUES ${valuesPlaceholders} ON CONFLICT (key, namespace) DO UPDATE SET updated_at = EXCLUDED.updated_at;`
+        const query = `INSERT INTO "${tableName}" (key, namespace, updated_at, group_id) VALUES ${valuesPlaceholders} ON CONFLICT (key, namespace) DO UPDATE SET updated_at = EXCLUDED.updated_at;`
         try {
             await queryRunner.manager.query(query, recordsToUpsert.flat())
             await queryRunner.release()
@@ -306,12 +320,13 @@ class PostgresRecordManager implements RecordManagerInterface {
 
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         const startIndex = 2
         const arrayPlaceholders = keys.map((_, i) => `$${i + startIndex}`).join(', ')
 
         const query = `
-        SELECT k, (key is not null) ex from unnest(ARRAY[${arrayPlaceholders}]) k left join "${this.tableName}" on k=key and namespace = $1;
+        SELECT k, (key is not null) ex from unnest(ARRAY[${arrayPlaceholders}]) k left join "${tableName}" on k=key and namespace = $1;
         `
         try {
             const res = await queryRunner.manager.query(query, [this.namespace, ...keys.flat()])
@@ -327,7 +342,9 @@ class PostgresRecordManager implements RecordManagerInterface {
 
     async listKeys(options?: ListKeyOptions): Promise<string[]> {
         const { before, after, limit, groupIds } = options ?? {}
-        let query = `SELECT key FROM "${this.tableName}" WHERE namespace = $1`
+        const tableName = this.sanitizeTableName(this.tableName)
+
+        let query = `SELECT key FROM "${tableName}" WHERE namespace = $1`
         const values: (string | number | (string | null)[])[] = [this.namespace]
 
         let index = 2
@@ -379,9 +396,10 @@ class PostgresRecordManager implements RecordManagerInterface {
 
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         try {
-            const query = `DELETE FROM "${this.tableName}" WHERE namespace = $1 AND key = ANY($2);`
+            const query = `DELETE FROM "${tableName}" WHERE namespace = $1 AND key = ANY($2);`
             await queryRunner.manager.query(query, [this.namespace, keys])
             await queryRunner.release()
         } catch (error) {
diff --git a/packages/components/nodes/recordmanager/SQLiteRecordManager/SQLiteRecordManager.ts b/packages/components/nodes/recordmanager/SQLiteRecordManager/SQLiteRecordManager.ts
index 3246e250ef8..c209f4956fc 100644
--- a/packages/components/nodes/recordmanager/SQLiteRecordManager/SQLiteRecordManager.ts
+++ b/packages/components/nodes/recordmanager/SQLiteRecordManager/SQLiteRecordManager.ts
@@ -156,6 +156,18 @@ class SQLiteRecordManager implements RecordManagerInterface {
         this.config = config
     }
 
+    sanitizeTableName(tableName: string): string {
+        // Trim and normalize case, turn whitespace into underscores
+        tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_')
+
+        // Validate using a regex (alphanumeric and underscores only)
+        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
+            throw new Error('Invalid table name')
+        }
+
+        return tableName
+    }
+
     private async getDataSource(): Promise<DataSource> {
         const { sqliteOptions } = this.config
         if (!sqliteOptions) {
@@ -170,9 +182,10 @@ class SQLiteRecordManager implements RecordManagerInterface {
         try {
             const dataSource = await this.getDataSource()
             const queryRunner = dataSource.createQueryRunner()
+            const tableName = this.sanitizeTableName(this.tableName)
 
             await queryRunner.manager.query(`
-CREATE TABLE IF NOT EXISTS "${this.tableName}" (
+CREATE TABLE IF NOT EXISTS "${tableName}" (
   uuid TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
   key TEXT NOT NULL,
   namespace TEXT NOT NULL,
@@ -180,10 +193,10 @@ CREATE TABLE IF NOT EXISTS "${this.tableName}" (
   group_id TEXT,
   UNIQUE (key, namespace)
 );
-CREATE INDEX IF NOT EXISTS updated_at_index ON "${this.tableName}" (updated_at);
-CREATE INDEX IF NOT EXISTS key_index ON "${this.tableName}" (key);
-CREATE INDEX IF NOT EXISTS namespace_index ON "${this.tableName}" (namespace);
-CREATE INDEX IF NOT EXISTS group_id_index ON "${this.tableName}" (group_id);`)
+CREATE INDEX IF NOT EXISTS updated_at_index ON "${tableName}" (updated_at);
+CREATE INDEX IF NOT EXISTS key_index ON "${tableName}" (key);
+CREATE INDEX IF NOT EXISTS namespace_index ON "${tableName}" (namespace);
+CREATE INDEX IF NOT EXISTS group_id_index ON "${tableName}" (group_id);`)
 
             await queryRunner.release()
         } catch (e: any) {
@@ -219,6 +232,7 @@ CREATE INDEX IF NOT EXISTS group_id_index ON "${this.tableName}" (group_id);`)
         }
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         const updatedAt = await this.getTime()
         const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {}
@@ -241,7 +255,7 @@ CREATE INDEX IF NOT EXISTS group_id_index ON "${this.tableName}" (group_id);`)
         ])
 
         const query = `
-        INSERT INTO "${this.tableName}" (key, namespace, updated_at, group_id)
+        INSERT INTO "${tableName}" (key, namespace, updated_at, group_id)
         VALUES (?, ?, ?, ?)
         ON CONFLICT (key, namespace) DO UPDATE SET updated_at = excluded.updated_at`
 
@@ -264,12 +278,13 @@ CREATE INDEX IF NOT EXISTS group_id_index ON "${this.tableName}" (group_id);`)
         if (keys.length === 0) {
             return []
         }
+        const tableName = this.sanitizeTableName(this.tableName)
 
         // Prepare the placeholders and the query
         const placeholders = keys.map(() => `?`).join(', ')
         const sql = `
     SELECT key
-    FROM "${this.tableName}"
+    FROM "${tableName}"
     WHERE namespace = ? AND key IN (${placeholders})`
 
         // Initialize an array to fill with the existence checks
@@ -299,7 +314,9 @@ CREATE INDEX IF NOT EXISTS group_id_index ON "${this.tableName}" (group_id);`)
 
     async listKeys(options?: ListKeyOptions): Promise<string[]> {
         const { before, after, limit, groupIds } = options ?? {}
-        let query = `SELECT key FROM "${this.tableName}" WHERE namespace = ?`
+        const tableName = this.sanitizeTableName(this.tableName)
+
+        let query = `SELECT key FROM "${tableName}" WHERE namespace = ?`
         const values: (string | number | string[])[] = [this.namespace]
 
         if (before) {
@@ -350,9 +367,10 @@ CREATE INDEX IF NOT EXISTS group_id_index ON "${this.tableName}" (group_id);`)
 
         const dataSource = await this.getDataSource()
         const queryRunner = dataSource.createQueryRunner()
+        const tableName = this.sanitizeTableName(this.tableName)
 
         const placeholders = keys.map(() => '?').join(', ')
-        const query = `DELETE FROM "${this.tableName}" WHERE namespace = ? AND key IN (${placeholders});`
+        const query = `DELETE FROM "${tableName}" WHERE namespace = ? AND key IN (${placeholders});`
         const values = [this.namespace, ...keys].map((v) => (typeof v !== 'string' ? `${v}` : v))
 
         // Directly using try/catch with async/await for cleaner flow
diff --git a/packages/components/nodes/sequentialagents/State/State.ts b/packages/components/nodes/sequentialagents/State/State.ts
index 7b3ded9bb06..31d7af0c0f7 100644
--- a/packages/components/nodes/sequentialagents/State/State.ts
+++ b/packages/components/nodes/sequentialagents/State/State.ts
@@ -196,7 +196,13 @@ class State_SeqAgents implements INode {
                 input
             }
 
-            let sandbox: any = {}
+            let sandbox: any = {
+                util: undefined,
+                Symbol: undefined,
+                child_process: undefined,
+                fs: undefined,
+                process: undefined
+            }
             sandbox['$vars'] = prepareSandboxVars(variables)
             sandbox['$flow'] = flow
 
@@ -212,7 +218,10 @@ class State_SeqAgents implements INode {
                 require: {
                     external: { modules: deps },
                     builtin: builtinDeps
-                }
+                },
+                eval: false,
+                wasm: false,
+                timeout: 10000
             } as any
 
             const vm = new NodeVM(nodeVMOptions)
diff --git a/packages/components/nodes/sequentialagents/commonUtils.ts b/packages/components/nodes/sequentialagents/commonUtils.ts
index 6133921767d..3fe298f7f04 100644
--- a/packages/components/nodes/sequentialagents/commonUtils.ts
+++ b/packages/components/nodes/sequentialagents/commonUtils.ts
@@ -153,7 +153,13 @@ export const processImageMessage = async (llm: BaseChatModel, nodeData: INodeDat
 export const getVM = async (appDataSource: DataSource, databaseEntities: IDatabaseEntity, nodeData: INodeData, flow: ICommonObject) => {
     const variables = await getVars(appDataSource, databaseEntities, nodeData)
 
-    let sandbox: any = {}
+    let sandbox: any = {
+        util: undefined,
+        Symbol: undefined,
+        child_process: undefined,
+        fs: undefined,
+        process: undefined
+    }
     sandbox['$vars'] = prepareSandboxVars(variables)
     sandbox['$flow'] = flow
 
@@ -169,7 +175,10 @@ export const getVM = async (appDataSource: DataSource, databaseEntities: IDataba
         require: {
             external: { modules: deps },
             builtin: builtinDeps
-        }
+        },
+        eval: false,
+        wasm: false,
+        timeout: 10000
     } as any
 
     return new NodeVM(nodeVMOptions)
diff --git a/packages/components/nodes/tools/ChatflowTool/ChatflowTool.ts b/packages/components/nodes/tools/ChatflowTool/ChatflowTool.ts
index 85edf6db8e3..06f2451afef 100644
--- a/packages/components/nodes/tools/ChatflowTool/ChatflowTool.ts
+++ b/packages/components/nodes/tools/ChatflowTool/ChatflowTool.ts
@@ -318,7 +318,15 @@ class ChatflowTool extends StructuredTool {
             body: JSON.stringify(body)
         }
 
-        let sandbox = { $callOptions: options, $callBody: body }
+        let sandbox = {
+            $callOptions: options,
+            $callBody: body,
+            util: undefined,
+            Symbol: undefined,
+            child_process: undefined,
+            fs: undefined,
+            process: undefined
+        }
 
         const code = `
 const fetch = require('node-fetch');
@@ -349,7 +357,10 @@ try {
             require: {
                 external: { modules: deps },
                 builtin: builtinDeps
-            }
+            },
+            eval: false,
+            wasm: false,
+            timeout: 10000
         } as any
 
         const vm = new NodeVM(vmOptions)
diff --git a/packages/components/nodes/tools/CustomTool/core.ts b/packages/components/nodes/tools/CustomTool/core.ts
index c8883eda7b2..e64f776b8b3 100644
--- a/packages/components/nodes/tools/CustomTool/core.ts
+++ b/packages/components/nodes/tools/CustomTool/core.ts
@@ -111,7 +111,13 @@ export class DynamicStructuredTool<
         _?: CallbackManagerForToolRun,
         flowConfig?: { sessionId?: string; chatId?: string; input?: string; state?: ICommonObject }
     ): Promise<string> {
-        let sandbox: any = {}
+        let sandbox: any = {
+            util: undefined,
+            Symbol: undefined,
+            child_process: undefined,
+            fs: undefined,
+            process: undefined
+        }
         if (typeof arg === 'object' && Object.keys(arg).length) {
             for (const item in arg) {
                 sandbox[`$${item}`] = arg[item]
@@ -137,7 +143,10 @@ export class DynamicStructuredTool<
             require: {
                 external: { modules: deps },
                 builtin: builtinDeps
-            }
+            },
+            eval: false,
+            wasm: false,
+            timeout: 10000
         } as any
 
         const vm = new NodeVM(options)
diff --git a/packages/components/nodes/tools/OpenAPIToolkit/core.ts b/packages/components/nodes/tools/OpenAPIToolkit/core.ts
index 162be092075..8341adc5400 100644
--- a/packages/components/nodes/tools/OpenAPIToolkit/core.ts
+++ b/packages/components/nodes/tools/OpenAPIToolkit/core.ts
@@ -196,7 +196,13 @@ export class DynamicStructuredTool<
         _?: CallbackManagerForToolRun,
         flowConfig?: { sessionId?: string; chatId?: string; input?: string; state?: ICommonObject }
     ): Promise<string> {
-        let sandbox: any = {}
+        let sandbox: any = {
+            util: undefined,
+            Symbol: undefined,
+            child_process: undefined,
+            fs: undefined,
+            process: undefined
+        }
         if (typeof arg === 'object' && Object.keys(arg).length) {
             for (const item in arg) {
                 sandbox[`$${item}`] = arg[item]
@@ -237,7 +243,10 @@ export class DynamicStructuredTool<
             require: {
                 external: { modules: deps },
                 builtin: builtinDeps
-            }
+            },
+            eval: false,
+            wasm: false,
+            timeout: 10000
         } as any
 
         const vm = new NodeVM(options)
diff --git a/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts b/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts
index c6f51bc10e9..1bfddefca69 100644
--- a/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts
+++ b/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts
@@ -117,7 +117,14 @@ class CustomFunction_Utilities implements INode {
             }
         }
 
-        let sandbox: any = { $input: input }
+        let sandbox: any = {
+            $input: input,
+            util: undefined,
+            Symbol: undefined,
+            child_process: undefined,
+            fs: undefined,
+            process: undefined
+        }
         sandbox['$vars'] = prepareSandboxVars(variables)
         sandbox['$flow'] = flow
         sandbox['$tools'] = tools
@@ -140,7 +147,10 @@ class CustomFunction_Utilities implements INode {
             require: {
                 external: { modules: deps },
                 builtin: builtinDeps
-            }
+            },
+            eval: false,
+            wasm: false,
+            timeout: 10000
         } as any
 
         const vm = new NodeVM(nodeVMOptions)
diff --git a/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts b/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts
index 15100158c90..08b81163652 100644
--- a/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts
+++ b/packages/components/nodes/utilities/IfElseFunction/IfElseFunction.ts
@@ -119,7 +119,14 @@ class IfElseFunction_Utilities implements INode {
             }
         }
 
-        let sandbox: any = { $input: input }
+        let sandbox: any = {
+            $input: input,
+            util: undefined,
+            Symbol: undefined,
+            child_process: undefined,
+            fs: undefined,
+            process: undefined
+        }
         sandbox['$vars'] = prepareSandboxVars(variables)
         sandbox['$flow'] = flow
 
@@ -141,7 +148,10 @@ class IfElseFunction_Utilities implements INode {
             require: {
                 external: { modules: deps },
                 builtin: builtinDeps
-            }
+            },
+            eval: false,
+            wasm: false,
+            timeout: 10000
         } as any
 
         const vm = new NodeVM(nodeVMOptions)
diff --git a/packages/components/nodes/vectorstores/Postgres/driver/Base.ts b/packages/components/nodes/vectorstores/Postgres/driver/Base.ts
index 1616c71f7d4..fc1379f2682 100644
--- a/packages/components/nodes/vectorstores/Postgres/driver/Base.ts
+++ b/packages/components/nodes/vectorstores/Postgres/driver/Base.ts
@@ -28,13 +28,25 @@ export abstract class VectorStoreDriver {
     }
 
     getTableName() {
-        return getTableName(this.nodeData)
+        return this.sanitizeTableName(getTableName(this.nodeData))
     }
 
     getEmbeddings() {
         return this.nodeData.inputs?.embeddings as Embeddings
     }
 
+    sanitizeTableName(tableName: string): string {
+        // Trim and normalize case, turn whitespace into underscores
+        tableName = tableName.trim().toLowerCase().replace(/\s+/g, '_')
+
+        // Validate using a regex (alphanumeric and underscores only)
+        if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
+            throw new Error('Invalid table name')
+        }
+
+        return tableName
+    }
+
     async getCredentials() {
         const credentialData = await getCredentialData(this.nodeData.credential ?? '', this.options)
         const user = getCredentialParam('user', credentialData, this.nodeData, process.env.POSTGRES_VECTORSTORE_USER)
diff --git a/packages/components/src/storageUtils.ts b/packages/components/src/storageUtils.ts
index 0c354fec1ff..2b7146cde02 100644
--- a/packages/components/src/storageUtils.ts
+++ b/packages/components/src/storageUtils.ts
@@ -122,10 +122,12 @@ export const addSingleFileToStorage = async (mime: string, bf: Buffer, fileName:
 
 export const getFileFromStorage = async (file: string, ...paths: string[]): Promise<Buffer> => {
     const storageType = getStorageType()
+    const sanitizedFilename = _sanitizeFilename(file)
+
     if (storageType === 's3') {
         const { s3Client, Bucket } = getS3Config()
 
-        let Key = paths.reduce((acc, cur) => acc + '/' + cur, '') + '/' + file
+        let Key = paths.reduce((acc, cur) => acc + '/' + cur, '') + '/' + sanitizedFilename
         if (Key.startsWith('/')) {
             Key = Key.substring(1)
         }
@@ -147,7 +149,7 @@ export const getFileFromStorage = async (file: string, ...paths: string[]): Prom
         const buffer = Buffer.concat(response.Body.toArray())
         return buffer
     } else {
-        const fileInStorage = path.join(getStoragePath(), ...paths, file)
+        const fileInStorage = path.join(getStoragePath(), ...paths, sanitizedFilename)
         return fs.readFileSync(fileInStorage)
     }
 }