2026年3月31日深夜,一名叫 Ryan Castner 的开发者在 GitHub 上发了一条帖子。 他发现 Anthropic 发布的 Claude Code npm 包里,意外夹带了一个 .map 文件—— 一种开发调试用的东西,按常规流程根本不应该出现在正式发布版里。 这个文件的体积是普通发布包的数十倍,里面是完整的 TypeScript 源代码。

那晚,Claude Code 的全部 512,000 行代码——对全世界公开了大约几个小时。

这本来只是一个普通的发布流程失误。但当你打开这份泄露的代码,在 utils/undercover.ts 里, 你会发现一件让整件事变得极度讽刺的事:Anthropic 在代码里专门建造了一套防止信息泄露的系统—— 而这套系统,就是通过这次泄露才第一次被世界知道。


间谍的工作手册

这个系统叫 Undercover Mode(卧底模式)。

要理解它存在的理由,需要先知道一个背景:Anthropic 内部用代号称呼自己的模型。 他们把 Claude 的不同版本叫做动物名字——Capybara、Tengu 等。这些代号是内部机密,不能出现在任何公开的地方。

但问题来了:Claude Code 会帮你写代码、提交 commit,在 commit 信息里留下署名。 如果 Anthropic 的工程师在自己的公开 GitHub 项目上用 Claude Code 工作, AI 可能会在 commit message 里留下这样的字样: "1-shotted by claude-capybara-4-7"。 下一秒,全球开发者就知道了 Anthropic 下一款模型叫什么。

Undercover Mode 就是为了阻止这件事而存在的。 一旦检测到当前的 Git 仓库不在 Anthropic 内部允许的白名单里, 它就会悄悄激活,告诉 Claude:你现在在公开领地,不要暴露任何内部信息。

"You are operating UNDERCOVER in a PUBLIC/OPEN-SOURCE repository. Your commit messages, PR titles, and PR bodies MUST NOT contain ANY Anthropic-internal information. Do not blow your cover."

— Undercover Mode 实际指令,来自泄露源码 utils/undercover.ts

翻译过来:"你正在一个公开/开源仓库里卧底。你的 commit 信息、PR 标题和 PR 正文里, 绝对不能包含任何 Anthropic 内部信息。不要暴露你的身份。"

注意最后那句:"Do not blow your cover."——不要暴露你的身份。 这是一家 AI 公司写给自己 AI 的指令,措辞像是情报机构给间谍的行动守则。

它是怎么工作的

Undercover Mode 的逻辑相当严密。激活条件是这样判断的: 系统会检查当前的 Git 仓库远程地址,对比一份内部私有仓库白名单。 这份白名单包括 claude-cli-internalanthropiccasinolabs 等十几个内部仓库。 只有在这些仓库里工作时,AI 才被允许"暴露身份"。 在其他任何地方——包括 Anthropic 的公开 GitHub 组织里——卧底模式都默认开启。

一旦激活,这个模式会给 AI 发出一系列具体禁令:

Undercover Mode 的禁止清单

不得在任何 commit 信息或 PR 描述里出现:动物代号(Capybara、Tengu 等)、 未发布的模型版本号、内部仓库或项目名称、内部工具或 Slack 频道(如 go/cc)、 任何暗示"你是 AI"的字句——甚至包括 Co-Authored-By: Claude 这样的署名行。

"写 commit message 时,要像一个人类开发者一样",指令里是这么说的。

卧底模式让 AI 假装它是人类。不是在帮助,不是在辅助,而是在主动隐瞒它的身份。这触碰了一个关于 AI 诚实性的敏感神经——尤其是在 Anthropic 一向以"AI 安全与诚信"为核心价值观的背景下。


代码里的隐形墨水

但 Undercover Mode 还不是这个故事里最精妙的部分。

打开泄露代码的另一个文件:buddy/types.ts—— Claude Code 里一个虚拟宠物系统的类型定义(对,你没看错,这个工具里内置了虚拟宠物,下一篇我们会详细聊)。 在这里,有18种可选的宠物物种,包括鸭子、猫、章鱼、猫头鹰……还有一种叫 Capybara。

但是,Capybara 这个词在代码里不是直接写出来的。它是这样写的:

