乐于分享
好东西不私藏

Bun 还原 claude code 源码泄露全过程为什么一个 `.map` 文件泄露等同于泄露?

Bun 还原 claude code 源码泄露全过程为什么一个 `.map` 文件泄露等同于泄露?

最近 claude code 源码被泄露,大家应该人手一份了吧。

本文从技术角度还原 claude code 源码泄露过程,解答为什么仅一个 .map 文件泄露几乎等同于源码泄露。

代码复原

pnpm init 生成如下目录结构:

❯ tree .       claude-code-leak-incident-reconstruction-using-bun├── LICENSE├── README.md├── cli.ts└── package.json

cli.ts:

#!/usr/bin/env node// (c) Anthropic PBC. All rights reserved. Use is subject to the Legal Agreements outlined here: https://code.claude.com/docs/en/legal-and-compliance.// Version: 2.1.89// Want to see the unminified source? We're hiring!// https://job-boards.greenhouse.io/anthropic/jobs/4816199008function greet(namestring) {          // 第1行// 第2行(空行)if (!name) {                        // 第3行(4空格缩进)return "Hello stranger";        // 第4行(8空格缩进)    }                                   // 第5行(4空格缩进)// 第6行(空行)return `Hello ${name}`;             // 第7行(4空格缩进)}                                       // 第8行// 第9行(空行)// 文件结束                              // 第10行console.log(greet('Claude Code.'))

可以看到我们故意加了很多空格和注释就是为了试试看,仅有一个 .map 是否能完完全全字符级别精确复原代码。

执行构建:bun build cli.ts --minify --outfile=cli.js --sourcemap=linked

生成两个文件,目前目录结构如下:

❯ tree .       claude-code-leak-incident-reconstruction-using-bun├── LICENSE├── README.md├── cli.js     # bun 构建生成├── cli.js.map # bun 构建生成├── cli.ts└── package.json

我们主要看看这个导致 Claude Code 泄露的“罪魁祸首” cli.js.map

{"version"3,  "sources"["cli.ts"],  "sourcesContent"["#!/usr/bin/env node\r\n// (c) Anthropic PBC. All rights reserved. Use is subject to the Legal Agreements outlined here: https://code.claude.com/docs/en/legal-and-compliance.\r\n\r\n// Version: 2.1.89\r\n\r\n// Want to see the unminified source? We're hiring!\r\n// https://job-boards.greenhouse.io/anthropic/jobs/4816199008\r\nfunction greet(name: string) {          // 第1行\r\n                                        // 第2行(空行)\r\n    if (!name) {                        // 第3行(4空格缩进)\r\n        return \"Hello stranger\";        // 第4行(8空格缩进)\r\n    }                                   // 第5行(4空格缩进)\r\n                                        // 第6行(空行)\r\n    return `Hello ${name}`;             // 第7行(4空格缩进)\r\n}                                       // 第8行\r\n                                        // 第9行(空行)\r\n// 文件结束                              // 第10行\r\n\r\nconsole.log(greet('Claude Code.'))"  ],  "mappings"";AAOA,SAAS,CAAK,CAAC,EAAc,CAEzB,GAAI,CAAC,EACD,MAAO,iBAGX,MAAO,SAAS,IAKpB,QAAQ,IAAI,EAAM,cAAc,CAAC",  "debugId""0CD11855101BFF5E64756E2164756E21",  "names"[]}

可以看到其实就是一个 json 文件,里面的 sourcesContent 包含了完整的源码!

sourcesContent 内容:

#!/usr/bin/env node// (c) Anthropic PBC. All rights reserved. Use is subject to the Legal Agreements outlined here: https://code.claude.com/docs/en/legal-and-compliance.// Version: 2.1.89// Want to see the unminified source? We're hiring!// https://job-boards.greenhouse.io/anthropic/jobs/4816199008function greet(namestring) {          // 第1行// 第2行(空行)if (!name) {                        // 第3行(4空格缩进)return "Hello stranger";        // 第4行(8空格缩进)    }                                   // 第5行(4空格缩进)// 第6行(空行)return `Hello ${name}`;             // 第7行(4空格缩进)}                                       // 第8行// 第9行(空行)// 文件结束                              // 第10行console.log(greet('Claude Code.'))

可以看到 shebang、注释、TS 类型(string)、空格、空行一个不少。对比下字符数:

❯ wc -m sourcesContent.ts867 sourcesContent.ts❯ wc -m cli.ts867 cli.ts

一模一样!这下大家知道了为何 sourcemap 文件泄露等同于源码泄露。

构建参数 –sourcemap=inline 会泄露源码吗?

结论先行,一样会!

- ❯ bun build cli.ts --minify --outfile=cli.js --sourcemap=linked+ ❯ bun build cli.ts --minify --outfile=cli.js --sourcemap=inline
function c(b){if(!b)return"Hello stranger";return`Hello ${b}`}console.log(c("Claude Code."));//# debugId=0CD11855101BFF5E64756E2164756E21- //# sourceMappingURL=cli.js.map+ //# sourceMappingURL=data:application/json;base64,ewogICJ2...fQ==

会生成一段类似乱码:data:application/json;base64,ewogICJ2ZXJ... 但仔细一看其实是 base64。我们可以通过 base64 cli 还原 ❯ echo 'ewogICJ2ZXJzaW9...==' | base64 -d

❯ echo 'ewogICJ2ZXJzaW9uIjogMywKICAi...ogW10KfQ==' | base64 -d

输出

{"version"3,  "sources"["cli.ts"],  "sourcesContent"["#!/usr/bin/env node\r\n// (c) Anthropic PBC. All rights reserved. Use is subject to the Legal Agreements outlined here: https://code.claude.com/docs/en/legal-and-compliance.\r\n\r\n// Version: 2.1.89\r\n\r\n// Want to see the unminified source? We're hiring!\r\n// https://job-boards.greenhouse.io/anthropic/jobs/4816199008\r\nfunction greet(name: string) {          // 第1行\r\n                                        // 第2行(空行)\r\n    if (!name) {                        // 第3行(4空格缩进)\r\n        return \"Hello stranger\";        // 第4行(8空格缩进)\r\n    }                                   // 第5行(4空格缩进)\r\n                                        // 第6行(空行)\r\n    return `Hello ${name}`;             // 第7行(4空格缩进)\r\n}                                       // 第8行\r\n                                        // 第9行(空行)\r\n// 文件结束                              // 第10行\r\n\r\nconsole.log(greet('Claude Code.'))"  ],  "mappings"";AAOA,SAAS,CAAK,CAAC,EAAc,CAEzB,GAAI,CAAC,EACD,MAAO,iBAGX,MAAO,SAAS,IAKpB,QAAQ,IAAI,EAAM,cAAc,CAAC",  "debugId""0CD11855101BFF5E64756E2164756E21",  "names"[]}

姐妹们,看到没有 inline 其实就是将整个 map json 文件 base64 了,解码后里面赫然存在 sourcesContent,其内容和 cli.ts 源码一模一样!

附录

本文还原 Claude Code 泄露过程的所有代码已经放在

legend80s/claude-code-leak-incident-reconstruction-using-bun

大家安装好 bun 后执行 ❯ bun build cli.ts --minify --outfile=cli.js --sourcemap=linked 或直接运行 bun run build

#claudecode #源码泄露 #claudecode源码泄露    #bun #anthropic  #ai #agent  #ClaudeCode #Claude编程助手#opencode #前端转AI #Agent