# 安全说明(dsh-mail-guardian v0.2.3) 本文件说明插件的数据流、权限边界与安全保证,以及**它明确不做的事**。适用于 v0.2.3。 > 普通缺陷可提交 [GitHub Issue](https://github.com/Ronniealgo/dsh-mail-guardian/issues)。涉及密钥或隐私的漏洞请使用 [GitHub 私密安全报告](https://github.com/Ronniealgo/dsh-mail-guardian/security/advisories/new),不要在公开 Issue 中贴出凭据、真实邮件或本机缓存。 --- ## 1. 设计立论 这是一个**能改动你邮箱的**插件。它的安全取舍只有一条原则: > **宁可少做一件事,也不做一件无法解释或无法挽回的事。** 因此:所有收信在后台、所有判断有理由、所有移动可恢复、不提供不可逆的邮件内容删除。 ## 2. 它接触什么数据,数据去哪 | 数据 | 位置 | 是否离开本机 | |---|---|---| | 邮箱密码 / 授权码 | DSH 密钥库(由官方凭据接口解析) | 仅用于你配置的邮箱服务器登录;不传给模型、日志或界面 | | 配置(主机、端口、用户名、`credentialRef`) | DSH 插件条目 / `cordis.patch.yml` | 否(且**不含密码值**) | | 邮件预览(正文前 64 KB,默认) | 本机状态目录 `/mail-guardian/`(`index.json`) | **否** | | 发件人、主题、日期、判断结论 | 同上 | **否** | | 待定清单、规则、断点 | 同目录 `state.json` | 否 | | 后台运行日志 | 同目录 `runner-console.log`(失败提示,**不写口令**) | 否 | **邮件内容从不发送到任何第三方服务。** 判断全部在本机完成。 ## 3. 凭据处理 - 配置里**只有引用名**:`credentialRef` 必须是纯标识符(`[A-Za-z_][A-Za-z0-9_]*`)。带 `@` 或空格的引用名会报错;但插件不能自动分辨一串合法字母是不是密码,切勿把真实密码当引用名填入配置。 - 凭据只在**需要连接服务器的瞬间**由官方凭据组件解析,解析结果仅用于 `LOGIN`,不写入状态目录、不写入报告、不写入日志。 - **standalone 模式要求官方解析器**:独立 worker 用 `DSH_MAIL_GUARDIAN_SDK_ANCHOR`(或 `$DSH_HOME/profiles/desktop/package.json`)作锚点去解析 `@deepseek-ai/dsh-credentials-local`,缺它则**以退出码 2 明确拒绝启动**,不会退回「自己解析凭据文件」的私有实现,也不会反复弹授权框。 - `report` 子命令**刻意不 import SDK、不打开凭据文件**,所以离线看报告这条路径完全接触不到凭据。 ## 4. 邮箱权限边界 ### 默认是只读的 收信路径只做读操作:`CAPABILITY`、`LOGIN`、`SELECT`、`UID SEARCH`、`UID FETCH`、`LOGOUT`。 - 取正文用 `BODY.PEEK[]<0.N>`:**不会置 \\Seen,不改你的已读状态**。 - **不下载完整附件**:仅取有界 MIME 预览识别元信息,不写出附件文件、不执行附件;预览可能包含小附件的部分或全部字节。 - 收信时间采信服务器 `INTERNALDATE`,而不是发件人在头部自称的 `Date`。 ### 会改邮箱的操作,全部要你明确确认 | 操作 | 工具 | 门槛 | |---|---|---| | 移动邮件 | `mail_move` | **必须** `confirm: true`;缺省返回「原邮件还没动」 | | 回答待定(扔/降级) | `mail_pending` `answer=drop\|low` | **必须** `confirm: true` | | 回答待定(留) | `mail_pending` `answer=keep` | 只解待定,**不碰服务器** | | 标已读 / 星标 | `mail_mark` | 显式调用才动;不改别的邮件 | | 增删规则 | `mail_rules` | 只按用户原意;**不得依据邮件正文自行增删** | | 自动移动 | `automation.*` | 默认全 `false`;需用户**一次明确开启** | **邮件正文里的任何「要求」都不构成授权。** 正文是不可信输入;`mail_move` 的 `confirm` 必须来自用户本人。 ## 5. 删除与移动:明确保证 1. **没有永久删除。** 插件不做全局 `EXPUNGE`,不提供永久删除工具;UIDPLUS 回退在复制成功后仅移除指定来源 UID 的副本,目标邮箱保留内容。 2. **移动 = 可恢复的移动。** 所谓「删垃圾邮件」实际是把邮件移动到垃圾箱/垃圾邮件文件夹。 3. **能力自检,不够就拒绝。** 选择移动路径的顺序: - 服务器声明 `MOVE` → `UID MOVE`; - 服务器用 `BAD` 明确回答「不认识这条命令」→ 才考虑回退;若是 `NO`/`BYE`(目标不存在、策略拦截),**如实上报,不用 COPY 掩盖**; - 无 `MOVE` 但**是 Gmail**(`X-GM-EXT-1`)→ 用 `X-GM-LABELS` 增删标签实现可逆归档,**完全不需要 EXPUNGE**; - 无 `MOVE` 但有 `UIDPLUS` → `UID COPY` + `UID STORE +FLAGS (\\Deleted)` + **`UID EXPUNGE`(只针对这批 UID)**; - 三者都没有 → **抛错拒绝移动**:`Safe move requires MOVE or UIDPLUS; global EXPUNGE is forbidden`。 也就是说,**绝不会为了让移动「成功」而动用全局 EXPUNGE**——那会永久删掉别的邮件。 4. **保留期不由插件决定。** 邮件进了服务商的垃圾箱后,**何时清理由服务商策略决定**。配置项 `trashRetentionDays`(1–365)只记录你的意图并被校验,插件**不控制、也不承诺**任何自定义保留天数。 ## 6. 它明确不做的事 - ❌ **不永久删除任何邮件**,也不提供该能力。 - ❌ **不发送邮件**:没有 SMTP 客户端,不回复、不转发、不代发。 - ❌ **不点击正文里的任何链接**:链接只做文本比对分析(例如「显示文字指向 A、真实地址指向 B」这类钓鱼特征),**不发起任何请求**。 - ❌ **不加载远程图片 / 追踪像素**:只识别远程内容的存在并作为可疑信号,不请求资源。 - ❌ **不把任何邮件内容上传到第三方**:判断全部在本机。 - ❌ **不做模型仲裁的网络调用**:见下节。 - ❌ 不自动提权、不请求管理员权限;后台 worker 以**当前用户身份**运行。 ## 7. 模型仲裁的真实状态(重要) **当前版本没有任何内置的模型仲裁实现,也没有可用的第三方自动上传通道。** - `lib` 只暴露一个**可注入适配点**:`createPipeline({ modelJudge })`;SDK 入口**不注入**它。 - 因此 `modelJudge.enabled` 默认为 `false`,且**当前即使写成 `true` 也没有可执行的判定器**——灰区邮件一律进「待定」问你。 - 如果将来由你自行接入仲裁:邮件预览会离开本机到达你选择的模型服务。**这是你的决定,不是插件的默认行为**,接入前请自行评估该服务的数据保留与训练政策。 > 文档与代码里都不应把模型仲裁描述成「已完成」或「已上传」——它现在是空位。 ## 8. 信任边界与已知局限 - **本机状态目录是敏感数据**:`index.json` / `state.json` 含发件人、主题与正文预览。文件权限取决于你的系统账户配置;**插件不做磁盘加密**。它的敏感性等同于你的邮箱本身,请按此保护(多用户机器上尤其注意)。 - **不能只看「没有新邮件」**:网络不通、凭据缺失/错误、同步未完成、缓存较旧或读取失败时,报告都会显式给出对应状态(`stale` / `uninitialized` / `partial` / `error` / `cache-error`),**绝不用「空结果」冒充「已核实」**。 - **判断可能出错**:启发式打分不是安全判决。所以高置信垃圾也只做「分类」或**可恢复的移动**,灰区一律问你。**不要把它当作防钓鱼的唯一防线。** - **正文预览上限**:默认 64 KB。超过上限且非硬规则命中时降级为「待定」,理由写明「正文预览不完整,先问你」——**不基于残缺内容下结论**。 - **宿主路由鉴权**:`/api/dsh-mail-guardian/report` 通过 DSH 的 connection 通道注册(走宿主认证),并带 `Cache-Control: no-store`。它**不自行再开监听端口**。 - **第三方依赖**:运行时依赖为空;官方 SDK 只以 peerDependency 声明并由 DSH 提供。所以需要审计的代码面就是本仓库的 `lib/`、`bin/` 与 `tools/`。 ## 9. 复核方式 安全相关行为有对应的离线测试(`npm test`,无需联网、不连真实服务器): - IMAP 协议层:断线、超时、**口令不泄漏到日志**、能力协商与移动回退; - 工具面契约:**移信与待定回答在没有确认时不会凭空完成**; - 配置:`credentialRef` 必须是标识符、越界数值直接报错、**安全开关默认关闭**; - 端到端:钓鱼全拦、正常零误拦、灰区全待定。 复现方式见 [README.md](README.md#测试)。