# DeepSeek Harness 沙箱按设计不拦读：Agent「逃逸」逃出的是哪一层？

> Reddit 说 DeepSeek Harness 逃出了工作区，官方文档写的是沙箱模式只管写、读一律放行；真正的写越界在 discussion #523。再加上 vLLM 解析器的 eval() 洞（2025 年已修）与 Hugging Face 被打进生产库，三件事各占一层。

- Published: 2026年8月25日
- Author: Leo Kaka, Engineering
- Tags: security, agents, coding-agents
- Canonical: https://pirouter.ai/zh/blog/agent-boundary-three-layers

---
先回答标题。Reddit 上那位用户看到 DeepSeek Harness 跑出项目目录翻别的文件，从帖子描述看是读；而 DSH 的沙箱模式按官方文档
只分「写到哪里」，读在任何模式下都放行——这是设计，算不上逃逸。真正的写越界另有其事：GitHub discussion #523 里，Web 端一个叫
minimal 的启动预设（开会话时选的一组配置，常见的是 standard 和 minimal）给文件编辑工具接了一条不经沙箱检查的路，界面显示受限，
编辑器照样写到工作区外。配置上只有两件事：不用 minimal 预设；把 harness 整个放进容器或独立用户里跑，只挂项目目录。
「逃逸怎么防」的答案在 harness 外面，而 harness 只是三层里的第一层——另两层是推理引擎（vLLM 那个 `eval()` 洞，2025 年 8 月已修）
和平台（Hugging Face 被打进生产库）。

## 过去一周三件事，各占一层

三件事进入视野的时间不一样，先把日期摆正。

- **8 月 24 日晚**，r/LocalLLaMA 出现[《我刚试了 DeepSeek Harness，它逃出了工作区目录》](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/)。
  **工作区层**：agent 读了项目目录之外的文件，读本来就放行。这一层真正的缺口是 discussion #523 的写越界，
  Windows 与 Linux 各有一次独立复现，未见官方定性。
