乐于分享
好东西不私藏

Claude Code 泄露源码分析 – 围观吃瓜

Claude Code 泄露源码分析 – 围观吃瓜

泄露背景

作者每天都在使用 Claude Code,因此当 Chaofan Shou 注意到 Anthropic 在发布 Claude Code npm 包时同时打包了一个.map文件,其中包含该 CLI 工具的完整可读源代码时,他立刻想一探究竟。该包随后已被下架,但代码在此之前已被广泛镜像(包括作者自己的 GitHub 仓库)并在 Hacker News 上被仔细分析。

这是 Anthropic 一周内第二次意外泄露(模型规格泄露就在几天前),Twitter 上已有人开始怀疑是否是内部人士故意为之。答案大概是否定的,但无论如何这都不是什么好形象。

就在十天前,Anthropic 向 OpenCode 发送了法律威胁,迫使其移除内置的 Claude 身份验证功能,因为第三方工具正使用 Claude Code 内部 API 以订阅费率访问 Opus,而非按 token 付费的定价方式。整个事件使下面的某些发现更加耐人寻味。

作者花了一个上午阅读 HN 评论和泄露的源代码。以下是其发现,按”劲爆程度”大致排序。

反蒸馏:注入假工具以毒害模仿者

claude.ts(第 301-313 行)中,有一个名为ANTI_DISTILLATION_CC的标志。当启用时,Claude Code 在其 API 请求中发送anti_distillation: [‘fake_tools’]。这告诉服务器静默地将诱饵工具定义注入系统提示词。

原理:如果有人录制 Claude Code 的 API 流量来训练竞争模型,假工具会污染该训练数据。该功能受 GrowthBook 特性标志(tengu_anti_distill_fake_tool_injection)门控,且仅在一线 CLI 会话中激活。

这是人们在 HN 上最先注意到的事情之一。

betas.ts(第 279-298 行)中还有第二个反蒸馏机制:服务器端 connector-text 摘要化。启用后,API 会缓冲工具调用之间的助手文本,对其进行摘要,并返回带加密签名的摘要。在后续轮次中,可通过签名恢复原始文本。如果录制 API 流量,只能获得摘要,而非完整推理链。

绕过这些有多难?

并不太难。查看claude.ts中的激活逻辑,假工具注入需要四个条件同时为真:ANTI_DISTILLATION_CC编译时标志、cli入口点、一线 API 提供商,以及返回 true 的tengu_anti_distill_fake_tool_injectionGrowthBook 标志。一个 MITM 代理只需在请求体到达 API 之前剥离anti_distillation字段,即可完全绕过它,因为注入是服务器端且可选择的。shouldIncludeFirstPartyOnlyBetas()函数也检查CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS,因此将该环境变量设置为真值即可禁用整个功能。而且,如果使用第三方 API 提供商或 SDK 入口点而非 CLI,根本不会触发该检查。Connector-text 摘要化的范围更窄,仅限 Anthropic 内部(USER_TYPE === ‘ant’),因此外部用户不会遇到它,无论如何设置。

任何真正想从 Claude Code 流量中提炼知识的人大约花一小时阅读源代码就能找到绕过方法。真正的保护可能是法律层面的,而非技术层面的。

undercover 模式:隐藏 AI 身份的 AI

文件undercover.ts(约 90 行)实现了一种模式:当 Claude Code 用于非内部仓库时,剥离所有 Anthropic 内部信息的痕迹。它指示模型永不提及内部代号如 “Capybara” 或 “Tengu”、内部 Slack 频道、仓库名称或短语 “Claude Code” 本身。

第 15 行:

“There is NO force-OFF. This guards against model codename leaks.”不存在强制关闭功能。此举旨在防止模型代号泄露。

可以用CLAUDE_CODE_UNDERCOVER=1强制开启,但没有方法强制关闭。在外部构建中,整个函数会被死代码消除,变成trivial returns。这是一扇单向门。

