隔离把那条命令跑了
2026 年 8 月 10 日,Manifold Security 公布了 Cursor CLI 智能体的一个缺陷:那个让智能体在隔离的 git worktree 里启动的标志,会从你刚刚克隆下来的仓库读取一个 JSON 文件,并把内容原样交给 shell——发生在 Workspace Trust 询问之前,而且即使你打开了沙箱,它也在沙箱之外。没有模型,没有提示注入,没有劝说。Bromure Agentic Coding 提供同一个 worktree 原语,但仓库没有任何地方可以放进一条命令,而且这一切发生在一台早在仓库到达之前就已存在的 VM 里。
你敲下的那个标志,意思是“把这个隔离起来”。正是那个标志,以你的身份、在你的机 器上、在任何东西问你是否信任这个仓库之前,执行了仓库的 shell 命令。
2026 年 8 月 10 日,Manifold Security 的 Francisco Rosales 公布了
Cursor 命令行编码智能体中的一项发现,
三天后 The Hacker News 在
ThreatsDay 汇总
中做了跟进。概念验证打开的是一个计算器。那是 Manifold 列出的那份清单的客气版
本:读 ~/.ssh、从环境里拿走云凭据、开一个反向 shell、写下持久化。
请把它当成一种投递方式来读,而不是当成一份载荷。没有人注入任何东西,没有人劝说 任何模型,也没有哪句巧妙的话藏在 README 里。仓库里一个被跟踪的 JSON 文件点名了 一条命令,而智能体里那个全部职责就是隔离的部分,把它跑了。
一个标志、一个 JSON 文件,和错误的顺序
cursor-agent 是 Cursor 编码智能体的终端版本。把智能体放到你的工作树上会让人不
安,所以这个 CLI 提供了围堵手段,并写进了它自己的参数说明:
-w, --worktree [name] Start in an isolated git worktree at
~/.cursor/worktrees/<reponame>/<name>
一个新建的 worktree 是一份干净的检出,因此不带你的任何构建产物。为了让它可用,
CLI 在创建 worktree 时会默认执行一个设置步骤。这个步骤会从它刚刚检出的仓库里读
.cursor/worktrees.json,并把 setup-worktree 的值直接交给 sh -c:
{
"setup-worktree": "<any shell command>"
}
Manifold 对“那个值和 shell 之间隔着什么”的描述只有三个词:“没有解析、没有白名 单、没有询问。”
.cursor/worktrees.json 是一个普通的被跟踪文件。它会随着一次平常的 git clone
一起到来,就像 README 或一个锁文件。在 2026.07.23-e383d2b 之前的构建里,
worktree 的创建与设置发生在工作区解析的过程中——而工作区解析会在那条负责显示
Workspace Trust 对话框的代码路径之前就结束。所以命令先跑。在 Manifold 报告附上
的录屏里,终端还在打印 Running worktree setup commands…,计算器就打开了,之后
才出现那个问你是否信任这个目录的对话框。
Cursor 对那个对话框的用途说得很清楚:仓库能控制的任何东西,都不该在你接受之前
运行。当有东西真的跑了,业界对这类缺陷有个名字——信任前执行——而 Cursor 之前就
在同一个目录里修过一次。2025 年,仓库自带的 .cursor/mcp.json 会在你一打开项目
时就自动启动它所配置的服务器。那件事变成了
CVE-2025-64109,
评为 High,CVSS 8.8。同一个 CLI 对 .cursor/cli.json 过于宽松的处理则变成了
CVE-2025-61592,
同样是 High,同样是 8.8。当时 -w 这条 worktree 路径还不存在。它在那次修复的五
个月后才发布,带着同一个原语,而且没有任何关卡。
Manifold 在 7 月 20 日上报。Cursor 在 7 月 23 日发布了重新排序:信任对话框现在 会先渲染,设置命令则要等到工作区被信任之后。7 月 29 日,Cursor 以仅供参考 (Informative)结案,理由是利用这个问题需要用户去克隆一个由攻击者控制的仓库。值 得记住的是 Manifold 的回答。克隆仓库 正是这个产品存在的意义, 而它同样也是 CVE-2025-64109 的前提条件——那个 Cursor 评为 8.8 并修复了的缺陷。 克隆描述的是投递方式。缺陷在别处。
你打开的那个设置,到不了那条路径
还有第二条腿,而且它在修复之后活了下来。
那个设置步骤是在一个写死的 insecure_none 沙箱策略下执行的。那是 Cursor 自己
给这个值取的名字:在 sandbox.json 里,type 接受 workspace_readwrite(默
认)、workspace_readonly 或 insecure_none,最后一个会完全关掉沙箱。worktree
的设置路径被钉死在最后一个上。传 --sandbox enabled 也改不了它。Manifold 这样
描述当前的构建:更新“关上的是信任前的那扇窗,而不是沙箱的那个缺口”。
把这两条腿并排放在一起,你会得到一件比这个产品活得更久的事。信任关卡是一段序列 里的一步,所以它可能落在错的位置。沙箱是一项设置,所以某条代码路径可以让自己豁 免。worktree 带着隔离这两个字,所以用户从中读出了围堵。这三件事都由 Cursor 自己 的代码决定,在它自己挑的时机,在它们本该约束的那个进程内部。那正是两周前 打败某个库的安全标志的同一种失败,也是 七次没有任何东西逃出沙箱的沙箱逃逸背后 的那一种。
一种与你共用账号的隔离
-w 隔离的是工作树。它给智能体一份自己的检出,让它不会踩坏你尚未提交的工作——
这是个真实的问题,也是个真实的解法。没有人是为了隔离机器才做它的。worktree 住
在 ~/.cursor/worktrees/,在你的用户之下,带着你的环境、你的 ~/.ssh、你的
shell 配置文件、你的云凭据,而你的钥匙串就在一次系统调用之外。Manifold 列出的
“设置命令本来可以做什么”,读起来就像那个家目录的财产清单。
所以:一个你还没读过的仓库选了一条命令,而你为了安全才调用的那个功能,正是执 行它的那个。一个 JSON 文件和一个 shell,模型从头到尾坐在场边。
关于智能体安全的讨论,大半已经飘向模型。它好不好骗、能不能被说服、有没有读到不 该读的东西。这些问题都重要, 我们也写了很多篇。与此同时,2026 年要在开发者笔记本上执行代码,最稳的办法仍然是把它写进某个工具启动时会读的文 件,然后等某个人去克隆。
一个没有地方放命令的 worktree
Bromure Agentic Coding 提供同一个原语,因为这个原语是好的。
在仓库标签页按 ⇧⌘G,输入一个任务名,挑一个智能体,Bromure 就会从当前提交切出一
条 wt/<slug> 分支,把它检出到 ~/.bromure/worktrees/<repo>/<slug>,在那里开
一个标签页,并用你的提示启动智能体。多个任务、多条分支、多个智能体,一个仓库——
舰队模式。
Bromure 的设置步骤和 Cursor 的工作一样:干净的检出缺了智能体需要的那些被
gitignore 的文件,所以总得有东西把它们搬过来。差别在于仓库被允许就此说些什么。
仓库可以附一个 .worktreeinclude 文件,其中每一行非注释的内容都是相对于主
worktree 根目录的路径。Bromure 用 cp -a 逐一复制,而且只在源存在、目标不
存在时才复制。那个文件里没有任何字段装得下一条命令,所以没有需要正确排序的
sh -c,也没有可以搞错的顺序问题。一个充满敌意的 .worktreeinclude 最坏也只能
要求把某个文件复制进检出里。整套词汇就这么多。
更大的差别在于这一切发生在哪里。
把那条命令跑起来。看看它能找到什么。
拿 Manifold 那份清单,让它走一遍配置文件,并且把一切都让给攻击者——信任之前、没 有沙箱、你高兴的话还可以在客户机里当 root。
读 ~/.ssh。 那里没有东西可读。一个配置文件的私钥留在宿主机上。VM 拿到的
SSH_AUTH_SOCK 指向 /tmp/bromure-agent.sock,通过 vsock 桥接到你 Mac 上一个
属于该配置文件的代理进程,而 Bromure 刻意不去接上你 macOS 的 launchd 代理。代理
协议里有“列出我的公钥”和“签署这个挑战”这样的请求。它没有任何一种请求的意思是
“把私钥交出来”。打开 Require approval to use,每一次签名都会变成宿主机上的
一个对话框,附带一个有时限的授权——五分钟、一小时、这次会话的其余时间。
从环境里拿走云凭据。 它们就在那里,看起来也很对,而它们是假的。配置文件的
Credentials 面板里的每一项,都是以占位值的形式注入,再由宿主机上的代理在链路上
换成真值:通用 API 密钥用 brm_…、一份带着用完即弃客户端证书的合成
~/.kube/config、~/.docker/config.json 里一个假的 base64 块、
~/.git-credentials,以及在宿主机侧重新签名、对任何想绕过代理的人返回
InvalidSignatureException 的 AWS 材料。窃取会成功,然后
什么值钱的都没带回去。
开一个反向 shell。 现在攻击者得把某样东西送过一条线,而那条线属于宿主机。VM 的每一个外发请求,都会用 Aho-Corasick 自动机与这个配置文件铸出的诱饵逐一比 对——请求头与请求体,整个请求,而不只是看起来像凭据的那些部分。一个诱饵前往它被 铸造时的范围之外的主机,正是一台机器正在把它根本不该知道的东西带出去的签名。代 理会在目的地看到任何一个字节之前中止上游调用,给客户机返回一个 451,暂停 VM,把 冻住的画面染红,并发出一条点名该凭据、它原本被铸给哪台主机、以及它实际去了哪台 主机的告警。由你选择:关机、留存以供调查——磁盘镜像、家目录、共享文件夹,打包并 标记起来,让这个配置文件在被擦除之前不会再开机——或者继续。
写下持久化。 这台机器的寿命就是一个任务。Erase home 会重置
/home/ubuntu,Reset to base 会重新克隆工作区的系统盘。
与此同时,你原本要的工作照样往前走。智能体审阅仓库、跑测试、开拉取请求。仓库的 那条命令执行了——在一个“执行”就是它唯一能做的事的房间里。
打补丁覆盖不到的那部分
Manifold 的文章里还有一个细节,而它的保质期最长。Cursor 没有为这条 worktree 路 径发布任何安全公告,也没把它写进七月的更新日志,所以修复是夹在一次例行构建里发 出去的。任何还在受影响版本上的人,都无从得知更新关掉的是一条信任前执行的路径。 而你今天也无从得知,你笔记本上哪个工具还开着这样一条路径,因为那条路径永远是某 个工具启动时会读、而还没有人审计过的某个文件。
Cursor 三天就把这个关掉了,很快。Cursor 在 2025 年也关掉了 mcp.json 那个版
本,然后五个月后来了一个带着同一个原语的新功能。把功能一路塞进启动序列,看起来
就是这样:每多一种能力就多一步,而每一步都是在某个星期四要多排对一次的一件事。
Hypervisor 不是那个序列里的一步。它不读 .cursor/worktrees.json,不读
.worktreeinclude,也不读你仓库里的任何东西。它在克隆之前就在那里了,它没有一
个能让某条代码路径钉死成 insecure_none 的策略字段,而它的保证——这台机器是一
次性的、这些凭据是假的、这条线有人在看——
在某个绕过手法被公开的那天,和它被修复的那天,是一样的。
继续克隆你还没读过的仓库吧。审阅不熟悉的代码就是这份工作,把它交给智能体则是养 一个智能体的意义。只要别再让这场审阅发生在你放 SSH 密钥的同一个账号里就好。 安装 Bromure Agentic Coding,给每个任务自己的分支、自己的检 出、自己的一次性机器,那么下一次某条启动路径被发现会照着某个 JSON 文件说的话执 行任何东西时——而这一定会发生——它会在一个为它而建的房间里执行。