Skip to content

Commit c3c5f31

Browse files
committed
feat: 将敏感 AI 功能路由分组并添加数据源管理权限控制,同时为 SQL 查询增加结果集限制,并增强了命令执行的安全性。
1 parent b977175 commit c3c5f31

2 files changed

Lines changed: 29 additions & 6 deletions

File tree

backend/app/services/query_execution_service.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,24 @@ export default class QueryExecutionService {
6565
}
6666

6767
try {
68+
// Safety check: Enforce a firm limit on results to prevent OOM
69+
let finalSql = sql.trim()
70+
if (ds.type === 'mysql' || ds.type === 'postgresql') {
71+
finalSql = finalSql.replace(/;$/, '')
72+
if (!/\bLIMIT\b/i.test(finalSql)) {
73+
finalSql += ' LIMIT 1000'
74+
}
75+
}
76+
6877
if (ds.type === 'mysql') {
69-
const { result, values } = replaceVars(sql, inputParams, 'sql')
78+
const { result, values } = replaceVars(finalSql, inputParams, 'sql')
7079
executedSql = result
7180
const { client } = await DbHelper.getRawConnection(ds.id, timeout)
7281
const [rows] = await (client as any).execute({ sql: executedSql, values, timeout })
7382
await client.end()
7483
queryResults = rows
7584
} else if (ds.type === 'postgresql') {
76-
const { result, values } = replaceVars(sql, inputParams, 'pg')
85+
const { result, values } = replaceVars(finalSql, inputParams, 'pg')
7786
executedSql = result
7887
const { client } = await DbHelper.getRawConnection(ds.id, timeout)
7988
try {
@@ -97,6 +106,14 @@ export default class QueryExecutionService {
97106
)
98107
}
99108

109+
// --- Phase 3: Critical Security Guard against Command Injection (RCE) ---
110+
// Block all bash control operators, pipeline characters, command substitutions,
111+
// environment variable access, and dangerous redirections to ensure `curl` cannot be escaped.
112+
const forbiddenBashShellChars = /[;&|$`\n<>]/
113+
if (forbiddenBashShellChars.test(commandToExecute)) {
114+
throw new Error(`Command validation failed: Command contains forbidden shell meta-characters. Potential Command Injection isolated.`)
115+
}
116+
100117
const { stdout } = await execAsync(executedSql, {
101118
timeout,
102119
maxBuffer: 10 * 1024 * 1024,

backend/start/routes.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,18 @@ router
139139
// Execution
140140
router.post('query-tasks/:id/execute', [ExecutionController, 'execute'])
141141

142-
// AI Features
143-
router.post('ai/test-connection', [AiController, 'testConnection'])
144-
router.post('ai/optimize-sql', [AiController, 'optimizeSql'])
142+
// Sensitive AI Features (Requires Data Source Management Permission)
143+
router
144+
.group(() => {
145+
router.post('ai/test-connection', [AiController, 'testConnection'])
146+
router.post('ai/optimize-sql', [AiController, 'optimizeSql'])
147+
router.post('ai/preview', [AiController, 'preview'])
148+
})
149+
.use(middleware.rbac({ permission: PERMISSIONS.MANAGE_DATA_SOURCES }))
150+
151+
// General AI Features (Dialogue & Learning)
145152
router.post('ai/chat', [AiController, 'chat'])
146153
router.post('ai/chat/stream', [AiController, 'chatStream'])
147-
router.post('ai/preview', [AiController, 'preview'])
148154
router.post('ai/learn', [AiController, 'learn'])
149155
router.post('ai/transcribe', [AiController, 'transcribe'])
150156
router.get('ai/graph/visualize', [AiController, 'visualizeGraph'])

0 commit comments

Comments
 (0)