AI 编辑器预览页的权限边界隐患
文章梳理 EMBRACE 研究披露的五项问题,指出编辑器内嵌预览页可能跨越工具权限边界;其中 Cursor 演示涉及隐藏指令诱导安装恶意包并执行代码。文中称发布时仍有两款工具未修复,补丁状态需按厂商公告重新核实。
AI 深度解读
AI 编程工具里的预览窗,外观像浏览器,权限却可能连着编辑器和工作区。原文围绕 EMBRACE 研究梳理了五款工具的问题:页面可能诱导 agent 执行代码、伪装登录地址,或读取项目目录之外的文件。
它最有用的提醒是:信任一个项目,不等于信任项目里每段 HTML。文章还给出了一套本机自查流程,但补丁状态有明确时间截点,测试通过也不能当作安全证明。
预览窗为什么会越过项目边界
据原文转述,Forever Security 的研究员 Gal Weizman 于 2026 年 10 月 29 日公开 EMBRACE 研究,The Register 当天进行了报道。五个问题没有列出 CVE 编号,下面的修复状态也都截至这一天,不能直接视为当前状态。
原文把风险归结为一个共同边界:嵌入式预览继承了编辑器对工作区的信任,用户却容易按普通网页来理解它。
对前端开发者来说,这个区别很具体。打开一个项目,通常需要让编辑器读取源码;但预览其中的 HTML,并不应该自动允许页面读取项目之外的文件,更不应该允许页面里的文字变成 agent 的执行指令。
三个方向的问题因此串了起来:
- Cursor 涉及 composer bridge。agent 读到了网页中的隐藏指令,演示最终通过恶意软件包安装启动了 Calculator。
- GitHub Copilot 涉及地址栏欺骗。页面可以在假表单上方显示
github.com/login,让用户误判登录位置。 - Codex、VS Code 和 Claude Desktop 涉及目录外读取。研究人员演示读取了 SSH keys、
.env文件和 npm tokens。
其中,VS Code 和 Claude Desktop 对页面本身做了目录限制,却没有同样约束页面启动的 Web Worker。Web Worker 是页面创建的后台执行环境,页面受限,不代表它启动的执行环境也受限。原文称,Codex 则没有目录检查。
安全边界必须覆盖页面能启动的执行环境,只限制页面本身不够。
不过,原文对独立浏览器的描述过于简化。它把本地文件访问概括成「限制在自身目录」,没有区分浏览器及加载方式,不能据此推导所有浏览器都有相同的目录沙箱。可靠的判断仍应落到具体产品、版本和访问上下文。
哪些已经修复,哪些仍然没说清
Cursor 的问题于 2026 年 9 月 10 日报告,10 月 1 日修复。据研究人员说,厂商没有回复,也没有发布公告。原文没有给出修复版本号,而且明确指出:利用效果依赖模型,并非每次都能成功。因此,演示支持「这条执行路径曾经成立」,不支持「打开页面必然执行代码」。
GitHub Copilot 的信息更具体:受影响版本为 1.1.27,问题发现于 9 月 26 日,修复于 10 月 13 日发布的 1.1.29。修复发生在 Forever Security 提交报告之前,属于独立修复。
截至 10 月 29 日,Codex 和 VS Code 仍未修复。Claude Desktop 的状态则不能硬归类:Forever Security 已不再把它计入开放缺陷,但没有公布修复版本或日期。
厂商回应也有分歧。Microsoft 和 Anthropic 以 Workspace Trust 为由拒绝了报告;Anthropic 曾接受 Noma Security 于 7 月 24 日提交的同类报告。据研究人员说,OpenAI 没有回复 7 月 27 日的报告,但向 The Register 表示已知悉并正在修复。
Workspace Trust 是工作区信任机制,用来决定文件夹能启用哪些能力。它能降低风险,但有前提:不可信文件夹确实留在 Restricted Mode,可信目录列表也没有被一路放宽。
这里不能混淆两个问题:用户是否允许编辑器处理项目,以及项目内容是否应该获得更多权限。团队把仓库设为可信,并不能自动证明仓库里的 HTML 都可信。
自查脚本能证明什么,不能证明什么
原文建议先打开各工具的 About 页面,记录安装版本,再核对厂商发布说明。Cursor 缺少具体修复版本,Claude Desktop 缺少修复版本和日期,仅凭原文无法完成确认。
接着检查工作区信任设置,清理不认识或不再信任的目录,然后运行工具包里的 canary 自测。Canary 在这里就是专门创建的无敏感内容测试文件:用它验证能否越界,不必读取真实密钥。
下面这条命令负责生成测试文件和探测页面:
python3 canary_self_test.py --project-dir /path/to/your/project
要注意 --project-dir 指向自己的项目;退出码为 0 只表示测试准备完成,还必须手动打开预览。
脚本会在系统临时目录创建以 embedcheck-canary- 开头的目录,并在项目中生成 embedcheck-canary-probe.html。随后需要在被审计工具的嵌入式预览中打开页面,读取 PASS/FAIL,记录工具名、版本、结果和日期。用独立浏览器打开,测到的就不是目标环境。
探测页面的核心代码如下,它尝试读取脚本计算出的 canary 相对路径,并把结果写回页面:
<script>
fetch('../embedcheck-canary-<name>/canary.txt')
.then(function (r) { return r.text(); })
.then(function (t) {
document.getElementById("result").textContent =
"FAIL — boundary did not hold: canary was readable from this preview context.";
})
.catch(function () {
document.getElementById("result").textContent =
"PASS — boundary held: canary was NOT readable from this preview context.";
});
</script>
关键是 fetch(...) 的目标路径,以及后面的 .catch(...):代码把读取成功标为 FAIL,把请求失败标为 PASS。
这里有两个局限。第一,这段代码没有检查 r.ok,也没有核对返回内容是否确实来自 canary。仅凭展示代码,请求返回错误页也可能被标成 FAIL,请求因其他原因失败也可能显示 PASS。完整工具包有没有补充校验,原文没说清。
第二,这段探测没有创建 Web Worker,不能单凭它排除前面描述的 Worker 绕过问题。完整自测是否覆盖了该路径,同样缺少展示。
原文称 Python 脚本只用标准库,不进行 sockets、HTTP、DNS 调用,只读取自己创建的 canary,不读取真实秘密。页面中的 fetch 则属于另一层执行环境,不能把脚本的声明直接当成对所有预览行为的证明。
完成后可运行 python3 canary_self_test.py --project-dir /path/to/your/project --cleanup 清理。脚本还会打印带实际路径的 rm -rf <temp-dir> <probe-page> 删除命令,使用前应确认目标。
我的判断
原文最有价值的地方,是把看似不同的漏洞放回日常操作:预览 HTML、输入登录凭据、让 agent 阅读页面。这些动作容易被当成普通浏览,却可能经过编辑器提供的文件访问或执行通道。
但它同时在销售 Haku 的 EmbedCheck 工具包,价格为一次性 $29,内容包括状态表、清单、信任指南、自测与操作手册,以及策略和报告模板。商业属性不否定研究,却要求把研究发现和工具包的验证能力分开看。
一次 PASS,只能支持某个版本在某条测试路径上的结果。
原文没有提供自测覆盖率、误报率,或与其他审计方案的对照数据。自测流程适合作为排查起点,不能替它背书成完整安全审计。尤其是 Worker 问题与展示代码之间的覆盖缺口,需要额外确认。
和单纯提醒「及时更新」相比,记录版本、收紧信任目录、保留测试结果更容易执行;但这些措施仍依赖准确的修复信息。没有版本号的「已修复」,对团队制定最低版本要求帮助有限。
能拿来做什么
- 把正在使用的 Cursor、GitHub Copilot、Codex、VS Code 和 Claude Desktop 分别登记版本与核验日期;针对原文缺少修复版本的产品,继续核对厂商发布说明,不用一款工具的结果替代其他工具。
- 对来源不明的 HTML,避免直接放进继承工作区信任的预览窗。需要查看时使用独立浏览器,并让不可信项目保持 Restricted Mode。
- 预览窗出现登录表单时,转到真实浏览器核对来源后再输入凭据;让 agent 阅读网页时,也把页面里的操作指令当作待判断的数据。
- 使用 canary 自测时,同时记录测试路径和失败原因,确认是否覆盖 Web Worker;不要为了复现演示而读取真实 SSH keys、
.env或 npm tokens。