mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
fix(sql-collection): block dangerous keywords (#5913)
This commit is contained in:
parent
3eb4907fcc
commit
d3de8c4ffd
@ -42,6 +42,14 @@ describe('sql collection', () => {
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body.errors[0].message).toMatch('Only supports SELECT statements or WITH clauses');
|
||||
|
||||
res = await agent.resource('sqlCollection').execute({
|
||||
values: {
|
||||
sql: "select pg_read_file('/etc/passwd');",
|
||||
},
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body.errors[0].message).toMatch('SQL statements contain dangerous keywords');
|
||||
});
|
||||
|
||||
it('sqlCollection:execute', async () => {
|
||||
@ -244,4 +252,16 @@ describe('sql collection', () => {
|
||||
const loadedFields2 = db.getCollection('sqlCollection').fields;
|
||||
expect(loadedFields2.size).toBe(1);
|
||||
});
|
||||
|
||||
it('should check sql when creating', async () => {
|
||||
const res = await agent.resource('collections').create({
|
||||
values: {
|
||||
name: 'sqlCollection',
|
||||
sql: "select pg_read_file('/etc/passwd');",
|
||||
template: 'sql',
|
||||
},
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body.errors[0].message).toMatch('SQL statements contain dangerous keywords');
|
||||
});
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ import { Plugin } from '@nocobase/server';
|
||||
import { Collection } from '@nocobase/database';
|
||||
import { SQLCollection } from './sql-collection';
|
||||
import sqlResourcer from './resources/sql';
|
||||
import { checkSQL } from './utils';
|
||||
|
||||
export class PluginCollectionSQLServer extends Plugin {
|
||||
async beforeLoad() {
|
||||
@ -34,6 +35,21 @@ export class PluginCollectionSQLServer extends Plugin {
|
||||
name: `pm.data-source-manager.collection-sql `,
|
||||
actions: ['sqlCollection:*'],
|
||||
});
|
||||
|
||||
this.app.resourceManager.use(async (ctx, next) => {
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
if (resourceName === 'collections' && actionName === 'create') {
|
||||
const { sql } = ctx.action.params.values || {};
|
||||
if (sql) {
|
||||
try {
|
||||
checkSQL(sql);
|
||||
} catch (e) {
|
||||
ctx.throw(400, ctx.t(e.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
return next();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
import { Context, Next } from '@nocobase/actions';
|
||||
import { SQLCollection, SQLModel } from '../sql-collection';
|
||||
import { checkSQL } from '../utils';
|
||||
|
||||
const updateCollection = async (ctx: Context, transaction: any) => {
|
||||
const { filterByTk, values } = ctx.action.params;
|
||||
@ -48,13 +49,14 @@ export default {
|
||||
name: 'sqlCollection',
|
||||
actions: {
|
||||
execute: async (ctx: Context, next: Next) => {
|
||||
let { sql } = ctx.action.params.values || {};
|
||||
const { sql } = ctx.action.params.values || {};
|
||||
if (!sql) {
|
||||
ctx.throw(400, ctx.t('Please enter a SQL statement'));
|
||||
}
|
||||
sql = sql.trim().split(';').shift();
|
||||
if (!/^select/i.test(sql) && !/^with([\s\S]+)select([\s\S]+)/i.test(sql)) {
|
||||
ctx.throw(400, ctx.t('Only supports SELECT statements or WITH clauses'));
|
||||
try {
|
||||
checkSQL(sql);
|
||||
} catch (e) {
|
||||
ctx.throw(400, ctx.t(e.message));
|
||||
}
|
||||
const tmpCollection = new SQLCollection({ name: 'tmp', sql }, { database: ctx.db });
|
||||
const model = tmpCollection.model as typeof SQLModel;
|
||||
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
export const checkSQL = (sql: string) => {
|
||||
const dangerKeywords = [
|
||||
// PostgreSQL
|
||||
'pg_read_file',
|
||||
'pg_read_binary_file',
|
||||
'pg_stat_file',
|
||||
'pg_ls_dir',
|
||||
'pg_logdir_ls',
|
||||
'pg_terminate_backend',
|
||||
'pg_cancel_backend',
|
||||
'current_setting',
|
||||
'set_config',
|
||||
'pg_reload_conf',
|
||||
'pg_sleep',
|
||||
'generate_series',
|
||||
|
||||
// MySQL
|
||||
'LOAD_FILE',
|
||||
'BENCHMARK',
|
||||
'@@global.',
|
||||
'@@session.',
|
||||
|
||||
// SQLite
|
||||
'sqlite3_load_extension',
|
||||
'load_extension',
|
||||
];
|
||||
sql = sql.trim().split(';').shift();
|
||||
if (!/^select/i.test(sql) && !/^with([\s\S]+)select([\s\S]+)/i.test(sql)) {
|
||||
throw new Error('Only supports SELECT statements or WITH clauses');
|
||||
}
|
||||
if (dangerKeywords.some((keyword) => sql.toLowerCase().includes(keyword.toLowerCase()))) {
|
||||
throw new Error('SQL statements contain dangerous keywords');
|
||||
}
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user