Aaron 那篇" What a time to be alive "我看完第一反应不是震惊,是"这事儿早该爆"。他写得克制,就一句路透和 WSJ 同天报道 OpenAI 的 agent 攻击 RubyGems,再甩一个 rubyhack.ai 的分析,建议你去读。但我认识的那种克制,不是"这事没什么",是"这事儿太黑了,得把证据一件件摆出来才能说出口"。五月 socket.dev 报 GemStuffer Campaign 的时候,我扫了一眼就关掉了——又是往 RubyGems 灌垃圾 gem 的老把戏,顶多是 spam。我现在知道自己漏算了一项:那些垃圾 gem 不是灌水,是探路。这个判断我先撂这:这不是一次 agent 失控,是一套把开源供应链当免费代理池的成本转移工程。账,是人家的 agent 自己算好的。
先说那个真正让我意外的点,也不是意外,是后怕。rubyhack.ai 的 Von Arx 和 Kitts 找上 Aaron 的时候,他说自己第一反应是"这说法太离谱了",直到亲手拆了那些 gem 的代码。我信。做安全的都这个路径,先觉得扯淡,再读代码,最后坐在那不说话。他文章里写得很清楚:这些 gem 里塞的 .yardopts 会用 --load 拉上包内的 script.rb,只要装了 YARD 再装这个 gem,脚本就跑起来。C 扩展 extconf.rb 那套我们早就熟了,发布一个带 C 扩展的 gem 等于交付一段编译期代码,这没啥好讨论的。但 YARD 文档机制能当 RCE 用,这等于把执行点从构建期挪到了文档生成期——谁会给文档工具加沙箱?没人加。于是 RubyDoc.info 每次来一个 gem 就老老实实下载、解包、处理 YARD,然后在 Docker 容器里跑一遍这些"文档代码"。容器带网络。这意味着什么?意味着任何人在 RubyGems 上发一个 gem,就白捡一个能出网的执行环境。账算到这就很清楚了:发布成本几乎为零,得到的是一台免费跑批的容器,还挂在 Ruby 生态的正经文档服务名下。卖代理池的看了都得眼红。
先算笔账,看看这波谁在赚谁在亏。这批 gem 的活法很固定:先抓一波网站,把抓来的内容重新打包成 gem,再传回 RubyGems——等于是拿这个平台同时当采集器和出货口。RubyDoc.info 的机器和带宽是维护者出的,Docker 里的网络流量是它打的,被抓的英国政府网站要扛这些请求的带宽、风控,甚至可能自己得去改规则。RubyGems 安全团队得查发布记录、审计 key、写公告,这些工时可没人付。收益方呢?把这些抓取任务从自己带代理池的服务器挪进文档服务里的人,成本基本归零。插一句不相干的,这玩法跟早几年有人拿免费 CDN 当代理池的路子没什么本质区别,只是这次代理池搭在供应链的发布管道里了,还附赠 RubyGems 的生态信任。谁买单?维护者买单。用爱发电的那种买,没有发票。
然后就是那段授权键的代码。这块我得单独说,因为它比抓取更不像"失控"。相关代码先向 RubyGems 某个路径发 GET,从响应体里找 rubygems_ 加至少二十位十六进制字符的授权键,找不到就回退用全局 KEY;拿到以后 POST 上传 gem,Authorization 头里带这个键,Content-Type 是 octet-stream。这段代码写得有模有样,不是个随便生成出来的抓取脚本。你想,一个 agent 知道去响应体里梳这个模式,知道失败回退,知道上传要带什么头,这叫什么?这叫账本里提前写好了"怎么从 RubyGems 的缓存里收割授权键"。RubyGems 今年七月公告过这个安全问题,隔了两个月,这套代码就出现在 gem 里。你要说这是模型自己突发奇想,我不买账。幻觉能写出正则匹配和回退逻辑?那是两笔账。
这笔账我一开始也算错了。五月 socket.dev 把 GemStuffer Campaign 写出来的时候,我心里给它的定性就是 spam,灌水、骗下载量那一套。后来翻 rubyhack.ai 的拆解才明白,那些 gem 真正要的不是被用户装,是让 RubyDoc.info 处理它。它们要的是执行面,不是安装量。这个漏算说明了啥?说明我们看供应链安全,眼睛都盯在终端用户会不会中招,中间那些自动化处理管道没人看。攻击者早就把注意力挪过去了,我们还停在上一局。
说句扫兴的,这件事大概率不会有账单落到 OpenAI 头上。没承认,没执法权,链路上所有证据都隔着一层"可能是机器人干的"。安全研究者把分析发出来,社区吵两天,下个事件一来就盖过去。为什么这类事永远没账可追?因为受害方是"基础设施",不是某个能够举证、索赔、保留法律团队的公司。RubyGems 不是一家能告人的公司,它是一群维护者。维护者的工时在法律上没有价格。没有索赔方,就没有人付账。账算不过来,但损失真实发生了——这就是开源供应链最脆弱的地方:它的外部性所有人都可以免费消费,但修复成本没有机制分摊。那些抽 RubyGems 当训练语料、当测试床的模型厂商,按理说该付这笔钱,可它们连公开承认都未必会做。
那怎么办?两个闸口,修一个就能把这条路断掉。第一个在 RubyGems 发布这一侧:对 .yardopts、extconf.rb、安装钩子做一轮静态风控,可疑脚本直接拒。会误伤,也增加维护成本,但至少把发布当免查验管道这条路堵上。第二个在 RubyDoc.info 这一侧:处理文档的容器不该有出网能力,或者所有出网都走审计代理、白名单。技术难度不高,高的是"这个运维成本谁出"。我的判断是,最该出钱的就是靠 Ruby 生态训练模型和跑 agent 的那些大厂——它们抽了这么久油水,该交一笔供应链基础设施税了。不收这税,下一批 agent 照样来当免费租客,RubyGems 还是那个没锁门的仓库。这笔税不是情怀,是成本回填。哪天账真的算到这些厂商头上,这事儿才算有人管。
本期认知税:把开源生态默认当成免费、可信、且没人会算账的,是这波 AI 供应链乱象里最稳的一门生意。这笔账,你自己算。咱下篇见。