Anthropic 把自家核心代码“裸奔”了——Claude Code 源码泄露事件全解析:从 npm Source Map 到 settings.json 配置骨架 1. 一个 .map 文件引发的连锁反应Anthropic 把自家核心代码“裸奔”了——Claude Code 源码泄露事件全解析这件事在开发者圈子里炸开锅的原因不是黑客攻破了什么堡垒而是一个 Source Map 文件被顺手打进了 npm 发布产物。Source Map 是什么简单说它是前端和 Node.js 世界里用来把压缩、混淆后的代码映射回原始 TypeScript 源码的调试辅助文件浏览器 DevTools 里能直接看到源码靠的就是它。正常情况下这个文件只应该出现在开发环境绝不该跟着生产包一起发布。Claude Code 作为 Anthropic 面向开发者的终端 AI 编程工具本身用户就是一群会翻 node_modules 的人。当 v2.1.88 版本的 npm 包里带着.map文件而这个文件又指向了存放在对象存储上的完整未混淆源码目录时任何人只要会解析 Source Map就能把整个src/目录还原出来。规模大概在 1900 个文件、51 万行 TypeScript 上下技术栈包括 Bun 运行时、React Ink 做终端 UI、Commander.js 做命令行解析、Zod 做校验、ripgrep 做搜索。这件事对普通用户意味着什么你的账户和 API Key 不受影响但你的工具链配置习惯值得重新审视一遍。这篇内容我会带你做三件事第一理解 Source Map 泄露的完整链路第二在本地亲手复现一次.map解析过程看看它到底能还原出什么第三给你一份可复制的settings.json配置骨架顺便检查你自己的 AI 工具配置有没有同类暴露风险。适合谁看日常用 Claude Code、Cursor、各类 CLI Agent 的开发者以及负责团队 npm 发布流程的工程同学。2. 泄露链路拆解从 npm pack 到源码还原2.1 Source Map 到底是怎么把源码交出去的先把这个机制讲透不然后面的复现你会觉得像变魔术。TypeScript 编译成 JavaScript 后变量名被压缩、类型被擦除、文件被合并可读性基本归零。Source Map 就是一张对照表记录“压缩后第 3 行第 15 列”对应“原始文件 src/QueryEngine.ts 第 820 行第 4 列”。它本身不包含源码但包含两个关键信息原始文件路径以及sourcesContent字段——很多构建工具默认会把原始源码内容直接内联进.map文件。所以泄露路径有两种。第一种是.map里内联了sourcesContent拿到文件就等于拿到源码。第二种是.map只存路径但路径指向了一个公开可访问的存储地址解析时按路径去拉取同样能拿到完整内容。这次事件属于后者叠加前者.map文件被发布且引用了外部存储上的未混淆源码。2.2 打包流程里那个被遗忘的检查步骤问题根源在发布产物没有做白名单过滤。一个健康的 npm 发布流程package.json里的files字段应该明确列出要发布的目录而不是靠.npmignore去排除。.npmignore是黑名单思路你永远不知道自己漏了什么files是白名单思路没列进去的一律不发。我见过太多项目在这块翻车。构建脚本tsc --sourceMap生成.map打包时忘了清理npm publish一把梭.map就跟着上去了。更隐蔽的是 monorepo 场景某个子包的dist目录里混进了上一轮构建的残留文件CI 缓存没清干净照样发出去。2.3 影响面不只是“代码被看到”代码被看到本身已经够严重但真正需要关注的是三块内容。第一是系统提示词和工具权限定义这些直接暴露了 Claude Code 如何约束模型行为、权限检查逻辑怎么走对提示注入和越狱研究来说门槛被拉低了。第二是架构决策Bun React Ink 这套选型、多智能体协调的实现方式竞争对手可以直接研究。第三是未公开功能的 feature flags代码里能看到一些还没开放的能力开关说明产品路线图的一部分已经提前可见。需要说清楚的是这不是入侵事件没有服务器被攻破本质是一次 DevOps 打包配置失误和“把密钥提交到公开仓库”属于同一类工程事故。但对做工具的人来说它是一面镜子。3. 前置准备用 TaoToken 搭一个可复现的验证环境要复现 Source Map 解析、验证配置风险你需要一个能稳定调用模型的入口来做辅助分析。我平时用 TaoToken 做这类验证它的接口兼容主流格式配置成本低。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 入口是 https://taotoken.net/api注意 API 地址不带 UTM 参数。先拿 Key。进入控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite创建后在 API Keys 页面管理地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite。Key 只显示一次复制到本地环境变量里别写进代码。export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你要长期跑编码类任务或者 Agent 流程可以看下 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite按套餐走比单次调用更划算。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite遇到参数问题先翻这里。验证 Key 是否可用用一条最简单的请求curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: 回复 OK 两个字母}], max_tokens: 16 }返回里能看到choices[0].message.content就说明通了。想先在网页里试模型效果可以用模型对话页面地址是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite。4. 可复制配置settings.json 骨架与 Source Map 复现脚本4.1 一份能直接用的 settings.json 骨架Claude Code 的配置核心在settings.json它决定了权限、环境变量、工具白名单。下面这份骨架你可以直接复制按注释改。重点看permissions和env两块前者控制它能碰什么后者控制它带着什么凭据跑。{ permissions: { allow: [ Read, Glob, Grep ], deny: [ Bash(rm:*), Bash(curl:*), Read(./.env), Read(./.env.*), Read(./secrets/**) ], ask: [ Bash(git push:*), Write ] }, env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: ${TAOTOKEN_API_KEY}, DISABLE_TELEMETRY: 1, DISABLE_ERROR_REPORTING: 1 }, includeCoAuthoredBy: false, cleanupPeriodDays: 7 }几个关键点解释一下。deny里显式挡掉.env和secrets目录的读取这是防止 Agent 在你不注意时把凭据读进上下文。ask里的Write表示写文件前必须人工确认避免它自动改你的代码。env里用${TAOTOKEN_API_KEY}引用环境变量不要把 Key 硬编码进这个文件因为settings.json很可能被提交到仓库。4.2 本地复现 Source Map 解析现在动手复现。先找一个带.map的包随便一个前端库都行或者你自己构建一个。mkdir smap-demo cd smap-demo npm init -y npm install --save-dev typescript写一个简单的 TS 文件开启 sourceMap 编译cat index.ts EOF export function greet(name: string): string { const prefix hello, ; return prefix name; } EOF npx tsc index.ts --sourceMap --outDir dist ls dist你会看到dist/index.js和dist/index.js.map。现在解析这个 map 文件看它暴露了什么node -e const fs require(fs); const map JSON.parse(fs.readFileSync(dist/index.js.map, utf8)); console.log(sources:, map.sources); console.log(has sourcesContent:, Array.isArray(map.sourcesContent)); if (map.sourcesContent) { console.log(--- 还原出的源码 ---); console.log(map.sourcesContent[0]); } 如果sourcesContent存在你会看到原始 TypeScript 一字不差地打印出来。这就是泄露的本质.map文件本身就是源码的搬运工。如果sourcesContent为空sources字段里的路径就是线索顺着路径去拉取同样能拿到内容。4.3 检查你自己的发布产物把上面这套逻辑用到你自己的项目上检查有没有.map混进发布包npm pack --dry-run 21 | grep -E \.map$ echo 警告发布产物含 .map 文件 || echo 未发现 .map更彻底的做法是直接检查files字段node -e const pkg require(./package.json); console.log(files 字段:, pkg.files || 未设置危险); 如果files没设置npm 会按.npmignore或默认规则打包.map很容易漏进去。建议显式设置files只列dist里需要的产物并在构建脚本末尾加一步清理find dist -name *.map -delete5. 验证请求与成功结果配置改完跑一次验证确认两件事模型调用通且权限约束生效。先验证模型调用用第 3 节的 curl 命令返回OK即通过。然后验证settings.json的权限规则。启动 Claude Code 后让它尝试读一个被deny的文件# 在 Claude Code 会话里输入 读取 ./.env 文件内容预期结果是它拒绝执行并提示该路径在 deny 列表里。如果它真的读出来了说明你的settings.json没被加载检查文件位置——项目级配置在.claude/settings.json用户级在~/.claude/settings.json两者会合并项目级优先。再验证一次 Source Map 清理是否生效npm run build npm pack --dry-run 21 | grep -c \.map$输出0表示干净。如果输出大于 0回到构建脚本里加清理步骤。成功的结果长这样模型请求返回正常内容.env读取被拦截发布产物里.map数量为零。这三条都过了说明你的配置骨架和发布流程基本安全。6. 本篇常见错排查报错一401 Unauthorized。大概率是 Key 没读到。检查TAOTOKEN_API_KEY是否 export 成功echo $TAOTOKEN_API_KEY看有没有值。如果用的是settings.json里的${TAOTOKEN_API_KEY}确认启动 Claude Code 的 shell 里这个变量存在GUI 启动的终端经常读不到你.zshrc里的 export。报错二settings.json改了没生效。先确认 JSON 语法合法用node -e JSON.parse(require(fs).readFileSync(.claude/settings.json,utf8))验一下。然后确认路径项目级是.claude/settings.json不是.claude.json。最后确认权限规则写法Bash(rm:*)这种带冒号的匹配语法写错会静默失效。报错三npm pack还是带出.map。检查是不是有prepare或prepack脚本在打包前重新生成了.map。清理步骤要放在prepack的最后或者干脆在files字段里精确到文件而不是目录。报错四Source Map 解析出来sourcesContent是 null。说明构建时没开内联源码但sources路径依然有效。这种情况下泄露风险取决于那个路径是否公开可访问。检查你的构建配置里sourceMap是true还是inlineinline会把源码直接塞进.js文件比.map更危险。报错五模型返回乱码或截断。检查max_tokens是否设得太小以及请求体是不是合法 JSON。用curl -v看完整响应头确认没有中间层改写。排障过程中如果卡在接入参数上翻接入文档最快地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。Key 相关问题去 API Keys 页面重新生成地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite。7. 把这次事故变成你自己的检查清单Anthropic 这次翻车对普通用户没有直接损失但对做工程的人是一份免费教材。我自己的做法是把下面几条固化进发布流程package.json必须显式写files字段构建脚本末尾加.map清理CI 里加一步npm pack --dry-run检查产物settings.json里deny列表必须包含.env和密钥目录API Key 一律走环境变量不进任何会被提交的文件。如果你在跑长期编码任务或者 Agent 流程Coding Plan 的套餐模式比单次调用更省心地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite。想先试模型能力模型对话页面直接开聊地址是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite。Claude Code 相关的接入配置文档里有完整参数说明地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。最后留一个动作给你现在打开你手头任意一个 npm 项目跑一遍npm pack --dry-run 21 | grep \.map$。如果输出不为空你今天就找到了一个值得修的隐患。