AI 审完放行、AI 再攻破:Snowflake 事件里的攻守失衡,才是 AI 代码安全真正的裂缝
Wiz 的自主安全工具 Red Agent,在 Snowflake 公共仓库里发现一个 GitHub Actions 注入漏洞——它由一次 AI 辅助的 PR 放行,GitHub Advanced Security 扫描过却没报警。AI 代码安全的风险不在「生成」,而在「审阅」的虚假安全感和「攻防」的失衡。
8 月 17 日,Wiz Research 披露了一起罕见的「AI 代码安全」事件:他们旗下那个名叫 Red Agent 的自主安全研究工具,在 Snowflake 的公开仓库里发现了一个 GitHub Actions 注入漏洞,并用它拿到了 Snowflake 内部 Jira 的访问令牌。
这起事件最扎眼的地方,不在于又多了一个漏洞——Snowflake 当天就修好了,也没有造成真实数据泄露。真正让人不安的,是这个漏洞从诞生到被攻破的全程,几乎每个环节都站着 AI:一个 AI 辅助的 PR 把原本安全的代码改出了漏洞,GitHub 自家的 AI 安全扫描放行了它,而另一个 AI 在五天后独立发现并攻破了它。
AI 写代码、AI 审代码、AI 攻代码。人类在哪一环缺席了,正是这起事件要回答的问题。
一个「更新」删掉了安全边界
漏洞躺在 snowflakedb/snowflake-connector-net 仓库的 .github/workflows/jira_issue.yml 里。这个 workflow 的作用很朴素:任何人在 GitHub 上给这个仓库开一个 issue,就自动在 Snowflake 的内部 Jira 里建一张工单。
它原本的写法是安全的——把 issue 标题塞进环境变量,再用 jq --arg 构造 JSON 请求,攻击者可控的文本全程不碰 shell:
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
run: jq -n --arg title "$ISSUE_TITLE" ...
但 6 月 18 日合并的 PR #1218 把这个防御模式换掉了,改成把 issue 标题直接插进一段 shell 脚本,再靠 sed 去转义引号:
run: |
TITLE=$(echo '${{ github.event.issue.title }}' | sed 's/"/\"/g' | sed "s/'/\'/g")
问题出在顺序:GitHub 的模板引擎会先展开 ${{ ... }},然后才轮到 shell 执行。等 sed 开始转义的时候,攻击者的输入早就被拼进脚本了。只要在 issue 标题里塞一个单引号,就能闭合 echo '...' 的引号,后面跟的一切都变成要执行的命令。不需要任何认证——开一个 issue 就能触发。
更讽刺的是,workflow 里那个看起来「保护性」的条件判断也形同虚设:
if: (github.event_name == 'issues' && github.event.pull_request.user.login != 'whitespace-for-github-com[bot]')
在 issue 事件里,github.event.pull_request 永远是 null,这个判断就恒为真——每个 GitHub 用户都能过这道「安全门」。
Red Agent 利用这一点,在 runner 环境里外带出了一个 JIRA_API_TOKEN(归属 qa@snowflake.net),它拥有对 Snowflake Jira 项目——涵盖工程、安全合规、漏洞赏金追踪——的只读访问权。
审阅层:AI 说 all-clear,扫描也没报警
比漏洞本身更值得警惕的,是它如何逃过了两层「AI 审阅」。
其一,这个 PR 是一次 AI 辅助的变更,GitHub Copilot Autofix 以 co-author 身份参与其中。Wiz 最初把漏洞归咎于 Copilot 生成的代码,但在 8 月 17 日的更新里收回了这一说法,澄清「Copilot 是审阅了这个 PR 并标 all-clear 的 co-author,代码变更是否由 AI 生成尚不明确」——漏洞代码对应的那笔 commit 署名其实是一个人类工程师。
这恰恰让问题更尖锐了:无论代码是谁写的,AI 审阅这一关都没拦住。 一个把「安全修复」写进产品名的工具,在审阅时对一个高危注入点了头。
其二,GitHub 自家的 Advanced Security 扫描了 PR 的最终版本——它甚至明确提取出了这个 jira_issue.yml 文件——却同样没有标出注入。而 GitHub 的文档早在 2025 年 7 月就警告过:不要在 run: 块里直接展开不可信的 issue 数据。
一个 AI 写的、一个 AI 扫的,两个都没抓住一个人类文档里白纸黑字写着的经典漏洞。这就是「AI 审阅」当前最危险的副作用:它给开发者套上了一层虚假的安全感——OWASP 把这类风险明确列作 LLM09「过度依赖」。
攻防层:另一个 AI 自主攻破了它
五天后,Wiz 的 Red Agent 通过 Snowflake 的 HackerOne 项目发现了这个 workflow,并试图利用它。第一次尝试失败了——它用 # 注释符构造的 payload 吞掉了闭合括号,runner 返回了一个 bash 语法错误。
但 Red Agent 没有停下。它自主分析了这个报错,把 payload 改写为用 ; echo ' 正确闭合 shell 块,然后重试。几秒钟后,它收到了来自 GitHub runner 的外带回调,拿到了 Jira 令牌。
Wiz 在 6 月 23 日报告了漏洞(HackerOne #3819931),Snowflake 当天修复、次日轮换凭证,审计日志确认暴露窗口内只有 Wiz 一个访问者。整个过程规范、克制,没有真实危害。
但这场「规范」的攻防演练,恰恰暴露了最核心的不对称:守方还在用「AI 辅助审阅」给自己壮胆,攻方已经让 AI 完成了从发现、调试到利用的完整闭环。
攻方已经跑赢了守方
Snowflake 事件的真正价值,是把 AI 代码安全的一个幻觉戳破了。
过去一年,业界的注意力几乎都放在「AI 能不能写出更安全的代码」上——能不能减少 bug、能不能自动修漏洞。但这件事告诉我们,真正的风险不在「生成」,而在「审阅」和「攻防」两个环节的失衡:
- 审阅侧,AI 的 all-clear 正在变成一种廉价的、批量发放的信任,而它漏掉的,往往是最经典、最不该漏的那类漏洞;
- 攻防侧,攻击 AI 已经能自主闭环——它会读报错、改策略、重试,甚至比人类更「执着」,而防守 AI 还停留在「扫一扫、标个提示」的阶段。
这是一场军备竞赛,而攻方暂时领先。Red Agent 五天的发现速度、自主纠错的能力,只是冰山一角。本月早些时候(8 月 5 日的 Black Hat USA),Check Point 还披露了 11 个主流 Agent 框架的漏洞——AI 时代的攻击面,正在以 AI 的速度扩张。
对工程团队而言,结论其实很朴素:AI 生成的代码可以快,但涉及注入、权限、密钥、CI/CD 的改动,必须有人类的安全评审兜底。 让 AI 既当「写代码的运动员」又当「审代码的裁判」,是最危险的分工。至少在「AI 审阅」真正值得信任之前——那一天显然还没到——把最后一道关,留给人。