Fuga del mapa de código fuente de Claude Code
Anthropic envió un mapa de código fuente con el código dentro. Los equipos deberían tratar eso como un fallo de lanzamiento.
Anthropic publicó @anthropic-ai/claude-code@2.1.88 en npm con cli.js.map en el tarball. Descargué el paquete del registro y lo comprobé yo mismo. El mapa de código fuente contenía 4.756 entradas de código fuente incrustadas. Eliminando node_modules, el paquete aún exponía 1.906 archivos propiedad de Anthropic y aproximadamente 515.000 líneas de código, casi todo TypeScript o TSX.
Si un mapa de código fuente se envía con sourcesContent, no se envió un archivo de depuración inofensivo. Se envió código fuente en JSON.
Para cuando lo comprobé, Anthropic ya había comenzado a limpiar. La página de la versión de npm estaba marcada como obsoleta con el mensaje Unpublished. El repositorio oficial de GitHub y los documentos de configuración ahora marcan la instalación de npm como obsoleta y dirigen a los usuarios hacia el instalador nativo. La respuesta fue rápida. El artefacto ya era público.
Las copias y el análisis ya estaban circulando para entonces. Así es como se comportan los artefactos públicos en internet. Una vez que algo útil llega a un registro, la limpieza es ir a la zaga.
Si le interesa más lo que dice el código expuesto sobre el diseño del agente que la fuga en sí, Shreyansh escribió un artículo aparte sobre lo que la arquitectura de Claude Code nos enseña sobre la construcción de agentes de IA de producción. Esta publicación trata sobre la parte más aburrida, la higiene de los lanzamientos, porque los fallos aburridos aún causan mucho daño.
Qué se envió
Esto es lo que pude verificar del paquete npm, los metadatos del registro y las páginas públicas de Anthropic:
@anthropic-ai/claude-code@2.1.88 se publicó en npm como un paquete público.
- El tarball incluía
cli.js.map junto con cli.js, README.md, LICENSE.md, package.json, bun.lock y sdk-tools.d.ts.
cli.js.map incluía un array sourcesContent poblado.
- El mapa contenía 4.756 archivos incrustados en total.
- Excluyendo
node_modules, el mapa aún cubría 1.906 archivos propiedad de Anthropic.
- 1.888 de esos archivos eran
.ts o .tsx.
- La parte del mapa propiedad de Anthropic contenía aproximadamente 515.000 líneas.
- La página de npm luego marcó la versión como obsoleta con el mensaje del autor
Unpublished.
- El repositorio y los documentos de configuración de Anthropic ahora marcan la instalación de npm como obsoleta.
Deliberadamente no estoy reproduciendo código del mapa ni enlazando a repositorios espejo. Ya hay suficiente ruido público al respecto. Este paquete expuso mucho más que un binario CLI.
Por qué esto importa
La gente todavía habla de los mapas de código fuente como si fueran restos inofensivos. A veces lo son. Los mapas de código fuente pueden tener un riesgo bajo cuando permanecen dentro del seguimiento de errores privado y omiten el código fuente original. Un mapa público con sourcesContent es un lanzamiento de código fuente.
Lo que más me molestó fue lo común que es este modo de fallo. El mapa no solo explicaba los rastros de pila. Expuso la estructura interna, el cableado de las indicaciones, la lógica de enrutamiento, las rutas de telemetría, la implementación de las banderas de características y las opciones de orquestación. Mantengo esa descripción a un alto nivel a propósito. No quiero convertir una publicación de seguridad en un recorrido guiado por el código de otra persona.
Para un producto de codificación de IA, este tipo de fuga reduce el costo de escrutinio, imitación y planificación de ataques. La ingeniería inversa de un CLI empaquetado lleva tiempo. Leer un mapa de código fuente con el código ya incrustado lleva minutos.
Misma familia, diferente causa
Escribí sobre el compromiso de Telnyx PyPI la semana pasada. Ese fue malicioso. Este parece un error de ingeniería de lanzamiento. Operacionalmente, riman.
Sigo volviendo a una frase: el artefacto es el perímetro.
Los equipos gastan la mayor parte de su energía en el código de la aplicación y los controles de tiempo de ejecución, luego tratan el paquete, la imagen del contenedor, GitHub Action, el gráfico de Helm y el paquete del navegador como detalles de empaquetado. Los atacantes no hacen esa distinción. Los accidentes tampoco.
Hemos visto el mismo patrón en la puerta trasera de xz, los compromisos de GitHub Action, los ladrones de credenciales de PyPI, las actualizaciones maliciosas de npm, las capas de contenedores expuestas y ahora un paquete público de npm que llevaba su propio árbol de código fuente como JSON. Los modos de fallo cambian. El límite de confianza no. Lo que sea que envíes se convierte en lo que la gente inspecciona, replica, compara, instala y ataca.
Cómo evitar que esto suceda
Evitar esto no es glamuroso. Es disciplina básica de lanzamiento, que es exactamente por lo que funciona.
1. No publiques mapas de código fuente con código fuente incrustado
Si envías un paquete npm de código cerrado, los mapas de código fuente públicos deben estar desactivados a menos que te sientas cómodo lanzando el código que contienen. La depuración privada está bien. sourcesContent público es un evento de lanzamiento.
Para compilaciones de frontend y CLI, eso generalmente significa:
- deshabilitar los mapas de código fuente públicos para el paquete
- generar mapas ocultos y subirlos a un seguimiento de errores privado
- usar mapas sin código fuente si necesitas símbolos sin enviar código
- revisar los valores predeterminados del empaquetador cada vez que cambies la cadena de herramientas de compilación
2. Haz de npm pack una puerta de lanzamiento estricta
No confíes en la carpeta dist/. Inspecciona el tarball que estás a punto de publicar.
Cada pipeline de lanzamiento debe construir el paquete, ejecutar npm pack, desempaquetar el tarball y fallar si el contenido se desvía de la política. Como mínimo, falla el lanzamiento cuando:
- un archivo
.map contiene sourcesContent
- un paquete incluye archivos fuera de una lista de permitidos
- el tarball contiene documentos internos, indicaciones, accesorios o configuración que no pertenecen al paquete
- el manifiesto cambia sin una revisión explícita
Una puerta simple es mejor que descubrir el problema en 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. Publica desde un directorio limpio y permitido
Muchas fugas ocurren porque los equipos publican desde la raíz de un monorepo o desde un directorio dist/ que creció por costumbre. Compila en un directorio de lanzamiento limpio. Copia solo lo que pertenece al paquete. Usa el campo files en package.json como una lista de permitidos. Trata .npmignore como una red de seguridad, no como el plan.
4. Trata los activos operativos de IA como sensibles a la publicación
Los equipos de IA suelen proteger los pesos, las claves API y los datos de los clientes. Se vuelven mucho más laxos con las indicaciones, la lógica de enrutamiento, el comportamiento de respaldo, los interruptores de telemetría, las banderas de características internas y los permisos de las herramientas. Eso es un error.
Incluso cuando esos activos no son secretos en el sentido legal, aún tienen valor de producto y valor de ataque. Si un artefacto de lanzamiento los incluye, asume que serán leídos.
5. Verifica lo que el registro sirve después de la publicación
Incluso los buenos pipelines de CI pasan cosas por alto. Extrae la versión exacta que acabas de publicar del registro o de un espejo, inspecciónala de nuevo y alerta sobre desviaciones. Si algo se escapa, los minutos importan.
Mantén los artefactos de depuración privados por defecto. Los mapas de código fuente pertenecen a los flujos de trabajo de observabilidad privada con mucha más frecuencia de lo que pertenecen a los registros de paquetes públicos.
Nuestro sesgo en Kolsetu
Construimos para industrias reguladas, por lo que no podemos pretender que la ingeniería de lanzamiento se encuentre fuera del modelo de seguridad. Un paquete, contenedor o paquete de navegador se puede descargar, replicar, indexar y buscar en cuestión de minutos. Esa suposición cambia la forma en que publicas software.
Nuestra postura predeterminada es simple:
- inspeccionar el artefacto final, no solo la carpeta que lo produjo
- mantener los activos de depuración privados
- enviar desde un directorio de lanzamiento permitido
- asumir que cualquier artefacto público se vuelve legible inmediatamente
Creo que muchos equipos de IA todavía subestiman la disciplina de lanzamiento porque los modelos son más divertidos de discutir, aunque el empaquetado es donde ocurre mucho daño evitable.
Reflexión final
Lo que sucedió aquí es vergonzoso para Anthropic. También es útil para el resto de la industria. Un paquete público de npm es un canal de divulgación, y un mapa de código fuente con sourcesContent es código fuente. Los equipos que construyen productos de IA dedican un tiempo interminable a modelos, indicaciones, evaluaciones y herramientas, luego apresuran la última milla del empaquetado. Esa última milla decide lo que el mundo realmente puede leer.
La próxima ronda de fallos de seguridad de la IA no provendrá solo del comportamiento del modelo. Algunos provendrán de CI, registros, contenedores, paquetes de navegador, GitHub Actions y salidas de compilación que nadie revisó lo suficientemente de cerca. El modelo acapara los titulares. El artefacto decide lo que el mundo puede ver.
Referencias