De 104.185 copias públicas de .claude/settings.local.json en GitHub, 1.122 contienen al menos un secreto real: 1.379 credenciales distintas, sobre todo contraseñas de bases de datos. Las publicó Apiiro el 5 de noviembre de 2026, y el origen no es un fallo del modelo sino un botón muy cómodo: «Sí, y no volver a preguntar».
Cómo acaba una contraseña en un repo público
Cuando Claude Code pide permiso para ejecutar un comando Bash y eliges esa opción, guarda el comando aprobado como regla allow en .claude/settings.local.json, en la raíz del repositorio. Si el comando era PGPASSWORD=... psql -h db.example.com o sshpass -p ... ssh root@..., la contraseña queda escrita tal cual en la regla.
Según la documentación de Claude Code, ese fichero no debería llegar nunca a git: la primera vez que lo escribe, añade **/.claude/settings.local.json a tus excludes globales de git. Es una protección real, pero vive en tu máquina, no en el proyecto. Basta un compañero sin esa exclusión, un fichero creado a mano, un git add -f o copiar la carpeta a otro equipo para que el fichero entre en un commit.
Lo que encontró Apiiro
Los investigadores Shmulik Cohen y Matan Giladi recopilaron los ficheros del 1 al 3 de noviembre, filtraron falsos positivos y pasaron los candidatos, enmascarados, por un revisor con IA:
| Hallazgo | Cifra |
|---|---|
Copias públicas de settings.local.json revisadas | 104.185 |
| Copias con al menos un secreto | 1.122 |
| Secretos distintos en esas copias | 1.379 |
| Contraseñas de bases de datos | 379 |
| Claves service role de Supabase | 122 |
| Accesos SSH a servidores públicos | 43 |
| Tokens de GitHub, GitLab y npm | 42 |
| Claves de AWS | 23 |
Total contando historial, settings.json compartido y transcripciones | 2.539 secretos en 1.994 repos |
El historial agrava el problema: hay 734 secretos más en versiones antiguas del fichero, repartidos por 509 repositorios, y 211 forks de 74 repos arrastran los mismos valores. Borrar el fichero hoy no los quita de ahí.
En 539 de los 1.105 repositorios que pudieron trazar, el fichero lo cambió un commit con Claude como coautor. El propio informe advierte de que eso no prueba que Claude introdujera cada credencial, pero sí que el agente también hace commits de su propia configuración cuando nadie lo impide.
Apiiro no probó las credenciales, así que no sabe cuántas siguen activas. Identificó 63 organizaciones afectadas, las avisó el 2 y el 3 de noviembre, y al menos una confirmó que su clave estaba viva.
Por qué los escáneres no lo ven
Sobre los 1.122 ficheros, Gitleaks marcó 564 y TruffleHog 643 (ambos sin su paso de verificación). 215 se les escaparon a los dos. La mayoría eran contraseñas dentro de un comando, detrás de PGPASSWORD=, sshpass -p o mysql -p: no tienen el formato reconocible de una clave de AWS o un token de GitHub, y para un escáner son texto.
Ese es el dato que más me preocupa. Muchos equipos confían en que el secret scanning del repositorio cazará cualquier fuga, y aquí uno de cada cinco ficheros pasa limpio por las dos herramientas libres más usadas.
Claude Code frente a los demás agentes
Apiiro comparó 16 agentes de código y Claude Code es el único que confirmó que guarda por defecto el comando aprobado completo en un fichero dentro del proyecto. No es un problema nuevo: el issue #5544 lo describió en agosto de 2025, y un bot lo cerró como «not planned» el 3 de enero de 2026 tras 60 días sin actividad.
En el hilo de Hacker News, Boris Cherny, del equipo de Claude Code, reconoció que la herramienta no ha hecho limpieza de secretos y que están estudiando qué hacer. Recomienda el modo automático en lugar de acumular reglas de permiso y activar el escaneo de secretos en el servidor de git, sea cual sea el agente.
Qué haría yo esta semana
- Comprobar si el fichero está versionado en tus repos, también en el historial:
git ls-files '*.claude/settings.local.json'
git log --all --oneline -- '*.claude/settings.local.json'- Si aparece, rotar primero cualquier credencial que haya contenido. Borrarlo no la retira del historial, de los forks ni de los clones.
- Dejar de versionarlo y añadir la exclusión al
.gitignoredel proyecto, que sí viaja con el repo:
git rm --cached .claude/settings.local.json
echo '.claude/settings.local.json' >> .gitignore- Revisar a mano las reglas
allowde ese fichero y borrar las que lleven valores literales.
La cautela que importa: pasar la contraseña por una variable en línea no basta. PGPASSWORD=s3cr3t psql … sigue dejando el valor en el texto del comando que se guarda; lo que no se filtra es una referencia que se resuelve al ejecutar, como una variable ya exportada o un gestor de secretos.