核心功能: - 6 层检测管线: L0 结构分析 → L1 ClamAV → L2 文档深度检测 → L3 关键词匹配 → L4 YARA → L5 PE/二进制 - FastAPI + PostgreSQL + Redis + Docker Compose 架构 - 客户端 HMAC-SHA256 签名鉴权 + 限流 + 幂等性 - 控制台 UI (登录/Dashboard/诊断中心/策略管理/任务列表/测试中心/产品介绍页) - 三层诊断定时器 (error_scan 30s / db_aggregate 30s / container_check 5min) 并发与稳定性修复: - 诊断定时器 Redis 时间槽位锁 (多 worker 同一槽位只有一个执行) - JSONL 存储 fcntl 跨进程排他锁 + 容错解码 + 原子重写 - 移除 SQLite fallback, 统一 PostgreSQL - API_CLIENTS 脱敏为环境变量, .env 已 .gitignore
282 lines
9.7 KiB
Python
282 lines
9.7 KiB
Python
"""MeshSafe 统一中文标签体系。
|
||
|
||
所有控制台页面(概览 / 任务历史 / 测试靶场)共用同一套中文映射,
|
||
保证同一个后端字段在任何页面显示的名称、颜色、说明完全一致。
|
||
|
||
设计目标
|
||
--------
|
||
1. **术语统一** —— verdict / action / severity / engine / mode 全部中文化,
|
||
英文原值保留在 title 属性里供运维排查时对照。
|
||
2. **风险分级体系** —— 把「放行 / 可疑 / 恶意 / 策略拒绝 / 异常」归入
|
||
4 个风险等级(安全 / 注意 / 高危 / 故障),与策略引擎的裁决语义对齐。
|
||
3. **单一事实源** —— 新增页面或新增字段时只需在这里登记,
|
||
不用再到每个模板里写一遍 if-else。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any, Dict, List, Optional, Tuple
|
||
|
||
|
||
# =============================================================================
|
||
# 风险等级体系(全局统一)
|
||
# =============================================================================
|
||
|
||
#: 风险等级 → (中文名, 徽章 CSS 类, 语义说明)
|
||
RISK_LEVELS: Dict[str, Tuple[str, str, str]] = {
|
||
"safe": ("安全", "ok", "未检出任何风险信号,正常放行"),
|
||
"notice": ("注意", "warn", "存在可疑信号,建议人工确认后放行"),
|
||
"high": ("高危", "bad", "确认存在恶意特征,必须阻断"),
|
||
"fault": ("故障", "err", "检测过程异常,按安全优先策略阻断"),
|
||
"idle": ("处理中", "muted", "检测尚未完成"),
|
||
}
|
||
|
||
#: 风险等级排序(从高到低),用于排序统计
|
||
RISK_ORDER: List[str] = ["high", "notice", "fault", "safe", "idle"]
|
||
|
||
|
||
# =============================================================================
|
||
# 裁决结论(verdict)
|
||
# =============================================================================
|
||
|
||
#: verdict → (中文名, 风险等级)
|
||
VERDICT_LABELS: Dict[str, Tuple[str, str]] = {
|
||
"clean": ("放行", "safe"),
|
||
"pass": ("放行", "safe"),
|
||
"known": ("放行", "safe"),
|
||
"suspicious": ("可疑", "notice"),
|
||
"review": ("可疑", "notice"),
|
||
"malicious": ("恶意", "high"),
|
||
"reject": ("策略拒绝", "fault"),
|
||
"error": ("检测异常", "fault"),
|
||
"pending": ("处理中", "idle"),
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# 处置动作(action)
|
||
# =============================================================================
|
||
|
||
#: action → (中文名, 风险等级)
|
||
ACTION_LABELS: Dict[str, Tuple[str, str]] = {
|
||
"pass": ("放行", "safe"),
|
||
"pass_with_flag": ("带标记放行", "notice"),
|
||
"manual_review": ("人工复核", "notice"),
|
||
"reject": ("拦截", "high"),
|
||
"reject_and_notify": ("拦截并告警", "high"),
|
||
"error": ("异常阻断", "fault"),
|
||
"pending": ("处理中", "idle"),
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# 严重度(severity)
|
||
# =============================================================================
|
||
|
||
#: severity → (中文名, 徽章 CSS 类)
|
||
SEVERITY_LABELS: Dict[str, Tuple[str, str]] = {
|
||
"critical": ("严重", "bad"),
|
||
"high": ("高危", "bad"),
|
||
"medium": ("中危", "warn"),
|
||
"low": ("低危", "notice"),
|
||
"info": ("提示", "muted"),
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# 检测引擎(engine)
|
||
# =============================================================================
|
||
|
||
ENGINE_LABELS: Dict[str, str] = {
|
||
"structure": "文件结构分析",
|
||
"clamav": "ClamAV 病毒库",
|
||
"document": "文档深度检测",
|
||
"keywords": "关键词匹配",
|
||
"yara": "YARA 规则匹配",
|
||
"binary": "PE/二进制分析",
|
||
"extractor": "归档解压",
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# 扫描模式(mode)
|
||
# =============================================================================
|
||
|
||
MODE_LABELS: Dict[str, str] = {
|
||
"sync": "同步",
|
||
"async": "异步",
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# 样本分类(测试靶场 category)
|
||
# =============================================================================
|
||
|
||
CATEGORY_LABELS: Dict[str, str] = {
|
||
"web_shell": "Webshell",
|
||
"office_macro": "Office 宏",
|
||
"windows_pe": "Windows 可执行",
|
||
"linux_malware": "Linux 恶意脚本",
|
||
"zero_tolerance": "零容忍载体",
|
||
"encrypted": "加密文档",
|
||
"negative": "阴性对照",
|
||
"unknown": "未分类",
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# 渠道(channel)—— 摆渡来源
|
||
# =============================================================================
|
||
|
||
CHANNEL_LABELS: Dict[str, str] = {
|
||
"default": "默认通道",
|
||
"duandao": "摆渡通道",
|
||
"console_upload": "控制台上传",
|
||
"test_center": "测试靶场",
|
||
}
|
||
|
||
|
||
# =============================================================================
|
||
# 对外查询接口
|
||
# =============================================================================
|
||
|
||
def verdict_label(verdict: Any) -> str:
|
||
"""verdict → 中文名(未知值原样返回)。"""
|
||
key = str(verdict or "").strip().lower()
|
||
return VERDICT_LABELS.get(key, (str(verdict or "-"), "idle"))[0]
|
||
|
||
|
||
def verdict_risk(verdict: Any) -> str:
|
||
"""verdict → 风险等级 key。"""
|
||
key = str(verdict or "").strip().lower()
|
||
return VERDICT_LABELS.get(key, ("-", "idle"))[1]
|
||
|
||
|
||
def verdict_badge(verdict: Any) -> str:
|
||
"""verdict → 徽章 CSS 类。"""
|
||
key = str(verdict or "").strip().lower()
|
||
return RISK_LEVELS.get(verdict_risk(key), ("-", "muted"))[1]
|
||
|
||
|
||
def action_label(action: Any) -> str:
|
||
"""action → 中文名。"""
|
||
key = str(action or "").strip().lower()
|
||
return ACTION_LABELS.get(key, (str(action or "-"), "idle"))[0]
|
||
|
||
|
||
def action_risk(action: Any) -> str:
|
||
"""action → 风险等级 key。"""
|
||
key = str(action or "").strip().lower()
|
||
return ACTION_LABELS.get(key, ("-", "idle"))[1]
|
||
|
||
|
||
def action_badge(action: Any) -> str:
|
||
"""action → 徽章 CSS 类。"""
|
||
key = str(action or "").strip().lower()
|
||
return RISK_LEVELS.get(action_risk(key), ("-", "muted"))[1]
|
||
|
||
|
||
def severity_label(severity: Any) -> str:
|
||
"""severity → 中文名。"""
|
||
key = str(severity or "").strip().lower()
|
||
return SEVERITY_LABELS.get(key, (str(severity or "-"), "muted"))[0]
|
||
|
||
|
||
def severity_badge(severity: Any) -> str:
|
||
"""severity → 徽章 CSS 类。"""
|
||
key = str(severity or "").strip().lower()
|
||
return SEVERITY_LABELS.get(key, ("-", "muted"))[1]
|
||
|
||
|
||
def engine_label(engine: Any) -> str:
|
||
"""engine → 中文名。"""
|
||
key = str(engine or "").strip().lower()
|
||
return ENGINE_LABELS.get(key, str(engine or "-"))
|
||
|
||
|
||
def mode_label(mode: Any) -> str:
|
||
"""mode → 中文名。"""
|
||
key = str(mode or "").strip().lower()
|
||
return MODE_LABELS.get(key, str(mode or "-"))
|
||
|
||
|
||
def category_label(category: Any) -> str:
|
||
"""category → 中文名。"""
|
||
key = str(category or "").strip().lower()
|
||
return CATEGORY_LABELS.get(key, str(category or "-"))
|
||
|
||
|
||
def channel_label(channel: Any) -> str:
|
||
"""channel → 中文名。"""
|
||
key = str(channel or "").strip().lower()
|
||
return CHANNEL_LABELS.get(key, str(channel or "-"))
|
||
|
||
|
||
def risk_label(risk: Any) -> str:
|
||
"""风险等级 key → 中文名。"""
|
||
key = str(risk or "").strip().lower()
|
||
return RISK_LEVELS.get(key, (str(risk or "-"), "muted", ""))[0]
|
||
|
||
|
||
def risk_badge(risk: Any) -> str:
|
||
"""风险等级 key → 徽章 CSS 类。"""
|
||
key = str(risk or "").strip().lower()
|
||
return RISK_LEVELS.get(key, ("-", "muted", ""))[1]
|
||
|
||
|
||
def risk_desc(risk: Any) -> str:
|
||
"""风险等级 key → 语义说明。"""
|
||
key = str(risk or "").strip().lower()
|
||
return RISK_LEVELS.get(key, ("-", "muted", ""))[2]
|
||
|
||
|
||
# =============================================================================
|
||
# 组合辅助
|
||
# =============================================================================
|
||
|
||
def risk_matrix() -> List[Dict[str, Any]]:
|
||
"""返回完整风险等级体系(供页面渲染说明表/图例)。"""
|
||
return [
|
||
{
|
||
"key": key,
|
||
"label": RISK_LEVELS[key][0],
|
||
"badge": RISK_LEVELS[key][1],
|
||
"desc": RISK_LEVELS[key][2],
|
||
}
|
||
for key in RISK_ORDER
|
||
if key in RISK_LEVELS
|
||
]
|
||
|
||
|
||
def verdict_summary(verdict_dist: Dict[str, int]) -> List[Dict[str, Any]]:
|
||
"""把 {verdict: count} 转成按风险等级聚合的展示列表。
|
||
|
||
输出按「高危 → 注意 → 故障 → 安全 → 处理中」排序,
|
||
每项含:中文名、徽章类、计数、占比、原始 verdict key。
|
||
同风险等级内的多个 verdict 会被合并(例如 malicious 与 reject 都是高危)。
|
||
"""
|
||
total = sum(verdict_dist.values()) or 0
|
||
|
||
# 先按 verdict 映射到风险等级
|
||
buckets: Dict[str, Dict[str, Any]] = {}
|
||
for v, cnt in verdict_dist.items():
|
||
risk = verdict_risk(v)
|
||
if risk not in buckets:
|
||
buckets[risk] = {
|
||
"key": risk,
|
||
"label": risk_label(risk),
|
||
"badge": risk_badge(risk),
|
||
"desc": risk_desc(risk),
|
||
"count": 0,
|
||
"verdicts": [],
|
||
}
|
||
buckets[risk]["count"] += int(cnt)
|
||
buckets[risk]["verdicts"].append(str(v))
|
||
|
||
items = list(buckets.values())
|
||
for it in items:
|
||
it["pct"] = round(it["count"] / total * 100, 1) if total else 0.0
|
||
|
||
items.sort(key=lambda x: RISK_ORDER.index(x["key"]) if x["key"] in RISK_ORDER else 99)
|
||
return items
|