"""工单风险等级自动评估。 根据 SQL 类型 + 操作对象 + 审核结果,自动计算 risk_level: - low: SELECT / 简单 DML (有 WHERE + LIMIT) - medium: 普通 DML / 低风险 DDL (CREATE TABLE) - high: 大表 ALTER / 无 WHERE 的 UPDATE/DELETE / 多表 JOIN - critical: DROP TABLE / TRUNCATE / FLUSHDB / FLUSHALL / dropDatabase 评估时机:工单创建时自动计算,写入 ticket.risk_level。 """ import re from typing import Optional def evaluate_risk_level( sql_content: str, db_type: str = "mysql", sql_type: str = "", audit_findings: Optional[list] = None, statement_count: int = 1, table_sizes: Optional[dict] = None, ) -> str: """评估 SQL 的风险等级。 Args: sql_content: SQL 文本(可能包含多条语句) db_type: 数据库类型 mysql/postgres/redis/mongodb sql_type: 前端传入的 SQL 类型标注 DDL/DML/DQL 等 audit_findings: 审核引擎返回的 findings 列表 statement_count: SQL 语句条数 table_sizes: EXPLAIN 预检返回的表大小 {table_name: row_count} Returns: risk_level: low / medium / high / critical """ sql_upper = sql_content.strip().upper() # ── Redis 特殊处理 ── if db_type == "redis": return _evaluate_redis_risk(sql_upper) # ── MongoDB 特殊处理 ── if db_type == "mongodb": return _evaluate_mongo_risk(sql_upper) # ── MySQL / PostgreSQL ── # 1. critical 级别 — 不可逆危险操作 critical_patterns = [ r"\bDROP\s+TABLE\b", r"\bDROP\s+DATABASE\b", r"\bDROP\s+SCHEMA\b", r"\bTRUNCATE\s", ] for pattern in critical_patterns: if re.search(pattern, sql_upper): return "critical" # 2. high 级别 — 高风险操作 high_indicators = 0 # ALTER 大操作(但不包括 ADD INDEX 这类较小操作) if re.search(r"\bALTER\s+TABLE\b", sql_upper): alter_high = [ r"\bDROP\s+COLUMN\b", r"\bMODIFY\s+COLUMN\b", r"\bCHANGE\s+COLUMN\b", r"\bDROP\s+INDEX\b", r"\bDROP\s+PRIMARY\s+KEY\b", r"\bCONVERT\s+TO\s+CHARACTER\s+SET\b", r"\bRENAME\s+(TO|AS)\b", ] for p in alter_high: if re.search(p, sql_upper): high_indicators += 1 break # 无 WHERE 的 UPDATE / DELETE if re.search(r"\bUPDATE\s", sql_upper) and not re.search(r"\bWHERE\b", sql_upper): high_indicators += 1 if re.search(r"\bDELETE\s+FROM\b", sql_upper) and not re.search(r"\bWHERE\b", sql_upper): high_indicators += 1 # 大量语句(>5条) if statement_count > 5: high_indicators += 1 # 审核结果有 ERROR 级别 if audit_findings: errors = [f for f in audit_findings if f.get("level") == "error"] if len(errors) >= 2: high_indicators += 1 if high_indicators > 0: return "high" # 2.5 大表检测 — EXPLAIN 预检返回的 table_sizes # ALTER/UPDATE/DELETE 涉及大表(>10万行)时升级为 high if table_sizes: max_rows = max(table_sizes.values()) if table_sizes else 0 if max_rows > 100_000 and re.search(r"\b(ALTER\s+TABLE|UPDATE|DELETE\s+FROM)\b", sql_upper): return "high" # 3. medium 级别 — 常规变更 medium_types = {"DDL", "DML", "SET", "HSET", "HMSET"} if sql_type in medium_types: return "medium" # 有 WHERE 但没有 LIMIT 的 DELETE if re.search(r"\bDELETE\s+FROM\b", sql_upper): if not re.search(r"\bLIMIT\b", sql_upper): return "medium" # 任何 DDL ddl_keywords = ["CREATE ", "ALTER ", "RENAME ", "GRANT ", "REVOKE ", "CREATE INDEX", "CREATE UNIQUE"] for kw in ddl_keywords: if kw in sql_upper: return "medium" # 4. low 级别 — 只读 / 安全操作 return "low" def _evaluate_redis_risk(sql_upper: str) -> str: """Redis 命令风险评估。""" critical_cmds = ["FLUSHDB", "FLUSHALL"] for cmd in critical_cmds: if sql_upper.startswith(cmd): return "critical" # 危险写操作 high_cmds = ["DEL ", "UNLINK ", "RENAME ", "FLUSHDB", "FLUSHALL"] for cmd in high_cmds: if sql_upper.startswith(cmd): return "high" # 一般写操作 write_cmds = ["SET ", "HSET ", "HMSET ", "LPUSH ", "RPUSH ", "SADD ", "ZADD ", "INCR", "DECR", "EXPIRE ", "PEXPIRE "] for cmd in write_cmds: if sql_upper.startswith(cmd): return "medium" # 只读命令 return "low" def _evaluate_mongo_risk(sql_upper: str) -> str: """MongoDB 操作风险评估。""" sql_lower = sql_upper.lower() critical_ops = ["dropdatabase", "drop database"] for op in critical_ops: if op in sql_lower: return "critical" high_ops = ["drop(", "dropcollection", "remove(", "deletemany"] for op in high_ops: if op in sql_lower: return "high" write_ops = ["insertone", "insertmany", "updateone", "updatemany", "deleteone", "createindex", "create_collection"] for op in write_ops: if op in sql_lower: return "medium" return "low" # ── 风险等级配置(用于前端展示 / 审批流匹配)── RISK_LEVEL_CONFIG = { "low": { "label": "低风险", "color": "#52c41a", "description": "只读查询 / 安全操作", "min_approvers": 1, }, "medium": { "label": "中风险", "color": "#faad14", "description": "常规数据变更 / DDL 操作", "min_approvers": 1, }, "high": { "label": "高风险", "color": "#fa8c16", "description": "无 WHERE 删除/更新 / 大表 ALTER / 批量操作", "min_approvers": 2, }, "critical": { "label": "极高风险", "color": "#f5222d", "description": "DROP TABLE / TRUNCATE / FLUSHDB 等不可逆操作", "min_approvers": 2, }, }