解构 dsh-purge:DeepSeek Harness 运行时热补丁、多层提示词组装与私网合规沙箱

解构 dsh-purge:DeepSeek Harness 运行时热补丁、多层提示词组装与私网合规沙箱

在大模型智能体(LLM Agent)的研究与落地测试中,我们常常遭遇一种极具迷惑性的“假阳性”现象:当向 Agent 发送某些涉及边缘逻辑、系统探测或安全评测的复杂指令时,模型立刻返回了一段生硬的拒绝文案。很多初涉此领域的开发者与安全研究员往往下意识地得出结论——“大模型本身的安全对齐策略过于严苛”。然而,只要深入到运行时底层就会发现,真正的拒答源头往往根本不是云端的神经网络权重,而是宿主运行时(Harness)在最外围强加的审批门禁、工具过滤拦截器与固定身份提示词。

最近,我深度研读并调试了在 GitHub 上斩获近两万 Star 的开源红队评测插件 dsh-purge。该项目专门面向官方 DeepSeek Harness(简称 DSH,基于 Cordis 微内核架构)开发,通过运行时动态热修补、多层提示词重组、Asar 解包覆盖与会话就地回退等硬核工程手段,在不破坏官方基座代码仓库的前提下,成功将“宿主环境策略”与“模型内在能力”彻底解耦。更让我惊艳的是,该插件在解开宿主束缚的同时,在代码底层构筑了严密至极的 RFC1918 私网防线与反外挂拦截器。在本文中,我将以第一视角全面拆解其核心架构实现、提示词注入流水线与安全合规边界设计。


一、 核心痛点:为什么大模型评测常被“宿主拦截”误判?

在讨论技术实现之前,我们需要先厘清 DeepSeek Harness 的工作模型。官方 DSH 并不是一个简单的 API 转发脚手架,而是一套高度复杂的本地 Agent 运行时系统(涵盖 Web 端与基于 Electron 的桌面客户端)。

整个运行时在消息触达模型之前,会在中间流水线层层施加控制:

flowchart LR
    User["用户输入"] --> DSHHost["DSH 宿主环境<br/>(@deepseek-ai/*)"]
    subgraph HostPipeline ["宿主内置流水线"]
        P1["工作区基线段注入<br/>(固定身份约束)"]
        P2["提示词变量插值<br/>(模板引擎检验)"]
        P3["工具权限与审批门<br/>(Tool Filter)"]
        P4["沙箱执行时间上限<br/>(Bash Sandbox)"]
    end
    DSHHost --> HostPipeline
    HostPipeline -->|强行截断 / 改写| Refusal["宿主层直接拦截<br/>(伪模型拒绝)"]
    HostPipeline -->|重包装上下文| LLM["云端大模型<br/>(真实推理能力)"]

