Jul 17, 2026 · 4 min · Dev Guides

Claude Code's source code has been leaked via a map file in their N...

Claude Code's source code has been leaked via a map file in their N...

Claude Code’s Source Code Has Been Leaked via a Map File in Their NPM Registry

An npm package can contain a 300 KB minified executable and, beside it, a 2 MB .map file holding the original TypeScript source, directory names, module boundaries, and developer comments. Anyone can retrieve both with one unauthenticated command:

npm pack <package-name>

That is the practical issue behind the Claude Code source exposure: a published source map reportedly included enough embedded source content to reconstruct code that was not intended to ship as readable source.

This is not the same as an attacker penetrating Anthropic’s infrastructure. It is a package publication failure. But the distinction should not make engineering teams dismiss it. Source maps can expose proprietary implementation details, internal file paths, unreleased feature flags, API assumptions, prompts, and security-sensitive logic. If credentials were accidentally hard-coded, they can expose those too.

The uncomfortable part is how ordinary the failure mode is. A single build option can produce the map, and a permissive npm packaging rule can publish it.

How a Source Map Becomes a Source Leak

JavaScript build tools transform source code through compilation, bundling, and minification. A source map connects the generated output back to the original files so that a stack trace in dist/cli.js can point to src/commands/login.ts:84.

A simplified map looks like this:

{
  "version": 3,
  "file": "cli.js",
  "sources": [
    "../src/auth/session.ts",
    "../src/api/client.ts"
  ],
  "sourcesContent": [
    "export async function loadSession() { /* original code */ }",
    "export class ApiClient { /* original code */ }"
  ],
  "names": ["loadSession", "ApiClient"],
  "mappings": "AAAA,MAAM..."
}

The critical field is sourcesContent. When present, it contains the source files themselves, not merely references to their names.

A map without sourcesContent still discloses useful information:

With sourcesContent, reconstruction may be as simple as parsing JSON and writing each entry to disk. Minification is no protection because the map exists specifically to reverse the minifier’s loss of readability.

What “leaked source” does and does not mean

The exposure of readable source does not automatically establish that API keys, customer data, or signing credentials were compromised. Those are separate questions that require inspecting the artifact.

It does mean that code embedded in the map became public to anyone able to download that package version. Unpublishing the package or replacing the current release limits new discovery, but it cannot revoke copies already cached by developers, mirrors, CI systems, or registry infrastructure.

Artifact shippedReadable implementationOriginal pathsTypical risk
Minified JavaScript onlyPartiallyUsually noReverse engineering remains possible
Map without sourcesContentPartiallyYesArchitecture and symbol disclosure
Map with sourcesContentYesYesNear-complete source disclosure
Public TypeScript sourceYesYesIntentional disclosure; secrets still prohibited
Private map uploaded to monitoringYes, access-controlledYesLower risk, dependent on access controls

Reproducing the Check Against Your Own Package

Do not inspect a package by looking only at its Git repository. The npm tarball is the release artifact, and it can differ substantially from the repository.

Run the same packaging operation npm will use:

npm pack --dry-run

This prints the files selected for publication. For a stronger check, create and inspect the actual tarball:

npm pack
tar -tzf your-package-1.4.0.tgz

Search for common map extensions:

tar -tzf your-package-1.4.0.tgz \
  | grep -E '\.(js|mjs|cjs|css)\.map$'

Then extract into a temporary directory:

tmp_dir="$(mktemp -d)"
tar -xzf your-package-1.4.0.tgz -C "$tmp_dir"
find "$tmp_dir" -type f -name '*.map' -print

A common gotcha is checking dist/ before publishing and assuming it matches the tarball. npm may include extra files because of package.json, .npmignore, workspace configuration, or generated artifacts left by an earlier build. In practice, the tarball itself must be tested.

Detect embedded source automatically

This Python script fails if any map includes non-empty sourcesContent:

#!/usr/bin/env python3
import json
import pathlib
import sys

root = pathlib.Path(sys.argv[1])
failures = []

for path in root.rglob("*.map"):
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except (UnicodeDecodeError, json.JSONDecodeError) as exc:
        failures.append(f"{path}: invalid source map: {exc}")
        continue

    embedded = data.get("sourcesContent")
    if embedded and any(content for content in embedded if content is not None):
        failures.append(f"{path}: embeds {len(embedded)} source entries")

if failures:
    print("Unsafe source maps detected:")
    for failure in failures:
        print(f"  - {failure}")
    sys.exit(1)

print("No embedded source content found.")

Run it after extracting the packed artifact:

python3 scripts/check-source-maps.py "$tmp_dir/package"

I also scan the tarball for credential-shaped strings. This is not a substitute for proper secret scanning, but it catches obvious failures:

rg -n \
  '(sk-ant-|AIza[0-9A-Za-z_-]{20,}|-----BEGIN .*PRIVATE KEY-----)' \
  "$tmp_dir/package"

