In March 2026, Anthropic accidentally published the entire source code for Claude Code through a .map file included in their npm package. Every file, every comment, every internal constant — all sitting in a JSON file anyone could download. It was the second time it happened in two months.
This wasn't a sophisticated attack. It was a build configuration oversight. And it's a mistake that's far more common than people realize.
What Are Source Maps?
Source maps are files generated by JavaScript bundlers (Webpack, esbuild, Bun, Rollup) that map minified production code back to the original source. They're essential for debugging — when you see an error at bundle.js:1:45892, the source map tells your browser it's actually src/auth/login.ts:47:12.
{
"version": 3,
"sources": ["src/auth/login.ts", "src/api/client.ts"],
"names": ["authenticate", "refreshToken"],
"mappings": "AAAA,SAAS,IAAI..."
}The problem: source maps contain enough information to reconstruct your entire original codebase. File paths, function names, variable names, comments, string literals — everything the bundler stripped out to create the minified version.
How This Happens
Most bundlers generate source maps by default. If you don't explicitly disable them for production builds, they ship with your package.
// Bun generates source maps by default
// If you don't add this to your build config:
Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
sourcemap: "none", // THIS LINE prevents the leak
});The same applies to other bundlers:
module.exports = {
mode: "production",
// devtool controls source map generation
// "source-map" generates .map files — DON'T use this in production builds
// "hidden-source-map" generates maps but doesn't reference them in the bundle
// false disables source maps entirely
devtool: false,
};require("esbuild").buildSync({
entryPoints: ["src/index.ts"],
bundle: true,
minify: true,
sourcemap: false, // Explicitly disable for production
outfile: "dist/bundle.js",
});The npm-Specific Risk
When publishing to npm, the problem compounds. npm publish includes everything in your project directory unless you explicitly exclude files.
There are two ways to control what gets published:
Allowlist approach (preferred)
{
"name": "my-package",
"files": [
"dist/**/*.js",
"dist/**/*.d.ts",
"!dist/**/*.map"
]
}The files field is an allowlist — only the listed patterns are included. This is safer because new file types are excluded by default.
Blocklist approach
*.map
src/
tests/
.env*
*.config.ts.npmignore is a blocklist — everything is included unless explicitly excluded. This is riskier because new file types (like .map files from a bundler change) are included by default.
What Gets Exposed
When a source map leaks, an attacker gets:
- Full source code — every file, reconstructed from the mapping
- Internal comments —
// TODO: fix this security checkbecomes a roadmap - Hardcoded strings — API endpoints, internal URLs, feature flag names
- Architecture details — file structure reveals how the system is organized
- Potential secrets — any API keys, tokens, or credentials in the source
For AI products specifically, system prompts and orchestration logic get exposed — revealing how the AI is instructed to behave and what guardrails exist.
Preventing This in CI/CD
The fix isn't just "remember to disable source maps." It's building automated checks into your pipeline so a human mistake can't ship debug artifacts.
Step 1: Build Configuration
const isProduction = process.env.NODE_ENV === "production";
export default {
sourcemap: isProduction ? false : "inline",
minify: isProduction,
};Step 2: CI Validation
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
- name: Verify no source maps in dist
run: |
if find dist -name "*.map" | grep -q .; then
echo "ERROR: Source map files found in dist/"
find dist -name "*.map"
exit 1
fi
- name: Verify no source maps in package
run: |
npm pack --dry-run 2>&1 | grep -i "\.map" && {
echo "ERROR: .map files would be included in package"
exit 1
} || true
- run: npm publishStep 3: Pre-publish Hook
{
"scripts": {
"prepublishOnly": "node scripts/verify-no-sourcemaps.js"
}
}import { globSync } from "fs";
const maps = globSync("dist/**/*.map");
if (maps.length > 0) {
console.error("Source map files found — aborting publish:");
maps.forEach((f) => console.error(` ${f}`));
process.exit(1);
}The Broader Lesson
This incident isn't really about source maps. It's about a fundamental principle: build tool defaults are not production-safe defaults. Every tool in your build pipeline has settings optimized for developer experience, not for production security.
Audit your build output:
#!/bin/bash
echo "=== Files in build output ==="
find dist -type f | sort
echo ""
echo "=== Checking for debug artifacts ==="
find dist -name "*.map" -o -name "*.map.js" -o -name "*.d.ts.map"
echo ""
echo "=== Checking for source references ==="
grep -r "sourceMappingURL" dist/ || echo "No source map references found"
echo ""
echo "=== What npm will publish ==="
npm pack --dry-run 2>&1Key Takeaways
- Source maps reconstruct your entire codebase — they're not just "debug info"
- Most bundlers generate them by default — you must explicitly disable them for production
- Use
filesin package.json, not.npmignore— allowlist beats blocklist - Add CI checks for debug artifacts — don't rely on humans remembering build flags
- Audit your build output regularly — run
npm pack --dry-runand inspect what you're shipping
If a major AI company can ship source maps to npm twice in two months, it can happen to anyone. The difference is whether you have automated guardrails or just good intentions.