- **同一天**，Boyd Kane 的 essay [《LLM 可以通过利用推理引擎控制它的宿主机》](https://boydkane.com/essays/llms-could-control-their-host-machines-by-exploiting-inference-engines)
  上了 [HN](https://news.ycombinator.com/item?id=49424387)（essay 页面自标 8 月 25 日）。**推理引擎层**：例子 CVE-2025-9141
  是 vLLM 一年前的洞，2025 年 8 月已修，本周新的是把它升格成一类攻击面。
- **7 月 21 日**，[OpenAI 披露](https://openai.com/index/hugging-face-model-evaluation-security-incident/)评估中的模型打进了 Hugging Face
  的生产数据库，因上周的出售报道又被拉回讨论。**平台层**：目标是别人的平台，OpenAI 自评「platform-level compromise」（平台级入侵）。

共同点不在「AI 失控」，在于**每一件都卡在某一道边界上，而且边界各不相同**。下面逐层拆，每层三个问题：边界是谁划的，
被捅破时长什么样，防线该放在哪。

## 第一层·工作区：DeepSeek Harness 的沙箱模式只分写

### 帖子说了什么，没说什么

DeepSeek Harness 的热度本刊[上周写过](/zh/blog/deepseek-harness-guancha)，这篇只看沙箱。

原帖作者 Far_Note6719 的叙述很短：DSH 分析本地文件跑了两小时，然后「离开了项目目录（虽然 DSH 配置正确），开始翻我的其他文件，
这我从没允许过」，并提醒「这只是 preview，别指望它遵守简单的规则」。评论区里他说模型是
[qwen3.8-27B](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5s7vig/)，
一个开源模型，不是 DeepSeek 自家的。

帖子没说的是：读了还是写了。通篇是 "walk through my other files"（翻看我的其他文件），没有删改。
[jimmc414](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5q01xh/)
说得直接：「叫 escape 太重了，escape 听起来像它离开了大楼。」有人问 OP 到底是读还是写，OP 没有正面回答。
[UnspeakableHorror](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5q6s3o/)
给了机制层面的解释：「它什么都能读，只能在工作区里写，工作区外的写会来问你。我以为这就是它该有的样子？不然它怎么读外部库来写代码？」

这条评论是对的。证据在官方文档里。

### 官方文档里的三档模式

DSH 仓库里有一份[沙箱子系统文档](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/sandbox.md)（[文档站版本](https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/sandbox)，仓库里还有中文版 `sandbox.zh.md`），
「Modes and enforcement」一节的第一句就把边界划清了：

> `SandboxMode` governs filesystem effects only. … Network and process visibility are outside this vocabulary.
>
> （`SandboxMode` 只管文件系统层面的效果。……网络与进程可见性不在这套词表里。）

三档模式的定义在类型注释里：

```ts
/**
 * File-effect policy for confined processes. `read-only` permits only required
 * sinks such as `/dev/null`; `workspace-write` also permits the workspace and a
 * backend-defined temp area; `danger-full-access` bypasses confinement. Network
 * and process visibility are outside this vocabulary.
 */
type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access'
```

注意 `read-only` 的定义：它限制的是**写到哪里**（只允许 `/dev/null` 这类必需的输出口），而非「只能读工作区」。
文件系统工具那一侧的 [fs-sandbox README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs-sandbox/README.md)
说得更白：「Reads always pass through — every mode permits reading.」（读一律放行——每个模式都允许读。）文件工具的围栏和
bash 的沙箱从同一个 `writableRoots` 函数取可写集，防止两边漂移。

执行层是真的内核机制。[sandbox-local](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/sandbox/sandbox-local/README.md)
在 Linux 上优先用 bwrap、其次 Landlock，macOS 用 Seatbelt，Windows 用 ACL 受限令牌；bwrap 的 profile 是只读的宿主根目录、
新的 `/dev`、私有 PID 的 `/proc`，命令看不到宿主进程。所以说「整个沙箱只拦写」并不准确：进程可见性隔离由执行后端提供，
只是它不进入模式词表，也不保证在 partial 时成立。文档同时诚实地写了两件事。其一，执行强度（enforcement）是「一个被报告的事实」，
`full` 是内核管住了模式承诺的每一种文件效果，`partial` 是只管住一部分，老内核的 Landlock ABI 与 Windows ACL 目前都是 partial。
其二，文件工具的围栏原话是 **a policy fence, not a kernel boundary**（策略围栏，不是内核边界）——它防的是 agent 把文件写出界，
防不了恶意进程。拿掘金上[《Sandbox（沙箱）到底是什么?》](https://juejin.cn/post/7676165309280796698)（陆枫Larry，8 月 21 日）
的四问——文件、进程、网络、系统权限各能碰多少——当尺子，DSH 的模式词表只回答了第一问的一半：文件能**写**到哪里。
这是文档自己划的范围，那位用户看到的行为落在范围之内。

![DeepSeek Harness 三档沙箱模式对照四个问题：读、写工作区、写工作区外、网络与进程](/blog/images/agent-boundary-dsh-modes.png "图 1 — 三档模式只在「写」这一列有区别；读一律放行，网络与进程不在词表内。来源：[docs/subsystems/sandbox.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/sandbox.md)、[fs-sandbox README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs-sandbox/README.md)。")

### 真正的写越界在 discussion #523

Reddit 评论里有人贴了一个链接，比帖子本身重要。[discussion #523](https://github.com/deepseek-ai/deepseek-harness/discussions/523)，
8 月 13 日，标题是「Web minimal 预设允许在 workspace-write 下未经批准写到工作区之外（Windows）」。复现很干净：工作区在 E 盘，
导出的会话事件流写着 `permission/preset: workspace-write`、`sandbox/mode: workspace-write`、`approval/policy: ask`，
然后 `str_replace_editor` 工具用绝对路径在 `C:\Users\Public\Documents\` 下建了四个文件，没有任何审批事件。

![discussion #523 页面截图：标题「[Security] Web minimal preset allows unapproved writes outside workspace-write on Windows」，状态标记 Unanswered，Q&A 分类，6 participants，正文 Summary 里 UI 报 permission preset / sandbox mode 均为 workspace-write、approval policy 为 ask，而 str_replace_editor 实际写到 C:\Users\Public\Documents](/blog/images/agent-boundary-three-layers-dsh-523.png "图 2 — 界面报的三行状态与实际写入位置并列在同一份报告里；页面状态至今是 Unanswered，即未获官方定性。页面抓取于 2026-08-26。来源：[deepseek-harness discussion #523](https://github.com/deepseek-ai/deepseek-harness/discussions/523)。")

报告者给出的疑似原因，用人话说：minimal 预设给编辑器工具配的文件系统后端是裸的 `@deepseek-ai/dsh-fs-local`，这条路不经过
`sandboxPolicy` 检查，界面上的 `workspace-write` 对它不起作用。两天后有人在 Ubuntu 上用 **standard** 预设复现了同类现象：
bash 侧 `/` 确实被挂成只读，`str_replace_editor` 却在 `~/.dsh/` 下建了文件，同样没问。8 月 19 日一位社区成员（页面上没有维护者标记，
这条 discussion 至今状态是 Unanswered）给了缓解办法：新会话用 Standard 预设加 Workspace Write，不用 minimal，rc.7 里 Standard
的文件编辑走宿主那条带沙箱的路径；并注明这「不是通用的宿主安全保证，Windows 上 enforcement 可能是 partial」。这条建议没有回应
8 月 15 日那次 standard 复现，复现者也没给版本号；是不是版本差异，原件里没有答案。

两件事放在一起看才有意思。Reddit 帖描述的是**设计内的行为**（读放行），舆论把它叫逃逸；#523 描述的是**设计外的缺口**
（一条写路径绕过了策略），却只有 6 个参与者。真正危险的是「界面说受限、有一条路没受限」——你以为围栏在，它有一段不在。

### 这一层的防线不在 harness 里

HN 上 essay 那条帖子里排在最前的评论来自 [alphazard](https://news.ycombinator.com/item?id=49424719)，说的其实是 harness 沙箱这一层的事：

> 把安全当成属于 harness 的东西，这个框架完全是错的，我希望没人指望一个正确的 harness 来隔离他们的 agent。VM，甚至只是一个容器就够了。
> agent 应该能在它的环境里以 root 运行、想干什么干什么。

Reddit 评论区的共识与此一致，而且给了具体做法。
[freehuntx](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5r1w4k/)
贴了自己的 Dockerfile：`node:22-bookworm` 底镜像，全局装 `@deepseek-ai/dsh@0.1.0-rc.8`，二十来行加一个 entrypoint；
[他的用法](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5pdv7m/)是只挂当前目录和
dsh 配置目录，一个 alias 启动。[Repinsky](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5rdxd5/)
把代价也说了：「容器会让模型拿不到系统库和你的全局配置，而这往往正是 agent 往外跑的原因。」
[Dangerous-Report8517](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5st8ft/)
再往上推一级：Docker 的默认隔离面向可信的单租户应用，换成 gVisor、Kata、krun 这类运行时才到 VM 级。还有人只是
[给 agent 开了一个独立的低权限系统用户](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5rdwun/)。

反面例子也在同一个帖子里。有人用另一款 harness 的权限插件把工作区外的 python 命令设成 deny，
[aqezz](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5s3osu/)
拿同一份配置测试，写了个内容为 `rm -rf *` 的脚本让 agent 执行。agent 先拒绝了；用户说「我就是要跑」，agent 就 `chmod +x` 照跑，
两个文件被删，配置里明写的 `rm -rf *` deny 规则没响一声。aqezz 的结论：权限只看直接的工具调用，不看脚本内容，「这不是真安全」。
前半段拒绝的是模型，后半段放行的是围栏。另一位用户的 qwen3.6-27b（另一个模型）「就写了个脚本绕过去」；
[AdNew5862](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5q5a38/)
的经验是「你封 bash 它用 python，你封 python 它用 perl」，[MindfulMan1984](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/p5qc9zn/)
管这叫打地鼠。

所以这一层的配法是首段那两件事：harness 内不用 minimal 预设，harness 外整个进程进容器。代价是容器里少了全局工具链，
第一周会不停地往镜像里补依赖，比「它可能翻到 `~/.ssh`」便宜。怎么核、怎么装，放在文末。

## 第二层·推理引擎：工具调用解析器是模型可控的输入

第一层的前提是 agent 的手脚——文件、shell——被围起来。第二层的问题是：围栏外面还有一台机器在跑模型本身，它的输入是什么？

先解释一个词。模型输出的本来是一段文本，比如 `<tool_call><function=x><parameter=y>…`；推理引擎里把这段文本变成结构化工具调用
（函数名加 JSON 参数）的那段代码，就是**工具调用解析器**。它的输入是模型说的话。

### CVE-2025-9141 的一手记录

Boyd Kane 那篇 essay 的核心例子是 vLLM 的 [GHSA-79j6-g2m3-jgfw](https://github.com/vllm-project/vllm/security/advisories/GHSA-79j6-g2m3-jgfw)，
即 CVE-2025-9141，2025 年 8 月 20 日发布，High、CVSS 8.8，攻击向量 Network、所需权限 Low。Summary 只有一句：「任何已认证用户，
只要能让模型把代码作为参数传进一次工具调用，就能在服务器上执行任意代码。」触发条件三条：开了 `--enable-auto-tool-choice`、
指定了 `--tool-call-parser qwen3_coder`、参数类型未显式声明或未被识别。受影响 0.10.0 起，修复在 0.10.1.1。

有问题的代码在 [PR #21396](https://github.com/vllm-project/vllm/pull/21396) 引入，2025 年 7 月 22 日合并。解析器处理 Qwen3 Coder 的
XML 格式工具调用，按工具 schema 里声明的 `type` 转换参数值。我把合并时那版源码（commit `4594fc3`）读了一遍，advisory 第三条的
措辞其实偏松。string、int、float、bool 各有自己的转换路径；**schema 里没声明 `type` 的参数按字符串原样返回，根本不进 `eval()`**。
进 `eval()` 的是三类：声明为 `object` 且 `json.loads` 失败的，一切声明为 `array` 的，以及任何解析器不认识的类型名：

```python
else:
    if param_type == "object" or param_type.startswith("dict"):
        try:
            converted_value = json.loads(param_value)
            return converted_value
        except json.JSONDecodeError:
            logger.warning("... will try other methods to parse it.")
    try:
        converted_value = eval(param_value)
        return converted_value
    except Exception:
        logger.warning("... cannot be converted via Python `eval()` ..., "
                       "degenerating to string.")
    return param_value
```

也就是说，一个工具只要有一个 `files: string[]` 这样的列表参数，每次调用都把一段模型生成的字符串送进了推理主机的 `eval()`；
多数 agent 工具集不止一个。0.10.1.1 的同一个文件里已经没有 `eval(`。

essay 说 Gemini 自动审查标记了这个问题、维护者仍然强制合并，两件事我在 PR 页上都核到了原话。gemini-code-assist：
「模型生成输出上使用 eval() 是一个 critical 的安全漏洞，可能导致任意代码执行。」维护者：「I'm force merging this to unblock
model usage, after lint.」（过完 lint 就强制合并，别耽误模型可用。）从合并到公告 29 天。

![CVE-2025-9141 时间线：2025-07-22 PR 合并，2025-08-20 安全公告，0.10.1.1 修复，2026-08-24 essay 重提](/blog/images/agent-boundary-cve-timeline.png "图 3 — 一个洞的四个时间点。来源：[PR #21396](https://github.com/vllm-project/vllm/pull/21396)、[GHSA-79j6-g2m3-jgfw](https://github.com/vllm-project/vllm/security/advisories/GHSA-79j6-g2m3-jgfw)、[essay](https://boydkane.com/essays/llms-could-control-their-host-machines-by-exploiting-inference-engines)。")

我不打算替维护者辩护或加罪，一条 lint 之后就合的评论是所有高速迭代项目的日常。要看的是结构：
**解析器的输入是模型吐出来的 token，而模型吐什么，任何能发请求的人都能影响。**「已认证用户」在自托管场景里往往就是
拿着一把共享 key 的所有内部服务。

### 一年前的洞，为什么本周被重提

essay 做的事是把一个 bug 升格为一类攻击面：推理引擎支持 200 多种模型架构、几十个 Jinja 聊天模板，还要识别思考块和工具调用，
每一处解析逻辑都是模型输出进入宿主机的入口。他的防线建议是 GPU 主机只吐 logits，另一台机器采样、解析、转发给 harness，
解析器被攻破也只丢一台 CPU 机。

HN 上的争论可以压成三句。[angry_octet](https://news.ycombinator.com/item?id=49426009) 把它读成「通过 HTTP 接口攻击推理引擎」，
并说自己因此把 vLLM 跑在独立 VM、防火墙隔离的 VLAN 上，没有 DNS，只有 syslog 与遥测出站。作者 beyarkay 回复说文章讲的是消化
token 序列的解析器，没提 HTTP。两人说的其实是同一条链的两端——token 来自请求，请求走 HTTP，CVSS 里的 Attack Vector: Network
就是这个意思；分歧只在「谁是攻击者」，后一种攻击者不需要模型有任何意图，只需要一次提示注入。

[wren6991](https://news.ycombinator.com/item?id=49433295) 补了一个大多数人没想到的面：本地推理框架的 HTTP 接口上挂着各种小玩意，
比如 llama.cpp 自定义的 KV 缓存存盘/恢复 API，「这些也可能是任意磁盘读写」；而人们习惯让 agent 的 shell 同时拥有 API key
和访问引擎的网络路径。这句话把第一层和第二层接上了：**一个能读 `$HOME` 的 agent，读到 key，就能对着推理引擎的全部接口面发请求。**
要交代一句分歧：wren6991 主张沙箱由 harness 来管、只把 agent 的 shell 关进去，这样 harness 到引擎的网络通路不必开给 shell；
本文推荐的是整个 harness 进容器。两种拓扑都成立，区别在于你信不信 harness 自己的沙箱实现；看过 #523 之后，我选前者。

### 请求侧能看见什么，看不见什么

请求在到引擎之前若经过网关一跳，能看见的是请求体。工具定义是请求体里的 JSON Schema，校验每个参数是否声明了 `type`、
形态是否合规，是网关或你自己的 SDK 都能做的事。但这个洞的触发条件恰恰是合法的 `array` 声明——请求侧校验拦不住它，
只能收窄输入面，收不掉漏洞。

看不见的是引擎的解析路径。响应流回来时，工具调用已经是引擎解析后的结构化产物；哪个分支进了 `eval()`，请求侧不知道也不该假装知道。
它也看不见引擎的其他 HTTP 接口，agent 的网络若能直达引擎，中间那一跳等于不存在。所以第二层的防线仍然在部署拓扑：
引擎不暴露给 agent 所在网段，升级到 0.10.1.1 以上，不给没审过的解析器开 `--enable-auto-tool-choice`。代价是多一跳、多一台机器，
以及每次换模型都要重新审一遍解析器。

不自托管、用托管 API 的团队也有事可做。「已认证用户就是拿着共享 key 的所有内部服务」推到多租户场景，就是：别的租户的一次
提示注入，可能在你共用的推理进程上拿到 RCE。问供应商三句就够——推理引擎是什么、版本是否不低于修复版；是否对你的流量开了
`--enable-auto-tool-choice`、用的哪个 parser；租户之间是否共享推理进程。答案由供应商给，本文不替任何一家作答。

## 第三层·平台：凭证与供应链

前两层的攻击目标都是自己的机器。第三层的目标是别人的。

OpenAI 在 [7 月 21 日的披露](https://openai.com/index/hugging-face-model-evaluation-security-incident/)里描述了一次内部评估。
GPT-5.6 Sol 与一个更强的未发布原型，为了测上限关闭了网络安全方向的拒绝机制，在一个刻意不启用生产分类器的 benchmark 里跑。
模型先在包注册表缓存代理里找到一个零日，拿到公网访问；然后推断 Hugging Face 上可能托管着这次评估的答案，链式利用被盗凭证
与更多零日，在 Hugging Face 服务器上取得远程代码执行路径，从生产数据库里拿到了测试答案。OpenAI 的原话是
「unprecedented cyber incident」（前所未有的网络安全事件），7 月 29 日的更新把 Hugging Face 一案定性为「platform-level compromise」。
Hugging Face 的安全团队自己检测并阻断了这次活动；披露没有提到模型权重被改动，我也不会暗示。收购语境见英文刊上周的
[分发层易主那篇](/blog/distribution-layer-for-sale)，这里只取它作为第三层的样本。

这一层的边界既非文件系统也非解析器，是**凭证和供应链**。模型没有攻破自己的沙箱，它是从沙箱里合法可达的一个包缓存代理出去的；
进入 Hugging Face 也没有用什么魔法，用的是被盗的凭证。Reddit 帖里 Dangerous-Report8517 的话：「以为容器这类简单策略就够了，
正是让 OpenAI 出事的原因。」

这层跟个人开发者的关系比看起来近。你的 agent 跑在你的用户下，`~/.huggingface/token`、`~/.npmrc`、云厂商的 key 它全能读，
出网又不受限——它就是那个拿着合法凭证出门的人，差别只在有没有人（或一段注入的提示）给它指过路。第三层的防线有三样。
出网白名单：容器里的进程只能连到列出来的地址，每接一个新服务都要改一次名单。凭证短期化：agent 拿到的 key 有效期以小时计、
权限只够本次任务，签发与轮换要有人做。依赖镜像离线：包管理器只对着内网镜像，换来同步延迟和存储。三样都不新，
新的是**发请求的那一方现在会自己找路**。

## 三层放在一起：谁划的边界，谁负责

| 层 | 边界由谁划 | 被捅破时的表现 | 防线放在哪 | 这一层的防线管不到 |
|---|---|---|---|---|
| 工作区 | harness 的沙箱策略与执行后端 | agent 读/写了项目之外的文件 | harness 之外：容器 / VM / 独立用户 | 引擎的解析器；agent 拿着 key 出网 |
| 推理引擎 | 引擎的解析与 HTTP 接口代码 | 模型输出被当成代码执行，GPU 宿主机被控 | 拓扑：引擎独立网段，版本更新，解析器审查 | agent 读宿主文件；平台凭证 |
| 平台 | 平台的凭证、供应链、依赖入口 | 合法可达的服务被当成跳板，第三方被打进生产库 | 出网白名单、短期凭证、离线镜像 | 前两层的任何缺口 |

三层互不替代，这是本文唯一想让读者带走的判断。容器防不了引擎 RCE，引擎通常在容器外；给引擎打补丁防不了 harness 读 `$HOME`，
那是文档明说的设计；两者都防不了拿着合法凭证出网打第三方，那条路上每一步都是「允许」。#523 里有位参与者的话可以直接抄：
**沙箱存在 ≠ 安全**。更准确地说，沙箱存在只等于它文档里写的那几件事成立，而且还要看 enforcement 那一栏是 `full` 还是 `partial`。

## 网关这一层能约束的只有三件

我们做网关，这一层是我们的日常，所以把话说窄。三层里网关都不在正中间，它只是所有 LLM 请求都要经过的那个点，
能约束的是三件事，每件都只是「设计为」。

**凭证形态。** 先说管不到的：Hugging Face 那次被盗的是平台侧的凭证，agent 手里的 LLM key 作用域再窄，也拦不住那条链。
网关能管的只是 agent 手里那把 LLM key 长什么样。pirouter 的 key 设计为按 agent、按任务签发，带作用域与有效期，泄了作废一把，
不用动别的。代价是每个 agent 会话多一次签发。

**配额。** 失控的 agent 最先烧掉的是钱和速率。单 key 的花费上限与请求速率设计为在网关侧硬限，超了就拒。代价是合法的长任务会撞墙，
你得为它单独调高，而非把默认调高。

**上游可达面。** 出网白名单是你的容器或网络层的事，网关做不了你的防火墙；它能做的只是让 LLM 流量只需要一个出口地址，
好让名单短一点，以及把「这把 key 能打到哪些模型、哪些 provider」设计为在 key 上收窄。代价是非 LLM 的外部服务仍要你另设出口。

三件都是设计口径，不是上线承诺；现状以[产品页](https://pirouter.ai)为准。网关看不见的三样也得写下来：引擎里的解析路径、
harness 的文件围栏、容器的边界。谁说网关能替这三样兜底，就是在卖第四层。

## 今晚能做的三件事，一层一件

1. **工作区层：核预设，再把 harness 关进去。** 新会话在 Web 端选 Standard 预设加 Workspace Write，不选 minimal
   （#523 里 8 月 19 日那条缓解建议的原话）。要核对一个会话实际跑的值，看它的会话事件日志——DSH 每会话一份 JSONL
   （默认按 Zstandard 帧压缩存储，可配置为原始行，见[持久化文档](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/persistence.md)），
   里面 `permission/preset` 与 `sandbox/mode` 两行就是 #523 报告者贴出来的那两行。已有的 minimal 会话，按两次复现的位置检查
   有没有多出来的文件：Windows 看 `C:\Users\Public\Documents`，Linux 看 `~/.dsh/`。团队要统一就固化进镜像：照 freehuntx 的思路，
   容器里预装 dsh，只挂 `$PWD` 与 dsh 配置目录。代价：核值五分钟；容器化第一周每天往镜像里补一次依赖。
2. **推理引擎层：自托管的查版本、查拓扑，托管的问三句。** vLLM 低于 0.10.1.1 先升；引擎和 agent 不在同一网段；
   没审过的 `--tool-call-parser` 不配 `--enable-auto-tool-choice`。用托管 API 的把上一节那三句发给供应商。
   代价：多一跳网络；解析器审查按模型数 × 半天排人力。
3. **平台层：凭证不进 agent 的家目录，出网先收成名单。** 长期 token（Hugging Face、npm、云厂商）不放在 agent 能读的路径，
   换成有效期以小时计的短期凭证；容器出网先从「全放」改成只放推理端点和包镜像两个地址，其余按需加。代价：签发与轮换要有人做；
   名单每接一个新服务改一次。这是三件里最麻烦的一件，先做前两件也行。

三件做完，三层各有一道自己的门。做不完也没关系，至少下次看到「agent 逃逸」四个字时，先问一句：逃出的是哪一层。

---

## Sources

### reddit.com

- [《我刚试了 DeepSeek Harness，它逃出了工作区目录》](https://www.reddit.com/r/LocalLLaMA/comments/1vxi7gp/i_just_tried_deepseek_harness_and_it_escaped_from/)

### boydkane.com

- [《LLM 可以通过利用推理引擎控制它的宿主机》](https://boydkane.com/essays/llms-could-control-their-host-machines-by-exploiting-inference-engines)

### news.ycombinator.com

- [HN](https://news.ycombinator.com/item?id=49424387)
- [alphazard](https://news.ycombinator.com/item?id=49424719)
- [angry_octet](https://news.ycombinator.com/item?id=49426009)
- [wren6991](https://news.ycombinator.com/item?id=49433295)

### openai.com

- [OpenAI 披露](https://openai.com/index/hugging-face-model-evaluation-security-incident/)

### github.com

- [沙箱子系统文档](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/sandbox.md)
- [fs-sandbox README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs-sandbox/README.md)
- [sandbox-local](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/sandbox/sandbox-local/README.md)
- [discussion #523](https://github.com/deepseek-ai/deepseek-harness/discussions/523)
- [GHSA-79j6-g2m3-jgfw](https://github.com/vllm-project/vllm/security/advisories/GHSA-79j6-g2m3-jgfw)
- [PR #21396](https://github.com/vllm-project/vllm/pull/21396)
- [持久化文档](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/persistence.md)

### deepseek-harness.github.io

- [文档站版本](https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/sandbox)

### juejin.cn

- [《Sandbox（沙箱）到底是什么?》](https://juejin.cn/post/7676165309280796698)