Avoid logging matches in a public CI job. Printing a discovered credential can turn an artifact mistake into a log disclosure.

Fix the Build and Publication Boundaries

There are two controls to apply: generate the right artifacts, then publish only the intended files.

Disable public source maps

For esbuild, turn source maps off for the distributed package:

import { build } from "esbuild";

await build({
  entryPoints: ["src/cli.ts"],
  outfile: "dist/cli.js",
  bundle: true,
  platform: "node",
  minify: true,
  sourcemap: false
});

For TypeScript, the relevant options are explicit:

{
  "compilerOptions": {
    "sourceMap": false,
    "inlineSourceMap": false,
    "inlineSources": false,
    "outDir": "dist"
  }
}

Deleting only external .map files is insufficient if inlineSourceMap remains enabled. An inline map is stored as a base64 data URL at the end of the JavaScript file.

Check for it directly:

rg -n 'sourceMappingURL=data:application/json' dist

Use an npm allowlist

A files allowlist in package.json is easier to reason about than a long denylist:

{
  "files": [
    "dist/**/*.js",
    "dist/**/*.cjs",
    "dist/**/*.mjs",
    "dist/**/*.d.ts",
    "README.md",
    "LICENSE"
  ]
}

However, patterns deserve testing. If dist/**/*.map is matched by a broader rule or build output changes extension, assumptions can fail. Treat npm pack as the definitive result.

A robust release sequence is:

  1. Remove the old build directory.
  2. Build from a clean checkout.
  3. Run unit and integration tests.
  4. Create the npm tarball.
  5. Extract and inspect the tarball.
  6. Run source-map and secret checks.
  7. Publish that verified artifact.

Cleaning matters. I have seen stale maps survive after a team disabled map generation because the build wrote new JavaScript over an existing dist/ directory without deleting old files.

Keep Production Debugging Without Publishing Maps

Source maps are valuable. Disabling them everywhere makes production incidents harder to diagnose, especially for a CLI where stack traces arrive from machines you do not control.

The better pattern is to generate private maps separately:

await build({
  entryPoints: ["src/cli.ts"],
  outfile: "dist/cli.js",
  bundle: true,
  sourcemap: "external",
  sourcesContent: true
});

Upload the maps to an access-controlled error-monitoring system, then remove them before packaging:

./scripts/upload-source-maps.sh dist
find dist -type f -name '*.map' -delete
npm pack

What actually happens when this is done poorly is that the upload step succeeds, the deletion step silently fails, and publication continues. Use strict shell behavior and verify the result:

set -euo pipefail

./scripts/upload-source-maps.sh dist
find dist -type f -name '*.map' -delete

if find dist -type f -name '*.map' | grep -q .; then
  echo "Source maps remain in dist" >&2
  exit 1
fi

Private maps still contain sensitive implementation details. Protect them with retention limits, scoped credentials, and restricted project access.

Why This Matters for Claude, GPT, and Gemini Applications

LLM applications often place more security-sensitive material in client code than conventional API integrations. I regularly see system prompts, tool schemas, routing rules, model fallbacks, and moderation logic bundled into desktop apps or npm CLIs.

A source map might reveal code like:

const routes = {
  planning: "Sonnet 4.6",
  extraction: "Haiku 4.5",
  longContext: "Fable 5",
  fallback: "GPT-5.5"
};

That is operationally useful to a competitor or attacker, but it is not a credential. The more serious error is embedding provider keys:

// Never ship this, with or without source maps.
const client = new ClaudeClient({
  apiKey: "sk-ant-example-hardcoded-key"
});

Minifying this code does not protect the key. Even without a map, the runtime must possess the string, so a user can extract it.

Put model credentials behind a server-side gateway:

import os
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.post("/api/chat")
def chat():
    user = authenticate(request)
    enforce_quota(user)

    model = choose_model(request.json["task"])
    result = call_model(
        model=model,
        api_key=os.environ["MODEL_GATEWAY_KEY"],
        messages=request.json["messages"],
    )
    return jsonify(result)

The gateway can enforce per-user quotas, redact logs, rotate credentials, and route between Claude, GPT, and Gemini. AI Prime Tech can fit at this layer when a team wants cheaper multi-model API access without distributing separate provider credentials to every application.

Source-map hygiene does not replace that architecture. It protects implementation details; the gateway protects authority.

Practical Takeaways

MR
Marcus Reed · Senior API Engineer

Marcus has spent 9 years building LLM-backed products and integrating the Claude, GPT and Gemini APIs into production systems. He writes about API cost optimization, agent architecture, and practical model selection.

Get cheaper Claude API access

One API key for Claude Opus 4.8, Sonnet 4.6, Haiku 4.5, Fable 5, plus GPT & Gemini — up to 80% off official pricing, pay-as-you-go.

Get Your API Key →
AI Prime Tech is an independent third-party API gateway. Claude™ and Anthropic® are trademarks of Anthropic, PBC. No affiliation or endorsement is implied.