mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(MySQL Node): Fix potential sql injection (#13818)
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
/* eslint-disable n8n-nodes-base/node-param-display-name-miscased */
|
||||
import mysql2 from 'mysql2/promise';
|
||||
import type { ILoadOptionsFunctions, INodeListSearchResult } from 'n8n-workflow';
|
||||
|
||||
import { searchTables } from '../../v1/GenericFunctions';
|
||||
|
||||
jest.mock('mysql2/promise');
|
||||
|
||||
describe('MySQL / v1 / Generic Functions', () => {
|
||||
let mockLoadOptionsFunctions: ILoadOptionsFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
||||
mockLoadOptionsFunctions = {
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
database: 'test_db',
|
||||
}),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
});
|
||||
|
||||
describe('searchTables', () => {
|
||||
it('should return matching tables', async () => {
|
||||
const mockRows = [{ table_name: 'users' }, { table_name: 'products' }];
|
||||
|
||||
const mockQuery = jest.fn().mockResolvedValue([mockRows]);
|
||||
const mockEnd = jest.fn().mockResolvedValue(undefined);
|
||||
|
||||
(mysql2.createConnection as jest.Mock).mockResolvedValue({
|
||||
query: mockQuery,
|
||||
end: mockEnd,
|
||||
});
|
||||
|
||||
const result: INodeListSearchResult = await searchTables.call(
|
||||
mockLoadOptionsFunctions,
|
||||
'user',
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: [
|
||||
{ name: 'users', value: 'users' },
|
||||
{ name: 'products', value: 'products' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(mockQuery).toHaveBeenCalledWith(
|
||||
`SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = ?
|
||||
AND table_name LIKE ?
|
||||
ORDER BY table_name`,
|
||||
['test_db', '%user%'],
|
||||
);
|
||||
|
||||
expect(mockEnd).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle empty search query', async () => {
|
||||
const mockRows: any[] = [];
|
||||
|
||||
const mockQuery = jest.fn().mockResolvedValue([mockRows]);
|
||||
const mockEnd = jest.fn().mockResolvedValue(undefined);
|
||||
|
||||
(mysql2.createConnection as jest.Mock).mockResolvedValue({
|
||||
query: mockQuery,
|
||||
end: mockEnd,
|
||||
});
|
||||
|
||||
const result = await searchTables.call(mockLoadOptionsFunctions);
|
||||
|
||||
expect(result).toEqual({ results: [] });
|
||||
expect(mockQuery).toHaveBeenCalledWith(
|
||||
`SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = ?
|
||||
AND table_name LIKE ?
|
||||
ORDER BY table_name`,
|
||||
['test_db', '%%'],
|
||||
);
|
||||
|
||||
expect(mockEnd).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle database errors', async () => {
|
||||
const mockError = new Error('Database connection failed');
|
||||
|
||||
(mysql2.createConnection as jest.Mock).mockRejectedValue(mockError);
|
||||
|
||||
await expect(searchTables.call(mockLoadOptionsFunctions)).rejects.toThrow(
|
||||
'Database connection failed',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -30,20 +30,21 @@ export async function createConnection(
|
||||
|
||||
export async function searchTables(
|
||||
this: ILoadOptionsFunctions,
|
||||
query?: string,
|
||||
tableName?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const credentials = await this.getCredentials('mySql');
|
||||
const connection = await createConnection(credentials);
|
||||
const sql = `
|
||||
SELECT table_name FROM information_schema.tables
|
||||
WHERE table_schema = '${credentials.database}'
|
||||
and table_name like '%${query || ''}%'
|
||||
ORDER BY table_name
|
||||
`;
|
||||
const [rows] = await connection.query(sql);
|
||||
const results = (rows as IDataObject[]).map((r) => ({
|
||||
name: r.TABLE_NAME as string,
|
||||
value: r.TABLE_NAME as string,
|
||||
const sql = `SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = ?
|
||||
AND table_name LIKE ?
|
||||
ORDER BY table_name`;
|
||||
|
||||
const values = [credentials.database, `%${tableName ?? ''}%`];
|
||||
const [rows] = await connection.query(sql, values);
|
||||
const results = (rows as IDataObject[]).map((table) => ({
|
||||
name: (table.table_name as string) || (table.TABLE_NAME as string),
|
||||
value: (table.table_name as string) || (table.TABLE_NAME as string),
|
||||
}));
|
||||
await connection.end();
|
||||
return { results };
|
||||
|
||||
Reference in New Issue
Block a user