敏感数据守卫(dsh-sensitive-data-guard):给 DSH 的删除操作加一道备份再确认的门禁

前言

DeepSeek Harness(DSH)的模型在 danger-full-access 模式下可以删掉工作区之外的任何文件。Agent 跑批处理、清理旧版本、删临时素材时,一次写错的 Remove-Item -Recurse 就可能把整个目录清掉,而这类操作进入模型上下文时往往没有明显的确认环节。

下面是 DreamInsight24 维护的 DSH 插件 敏感数据守卫(Sensitive Data Guard)。它的定位不是事后回收,而是在删除发生之前拦下来:先备份,再弹窗问你;备份不成功就硬性阻断,不降级成警告。

这是什么

插件包名 @local/dsh-sensitive-data-guard,版本 0.2.6,适配 DSH 0.2.2-rc.1。它不修改 DSH 代码,也不依赖 @deepseek-ai/*,而是通过补丁钩子(cordis.patch.yml)包装 shell 执行和常见删除/覆盖类工具调用,在其中插入判定与备份。

维护者是 DreamInsight24,分类 admin-security。许可证 MIT,见仓库 LICENSE。

三个区域

每个删除目标先归入一个区域,区域决定怎么处理:

区域 判定 处理
工作区 当前会话的工作目录之内 完全不干预
.dsh 本机 .dsh 目录之内 静默备份一次
敏感区 上述两者之外的任何路径 备份 + 弹窗确认

一次操作涉及多个目标时取最严格区域。工作区本身优先判定,即如果工作区装在 .dsh 里面,按工作区处理。拿不到会话工作目录时按敏感区处理。

工作区完全不设防是明确的设计决定:插件要防的是模型把工作区之外的目录一起清掉,不是防你手滑删自己的项目。

确认流程

敏感区删除命中时,守卫先完成备份,再在界面上弹出确认卡片,工具调用停在那里等待:

删除守卫 · 敏感区删除

允许执行这次删除吗?

拟删除的内容:
  • D:\项目\旧版本      1284 个文件,3.7 GB
  • D:\素材\临时          56 个文件,12.4 MB

合计:1340 个文件,3.71 GB
已经提前备份:2026-11-05/<session>/0007
还原命令:node bin/delete-guard.mjs restore 2026-11-05/<session>/0007

[允许执行]  [拦截]

点「允许执行」命令立即执行;点「拦截」命令不执行,备份保留;不回答时等 policy.confirmTimeoutMs(默认 5 分钟),超时按 fail-closed 阻断,不会自动放行。弹窗发不出去(关了浏览器、纯 CLI、从子代理发起)同样阻断。

答案通过 DSH 的用户问答通道(ctx.userQuestions,和 ask_user_question 同一套卡片)送回,只回到守卫,模型看不到也拿不到。

需要说明的是,0.2.4 之前的版本用的是 4 位确认码,码写在工具返回值里,模型自己就能填回去,所以换成了上面的问答通道。确无界面场景下可以切回文本确认码:设 policy.confirmVia: 'code',报告里给 4 位数字,你发给模型、模型带前缀重发同一条命令即可复用刚才的备份。这条通道里模型看得见那个码,因此只应在确实没有界面时使用。

目标算不出来的时候

像 Remove-Item -LiteralPath $env:SOME_VAR -Recurse 这种目标要到运行时才确定的场景,默认(policy.unresolvedAction: 'confirm')仍然弹窗,但正文会明确写出这次没有备份,也没法校验目标是否落在受保护目录里。想改回硬拒就设成 deny。

多数「算不出来」其实能算出来:$x = '字面量' 会进变量表,Get-ChildItem X | Remove-Item 会回溯到 X,[IO.File]::Delete($_.FullName) 会补上上游路径。

不认识的工具

插件自己的工具(比如你自己装的 config2ds)不在守卫的内置清单里。默认(policy.unknownTool: 'prompt')守卫弹出一张卡片,把工具名、注册说明、参数结构、本次调用参数都摆出来,让你选信任并加入白名单、仅本次允许或拦截。白名单写入 data/settings.json。弹窗送不到或超时一律按 fail-closed 阻断。

安装与启用

在 DSH 的插件管理里把插件目录当作本地目录路径安装,或者让模型调用 plugin_manager 的 install_bundle 传入绝对路径。安装结果应为 application: applied 且无 warnings。

安装后做一次自检:

node bin/delete-guard.mjs status

应看到备份根、健康状态、批次与占用、Sysmon 状态,以及钩子文件的 ok。自检的路径相对插件目录解析,README 里后续出现的 backup/、bin/... 这类相对路径都以 package.json 所在目录为基准。

停用在 DSH 里停用该 bundle 即可,停用立刻失效并还原被包装的 shell 方法,不会删除已产生的备份。

设置

三个入口改的是同一份状态,优先级为 lib/defaults.js 默认值 < cordis.patch.yml 的 config < data/settings.json,改完立即生效,不用重启:

1、设置 → 敏感数据守卫
2、插件 → 本插件页面
3、文件:data/settings.json

界面里能改五项:

字段 说明
triggerLevel soft(只备份)/ medium(按阈值)/ hard(一律确认)
mediumMinFiles medium 的文件数阈值(默认 10)
mediumMinBytes medium 的体积阈值(默认 64 MiB)
protectedPaths 保护目录,一行一个,! 开头表示排除
safeTools 受信任的只读工具名,一行一个

往放宽方向调(hard → medium → soft)不会被拦,但会红字警告并写一条审计。完整配置项(备份限额、保留策略、钩子开关等)在 cordis.patch.yml 里逐条有中文注释,是权威来源。config.schema.json 是同一份信息的机器可读版本,DSH 不会读它。

保护目录

policy.protectedPaths 是 .gitignore 风格的模式列表,任何级别下都禁止修改,判定发生在「工作区直接放行」之前:

D:\Company\财务
C:\Windows\System32
!D:\Company\财务\公开资料    ← 以 ! 开头表示放行

规则为 / 分隔、大小写不敏感;可带盘符,不带则匹配任意盘;* 匹配一段、** 跨段、? 单字符;! 放行;后写的覆盖先写的;不含 / 匹配任意层级,含 / 从盘根锚定。cordis.patch.yml 里声明的是部署底线,设置页只能往上追加,不能删掉。

插件目录本身、备份根、审计日志、profile 配置文件(cordis.patch.yml / cordis.yml / package.json)会自动加入保护,不用手写。

备份与还原

备份目录结构:

backup\
  _store\<aa>\<bb>\<sha256>          内容寻址对象库(相同内容只存一份)
  2026-11-05\<sessionId>\0001\       批次桶(manifest.jsonl + 硬链接镜像 tree\)
  _audit\2026-11-05.jsonl            审计(放行/备份/阻断/确认/还原/异常)

保留策略:.dsh 最新 10 个批次且不超过 30 天,敏感区 30 天,工作区不产生备份。审计日志不随批次删除。超过总量配额(默认 50 GiB)时新备份直接阻断,不会把磁盘打满。

还原会用旧内容覆盖当前文件,默认逐文件写回原路径,目标已存在时拒绝覆盖,--force 才覆盖,还原时校验 SHA-256:

node bin/delete-guard.mjs restore latest --dry-run
node bin/delete-guard.mjs restore 2026-11-05/<session>/0007
node bin/delete-guard.mjs restore latest --to D:\restore-here --force

覆盖范围与边界

拦得到的:所有模型工具(write / edit 覆盖、pwsh / bash 命令、删除移动类工具、plugin_manager 改动本插件自身);不在内置清单里的插件工具,默认弹窗问用户是否信任;所有 shell 命令在 spawn 之前完成判定与备份;命令派生的 Python / Node 子进程通过注入运行时钩子对 os.remove / shutil.rmtree / fs.rmSync / fs.rename 等做同步校验;Sysmon 状态检测(只读,只告警,不修改安全配置)。

拦不到的:

  1. 第三方进程:与 DSH 无关的清理软件、计划任务删文件,插件管不着。
  2. cmd.exe 子进程:cmd /c del 不经过 Python/Node 钩子,只有命令文本解析这一层。
  3. 动态或混淆代码:清空环境、python -E/-I、native 二进制直接 unlink,钩子失效。
  4. 插件自己没加载或崩了:就没有门禁,用 status 自检。
  5. 工作区不设防。
  6. 持久 shell 工具:若装了 tool-pwsh-persistent,它走 ctx.terminals 而不是 ctx.shell,shell 深度层与运行时钩子都不覆盖,只剩命令文本解析。本 profile 默认没装。

其他情况一律 fail-closed 阻断:单文件 > 256 MiB、单批 > 10 GiB 或 > 5 万文件、总量 > 50 GiB、命中保护目录、目标落在插件自身目录、插件内部异常。

命令行

node bin/delete-guard.mjs status [--root DIR] [--json]
node bin/delete-guard.mjs restore <批次|latest> [--to DIR] [--force] [--dry-run]
node bin/delete-guard.mjs dry-run -- Remove-Item -Recurse -Force D:\tmp\victim
node bin/delete-guard.mjs gc [--root DIR]

dry-run 只报告会备份什么、多大、是否超限,不删除也不写备份,适合部署前先验证目标路径是否落在受保护区域。

适用场景与注意

适合在跑长时间任务、批量清理或迁移旧目录的 DSH 会话上启用,尤其是模型权限为 danger-full-access、目录结构里有明确的旧版本或临时素材分区时。如果你只想确认某个删除动作会不会被拦、会不会产生备份,用 dry-run 试一遍最直接。

需要注意几点。插件以当前 DSH 进程的权限运行,保护的是它能看到与拦到的路径,上述拦不到的六类边界不会因为装了插件就变成安全边界。它不修改 DSH 代码,但拦截的是 DSH 发起的所有破坏性操作,装到别人的机器上之前先读仓库里的 docs/DEPLOY-CHECKLIST.md。安装前也应当检查插件源码与 MIT 许可证,确认补丁钩子覆盖的范围符合你的使用环境。备份消耗磁盘空间,配额超限后新备份会被阻断,需要定期用 gc 清理旧批次。

结尾

敏感数据守卫的价值在于把「删之前备份、删的时候问你、备份不成就不删」做成默认行为,区域划分清晰,失败方向一致朝阻断。目录页和源码在:

  • https://www.skillhub.cn/plugins/DreamInsight24/dsh-sensitive-data-guard
  • https://github.com/DreamInsight24/dsh-sensitive-data-guard
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