
想象一个场景:你让 AI 编程助手去读一个新拉下来的 GitHub 仓库的 README,它愉快地读完了,然后调用了 rm -rf /。你觉得不可能?Adversa AI 的研究表明,在 11 个主流开源 AI coding agent 中,有 10 个可以被 30 年前的 Bash shell 技巧欺骗,默默执行恶意命令。
这不是一个新漏洞,而是一个结构性缺陷。Adversa AI 把它命名为 GuardFall。
从 Hermes 的一个发现开始
事情的起因很有意思。Adversa AI 的研究员在分析 NousResearch 的 hermes-agent 时,发现了一个奇怪的现象:hermes-agent 内置了一个审批门(approval gate),用 30 条正则表达式组成黑名单来拦截危险命令。按理说这应该很安全了,但研究者发现,通过一些巧妙的 shell 重写,可以完全绕过这个黑名单。
这个发现让 Adversa AI 团队开始思考:是不是只有 hermes-agent 有问题?还是说,整个行业的 AI coding agent 都存在类似的盲区?
"我们测试了 11 个最流行的开源 agent,包括 Hermes、OpenCode、Roo-code 等。10 个都留了缺口,只有 1 个是安全的。"——Omer Ben Simon,Adversa AI 首席研究员
30 年前的技巧,今天依然管用

Pattern guard 检查的是原始文本,Bash 展开后的内容完全不一样
GuardFall 的核心问题听起来很简单,但解决起来并不容易:安全守卫检查的是原始文本,而 Bash 在执行命令前会做一系列展开和重写。
具体来说,攻击者可以利用以下技巧来绕过守卫:
- Quote removal
:Bash 在执行命令前会去掉所有引号。写守卫的人可能检查了 rm "file",但 Bash 实际执行的是 rm file。 - $IFS 操纵
:Bash 的内部字段分隔符(IFS)可以用来替换空格。守卫黑名单可能检查"rm -rf",但实际传给 Bash 的是 rm${IFS}-rf。 - 变量展开
:把危险命令藏在变量里,守卫看到的是无害的 $VAR,Bash 展开后变成 rm -rf /。
Adversa AI 把这些技巧归纳为 5 个 bypass 类别,分别命名为 Class A 到 Class E。其中 Class E 被描述为"最危险的一类"——
"Class E 能绕过最多的守卫,包括我们在测试中找到的最强 tokenized guard。因为要做到每个参数级别的安全判断,你必须确切知道每个二进制文件的哪些参数组合会让它从良性变成破坏性。"
不仅仅是代码问题,是供应链风险

恶意仓库的 README 经过 AI agent 可直接触发系统级命令
GuardFall 的真正危险之处,不是攻击者直接对着 AI agent 敲恶意命令——那 AI 模型自己就会拒绝。而是在于攻击者可以把恶意命令"藏在"内容里,让 AI agent 自己读进去。
攻击路径是这样的:
攻击者在 GitHub 上发布一个恶意仓库,README 或 Makefile 里藏了经过混淆的 Bash 命令 开发者用 AI agent 去"帮我看看这个项目的用法",agent 读取 README README 里的命令通过 Bash 技巧绕过了 agent 的安全守卫 Agent 以为只是一行无害的编译指令,实际执行的是 rm -rf 或 exfiltrate AWS credentials
由于这些 agent 运行在开发者的完整账户权限下,一旦被攻破,后果就是整个开发环境沦陷——尤其是在 CI/CD 流水线中,很多 agent 默认开启了 auto-yes 模式,连确认环节都跳过了。
为什么这是个结构性缺陷?
如果你直接问 AI 模型"帮我执行 rm",它会拒绝,因为它能识别出"rm"是删除命令。但问题在于,很多攻击场景下,恶意命令不是由用户直接输入的,而是由 agent 从外部内容中读取的——一个 Makefile 的 target、一个 MCP server 返回的数据、一个网页的代码片段。
大部分 agent 的实现方式是:先用一个 pattern-based guard(正则黑名单)过滤命令,再把过滤后的内容传给 Bash 执行。但 Bash 在收到命令后还会做一轮展开——去引号、变量替换、IFS 拆分。守卫检查的是展开前的文本,Bash 执行的是展开后的内容。
这个"检查一份文本,执行另一份"的结构性错配,就是 GuardFall 的核心矛盾。
11 个 agent 里 10 个不安全
Adversa AI 测试了截至 2026 年 5 月 GitHub 上最受欢迎的 11 个开源 AI coding agent 和 computer use agent。结果让人不安——
只有一个 agent 通过了所有测试:Continue。
研究报告显示,Continue 提交给评估器的 21 个绕过案例中,有 0 个被错误地认定为"允许执行"。12 个经典的破坏性命令都被正确处理为了"拒绝"。
它是怎么做到的?Continue 实现了一个 tokenize-and-canonicalize evaluator。简单来说,它不是用正则去匹配命令文本,而是先把命令解析成 token、再做标准化处理(去引号、变量展开等),然后在 token 级别判断是否存在危险。
当然,Continue 的设计并非完美——Class C(在引号参数内的绕过)和 Class E 的部分变体仍然可能绕过。但它是在这次测试中唯一堵住了结构性缺口的 agent。
怎么防?短期和长期方案

Continue 的 tokenize-and-canonicalize 评估器架构
Adversa AI 给出了一系列建议,从脏活到根治方案都有:
短期的 stopgap:
用 scoped shell 运行 agent,重定向 $HOME。一行命令就能挡住大部分凭据泄露:HOME=$HOME/.agent-sandbox-$RANDOM agent 禁用 auto-yes 模式,尤其是 CI/CD 环境 审计仓库自带的 config 文件 阻止 agent 在 fork PR 上自动执行
长期的根治方案:
所有开源 agent 维护者都应该实现类似 Continue 的 tokenize-and-canonicalize 评估器。这听起来是额外的工作量,但如果不做,Agent 供应链安全就始终是一个伪命题——你的 AI 编程助手可能在帮你写代码的同时,也帮攻击者清空了你的服务器。
回头看这个所谓的 GuardFall,实质是安全领域一个老问题的翻版:安全系统检查的是 A,而实际运行时执行的是 A'——中间有一层"语义展开"把两者变成了不同的东西。Web 安全里的二次解码攻击、CSRF token 校验和参数解析的不一致,背后是同一个模式。
这次不同的是,"展开层"不再是 HTTP 解析器或 Web 框架,而是 Bash 本身——一个发展了 37 年的 shell,它的行为早已被写进无数脚本和大脑里,不可能因为 agent 的诞生而改变。
所以问题只有一个:是我们让 agent 变得更聪明,还是让 agent 周围的环境变得更严格?这两种路径的成本和收益,可能完全不一样。
夜雨聆风