TECH ARTICLES
Claude Code 供应链安全 AI 工具

Claude Code 源码泄漏:一个 .map 文件,扒光了 AI 编程王者的底裤

Jackie Zhan 2026-03-31
目录
到底发生了什么? Claude Code 的架构长什么样? 安全机制是怎么设计的? 代码质量经得起"裸检"吗? 给每个 npm 开发者的警钟 三个没有答案的问题

2026 年 3 月 31 日,安全研究员 Chaofan Shou 发了一条推文。

内容很短,大意是:Anthropic 的 Claude Code,那个被无数开发者奉为"AI 编程神器"的 CLI 工具,它的全部源码正躺在 npm 公开仓库里,任何人都可以看。

不是黑客攻击,不是内部泄密。是 Anthropic 自己把 source map 文件打包进了 npm 发布物。

一个 57MB 的 cli.js.map 文件,里面藏着 4756 个源文件的完整内容——其中 1906 个是 Claude Code 自身的 TypeScript 源码,剩下 2850 个是 node_modules 依赖。总计超过 51.2 万行代码,一览无余。

这件事有意思的地方不在于"泄漏"本身——毕竟这不涉及模型权重或用户数据。有意思的是,当你真的打开这 51 万行代码,你会发现:原来 AI 编程工具的王者,内部是这样运转的。

今天我们就来做一次"开箱验尸"。从泄漏机制到架构设计,从安全体系到代码质量,一层一层扒开看。


到底发生了什么?

先说清楚 source map 是什么。

你写了一段 TypeScript 代码,上线前要编译、压缩、混淆,变成一坨人类看不懂的 JavaScript。但如果线上出了 bug,你怎么调试?总不能对着 a.b(c,d,e) 猜原来的变量名吧?

Source map 就是为了解决这个问题。它是一个映射文件,记录了"压缩后的第 X 行第 Y 列"对应"原始代码的第 M 行第 N 列"。浏览器 DevTools 或调试器读了它,就能还原出原始代码。

打个比方:你交了一份正式的打印版论文(压缩后的 JS),但不小心把那份写满批注、修改痕迹的草稿(source map)也一起交了。老师不光能看到你的最终答案,还能看到你每一步的思考过程。

常见误解
很多人以为 source map 只是个"索引",里面不包含实际代码。错了。source map 文件中有一个 sourcesContent 字段,会完整嵌入所有原始源文件的内容。一个 .map 文件 = 整个源代码仓库的快照。

那 Anthropic 是怎么把它发出去的?

npm 发包的时候,有两种方式控制哪些文件上传:

Anthropic 大概率是用了黑名单模式,但忘记把 *.map 加进去。或者更糟:构建脚本改了,但 .npmignore 没跟着改。

讽刺的是,这不是第一次。2025 年 2 月 Claude Code 刚发布时,同样的问题就出现过。当时 Anthropic 的做法是删掉旧版本、移除 source map。但到了 2026 年 3 月,v2.1.88 版本里,这个 60MB 的 .map 文件又回来了。

同一个坑,踩了两次。这就不是"手滑"能解释的了,这是构建流程里缺少了一道系统性的检查。


Claude Code 的架构长什么样?

好了,既然源码都摆在桌上了,我们就不客气了。

Claude Code 的整体架构,可以用一句话概括:一个用 React 画界面的终端程序,指挥着 40 个工具和一群子 Agent,围着一个 46000 行的查询引擎转。

听起来有点疯狂?我们拆开看。

技术选型:不走寻常路

首先,运行时不是 Node.js,是 Bun。选 Bun 的原因很实际——更好的死代码消除和更快的启动速度。对于一个 CLI 工具来说,用户按下回车到看到第一个字符的延迟,直接决定了体验。

然后,终端 UI 用的是 React + Ink。对,你没看错,React 那个 React。Ink 是一个让你用 React 组件写终端界面的库。整个代码库里有大约 140 个 Ink 组件、若干 React hooks。

这意味着 Claude Code 的终端界面是组件化的、响应式的,跟你写一个 Web App 的思路一模一样。

核心三件套

工具系统 (~40 tools) File Read/Write Bash Execute Web Fetch Glob / Grep / LSP 29,000 行基础定义 Query Engine 46,000 行 LLM API 调用 流式响应 工具调用循环 Token 计费 重试 & 缓存 编排层 Sub-Agent 派发 IDE Bridge (JWT) 50+ Slash 命令 Memory 持久化
Claude Code 核心架构三件套