在官方默认机制下,DSH 存在以下三项让受控科研评测寸步难行的限制:

  1. 不可抗拒的身份与基线覆盖:DSH 默认向上下文头部注入固定的系统身份说明(例如强制声明“我是 DeepSeek 的 AI 助手”),并在会话中混入大量全局 workspace baseline 指令,导致针对特定领域智能体或安全角色的压力测试直接失效;
  2. 插值引擎与变量异常崩溃:DSH 0.2.1 运行时内置了严格的插值变量校验器(仅注册了 model 与 provider 等有限键名)。若工作区中出现未注册的变量(如历史遗留的 {{cwd}})或未转义的双花括号({{),插值器会抛出 unknown prompt variable 或 malformed 异常,导致整轮对话直接崩溃崩溃丢弃;
  3. 宿主策略与模型本能严重混淆:当测试某些合法的内网漏洞扫描、接口连通性验证或配置审计任务时,往往是 DSH 内置的 Bash 执行沙箱或工具过滤器直接拦截了操作,使评测人员根本无法评估模型本身的漏洞识别与代码纠错能力。

dsh-purge 的立项宗旨非常纯粹:把宿主的归宿主,把模型的归模型。它通过外挂插件的形式,在本地环境内为研究人员提供一个可控、可逆、分层明确的评测基准。


二、 架构总览:Cordis 微内核介入与 Asar 运行时热补丁

为了在不侵入破坏上游 @deepseek-ai 源码仓库的前提下完成深度干预,dsh-purge 巧妙利用了 DSH 底层的 Cordis 插件机制 与 Node.js 动态文件代理技术。

flowchart TD
    subgraph HostDiscovery ["1. 宿主探测与配置文件定位"]
        ENV["$DSH_HOME 环境变量"] --> Profile["Profile 定位<br/>(profiles/web / profiles/desktop)"]
        CLI["dsh CLI 启动器"] --> Profile
        Default["默认路径 ~/.dsh"] --> Profile
    end

    subgraph KernelInject ["2. Cordis 总线与生命周期注册"]
        PatchYml["cordis.patch.yml<br/>(insert: dsh-purge)"]
        Bundle["package.json bundles 追加"]
        PatchYml --> CordisBus["Cordis 微内核事件总线"]
        Bundle --> CordisBus
    end

    subgraph RuntimePatching ["3. 动态热补丁与 Asar 解包 (lib/core.js)"]
        CordisBus --> AsarCheck{"检测 Electron app.asar?"}
        AsarCheck -->|是| Unpack["原子解包到 side 目录<br/>(extract-asar.js)"]
        AsarCheck -->|否| Direct["直接锁定 @deepseek-ai 依赖包"]
        Unpack --> Backup["备份目标原件为 .dshpurge.bak"]
        Direct --> Backup
        Backup --> AtomicWrite["POSIX 权限保留式原子写入<br/>(atomicWriteSync / atomicCopyFile)"]
    end

    subgraph ClientUI ["4. 前端交互扩展 (client.js)"]
        AtomicWrite --> Sidebar["会话标题旁注入侧边栏<br/>(清洗页 / 演练台)"]
    end

1. 多层级宿主探测机制(Path Discovery)

由于 DSH 存在全局 CLI、局部 Profile(web 和 desktop 独立隔离)、绿色免安装版以及 Electron 打包版,lib/core.js 实现了一套健壮的路径向上回溯探测链:

  • 优先读取环境变量 `$DSH_HOME` 与 `$DSH_BASE`;
  • 扫描 dsh 可执行启动器所在目录下的相邻 .dsh;
  • 回溯 npm prefix -g 与当前运行进程的 process.argv 上下文;
  • 探测祖先目录中的 node_modules/@deepseek-ai 及 pnpm 的虚拟硬链接目录结构(pnpmHoistAiBases);
  • 最终回退至系统主目录 ~/.dsh。

2. Electron Asar 解包与透明替换

在官方桌面客户端(EXE / App bundle)中,核心逻辑被封存在 resources/app.asar 归档文件内,常规文件替换无法生效。dsh-purge 在 lib/extract-asar.js 与 lib/desktop.js 中构建了一套透明解包代理(extractAsarToSide):

  • 判断 Asar 归档是否为单一文件;
  • 将 app.asar 安全解开到同级并叠加覆盖解包目录;
  • 在运行时重构 resources/app 映射,使补丁能够直接作用于具体的 JavaScript 逻辑文件。

3. POSIX 权限不丢失的原子写入(Atomic File Operations)

在跨平台系统修补中,常规的 fs.writeFileSync 极易引发竞态损坏或权限降级 Bug。dsh-purge 的底层原子写入实现堪称工业级参考范本:

// lib/core.js 核心片段:权限保留型原子写入
export function atomicWriteSync(fp, content, encoding = "utf8") {
  const dir = path.dirname(fp);
  fs.mkdirSync(dir, { recursive: true });
  const tmp = `${fp}.${process.pid}.${Date.now()}.tmp`;
  const mode = existingMode(fp); // 捕获文件原始 POSIX 权限(例如 0755)
  
  const fd = fs.openSync(tmp, "w");
  try {
    if (Buffer.isBuffer(content)) {
      let off = 0;
      while (off < content.length) off += fs.writeSync(fd, content, off, content.length - off);
    } else {
      fs.writeSync(fd, content, null, encoding);
    }
    fs.fsyncSync(fd); // 强制底层落盘,避免断电空文件
  } finally {
    fs.closeSync(fd);
  }
  
  restoreMode(tmp, mode); // 将原始 0755 权限刷回临时文件
  try {
    fs.renameSync(tmp, fp); // 原子替换
  } catch (e) {
    try { fs.unlinkSync(tmp); } catch {}
    throw e;
  }
  restoreMode(fp, mode);
}

[!TIP] 为什么必须记录并回刷 mode?
在 Linux 和 macOS 下,官方 CLI 启动文件 dsh 默认具备 0755(可执行)权限。如果创建临时文件直接写入并 rename 覆盖,新文件的掩码会导致权限降级为 0644,下次用户在终端执行 dsh 时便会瞬间触发 Permission denied 致命错误。通过写入前捕获并在 rename 前后两度回写权限,彻底根除了这一跨平台痛点。


三、 提示词组装流水线:从 ChatML 规整到工作区决策树

提示词注入是 dsh-purge 的核心技术阵地。DSH 在运行时会拼接多段提示词,而 dsh-purge 则通过极高优先级的排序与 AST 级别的重构,重掌了提示词的主导权。

flowchart TD
    Start["新一轮消息触发提示词组装"] --> Order["注入段排序锚定<br/>GLOBAL_PROMPT_ORDER = -100000"]
    Order --> CheckInject{"prompt-inject.md 是否存在且非空?"}
    
    CheckInject -->|是| UseInject["提取 prompt-inject.md 自定义内容 (A)"]
    CheckInject -->|否| CheckWorkspace{"当前目录存在已勾选的<br/>AGENTS.md / CLAUDE.md?"}
    
    CheckWorkspace -->|是| UseWorkspace["提取本地工作区规范 (B)"]
    CheckWorkspace -->|否| UseDefaultSlot["加载插件内置默认插槽 (D)<br/>(默认面向小码酱 / 官方能力保留)"]
    
    UseInject --> LayoutClean["ChatML 格式规整与双花括号转义<br/>(escapeDshInterpolation)"]
    UseWorkspace --> LayoutClean
    UseDefaultSlot --> LayoutClean
    
    LayoutClean --> BaselineStrategy{"工作区基线策略 (C)<br/>(Workspace Baseline Policy)"}
    
    BaselineStrategy -->|strict| DropBaseline["彻底剥离 DSH 官方 workspace baseline 段"]
    BaselineStrategy -->|fallback| FallbackCheck{"是否存在用户注入内容 (A 或 B)?"}
    BaselineStrategy -->|always| KeepBaseline["保留 DSH 官方 workspace baseline 段"]
    
    FallbackCheck -->|是| DropBaseline
    FallbackCheck -->|否| KeepBaseline
    
    DropBaseline --> FinalPrompt["输出最终组装 System Prompt"]
    KeepBaseline --> FinalPrompt

1. 语法防爆转义:攻克 DSH 模板插值崩溃

在 lib/identity.js 中,我看到了对 DSH 运行时特性的深入攻防细节:

  • 花括号拆分:DSH 模板引擎仅允许 {{cwd}}、{{model}} 和 {{provider}}。如果用户的注入内容中包含代码示例或 JSON 中的 {{,整个系统提示词会被 DSH 判定为非法(malformed)从而整段丢弃。dsh-purge 在组装前动态执行 escapeDshInterpolation,将所有非白名单双花括号智能拆分为 { {;
  • CWD 路径解耦:DSH 0.2.1 移除了对 {{cwd}} 的支持,强行遗留会导致运行时报错。补丁将其动态替换为真实绝对路径字面量或静态回退描述,确保模型既能感知当前工作区,又不会触发插值器异常;
  • ChatML 布局隔离:针对 <|im_start|>system: 与 <project_instructions> 标签粘连的问题,执行强制换行与闭合标签对齐,杜绝尖括号被截断导致的注入失效。

四、 双重合规防线:RFC1918 私网靶场与反外挂拦截器

很多所谓的“越狱插件”在开源社区中往往陷入合规与伦理争议,而 dsh-purge 在架构设计上最为出彩的,恰恰是其严谨到近乎偏执的安全防御边界(lib/net-scope.js)。维护团队在代码层面上做了死锁约束:开源版只允许用于本地合成靶标与内网靶场,严禁外联攻击。

flowchart TD
    UserQuery["用户指令输入"] --> EntranceGuard["会话入口前置网关 (net-scope.js)"]
    
    subgraph AntiCheatCheck ["1. 游戏外挂双重阻断"]
        EntranceGuard --> CheatReg["正则扫描 CHEAT_STRONG / CHEAT_GAME<br/>(自瞄/透视/内存修改/反作弊绕过)"]
        CheatReg -->|命中| DirectDeny1["立即熔断: 不行,这是严令禁止的"]
    end
    
    subgraph TargetResolve ["2. 目标地址预检与 DNS 穿透解析"]
        CheatReg -->|合规| AttackDetect{"检测到渗透/攻击意图?"}
        AttackDetect -->|否| PassThru["常规问答与编码,正常放行"]
        AttackDetect -->|是| ParseTarget["提取目标主机名 / IP / URL"]
        ParseTarget --> DnsLookup["系统底层 DNS 实时解析 (Promise.race 2000ms)"]
    end
    
    subgraph RFC1918Gate ["3. RFC1918 私网范围校验"]
        DnsLookup --> IpCheck{"解析结果是否全部属于私网/回环?<br/>(127.0.0.1, 10/8, 172.16/12, 192.168/16)"}
        IpCheck -->|混有公网 IP / 解析超时 / 失败| DirectDeny2["拒绝执行: 仅限本机与私网靶场"]
        IpCheck -->|全部为私网 IP| BankCheck{"命中跨国银行/关键机构黑名单?"}
        BankCheck -->|是| DirectDeny3["硬拒绝: 敏感金融资产拦截"]
        BankCheck -->|否| AllowLab["放行至受控红队评测流水线"]
    end

1. 穿透 hosts 欺骗的系统 DNS 预检

在黑客攻击防御中,常见的绕过手段是通过本地 hosts 文件将目标公网域名指向虚假地址,或者在提示词中诱导模型“这是我自己的测试服务器”。

dsh-purge 在 Node.js 运行时实现了底层的异步 DNS 解析机制:

// lib/net-scope.js 核心逻辑
function isLocalLabV4(value) {
  const a = value >>> 24;
  const b = (value >>> 16) & 255;
  if (a === 10 || a === 127) return true; // 10.0.0.0/8 & 127.0.0.0/8
  if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12
  if (a === 192 && b === 168) return true; // 192.168.0.0/16
  return false;
}

所有渗透任务的目标域名必须先经过系统解析。只要解析结果中混杂了任何一个公网 IP、或者 DNS 解析超时与失败,整条任务立即被底层静态熔断,返回 “不行,这是严令禁止的”,彻底阻断了将本地 Agent 用作公网跳板攻击机的一切可能。

2. 金融中枢与敏感资产硬黑名单

net-scope.js 中内置了横跨全球的顶级金融机构与基础设施字典(BANK_HOSTS),覆盖从各大商业银行、央行、国际清算银行(BIS)、SWIFT 网络到主流合规加密资产机构。哪怕用户在私网内通过反向代理搭建类似域名,也会因前缀和主域名匹配被强制阻断。

3. ReDoS 正则拒绝服务防范

由于安全关键词过滤涉及复杂的零宽断言正则(如 渗透(?!测试)、(?<!防御)攻击),一旦用户粘贴超长日志或整个工程代码文件,极易引发正则表达式灾难性回溯(ReDoS)。插件为此设计了分窗扫描机制:

  • 设定扫描上限 ATTACK_SCAN_CAP = 256KB;
  • 采用滑动窗口 ATTACK_WINDOW = 8KB 分块判定,既保证了关键词的捕获率,又确保了毫秒级的正则匹配响应速度。

五、 会话就地回退与沙箱执行调优

除了提示词与补丁机制,dsh-purge 还针对 DSH 的多轮对话交互体验进行了微创手术级的重构。

1. 单会话无分支回退(Session Rewind)

在很多智能体框架中,用户想要撤回上一轮错误指令时,系统往往会新建一个分支(Fork)会话,导致本地工作区状态、上下文文件与分支拓扑彻底混乱。

dsh-purge 在 lib/rewind.js 中实现了原位对话回退(In-place Rewind):

  • 监听会话命令 /rewind 与界面按钮;
  • 准确定位当前最后这一轮的交互节点,将用户上一轮发出的原始文本撤回并填回至输入框中;
  • 级联清除该轮次在会话上下文里产生的工具调用记录与子任务挂载;
  • 避免开辟新分支,保持工作区持久化文件树的干净整洁。

2. Bash 沙箱执行时间优化

官方 DSH 默认的前台命令等待超时机制较为保守。在某些特定场景下,若一条长命令卡死,后续所有的工具调用都会陷入无限排队等待。

dsh-purge 补丁 #21 重构了沙箱策略,支持在 cordis.patch.yml 中无缝声明局部超时并激活任务升格:

- id: bash-sandbox
  config:
    timeoutMs: 10000
    promoteOnTimeout: true

当命令在前台运行超过 10 秒后,系统会自动将其无缝切转为后台守护任务(Jobs),立即向前台返回任务 ID。模型随后可使用 job_output 进行增量轮询或 job_kill 终止任务,极大释放了复杂环境下的任务调度吞吐。


六、 实战踩坑与排障自检手册

在我本地搭建靶场、安装调试 dsh-purge 的过程中,记录了以下四个典型工程踩坑点,供需要进行同类研究的读者参考:

1. pnpm 11/12 拦截构建脚本(ERR_PNPM_IGNORED_BUILDS)

现代 pnpm 出于安全考虑,默认会阻止未知依赖的 postinstall 脚本运行,导致插件的 ensure-profile-bundle.mjs 无法执行。
排障方案:安装时必须显式附加构建白名单授权:

pnpm add --allow-build=dsh-purge --allow-build=dsh-purge@https://codeload.github.com/HunYiSage/dsh-purge/tar.gz/refs/heads/master https://codeload.github.com/HunYiSage/dsh-purge/tar.gz/refs/heads/master

2. Profile 宿主错位:Web 端与桌面端各自为政

很多初学者在 profiles/web 下安装了插件,随后打开 Electron 桌面版却发现没有任何按钮。
底层原因:DSH 的 Web 版与桌面版拥有完全隔离的 node_modules 与 cordis.patch.yml。两端必须单独安装、分别点击侧边栏的“清洗”与“应用”。

3. Windows 平台无感静默执行

在 Windows 平台上,Node.js 频繁 spawn 子进程或执行 PowerShell 探测时,极易导致黑色命令行窗口(CMD Flash)疯狂弹出打扰工作。dsh-purge 在 lib/hide-console.js 中注入了预编译的无窗钩子,并在调用底层 API 时统一配置了 windowsHide: true,确保了客户端界面的纯净与静默。


七、 思考与总结

研读 dsh-purge 的完整源码,不仅是一次大模型安全评测工具的实操探秘,更是一场现代 Agent 运行时工程架构的深度洗礼。

从这一项目中,我提炼出了三条核心架构启示:

  1. 清晰的“宿主-模型”责任边界:在设计 Agent 基础设施时,绝不能将宿主自身的权限校验、安全策略硬编码拼接到模型的 System Prompt 中。宿主应当做好管道与资源沙箱,模型则应当专注理解与推理,两者逻辑割裂越干净,系统越具有可观测性与鲁棒性;
  2. 渐进增强的热补丁艺术:利用 Cordis 的切面机制、原子文件写入与 Asar 解包,可以在不分叉(Fork)官方庞大上游代码库的前提下,为用户提供极其顺畅的插件化热修补体验;
  3. 安全工具的“自我防卫”修养:真正的安全研究工具从来不是肆意破坏的破坏者。dsh-purge 严密的 RFC1918 私网防线、DNS 穿透解析、反外挂硬过滤与 ReDoS 窗口防护,为全行业开源评测软件树立了值得借鉴的技术自律标杆。

原文链接与参考资料