Claude Code source map leak
Anthropic shipped a source map with the code inside. Teams should treat that as a release failure.
Anthropic published @anthropic-ai/claude-code@2.1.88 to npm with cli.js.map in the tarball. I downloaded the package from the registry and checked it myself. The source map held 4,756 embedded source entries. Remove node_modules, and the package still exposed 1,906 Anthropic-owned files and about 515,000 lines of code, almost all TypeScript or TSX.
If a source map ships with sourcesContent, you did not ship a harmless debug file. You shipped source code in JSON.
By the time I checked, Anthropic had already started cleaning up. The npm version page was marked deprecated with the message Unpublished. The official GitHub repo and setup docs now mark npm installation as deprecated and push users toward the native installer. The response was quick. The artifact was already public.
Copies and analysis were already moving by then. That is how public artifacts behave on the internet. Once something useful lands in a registry, cleanup is playing from behind.
If you care more about what the exposed code says about agent design than the leak itself, Shreyansh wrote a separate piece on what Claude Code's architecture teaches us about building production AI agents. This post is about the more boring part, release hygiene, because boring failures still do plenty of damage.
What shipped
Here is what I could verify from the npm package, registry metadata, and Anthropic's public pages:
@anthropic-ai/claude-code@2.1.88 was published to npm as a public package.
- The tarball included
cli.js.map alongside cli.js, README.md, LICENSE.md, package.json, bun.lock, and sdk-tools.d.ts.
cli.js.map included a populated sourcesContent array.
- The map contained 4,756 embedded files in total.
- Excluding
node_modules, the map still covered 1,906 Anthropic-owned files.
- 1,888 of those files were
.ts or .tsx.
- The Anthropic-owned portion of the map contained about 515,000 lines.
- The npm page later marked the version deprecated with the author message
Unpublished.
- Anthropic's repo and setup docs now mark npm installation as deprecated.
I am deliberately not reproducing code from the map or linking to mirror repos. There is enough public noise around that already. This package exposed much more than a CLI binary.
Why this matters
People still talk about source maps like they are harmless leftovers. Sometimes they are. Source maps can be low risk when they stay inside private error tracking and omit original source. A public map with sourcesContent is a source release.
What bothered me most was how ordinary this failure mode is. The map did not just explain stack traces. It exposed internal structure, prompt wiring, routing logic, telemetry paths, feature flag plumbing, and orchestration choices. I am keeping that description high level on purpose. I do not want to turn a security post into a guided tour of someone else's codebase.
For an AI coding product, this kind of leak lowers the cost of scrutiny, imitation, and attack planning. Reverse engineering a bundled CLI takes time. Reading a source map with the code already embedded takes minutes.
Same family, different cause
I wrote about the Telnyx PyPI compromise last week. That one was malicious. This one looks like a release engineering mistake. Operationally, they rhyme.
I keep coming back to one line: the artifact is the perimeter.
Teams spend most of their energy on application code and runtime controls, then treat the package, container image, GitHub Action, Helm chart, and browser bundle like packaging details. Attackers do not make that distinction. Accidents do not either.
We have seen the same pattern in the xz backdoor, GitHub Action compromises, PyPI credential stealers, malicious npm updates, exposed container layers, and now a public npm package that carried its own source tree as JSON. The failure modes change. The trust boundary does not. Whatever you ship becomes the thing people inspect and attack.
Preventing this in your own build
Avoiding this is not glamorous. It is basic release discipline, which is exactly why it works.
1. Do not publish source maps with embedded source
If you ship a closed-source npm package, public source maps should be off unless you are comfortable releasing the code they contain. Private debugging is fine. Public sourcesContent is a release event.
For frontend and CLI builds, that usually means:
- disable public source maps for the package
- generate hidden maps and upload them to private error tracking
- use no-sources maps if you need symbols without shipping code
- review bundler defaults whenever you change the build toolchain
2. Make npm pack a hard release gate
Do not trust the dist/ folder. Inspect the tarball you are about to publish.
Every release pipeline should build the package, run npm pack, unpack the tarball, and fail if the contents drift outside policy. At minimum, fail the release when:
- a
.map file contains sourcesContent
- a package includes files outside an allowlist
- the tarball carries internal docs, prompts, fixtures, or config that do not belong in the package
- the manifest changes without explicit review
A simple gate is better than discovering the problem on X:
npm pack
tar -xzf *.tgz
node -e 'const fs=require("fs"); const map=JSON.parse(fs.readFileSync("package/cli.js.map","utf8")); if (Array.isArray(map.sourcesContent) && map.sourcesContent.some(Boolean)) { console.error("Refusing to publish embedded source maps"); process.exit(1); }'
3. Publish from a clean, allowlisted directory
A lot of leaks happen because teams publish from the root of a monorepo or from a dist/ directory that grew by habit. Build into a clean release directory. Copy in only what belongs in the package. Use the files field in package.json as an allowlist. Treat .npmignore as a safety net, not the plan.
4. Treat operational AI assets as publish-sensitive
AI teams usually protect weights, API keys, and customer data. They get much looser with prompts, routing logic, fallback behavior, telemetry switches, internal feature flags, and tool permissions. That is a mistake.
Even when those assets are not secret in the legal sense, they still carry product value and attack value. If a release artifact includes them, assume they will be read.
5. Verify what the registry serves after publish
Even good CI pipelines miss things. Pull the exact version you just published from the registry or a mirror, inspect it again, and alert on drift. If something slips through, minutes matter.
Keep debug artifacts private by default. Source maps belong in private observability workflows far more often than they belong in public package registries.
What do we do:
We build for regulated industries, so we do not get to pretend release engineering sits outside the security model. A package, container, or browser bundle can be downloaded, mirrored, indexed, and searched within minutes. That assumption changes how you publish software.
Our default posture is simple:
- inspect the final artifact, not just the folder that produced it
- keep debug assets private
- ship from an allowlisted release directory
- assume any public artifact becomes readable immediately
I think a lot of AI teams still underprice release discipline because models are more fun to talk about, even though packaging is where a lot of avoidable damage happens.
Final thought
What happened here is embarrassing for Anthropic. It is also useful for the rest of the industry. A public npm package is a disclosure channel, and a source map with sourcesContent is source code. Teams building AI products spend endless time on models, prompts, evals, and tooling, then rush the last mile of packaging. That last mile decides what the world can actually read.
The next round of AI security failures will not come only from model behavior. Some will come from CI, registries, containers, browser bundles, GitHub Actions, and build outputs that nobody reviewed closely enough. The model gets the headlines. The artifact decides what the world can see.
References