第一件:工具系统。大约 40 个工具,每个工具是一个独立的、带权限控制的能力单元。读文件是一个工具,执行 Bash 是一个工具,搜索代码是一个工具。光是工具的基础定义就写了 29000 行 TypeScript。

第二件:查询引擎(Query Engine)。这是整个系统最大的单一模块,46000 行代码。它负责所有跟 LLM 的交互——API 调用、流式响应、工具调用循环、思考模式切换、重试逻辑、Token 计费。如果说工具系统是手脚,查询引擎就是大脑。

第三件:编排层。包括子 Agent 派发(可以把复杂任务拆成多个并行的子任务)、IDE 通信桥(通过 JWT 认证连接 VS Code 和 JetBrains 插件)、50 多个 slash 命令、以及基于文件的记忆系统。

insider 视角
Claude Code 的模型选择策略很有意思:复杂推理任务用 Sonnet(如今天的 claude-sonnet-4-6),简单的解析操作(比如分析一条 bash 命令是否安全)用 Haiku。这不是"能力不够用小模型凑",而是成本和延迟的精确权衡——每次 bash 执行前都要做安全检查,如果每次都调 Opus,成本和延迟都扛不住。

如果把 Claude Code 比作一个装修队,工具系统是锤子、电钻、油漆刷;查询引擎是工头,决定什么时候用什么工具;编排层是项目经理,负责分配任务和跟甲方(你)沟通。

真正的创新不在于某个工具多厉害,而在于调度——怎么让 40 个工具在正确的时机、以正确的顺序、带着正确的权限被调用。


安全机制是怎么设计的?

这是整个泄漏事件中我觉得最有价值的部分。

蚂蚁集团的前端框架 Umi.js 作者陈成,逆向了 Claude Code v2.1.81 的源码,完整还原了 Auto Mode 背后的安全机制。结论是:每一次工具调用,都要过一条四层决策流水线。

就像你过机场安检,不是只过一道门,而是要经过四道关卡:

第一层:权限规则匹配

检查你之前有没有明确授权过这个操作。比如你已经允许了"读取当前目录的文件",那后续的文件读取直接放行,不再打扰你。

第二层:低风险模拟

模拟 acceptEdits 模式——也就是"允许编辑文件"的权限级别。如果这个操作在编辑模式下就会通过,说明风险不高,跳过后续检查。

第三层:只读白名单

Read、Grep、Glob、LSP、WebSearch……这些只读操作不修改任何状态,无条件放行。

第四层:AI 安全分类器

前三层都无法判断时,才会触发这个"终极关卡"——发一个独立的 API 请求给 Claude Sonnet,让它做安全分类。

权限规则 第一层 未命中 低风险模拟 第二层 未命中 只读白名单 第三层 未命中 AI 安全分类器 第四层 (Sonnet) 命中 → 放行 命中 → 放行 命中 → 放行 判定安全 → 放行
Auto Mode 四层安全决策流水线

这个分类器有几个很精妙的设计细节:

拦截规则覆盖 22 个以上类别:force push、直推 main 分支、下载并执行外部代码、生产部署、数据外泄、自我修改权限、创建远程代码执行面、凭证泄露……

同时也有 7 类放行例外:测试用的硬编码密钥、工作目录内的本地文件操作、只读 GET 请求、安装已声明的依赖等。

关键区别
还有一套熔断机制:连续 3 次被拒绝或累计 20 次被拒绝后,系统自动降级为手动确认模式。Headless(无人值守)模式下则直接中止 Agent。这是"fail-safe"思维——当系统不确定时,宁可停下来,也不冒险放行。

最好的安全设计,不是让你觉得处处受限,而是让你几乎感觉不到它的存在——直到真正危险的操作出现。


代码质量经得起"裸检"吗?

这是我最想聊的部分,也是社区讨论最热烈的部分。

先说结论:有惊喜,也有惊吓。

惊喜在架构层面——工具系统的插件化设计、查询引擎的模块化封装、四层安全流水线的巧思,这些都是教科书级别的系统设计。

惊吓在代码层面。

Hacker News 上有人翻到了 src/cli/print.ts 里的一个函数——3167 行,12 层嵌套,大约 486 个条件分支。一个函数,三千多行。

这是什么概念?很多人写的整个项目都没有 3167 行。

还有更离谱的:一家 LLM 公司,在自己的 AI 编程工具里,用正则表达式做情感分析——检测用户输入中是否包含脏话,然后记录到日志里。

"一家做大语言模型的公司,不用自己的模型做情感分析,而是用 regex 匹配脏话?"
—— Hacker News 网友