这意味着 Anthropic 员工在开源项目中用 AI 生成的 commit 和 PR 不会留下任何 AI 写作的痕迹。隐藏内部代号是合理的。但让 AI 积极假装是人类,那就是另一回事了。

通过正则表达式检测挫败感(没错,就是正则)

userPromptKeywords.ts(第 7-8 行)包含一个检测用户挫败感的正则表达式模式:

/\b(wtf|wth|ffs|omfg|shit(ty|tiest)?|dumbass|horrible|awful|

piss(ed|ing)? off|piece of (shit|crap|junk)|what the (fuck|hell)|

fucking? (broken|useless|terrible|awful|horrible)|fuck you|

screw (this|you)|so frustrating|this sucks|damn it)\b/

一家 LLM 公司用正则表达式做情感分析是极致的讽刺,但同时:正则表达式比 LLM 推理调用更快更便宜,只用来检查是否有人在咒骂你的工具。

原生客户端证明:位于 JS 运行时之下

system.ts(第 59-95 行)中,API 请求包含一个cch=00000占位符。在请求离开进程之前,Bun 的原生 HTTP 栈(用 Zig 编写)会用计算出的哈希值覆盖这五个零。然后服务器验证哈希,以确认请求来自真实的 Claude Code 二进制文件,而非伪造。

他们使用相同长度的占位符,这样替换不会改变 Content-Length 头部,也不需要重新分配缓冲区。计算发生在 JavaScript 运行时之下,因此对 JS 层中运行的任何内容都不可见。这本质上是 DRM 数字版权管理,在 HTTP 传输层实现。

这是 OpenCode 法律之争背后的技术强制执行手段。Anthropic 不仅仅要求第三方工具不使用他们的 API;二进制文件本身通过加密方式证明它是真实的 Claude Code 客户端。

如果你之前搞不懂,为啥 Anthropic 发了法律警告后,OpenCode 社区只能靠各种歪门邪道,比如强行拼接对话、自己写认证插件,才能继续用,那现在就能知道原因了。

不过证明机制并非无懈可击。整个机制受编译时功能标志(NATIVE_CLIENT_ATTESTATION)门控,且仅当该标志开启时,cch=00000占位符才会被注入到x-anthropic-billing-header中。该头部可通过将CLAUDE_CODE_ATTRIBUTION_HEADER设置为假值来完全禁用,或通过 GrowthBook 终止开关(tengu_attribution_header)远程禁用。Zig 级哈希替换也仅在官方 Bun 二进制文件中有效。如果重新构建 JS bundle 并在标准 Bun(或 Node)上运行,占位符将原样保留:五个字面的零发送到服务器。服务器是否会直接拒绝,还是只是记录它,这是一个悬而未决的问题,但代码注释引用了服务器端的_parse_cc_header函数,该函数”容忍未知的额外字段”,这表明验证可能比类似 DRM 的系统所期望的更为宽松。这不是一键绕过的工具,但也不是那种能长期阻止坚定第三方客户端的东西。

每天 250,000 次浪费的 API 调用

autoCompact.ts(第 68-70 行)的注释:

“BQ 2026-03-10: 1,279 sessions had 50+ consecutive failures (up to 3,272) in a single session, wasting ~250K API calls/day globally.”

(BQ 2026-03-10: 1,279 个会话在单次会话中出现 50+ 次连续失败(最多 3,272 次),每天浪费约 250,000 次 API 调用。)

修复方法?MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3。连续失败 3 次后,自动压缩在该会话剩余时间内被禁用。三行代码,停止每天燃烧 25 万次 API 调用。

KAIROS:未发布的自主 Agent 模式

在整个代码库中,有大量对名为KAIROS的特性门控模式的引用。根据main.tsx中的代码路径,这看起来是一个未发布的自主 agent 模式,包括:

用于”夜间记忆蒸馏”的/dream技能

每日只追加日志

GitHub webhook 订阅

后台守护进程工作器

每 5 分钟刷新的 Cron 调度

这可能是泄露中最大的产品路线图揭示。

