claude code leak分析

2026-04-01

1. 起因

https://www.npmjs.com/package/@anthropic-ai/claude-code?activeTab=code中泄露了claude-code 的源代码映射文件cli.js.map
PS: claude-code的人已经更新并删除了npm包仓库中的cli.js.map文件,所以现在点进去网站什么东西都看不到。
cli.js.map文件十分重要,它是JavaScript Source Map 文件,一般情况下,发布到npm中的cli.js并不是程序员开发的原始版本,其发布往往经过如下几个步骤:

1. typescript -> javascript
2. 多个文件合并
3. 代码压缩、混淆,通常去除了符号
4. Babel 转译:Babel是JavaScript的编译器,通过Babel可以将新特性代码翻译成符合各个老式浏览器适配的代码。

虽然理论上可以还原这些步骤,通常情况还是很难做到的,需要资深逆向人员进行长时间的还原尝试才有可能做到(而且还原效果不敢保证)。
cli.js.map文件,可以理解为开发人员在生成cli.js时的调试文件,用于把压缩/编译后的 cli.js,映射回原始源码,方便调试、报错定位、逆向分析。
😀,我合理怀疑打包上传npm的步骤已经由ai代劳了,而ai没有关注源码泄露的事情,直接把cli.js.map上传到插件包网站了。
PS: claude code的源码还保留在https://github.com/instructkr/claw-code

2. claude code 代码分析

2.1 map文件还原源码

AI代劳(AI还是太好用了)
顺便附上ai写的提取脚本:

import fs from "node:fs";
import path from "node:path";

const cwd = process.cwd();
const mapPath = path.join(cwd, "cli.js.map");
const outputRoot = path.join(cwd, "claude_src");

function ensureInsideOutput(resolvedPath) {
  const relative = path.relative(outputRoot, resolvedPath);
  if (relative.startsWith("..") || path.isAbsolute(relative)) {
    throw new Error(`Refusing to write outside output directory: ${resolvedPath}`);
  }
}

function normalizeSourcePath(sourcePath, index) {
  const cleaned = sourcePath.replace(/\\/g, "/");
  const withoutPrefix = cleaned.replace(/^(\.\.\/)+/, "");
  const safePath = withoutPrefix || `unknown/source_${index}.txt`;
  return safePath;
}

function main() {
  if (!fs.existsSync(mapPath)) {
    throw new Error(`Source map not found: ${mapPath}`);
  }

  fs.mkdirSync(outputRoot, { recursive: true });

  const raw = fs.readFileSync(mapPath, "utf8");
  const map = JSON.parse(raw);
  const sources = Array.isArray(map.sources) ? map.sources : [];
  const contents = Array.isArray(map.sourcesContent) ? map.sourcesContent : [];

  let written = 0;
  let skipped = 0;

  for (let i = 0; i < sources.length; i += 1) {
    const sourcePath = sources[i];
    const sourceContent = contents[i];

    if (typeof sourcePath !== "string" || typeof sourceContent !== "string") {
      skipped += 1;
      continue;
    }

    const relativePath = normalizeSourcePath(sourcePath, i);
    const destination = path.resolve(outputRoot, relativePath);
    ensureInsideOutput(destination);

    fs.mkdirSync(path.dirname(destination), { recursive: true });
    fs.writeFileSync(destination, sourceContent, "utf8");
    written += 1;
  }

  const summaryPath = path.join(outputRoot, "_extraction_summary.txt");
  const summary = [
    `map: ${mapPath}`,
    `output: ${outputRoot}`,
    `sources: ${sources.length}`,
    `written: ${written}`,
    `skipped: ${skipped}`,
    "",
    "Top-level restored directories:",
    ...Array.from(
      new Set(
        sources
          .filter((entry) => typeof entry === "string")
          .map((entry, index) => normalizeSourcePath(entry, index).split("/")[0]),
      ),
    ).sort(),
    "",
  ].join("\n");

  fs.writeFileSync(summaryPath, summary, "utf8");

  process.stdout.write(
    [
      `Restored ${written} source files to ${outputRoot}`,
      `Skipped ${skipped} entries without inline content`,
      `Summary: ${summaryPath}`,
    ].join("\n"),
  );
}

main();

2.2 claude code封号机制逆向

前人的研究:
https://bytedance.larkoffice.com/docx/E2JudVzf7oCNfhxyxaQcZIW1n0g
让我震惊的是codex的逆向分析,和前人的研究结果非常类似,把我震惊到了!
上报远端的字符分类竟然一致,难以置信了说是:

3. 后续

3.1 claude code gateway

在人们发现claude code是在前端收集的信息来判断账号是否异常时,很快啊,有人马上就写了claude code的gateway来让claude code官方认为账号是安全稳定的,链接如下:
https://github.com/motiful/cc-gateway

3.2 claude rust

有人用rust语言重写了一边 claude code cli:
https://github.com/Kuberwastaken/claurst