fix: 审核引擎优化 + 执行结果结构化展示
后端审核规则修复: - Redis SEC-001: SET/DEL等常规命令不再误报AUTH要求,改为仅对管理级命令WARNING - Redis SEC-003: 降级为INFO,排除已由CMD-001拦截的FLUSHDB/FLUSHALL - Redis key提取: 排除CONFIG/ACL等管理命令,不再把子命令参数误认为key名 - MySQL SEC-006: 新增GRANT ALL/REVOKE ALL全局权限拦截 - MySQL SEC-007: 新增LOAD DATA/LOAD XML拦截 - MySQL SEC-008: 新增SLEEP()/BENCHMARK()时间盲注拦截 - MySQL SEC-009: 新增INTO OUTFILE/DUMPFILE文件写入拦截 前端执行结果结构化: - TicketDetailPage执行结果从原始JSON改为结构化展示 - 解析JSON展示: 状态徽章+影响行数+耗时+验证状态 - Errors/Warnings逐条渲染: 红色错误框+黄色警告框+具体消息 - 删除废弃的executeStats/renderExecuteStats 其他: 审核规则6分组/慢查询移除/查询数据重命名/AuditLogs修复 测试: 1040 passed, TSC零错误
This commit is contained in:
1 parent
9f56e866f2
commit
4ac2528f8e
19 files changed
+1202
-325
No files matched your search
@@ -26,7 +26,7 @@ interface AuditRule {
|
||||
}
|
||||
|
||||
type DbType = 'mysql' | 'postgresql' | 'redis' | 'mongodb';
|
||||
type RuleGroup = 'DDL' | 'DML' | 'DCL' | 'DQL';
|
||||
type RuleGroup = 'DDL' | 'DML' | 'DCL' | 'DQL' | 'security' | 'performance';
|
||||
type SeverityFilter = 'all' | 'error' | 'warning' | 'info';
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -51,6 +51,8 @@ const GROUP_CONFIG: Record<RuleGroup, { label: string; icon: React.ElementType;
|
||||
DML: { label: 'DML 规则', icon: Shield, color: 'text-blue-400 bg-blue-500/15' },
|
||||
DCL: { label: 'DCL 规则', icon: Shield, color: 'text-purple-400 bg-purple-500/15' },
|
||||
DQL: { label: 'DQL 规则', icon: FileSearch, color: 'text-green-400 bg-green-500/15' },
|
||||
security: { label: '安全规则', icon: AlertTriangle, color: 'text-red-400 bg-red-500/15' },
|
||||
performance: { label: '性能规则', icon: Info, color: 'text-cyan-400 bg-cyan-500/15' },
|
||||
};
|
||||
|
||||
const SEVERITY_CONFIG: Record<string, { label: string; color: string; dot: string }> = {
|
||||
@@ -59,34 +61,17 @@ const SEVERITY_CONFIG: Record<string, { label: string; color: string; dot: strin
|
||||
info: { label: '提示', color: 'text-blue-400', dot: 'bg-blue-400' },
|
||||
};
|
||||
|
||||
const DDL_KEYWORDS = [
|
||||
'DDL', 'CREATE', 'ALTER', 'DROP', 'TRUNCATE', 'RENAME',
|
||||
'TABLE', 'INDEX', 'KEY', 'COLUMN', 'PARTITION', 'VIEW',
|
||||
'DATABASE', 'SCHEMA', 'SEQUENCE', '集合', 'COLLECTION',
|
||||
' Validator', 'CAPPED', 'SCHEMA',
|
||||
];
|
||||
|
||||
const DML_KEYWORDS = ['DML', 'INSERT', 'UPDATE', 'DELETE', 'REPLACE', 'MERGE', 'BULK', '写入', 'UNSET'];
|
||||
|
||||
const DCL_KEYWORDS = ['DCL', 'GRANT', 'REVOKE', 'DENY'];
|
||||
|
||||
const DQL_KEYWORDS = ['DQL', 'SELECT', 'EXPLAIN', 'DESCRIBE', 'SHOW', 'FIND', '查询', '聚合', 'AGGREGAT', 'PROJECTION', 'FILTER', 'REGEX', '$WHERE'];
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Helpers */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
function getRuleGroup(name: string): RuleGroup {
|
||||
const upper = name.toUpperCase();
|
||||
if (DDL_KEYWORDS.some((kw) => upper.includes(kw))) return 'DDL';
|
||||
if (DML_KEYWORDS.some((kw) => upper.includes(kw))) return 'DML';
|
||||
if (DCL_KEYWORDS.some((kw) => upper.includes(kw))) return 'DCL';
|
||||
if (DQL_KEYWORDS.some((kw) => upper.includes(kw))) return 'DQL';
|
||||
// MongoDB security rules -> DCL, performance rules -> DQL
|
||||
if (upper.includes('SECURITY') || upper.includes('安全') || upper.includes('审计') || upper.includes('注入') || upper.includes('EVAL') || upper.includes('MAPSQL_REDUCE')) return 'DCL';
|
||||
if (upper.includes('PERFORMANCE') || upper.includes('性能') || upper.includes('索引') || upper.includes('扫描') || upper.includes('分片') || upper.includes('SHARD')) return 'DQL';
|
||||
// Default to DDL for structural rules, DML for data rules
|
||||
if (upper.includes('WHERE') || upper.includes('写入') || upper.includes('UPDATE') || upper.includes('DELETE')) return 'DML';
|
||||
const VALID_GROUPS: RuleGroup[] = ['DDL', 'DML', 'DCL', 'DQL', 'security', 'performance'];
|
||||
|
||||
function getRuleGroup(rule: AuditRule): RuleGroup {
|
||||
const rt = rule.rule_type;
|
||||
if (VALID_GROUPS.includes(rt as RuleGroup)) return rt as RuleGroup;
|
||||
// Fallback: map unknown types
|
||||
if (rt === 'safety' || rt === 'naming' || rt === 'syntax') return 'DDL';
|
||||
return 'DDL';
|
||||
}
|
||||
|
||||
@@ -99,7 +84,7 @@ function sortRules(rules: AuditRule[], sortBy: 'severity' | 'name' | 'type'): Au
|
||||
case 'name':
|
||||
return sorted.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'));
|
||||
case 'type':
|
||||
return sorted.sort((a, b) => getRuleGroup(a.name).localeCompare(getRuleGroup(b.name)));
|
||||
return sorted.sort((a, b) => getRuleGroup(a).localeCompare(getRuleGroup(b)));
|
||||
default:
|
||||
return sorted;
|
||||
}
|
||||
@@ -239,9 +224,11 @@ export default function AuditRulesPage() {
|
||||
DML: [],
|
||||
DCL: [],
|
||||
DQL: [],
|
||||
security: [],
|
||||
performance: [],
|
||||
};
|
||||
for (const rule of filteredRules) {
|
||||
const group = getRuleGroup(rule.name);
|
||||
const group = getRuleGroup(rule);
|
||||
groups[group].push(rule);
|
||||
}
|
||||
return groups;
|
||||
@@ -580,8 +567,10 @@ export default function AuditRulesPage() {
|
||||
<div className="space-y-6">
|
||||
{renderRuleGroup('DDL', groupedRules.DDL)}
|
||||
{renderRuleGroup('DML', groupedRules.DML)}
|
||||
{renderRuleGroup('security', groupedRules.security)}
|
||||
{renderRuleGroup('DCL', groupedRules.DCL)}
|
||||
{renderRuleGroup('DQL', groupedRules.DQL)}
|
||||
{renderRuleGroup('performance', groupedRules.performance)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user