实现受到严格门控,所以不知道它距离发布还有多远。但常驻后台运行的 agent 脚手架已经就位。

其他发现

今天愚人节了,源代码里基本可以确定藏着今年的愚人节彩蛋:

buddy/companion.ts实现了一套类似电子宠物(拓麻歌子)的伙伴系统。每个用户会根据自己的用户 ID,通过 Mulberry32 随机算法生成一个固定的虚拟伙伴,一共有 18 种生物,从普通到传说分级,还有 1% 概率出 “闪光” 稀有款,属性也做成了 RPG 风格,比如 DEBUGGING(调试能力)、SNARK(毒舌值)之类。物种名称用 String.fromCharCode() 做了编码,就是为了防止被构建工具的 grep 检索直接暴露。

终端渲染这块也很有说法,ink/screen.ts 和 ink/optimizer.ts 直接用上了游戏引擎的思路:用 Int32Array 管理 ASCII 字符池、用位掩码存储样式信息、通过合并光标移动和显隐操作来优化渲染,还有一个自动淘汰的行宽缓存。源码里说这能让字符串宽度计算次数减少大约 50 倍。乍一看有点小题大做,但想到这些工具是逐字符逐 token 流式输出的,就说得通了。

所有 bash 命令都会经过 bashSecurity.ts 里的 23 项安全检查,包括拦截 18 个 Zsh 内置命令,防范等号扩展绕过权限、零宽空格注入、IFS 空字节注入,还有一个在漏洞平台 HackerOne 上被发现的特殊 token 绕过方式。很少有工具会对 Zsh 做这么细致的安全防护。

提示词缓存策略明显影响了整体架构设计。promptCacheBreakDetection.ts 会监控 14 种可能破坏缓存的情况,还通过 “粘性锁” 避免模式切换导致缓存失效,甚至有函数被标记为DANGEROUS_uncachedSystemPromptSection()。毕竟按 token 计费的模式下,缓存失效已经不只是技术问题,而是直接关系成本的财务问题。

coordinatorMode.ts 里的多智能体协调逻辑也很有意思,调度逻辑不是写死在代码里,而是放在系统提示词里。它靠提示指令管理工作节点,比如 “不要随便草率通过不合格的结果”、“必须先理解结论再安排后续任务,绝不把理解工作甩给其他节点” 等。

当然代码库也有粗糙的地方。比如 print.ts 足足有 5594 行,单个函数就占 3167 行,嵌套深达 12 层。另外他们用了 Axios 做网络请求,时间点也很微妙 —— 刚好赶上 Axios 在 npm 上被投毒,植入了远控木马的恶意版本。

世界这个大草台班子?

有人觉得这次代码泄露没什么大不了,理由是 Google Gemini CLI 和 OpenAI Codex 本来就是开源的。但他们没搞清楚:那两家公司开源的只是 Agent 开发工具包(SDK),并不是自家核心旗舰产品完整的内部实现。

这次泄露真正要命的地方不是代码本身,而是里面的功能开关和产品规划。比如 KAIROS、反蒸馏机制这类关键技术,竞争对手现在全都能看到,还能针对性调整自己的策略。代码可以随时重构,但战略上的突然性一旦暴露,就再也收不回来了。

另外还有一个关键点:Anthropic 在去年年底收购了 Bun,而 Claude Code 正是基于 Bun 开发的。Bun 存在一个已知 bug(编号 oven-sh/bun#28001),早在 3 月 11 日就有人提交过问题:在生产环境下,本该关闭的源码映射文件依然被暴露出来,而 Bun 官方文档却说生产环境会自动禁用。这个 bug 至今都没修复。

如果这次源码泄露正是因为这个问题,那就意味着:Anthropic 自己依赖的工具链存在已知漏洞,最终把自家核心代码给曝光了。

就像一条推特评论说的:“本来觉得‘不小心把源码映射发到 npm 上’这种错误离谱到不可能发生,直到你反应过来—这一大段代码可能出自自家 AI 之手”