社区还发现了一些架构上的"粗糙"之处:到处散落的 process.argvprocess.env 访问、重复实现的哈希函数、缺少高层次的架构文档。

但话说回来,你猜社区怎么评价的?

大部分人的反应不是嘲笑,而是——"嗯,跟我们公司的代码差不多。"

这才是最真实的启示:就算是 Anthropic 这种顶级 AI 公司,他们的工程代码也不是完美的。有技术债务,有赶工留下的痕迹,有"先跑起来再说"的妥协。51 万行代码里有精妙的设计,也有粗暴的补丁。

延伸思考
有趣的是,泄漏还暴露了一些未发布的功能:一个代号 Kairos 的"Assistant Mode"、一个叫 Buddy System 的"电子宠物"系统(带 ASCII 像素精灵和扭蛋机制,被确认是愚人节彩蛋),以及一个叫 Undercover Mode 的模式——在开源贡献时剥离 Anthropic 内部信息。最后这个引发了一些争议:"一个'假装不是 AI 写的'功能?"

AI 公司的代码不是银弹。产品好不好,跟代码漂不漂亮,从来就是两件事。


给每个 npm 开发者的警钟

聊完了 Claude Code 内部,我们说说这件事对你我的影响。

Anthropic 犯的这个错误,任何一个 npm 包的作者都可能犯

问题的根源在于 npm 发包的"默认行为"——如果你既没有 .npmignore 也没有 files 字段,npm 会上传几乎所有文件。而很多开发者压根不知道自己的包里到底有什么。

解法很简单,但极少有人真的在做:

# 发包之前,永远先跑一遍这个命令
npm pack --dry-run

# 它会告诉你:将要上传哪些文件、总大小是多少
# 如果你看到 .map 文件、.env 文件、test 目录——
# 恭喜,你刚救了自己一命

更根本的做法是:用白名单,不用黑名单。

// package.json
{
  "files": [
    "dist/",
    "README.md",
    "LICENSE"
  ]
}

files 字段明确声明"只上传这些",比用 .npmignore 声明"不上传那些"安全得多。因为黑名单的问题是:你永远不知道下一次构建会多出什么新文件。

方式思路风险
.npmignore黑名单:排除不要的新增文件可能被遗漏,默认上传
files 字段白名单:只包含要的新增文件默认不上传,需手动添加
两者都用files 优先容易混淆,不推荐
踩坑记录
还有一个经典陷阱:如果你的项目同时有 .gitignore.npmignore,npm 会完全忽略 .gitignore,只看 .npmignore。这意味着你以为被 .gitignore 排除的 .env 文件,可能正在被 npm publish 上传到全世界都能下载的地方。

理想的 CI/CD 流程里,应该有一步自动化检查:每次发包前,扫描 tarball 内容,发现 .map.envcredentials 等敏感文件就阻断发布。

安全不是一道墙,而是一个习惯。墙可以绕过去,但习惯会一直保护你。


三个没有答案的问题

聊到这里,回顾一下今天的收获:

我们从一个 57MB 的 source map 文件出发,看到了 Claude Code 的完整架构——40 个工具、46000 行查询引擎、四层安全流水线。我们也看到了 AI 顶级公司代码中的粗糙和妥协。更重要的是,我们看到了 npm 供应链安全这个被严重忽视的问题。

但有三个问题,我到现在也没有想清楚:

第一,当 AI 编程工具的源码完全透明,攻击者会怎么利用?

现在所有人都能看到 Claude Code 的安全分类器逻辑、拦截规则、放行例外。这意味着恶意 prompt 可以更精准地绕过安全检查。source map 虽然不涉及模型权重,但它暴露了"围墙上哪里有缝"。

第二,闭源 AI 工具的"安全感"还剩多少?

Anthropic 选择不开源 Claude Code,理由之一一定包含"保护商业秘密和安全机制"。结果呢?同一个错误犯了两次。当"闭源"连 source map 都管不住的时候,"开源"反而可能是更诚实的选择——至少你知道有多少双眼睛在帮你看代码。

第三,AI 工具调用越来越自主,"权限边界"谁来画?

Claude Code 的四层安全流水线设计得很精巧,但它本质上是在用一个 AI(Sonnet)来审判另一个 AI(主 Agent)的操作。用 AI 守护 AI,这条路能走多远?当 Agent 的能力从"编辑文件"进化到"部署服务"甚至"管理基础设施",当前的安全模型还够用吗?

我不知道答案。但我觉得,能把这三个问题想清楚的人,会在 AI 工具链的下一波变革中站得更稳。