# Anthropic Accidentally Shipped Their Source Code: What Actually Happened

By Leon Johnson, 2026-04-07. Web version: https://sholuv.net/blog/posts/anthropic-source-map-leak/

You've probably seen the memes. "Anthropic lost all their source code!" "TypeScript destroyed an AI company!" Here's what actually happened, why it matters, and how you can check if you're making the same mistake.

## What Actually Happened

Anthropic dropped a new release on us — they didn't "lose" their source code. They accidentally **published** it.

When they released Claude Code as an npm package, someone forgot to exclude a `.map` file from the build. That file was a **TypeScript source map** — a 59.8 MB file included in version 2.1.88 of `@anthropic-ai/claude-code`. It contained everything needed to reconstruct the original source code.

The result: ~512,000 lines across ~1,900 files of Claude Code's internal TypeScript codebase became public.

No customer data. No credentials. No model weights. Just the code behind Claude Code itself.

Anthropic confirmed it was human error in the release process, not a hack. Then [Chaofan Shou (@Fried_rice)](https://x.com/Fried_rice/status/2038894956459290963) dropped this tweet on us while interning at Solayer Labs.

This was the **second time** Anthropic leaked Claude Code source via source maps. A [similar incident](https://www.theregister.com/2026/03/31/anthropic_claude_code_source_code/) was reported to have happened with an earlier version in February 2025.

<div style="text-align: center; margin: 32px 0;">
<a href="https://www.youtube.com/watch?v=mBHRPeg8zPU" target="_blank">
<img src="youtube-thumbnail.png" alt="YouTube video explaining the Anthropic source map leak" style="max-width: 480px; border-radius: 8px; border: 1px solid #333;">
</a>
<p style="color: #8b949e; font-size: 14px; margin-top: 8px;">Video explainer covering the full situation</p>
</div>

## What's a Source Map?

When you view a page that uses JavaScript, your browser has to be able to read the code to execute it — and you can view it the same way the browser does. To make it harder to read, developers minify and bundle their code, stripping whitespace and renaming variables into an unreadable mess.

Source maps reverse that process. They're debugging files that map compiled/minified code back to the original source. Essential for development, but they should **never** ship to production.

Think of it like a bank vault blueprint. Minified code is the steel door — it makes it hard to see what's inside. A source map is the blueprint showing every room, every lock combination, and where the money is. Leaving it on a public server is like pinning the blueprint to the front door.

Pick your speed:

<div style="display:flex;gap:10px;margin:16px 0;">
  <button id="mode-demo" onclick="document.getElementById('sm-quick').style.display='block';document.getElementById('sm-walkthrough').style.display='none';this.style.background='#238636';this.style.color='#fff';document.getElementById('mode-walk').style.background='#21262d';document.getElementById('mode-walk').style.color='#e6edf3';" style="padding:10px 20px;border-radius:8px;border:1px solid #30363d;background:#238636;color:#fff;font-size:14px;font-weight:600;cursor:pointer;font-family:'Inter',sans-serif;">Quick Demo</button>
  <button id="mode-walk" onclick="document.getElementById('sm-walkthrough').style.display='block';document.getElementById('sm-quick').style.display='none';this.style.background='#238636';this.style.color='#fff';document.getElementById('mode-demo').style.background='#21262d';document.getElementById('mode-demo').style.color='#e6edf3';" style="padding:10px 20px;border-radius:8px;border:1px solid #30363d;background:#21262d;color:#e6edf3;font-size:14px;font-weight:600;cursor:pointer;font-family:'Inter',sans-serif;">Walk Me Through It</button>
</div>

<div id="sm-quick">
<div id="sourcemap-demo"></div>
</div>

<style>
  .sm-demo {
    font-family: 'Source Code Pro', monospace;
    background: #161b22;
    border: 1px solid #30363d;
    border-radius: 12px;
    padding: 24px;
    margin: 24px 0;
    max-width: 100%;
    overflow: hidden;
  }
  .sm-demo h3 {
    margin: 0 0 16px 0;
    font-family: 'Inter', sans-serif;
    font-size: 15px;
    color: #8b949e;
    text-transform: uppercase;
    letter-spacing: 0.1em;
  }
  .sm-columns {
    display: flex;
    gap: 16px;
    margin-bottom: 16px;
  }
  .sm-col {
    flex: 1;
    min-width: 0;
  }
  .sm-col-label {
    font-size: 12px;
    color: #2eafa7;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: 8px;
    font-family: 'Inter', sans-serif;
  }
  .sm-code {
    background: #0d1117;
    border: 1px solid #21262d;
    border-radius: 8px;
    padding: 16px;
    font-size: 12px;
    line-height: 1.6;
    color: #e6edf3;
    white-space: pre-wrap;
    word-break: break-all;
    min-height: 160px;
    transition: all 0.3s ease;
    overflow-x: auto;
  }
  .sm-code .keyword { color: #ff7b72; }
  .sm-code .string { color: #a5d6ff; }
  .sm-code .func { color: #d2a8ff; }
  .sm-code .comment { color: #8b949e; }
  .sm-code .type { color: #79c0ff; }
  .sm-code .highlight { background: rgba(46, 175, 167, 0.2); display: inline; padding: 1px 2px; border-radius: 2px; }
  .sm-btn-row {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 16px;
  }
  .sm-btn {
    padding: 10px 20px;
    border-radius: 8px;
    border: 1px solid #30363d;
    background: #21262d;
    color: #e6edf3;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s;
    font-family: 'Inter', sans-serif;
    -webkit-tap-highlight-color: transparent;
  }
  .sm-btn:hover { border-color: #2eafa7; }
  .sm-btn.active { background: #2eafa7; color: #fff; border-color: #2eafa7; }
  .sm-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    color: #2eafa7;
    padding-top: 40px;
  }
  .sm-status {
    text-align: center;
    padding: 12px;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 600;
    margin-top: 16px;
    font-family: 'Inter', sans-serif;
    transition: all 0.3s;
  }
  .sm-status.safe { background: rgba(63, 185, 80, 0.15); color: #3fb950; border: 1px solid rgba(63, 185, 80, 0.3); }
  .sm-status.danger { background: rgba(248, 81, 73, 0.15); color: #f85149; border: 1px solid rgba(248, 81, 73, 0.3); }
  @media (max-width: 600px) {
    .sm-columns { flex-direction: column; }
    .sm-arrow { transform: rotate(90deg); padding: 8px 0; }
  }
</style>

<script>
(function() {
  var container = document.getElementById('sourcemap-demo');
  if (!container) return;

  var originalTS = '<span class="comment">// src/agent/tools/bash.ts</span>\n<span class="keyword">import</span> { <span class="type">ToolResult</span> } <span class="keyword">from</span> <span class="string">\'../types\'</span>;\n\n<span class="keyword">interface</span> <span class="type">BashConfig</span> {\n  <span class="highlight">timeout</span>: <span class="type">number</span>;\n  <span class="highlight">sandbox</span>: <span class="type">boolean</span>;\n  <span class="highlight">allowedCommands</span>: <span class="type">string</span>[];\n}\n\n<span class="keyword">export async function</span> <span class="func">executeBash</span>(\n  command: <span class="type">string</span>,\n  config: <span class="type">BashConfig</span>\n): <span class="type">Promise</span>&lt;<span class="type">ToolResult</span>&gt; {\n  <span class="keyword">if</span> (config.sandbox) {\n    <span class="keyword">await</span> <span class="func">validateSandbox</span>(command);\n  }\n  <span class="comment">// ... internal logic</span>\n}';

  var compiledJS = '<span class="comment">// dist/cli.js (minified)</span>\n<span class="keyword">var</span> a=<span class="func">require</span>(<span class="string">"../types"</span>);\n<span class="keyword">async function</span> <span class="func">e</span>(t,n){\n<span class="keyword">if</span>(n.s){<span class="keyword">await</span> <span class="func">r</span>(t)}\n<span class="comment">// ... 500k more lines</span>\n}\nmodule.exports={e:e};';

  var sourceMap = '{\n  <span class="string">"version"</span>: 3,\n  <span class="string">"file"</span>: <span class="string">"cli.js"</span>,\n  <span class="string">"sources"</span>: [<span class="string">"../src/agent/tools/bash.ts"</span>, <span class="string">"..."</span>],\n  <span class="string">"sourcesContent"</span>: [<span class="highlight">"/* FULL ORIGINAL SOURCE HERE */"</span>],\n  <span class="string">"mappings"</span>: <span class="string">"AAAA;AACA;AAEA..."</span>\n}';

  var step = 0;

  function render() {
    var html = '<div class="sm-demo">';
    html += '<h3>How a source map leaks your code</h3>';
    html += '<div class="sm-btn-row">';
    html += '<button class="sm-btn' + (step === 0 ? ' active' : '') + '" onclick="window._smStep(0)">1. Write TypeScript</button>';
    html += '<button class="sm-btn' + (step === 1 ? ' active' : '') + '" onclick="window._smStep(1)">2. Compile + Ship</button>';
    html += '<button class="sm-btn' + (step === 2 ? ' active' : '') + '" onclick="window._smStep(2)">3. Decode (oops)</button>';
    html += '</div>';
    html += '<div class="sm-columns" style="margin-top:16px">';
    html += '<div class="sm-col">';
    if (step === 0) {
      html += '<div class="sm-col-label">Your Private TypeScript</div>';
      html += '<div class="sm-code">' + originalTS + '</div>';
    } else {
      html += '<div class="sm-col-label">Published npm Package</div>';
      html += '<div class="sm-code">' + compiledJS + '</div>';
    }
    html += '</div>';
    html += '<div class="sm-arrow">' + (step === 2 ? '&larr;' : '&rarr;') + '</div>';
    html += '<div class="sm-col">';
    if (step === 0) {
      html += '<div class="sm-col-label">Compiled JavaScript</div>';
      html += '<div class="sm-code">' + compiledJS + '</div>';
    } else if (step === 1) {
      html += '<div class="sm-col-label">Accidentally Included .map</div>';
      html += '<div class="sm-code">' + sourceMap + '</div>';
    } else {
      html += '<div class="sm-col-label">Recovered Original Source</div>';
      html += '<div class="sm-code">' + originalTS + '</div>';
    }
    html += '</div></div>';
    if (step === 0) html += '<div class="sm-status safe">Normal workflow: TypeScript compiles to JavaScript. Source stays private.</div>';
    else if (step === 1) html += '<div class="sm-status danger">The .map file shipped with the npm package. It contains a decoder ring back to the original source.</div>';
    else html += '<div class="sm-status danger">Anyone can now reconstruct the original TypeScript. ~512,000 lines exposed.</div>';
    html += '</div>';
    container.innerHTML = html;
  }

  window._smStep = function(s) { step = s; render(); };
  render();
})();
</script>

<div id="sm-walkthrough" style="display:none;">
<div id="sourcemap-tutorial"></div>
</div>

<style>
  .smt { background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 24px; margin: 24px 0; }
  .smt * { box-sizing: border-box; }
  .smt-nav { display: flex; justify-content: center; gap: 8px; margin-bottom: 20px; flex-wrap: wrap; }
  .smt-dot { width: 36px; height: 36px; border-radius: 50%; border: 2px solid #30363d; background: #21262d; color: #8b949e; font-weight: 700; font-size: 13px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s; }
  .smt-dot.active { border-color: #58a6ff; color: #58a6ff; box-shadow: 0 0 10px rgba(88,166,255,0.3); }
  .smt-dot.done { border-color: #3fb950; color: #3fb950; }
  .smt-slide { display: none; animation: smtFade 0.4s ease; }
  .smt-slide.active { display: block; }
  @keyframes smtFade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
  .smt-slide h3 { font-size: 18px; margin: 0 0 12px 0; color: #58a6ff; font-family: 'Inter', sans-serif; }
  .smt-slide p, .smt-slide li { font-size: 14px; line-height: 1.7; color: #e6edf3; margin-bottom: 8px; }
  .smt-slide ul { padding-left: 20px; margin-bottom: 12px; }
  .smt-code { background: #0d1117; border: 1px solid #21262d; border-radius: 8px; padding: 16px; font-family: 'SF Mono','Fira Code',monospace; font-size: 12px; line-height: 1.7; color: #e6edf3; overflow-x: auto; margin: 12px 0; position: relative; }
  .smt-code .lbl { position: absolute; top: 6px; right: 10px; font-size: 10px; color: #484f58; text-transform: uppercase; letter-spacing: 0.05em; }
  .smt-split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 12px 0; }
  @media (max-width: 640px) { .smt-split { grid-template-columns: 1fr; } }
  .smt .kw { color: #d2a8ff; } .smt .fn { color: #79c0ff; } .smt .str { color: #a5d6ff; } .smt .cm { color: #484f58; } .smt .num { color: #ffa657; } .smt .minified { color: #f85149; word-break: break-all; } .smt .hl { background: rgba(88,166,255,0.15); padding: 0 3px; border-radius: 3px; }
  .smt-btn { display: inline-block; padding: 8px 18px; background: #238636; color: #fff; border: none; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; margin: 6px 6px 6px 0; font-family: 'Inter',sans-serif; }
  .smt-btn:hover { background: #2ea043; }
  .smt-btn.nav { background: #21262d; border: 1px solid #30363d; }
  .smt-btn.nav:hover { border-color: #58a6ff; }
  .smt-warn { background: rgba(248,81,73,0.1); border: 1px solid rgba(248,81,73,0.3); border-radius: 8px; padding: 16px; margin: 12px 0; }
  .smt-warn h4 { color: #f85149; margin: 0 0 8px 0; font-size: 14px; }
  .smt-safe { background: rgba(63,185,80,0.08); border: 1px solid rgba(63,185,80,0.25); border-radius: 8px; padding: 16px; margin: 12px 0; }
  .smt-safe h4 { color: #3fb950; margin: 0 0 8px 0; font-size: 14px; }
  .smt-card { background: #0d1117; border: 1px solid #21262d; border-radius: 8px; padding: 16px; margin: 10px 0; }
  .smt-card h4 { color: #ffa657; margin: 0 0 6px 0; font-size: 14px; }
  .smt-map-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: 6px; cursor: pointer; transition: background 0.2s; margin: 4px 0; }
  .smt-map-row:hover, .smt-map-row.lit { background: rgba(88,166,255,0.08); }
  .smt-map-from { flex: 1; font-family: monospace; font-size: 12px; color: #f85149; }
  .smt-map-arrow { color: #58a6ff; font-size: 16px; }
  .smt-map-to { flex: 1; font-family: monospace; font-size: 12px; color: #3fb950; }
  .smt-file-tree { font-family: monospace; font-size: 13px; padding: 14px; background: #0d1117; border-radius: 8px; border: 1px solid #21262d; margin: 12px 0; line-height: 1.8; }
  .smt-file-tree .folder { color: #58a6ff; } .smt-file-tree .danger { color: #f85149; } .smt-file-tree .file { color: #e6edf3; }
  .smt-arrow-bounce { display: flex; justify-content: center; margin: 10px 0; font-size: 20px; color: #58a6ff; animation: smtBounce 1.5s infinite; }
  @keyframes smtBounce { 0%,100% { transform: translateY(0); } 50% { transform: translateY(5px); } }
  .smt-foot { display: flex; justify-content: space-between; margin-top: 20px; padding-top: 16px; border-top: 1px solid #21262d; }
</style>

<script>
(function() {
  var el = document.getElementById('sourcemap-tutorial');
  if (!el) return;
  var cur = 0, total = 6;

  function go(n) {
    if (n < 0 || n >= total) return;
    cur = n;
    el.querySelectorAll('.smt-slide').forEach(function(s){ s.classList.remove('active'); });
    el.querySelectorAll('.smt-dot').forEach(function(d, i){ d.classList.remove('active'); d.classList.toggle('done', i < n); });
    el.querySelector('[data-s="'+n+'"]').classList.add('active');
    el.querySelectorAll('.smt-dot')[n].classList.add('active');
    var pb = el.querySelector('#smt-prev');
    var nb = el.querySelector('#smt-next');
    pb.style.visibility = n === 0 ? 'hidden' : 'visible';
    nb.style.visibility = n === total-1 ? 'hidden' : 'visible';
  }

  window._smtGo = go;
  window._smtNext = function(){ go(cur+1); };
  window._smtPrev = function(){ go(cur-1); };

  window._smtMinify = function() {
    var o = document.getElementById('smt-orig');
    var m = document.getElementById('smt-mini');
    var b = document.getElementById('smt-minbtn');
    var x = document.getElementById('smt-minexp');
    o.style.opacity = '0.3'; o.style.transform = 'scaleY(0.6)'; b.style.display = 'none';
    setTimeout(function(){ o.style.display='none'; m.style.display='block'; x.style.display='block'; m.style.animation='smtFade 0.4s ease'; x.style.animation='smtFade 0.4s ease'; }, 350);
  };

  window._smtMapClick = function(row) {
    el.querySelectorAll('.smt-map-row').forEach(function(r){ r.classList.remove('lit'); });
    row.classList.add('lit');
    var d = document.getElementById('smt-mapdetail');
    d.style.display = 'block'; d.style.animation = 'smtFade 0.3s ease';
    d.querySelector('h4').textContent = row.getAttribute('data-tip');
  };

  window._smtReveal = function() {
    document.getElementById('smt-revealbtn').style.display = 'none';
    var s = document.getElementById('smt-secrets');
    s.style.display = 'block'; s.style.animation = 'smtFade 0.4s ease';
  };

  el.innerHTML = '<div class="smt">'
    // Nav dots
    + '<div class="smt-nav">'
    + '<div class="smt-dot active" onclick="_smtGo(0)">1</div>'
    + '<div class="smt-dot" onclick="_smtGo(1)">2</div>'
    + '<div class="smt-dot" onclick="_smtGo(2)">3</div>'
    + '<div class="smt-dot" onclick="_smtGo(3)">4</div>'
    + '<div class="smt-dot" onclick="_smtGo(4)">5</div>'
    + '<div class="smt-dot" onclick="_smtGo(5)">6</div>'
    + '</div>'

    // Step 1: Readable code
    + '<div class="smt-slide active" data-s="0">'
    + '<h3>Step 1: Developers Write Readable Code</h3>'
    + '<p>When developers build a website or app, they write clean, organized code with meaningful names and comments.</p>'
    + '<div class="smt-code"><span class="lbl">Original Source</span>'
    + '<span class="cm">// Calculate the user\'s subscription price</span><br>'
    + '<span class="kw">function</span> <span class="fn">calculatePrice</span>(user) {<br>'
    + '&nbsp;&nbsp;<span class="kw">const</span> basePrice = <span class="num">9.99</span>;<br>'
    + '&nbsp;&nbsp;<span class="kw">const</span> discount = user.<span class="fn">isPremium</span> ? <span class="num">0.20</span> : <span class="num">0</span>;<br>'
    + '&nbsp;&nbsp;<span class="kw">const</span> apiKey = <span class="str">"sk-secret-api-key-12345"</span>;<br><br>'
    + '&nbsp;&nbsp;<span class="kw">if</span> (user.region === <span class="str">"EU"</span>) {<br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="cm">// GDPR compliance - EU gets different pricing</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="kw">return</span> basePrice * (<span class="num">1</span> - discount) * <span class="num">1.20</span>; <span class="cm">// +20% VAT</span><br>'
    + '&nbsp;&nbsp;}<br><br>'
    + '&nbsp;&nbsp;<span class="kw">return</span> basePrice * (<span class="num">1</span> - discount);<br>'
    + '}</div>'
    + '<p>You can read this and understand exactly what it does — the pricing logic, the API key, the business rules. <strong>But this code can\'t go directly to your browser...</strong></p>'
    + '</div>'

    // Step 2: Minification
    + '<div class="smt-slide" data-s="1">'
    + '<h3>Step 2: Code Gets Crushed Before Shipping</h3>'
    + '<p>Before code reaches your browser, it gets <strong>minified</strong> — shrunk to make websites load faster.</p>'
    + '<div id="smt-orig" class="smt-code" style="transition: all 0.4s;"><span class="lbl">Before</span>'
    + '<span class="cm">// Calculate the user\'s subscription price</span><br>'
    + '<span class="kw">function</span> <span class="fn">calculatePrice</span>(user) {<br>'
    + '&nbsp;&nbsp;<span class="kw">const</span> basePrice = <span class="num">9.99</span>;<br>'
    + '&nbsp;&nbsp;<span class="kw">const</span> discount = user.<span class="fn">isPremium</span> ? <span class="num">0.20</span> : <span class="num">0</span>;<br>'
    + '&nbsp;&nbsp;<span class="kw">const</span> apiKey = <span class="str">"sk-secret-api-key-12345"</span>;<br>'
    + '&nbsp;&nbsp;<span class="kw">if</span> (user.region === <span class="str">"EU"</span>) { <span class="kw">return</span> basePrice * (<span class="num">1</span> - discount) * <span class="num">1.20</span>; }<br>'
    + '&nbsp;&nbsp;<span class="kw">return</span> basePrice * (<span class="num">1</span> - discount);<br>'
    + '}</div>'
    + '<button class="smt-btn" id="smt-minbtn" onclick="_smtMinify()">Minify It</button>'
    + '<div id="smt-mini" class="smt-code" style="display:none;"><span class="lbl">After</span><span class="minified">function c(u){const b=9.99,d=u.isPremium?.2:0,a="sk-secret-api-key-12345";if(u.region==="EU")return b*(1-d)*1.2;return b*(1-d)}</span></div>'
    + '<div id="smt-minexp" style="display:none;">'
    + '<p>The minifier removed comments, renamed <code>calculatePrice</code> to <code>c</code>, <code>user</code> to <code>u</code>, and crushed everything into one line.</p>'
    + '<p>It still works the same — but now it\'s <strong>unreadable</strong>. That\'s actually good for security. <strong>But there\'s a catch...</strong></p>'
    + '</div>'
    + '</div>'

    // Step 3: The decoder ring
    + '<div class="smt-slide" data-s="2">'
    + '<h3>Step 3: Source Maps — The Decoder Ring</h3>'
    + '<p>Developers need to debug minified code. If an error happens at character 47 of the crushed line, how do you find the problem?</p>'
    + '<p>Enter <strong>source maps</strong> — a file that maps crushed code back to the original. <strong>Click any row</strong> to see how:</p>'
    + '<div class="smt-map-row" onclick="_smtMapClick(this)" data-tip="\'calculatePrice\' was shortened to \'c\' to save bytes. The source map remembers the original name.">'
    + '<span class="smt-map-from">c(u)</span><span class="smt-map-arrow">&harr;</span><span class="smt-map-to">calculatePrice(user)</span></div>'
    + '<div class="smt-map-row" onclick="_smtMapClick(this)" data-tip="\'basePrice\' became \'b\'. The source map knows it was on line 3 of \'pricing.js\'.">'
    + '<span class="smt-map-from">b=9.99</span><span class="smt-map-arrow">&harr;</span><span class="smt-map-to">const basePrice = 9.99</span></div>'
    + '<div class="smt-map-row" onclick="_smtMapClick(this)" data-tip="\'discount\' became \'d\'. Source maps store the original variable name and its exact position.">'
    + '<span class="smt-map-from">d=u.isPremium?.2:0</span><span class="smt-map-arrow">&harr;</span><span class="smt-map-to">const discount = user.isPremium ? 0.20 : 0</span></div>'
    + '<div class="smt-map-row" onclick="_smtMapClick(this)" data-tip="Even hardcoded API keys get mapped. If \'sourcesContent\' is included, the ENTIRE original file is embedded in the map.">'
    + '<span class="smt-map-from">a="sk-secret-..."</span><span class="smt-map-arrow">&harr;</span><span class="smt-map-to">const apiKey = "sk-secret-api-key-12345"</span></div>'
    + '<div id="smt-mapdetail" class="smt-safe" style="display:none;"><h4></h4></div>'
    + '<p>A comment at the end of the minified file points browsers to the map:</p>'
    + '<div class="smt-code"><span class="minified">function c(u){const b=9.99...}</span><br><span class="cm">//# sourceMappingURL=app.js.map</span></div>'
    + '</div>'

    // Step 4: The risk
    + '<div class="smt-slide" data-s="3">'
    + '<h3>Step 4: The Security Risk</h3>'
    + '<p>Source maps are for developers only. They should never be accessible on a live website. But sometimes they are.</p>'
    + '<div class="smt-warn"><h4>What Attackers Get From Exposed Source Maps</h4><ul>'
    + '<li><strong>Complete source code</strong> — every file, every function, every comment</li>'
    + '<li><strong>API keys and secrets</strong> accidentally left in code</li>'
    + '<li><strong>Business logic</strong> — how pricing, auth, and permissions actually work</li>'
    + '<li><strong>Internal file paths</strong> — revealing server structure</li>'
    + '<li><strong>Comments</strong> — "TODO: fix this security hole"</li>'
    + '</ul></div>'
    + '<button class="smt-btn" id="smt-revealbtn" onclick="_smtReveal()">Open the Source Map</button>'
    + '<div id="smt-secrets" style="display:none;">'
    + '<div class="smt-file-tree">'
    + '<span class="folder">src/</span><br>'
    + '&nbsp;&nbsp;<span class="folder">api/</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="danger">auth.js — login logic, JWT secret, admin bypass</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="danger">payments.js — Stripe keys, pricing overrides</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="file">users.js — user roles, permissions</span><br>'
    + '&nbsp;&nbsp;<span class="folder">config/</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="danger">database.js — DB connection strings</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="danger">secrets.js — API keys for 3rd-party services</span><br>'
    + '&nbsp;&nbsp;<span class="folder">utils/</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="file">validation.js — input validation rules</span><br>'
    + '&nbsp;&nbsp;&nbsp;&nbsp;<span class="file">crypto.js — encryption implementation</span><br><br>'
    + '<span style="color:#f85149;">512,000+ lines of original source code exposed</span>'
    + '</div>'
    + '<p style="color:#8b949e;font-size:13px;">(Based on a real incident where an AI company accidentally shipped their entire codebase in a source map.)</p>'
    + '</div>'
    + '</div>'

    // Step 5: How it happens
    + '<div class="smt-slide" data-s="4">'
    + '<h3>Step 5: How Does This Happen?</h3>'
    + '<div class="smt-card"><h4>Default Build Settings</h4><p>Many build tools (Webpack, Vite, etc.) generate source maps <strong>by default</strong>. If you don\'t explicitly turn them off for production, they ship automatically.</p></div>'
    + '<div class="smt-card"><h4>npm Packages</h4><p>Libraries published to npm often include <code>.map</code> files. These get bundled into apps and deployed, leaking the library\'s (or your app\'s) internals.</p></div>'
    + '<div class="smt-card"><h4>CDN &amp; Hosting Configs</h4><p>Even if source maps are "hidden," browsers know to look for them. If the file exists on the server, anyone can download it.</p></div>'
    + '<div class="smt-code"><span class="lbl">How Easy Is It?</span>'
    + '<span class="cm"># Just add .map to any JavaScript URL:</span><br><br>'
    + '<span class="str">https://example.com/static/app.js</span> <span class="cm">&larr; minified</span><br>'
    + '<span class="str">https://example.com/static/app.js<span class="hl">.map</span></span> <span class="cm">&larr; full source!</span>'
    + '</div>'
    + '</div>'

    // Step 6: Prevention
    + '<div class="smt-slide" data-s="5">'
    + '<h3>Step 6: How to Stay Safe</h3>'
    + '<div class="smt-safe"><h4>Prevention Checklist</h4><ul>'
    + '<li><strong>Disable source maps in production builds</strong></li>'
    + '<li><strong>Block <code>.map</code> files on your server</strong> — configure nginx/Apache to return 404</li>'
    + '<li><strong>Scan your deployments</strong> — use <a href="https://github.com/sho-luv/srcleaks" style="color:#58a6ff;">srcleaks</a> to check for exposed maps</li>'
    + '<li><strong>Audit npm packages</strong> before publishing</li>'
    + '<li><strong>Remove the <code>sourceMappingURL</code> comment</strong> from production builds</li>'
    + '</ul></div>'
    + '<div class="smt-code"><span class="lbl">Scan with srcleaks</span>'
    + '<span class="cm"># Scan a website</span><br>'
    + '<span class="fn">srcleaks</span> <span class="str">https://example.com</span><br><br>'
    + '<span class="cm"># Scan an npm package</span><br>'
    + '<span class="fn">srcleaks</span> <span class="str">some-npm-package</span><br><br>'
    + '<span class="cm"># Scan all packages in an npm org</span><br>'
    + '<span class="fn">srcleaks</span> <span class="str">--org company-name</span>'
    + '</div>'
    + '<p><strong>Bottom line:</strong> minification is obscurity, not security — but there\'s no reason to hand attackers the cheat sheet.</p>'
    + '<div style="text-align:center;margin-top:16px;"><button class="smt-btn" onclick="_smtGo(0)">Start Over</button></div>'
    + '</div>'

    // Nav buttons
    + '<div class="smt-foot">'
    + '<button class="smt-btn nav" id="smt-prev" onclick="_smtPrev()" style="visibility:hidden;">&larr; Previous</button>'
    + '<button class="smt-btn nav" id="smt-next" onclick="_smtNext()">Next &rarr;</button>'
    + '</div>'
    + '</div>';
})();
</script>

## The Internet Had a Field Day

The memes were immediate.

<div style="text-align: center;">
<a href="https://x.com/thekitze/status/2038936101008584784" target="_blank">
<img src="meme-kitze.png" alt="@thekitze tweet — POV: opencode team today, Mr. Bean exam cheating meme" style="max-width: 480px; border-radius: 8px; border: 1px solid #333;">
</a>
</div>

<div style="text-align: center;">
<a href="https://x.com/airesearch12/status/2038922706691703207" target="_blank">
<img src="meme-airesearch12.png" alt="@airesearch12 tweet — Vibe Coding vs Vulnerability As A Service meme" style="max-width: 480px; border-radius: 8px; border: 1px solid #333;">
</a>
</div>

But the most interesting fallout wasn't memes — it was what people *built*.

## The Code Goes Viral

### claw-code: The Clean-Room Rewrite

[claw-code](https://github.com/ultraworkers/claw-code) — a clean-room **Rust** rewrite inspired by the leaked architecture. It wasn't a copy of the TypeScript source; it was a from-scratch implementation based on understanding how Claude Code works under the hood.

It became one of the fastest-growing repos in GitHub history:

<img src="claw-code-stars.png" alt="Star history chart showing claw-code reaching 50K stars in about 2 hours" style="max-width: 100%; border-radius: 8px; border: 1px solid #333;">

**50,000 stars in about 2 hours.** It hit 100K within a day.

### ccunpacked.dev: The Architecture Explorer

[Zakaria O.I.A. (@zackautocracy)](https://github.com/zackautocracy) built [ccunpacked.dev](https://ccunpacked.dev/) — an interactive visual guide that maps Claude Code's internals: the agent loop, 50+ tools, multi-agent orchestration, and some unreleased features like an autonomous agent mode called "KAIROS."

<a href="https://sholuv.net/gitwho/?u=zackautocracy" target="_blank">
<img src="gitwho-zackautocracy.png" alt="gitwho profile of zackautocracy showing GitHub activity and contribution stats" style="max-width: 100%; border-radius: 8px; border: 1px solid #333;">
</a>

<small><em>gitwho profile of @zackautocracy — <a href="https://sholuv.net/gitwho/?u=zackautocracy">sholuv.net/gitwho</a></em></small>

If you're building with Claude Code and want to understand what's happening behind the scenes, it's worth a look.

## The DMCA Fiasco

Anthropic's next move made things worse. They filed a [DMCA takedown notice](https://github.com/github/dmca/blob/master/2026/03/2026-03-31-anthropic.md) on GitHub — but due to how GitHub's fork network works, a single notice accidentally **disabled 8,100+ repositories**, including legitimate forks of Anthropic's own public Claude Code repo.

Boris Cherny, Anthropic's head of Claude Code, called the mass takedown accidental. Anthropic retracted most of the notices, limiting takedowns to one repository and 96 forks that actually contained the leaked source. GitHub restored access to the rest.

The Streisand Effect was in full force — the DMCA attempt drew even more attention to the leak than the leak itself. [This video](https://www.youtube.com/watch?v=icu0GZcSb58) covers the DMCA fallout well.

## Then Came the Malware

Within 24 hours of the leak making headlines, a different kind of repo started appearing — and these weren't built by curious developers.

Threat actors spun up fake GitHub repositories claiming to host the leaked Claude Code source, optimized them for search engines, and watched them climb to the top of Google results for queries like "leaked Claude Code" and "Claude Code download." [Zscaler's ThreatLabz](https://www.zscaler.com/blogs/security-research/anthropic-claude-code-leak) caught it first.

**How the trap worked:**

The repos looked convincing — clean READMEs promising "unlocked enterprise features" and "no message limits," with a `.7z` archive in the GitHub Releases section labeled "Claude Code - Leaked Source Code." Inside was a file called `ClaudeCode_x64.exe`.

**How we know it was malware:**

The `.exe` was a Rust-compiled dropper. On execution it deployed two payloads:

- **[Vidar](https://malpedia.caad.fkie.fraunhofer.de/details/win.vidar) v18.7** — an infostealer that siphons browser credentials, saved passwords, credit card data, crypto wallets, and session cookies, then exfiltrates everything to a C2 server
- **[GhostSocks](https://malpedia.caad.fkie.fraunhofer.de/details/win.ghostsocks)** — a SOCKS5 proxy backdoor that turns infected machines into relay nodes, letting attackers route their traffic through your computer

**The red flags researchers spotted:**

- The repo was run by account **"idbzoomh"** — a throwaway account with no history
- A second identical repo appeared under account **"my3jie"** — same code, same payload, same actor
- The archive was updated in short intervals, swapping in fresh payloads to evade signature detection
- One version included a fake "Download ZIP" button embedded as an image — not a real GitHub button, just a clickable image linking to the malicious archive
- No actual TypeScript source code in the repo, just the dropper

According to [Trend Micro's analysis](https://www.trendmicro.com/en_us/research/26/d/weaponizing-trust-claude-code-lures-and-github-release-payloads.html), this wasn't even a one-off. The Claude Code bait was part of a **rotating lure operation active since February 2026**, impersonating 25+ software brands while delivering the same Rust-compiled infostealer payload. Claude Code was just the flavor of the week.

The lesson: when something big leaks, the most dangerous repos aren't the ones with the leaked code — they're the ones *pretending* to have it.

## Are You Shipping Source Maps?

This is not an Anthropic-only problem. Any project that compiles TypeScript, uses Webpack, Vite, or any bundler can make this exact mistake. One missed `.npmignore` rule, one wrong `tsconfig.json` setting, and your private source is public.

So I built [**srcleaks**](https://github.com/sho-luv/srcleaks) — a CLI tool that scans npm packages and live websites for accidentally shipped source maps.

```bash
# Install
go install github.com/sho-luv/srcleaks@latest

# Scan npm packages
srcleaks @anthropic-ai/claude-code react express

# Scan a live website
srcleaks https://example.com

# Scan all packages from an npm org
srcleaks --org anthropic-ai
```

It auto-detects whether you give it a package name or URL, checks for `.map` files, and reports one of three statuses: **EXPOSED** (source map found on a proprietary package), **LEAK** (map contains recoverable source code), or **CLEAN**. Open source packages are automatically marked clean — shipping source maps isn't a leak if your code is already public.

Use `--proof` to see the actual recovered source code as evidence.

![srcleaks demo](https://raw.githubusercontent.com/sho-luv/srcleaks/main/demo/srcleaks-demo.gif)

## The Takeaway

- **What happened:** Anthropic shipped a 59.8 MB TypeScript source map in their npm package by accident — reportedly for the second time
- **Who found it:** [@Fried_rice](https://x.com/Fried_rice/status/2038894956459290963) (Chaofan Shou, Solayer Labs intern)
- **What it exposed:** ~512,000 lines of Claude Code's internal source (not customer data, not model weights)
- **What the community built:** [claw-code](https://github.com/ultraworkers/claw-code) (100K+ stars in a day), [ccunpacked.dev](https://ccunpacked.dev/) (architecture explorer)
- **The DMCA backfire:** Anthropic accidentally took down [8,100+ repos](https://github.com/github/dmca/blob/master/2026/03/2026-03-31-anthropic.md), then retracted
- **Why it matters:** This is a packaging mistake anyone can make — and most teams don't check for it
- **What to do:** Audit your npm packages with [srcleaks](https://github.com/sho-luv/srcleaks), or manually run `npm pack --dry-run` and look for `.map` files. Check your `.npmignore` and `tsconfig.json`. Don't ship your decoder ring.