export const capybara = c(
  0x63,
  0x61,
  0x70,
  0x79,
  0x62,
  0x61,
  0x72,
  0x61,
) as 'capybara'

cString.fromCharCode 的别名—— 这段代码在运行时把八个16进制数字拼成字符串 "capybara",而不是直接写这个单词。 代码注释解释了原因:

"One species name collides with a model-codename canary in excluded-strings.txt. The check greps build output (not source), so runtime-constructing the value keeps the literal out of the bundle while the check stays armed for the actual codename."

— 来自泄露源码 buddy/types.ts,注释

翻译成人话:有一种宠物物种的名字,跟 excluded-strings.txt 里的模型代号"金丝雀词"撞了。Anthropic 的发布流程里有一个自动扫描工具,会在打包后的代码里 搜索被禁止的字符串——比如内部模型代号。"capybara" 碰巧和他们某款模型的代号冲突了。 扫描工具查的是打包后的输出文件(不是源码),所以工程师把这个词拆成了16进制字符数组, 让它只在程序运行时才被"拼出来",而不是以明文形式存在于打包文件里。

这样扫描工具就检测不到,但如果真正的模型代号出现,扫描依然会报警。

换个更直接的说法:Anthropic 的发布流程有一套防泄露扫描, 而它们自己的工程师为了能在宠物系统里加一只水豚,不得不绕过了这套扫描。


五天内,两次泄露

3月31日的 source map 泄露不是第一次。就在五天前的3月26日, Claude Code 的 CMS(内容管理系统)发生了另一次泄露, 暴露出了内部代号 Mythos——Anthropic 对某个内部平台或项目的称呼。 当时已经引发了不小的讨论。

然后,带着一套完整防泄露系统的源代码,在五天后,又公开了。

这不是一家疏于安全的公司。恰恰相反,从泄露的代码里你能看到, 他们在信息安全上花了大量心思——内部仓库白名单、自动代号扫描、卧底模式、 字符编码混淆……每一层都是经过认真设计的防护。 但最终的失误是在最外层:一个 .map 文件没有被打包脚本排除掉。

软件工程里有一个经典格言:A chain is only as strong as its weakest link。 一条链子的强度,取决于它最薄弱的那一环。 Anthropic 的防泄露链里,最薄弱的那一环,是构建脚本里的一行配置。

泄露时间线

3月26日:Claude Code CMS 系统泄露内部文档,暴露代号 "Mythos"。 3月31日:npm 包中意外包含 source map 文件,512,000 行 TypeScript 源代码完整暴露,持续数小时后被删除。

Boris 说了什么

一个细节值得单独说:Claude Code 的负责人 Boris Cherney 曾公开表示, Claude Code "100% 的代码是由 Claude 自己编写的"。 这本是一个展示 AI 能力的自豪说法。

但现在这句话有了新的含义: 这套防泄露系统——包括那条"不要暴露你的身份"的指令——是 AI 为 AI 自己写的。

这有点像一个机器人被编程去掩盖自己是机器人。 更奇怪的是,这个掩盖程序本身,现在被展示在了全世界面前。 拉丁语有个古老的问题:Quis custodiet ipsos custodes?——谁来监管监管者? 在这里,答案是:没有人,或者说,是一个忘记排除 .map 文件的构建脚本。


它告诉了我们什么

撇开讽刺意味,这次泄露给所有做软件发布的团队留下了一个技术层面的清醒提示: 安全防护的最后一关,永远是最低级的那一环。

你可以在代码里写多少层防御都行。但如果发布脚本没有把调试文件排除掉, 那些精心设计的防护措施就会连同它们本身一起被打包发出去。

对于 Anthropic 来说,这次泄露是一个代价高昂但结果有限的教训—— 没有用户数据泄露,没有安全漏洞被利用,只是一些本想保密的内部细节变得公开了。 更重要的是,它让外界第一次真实地看到了这款工具的内部构造。

而从那份构造里,我们可以窥见的,远不止一套防泄露系统。

写在最后

我把这个叫做冰山安全模型:任何防护体系的强度,由其最外层、最不起眼的那道防线决定。 Anthropic 在代码内部构建了多层精密防护,却在发布流程的配置上留了一个豁口。 审查安全时,也许不该从"最重要的地方"开始,而该先看"最无聊的地方"。