El "Don't Ask Again" de Claude Code está filtrando API keys en GitHub: cómo comprobarlo y solucionarlo

Aprobar un comando con "No volver a preguntar" lo guarda tal cual, contraseñas incluidas. Aquí tienes la verificación de un solo comando para tus repositorios, el orden de limpieza y cómo evitar que tus claves queden expuestas en las aprobaciones del agente.

Ahmed Waheed

Ahmed Waheed

12 November 2026

El "Don't Ask Again" de Claude Code está filtrando API keys en GitHub: cómo comprobarlo y solucionarlo

Apidog para empresas

Despliegue local

SSO & RBAC

Conforme con SOC 2

Explorar Apidog Enterprise

El botón “Don’t Ask Again” (No volver a preguntar) de Claude Code guarda el comando exacto que aprobaste como una regla de tipo allow en .claude/settings.local.json. Si ese comando contenía una contraseña o una clave de API, el secreto queda ahora en texto plano en la carpeta de tu proyecto y, si el archivo se sube al repositorio, se hace público. La firma de seguridad Apiiro encontró 1.122 de estos archivos en GitHub público que contenían 1.379 secretos reales. Aquí te explicamos cómo revisar tus propios repositorios con un solo comando, limpiar el problema y evitar que vuelva a ocurrir.

Qué ocurrió: los hallazgos de Apiiro en números

Apiiro buscó en GitHub público copias de .claude/settings.local.json, el archivo por proyecto donde Claude Code guarda tus aprobaciones de permisos personales. Encontró 104.185 copias en 103.617 repositorios. En 1.122 de ellas había al menos un secreto real, 1.379 secretos distintos en total, según el artículo de iTnews sobre la investigación.

Tipo de secreto Cantidad
Contraseñas de bases de datos 379
Claves service-role de Supabase 122
Inicios de sesión SSH para servidores públicos 43
Tokens de GitHub, GitLab y npm 42
Claves de AWS 23

Cuando Apiiro amplió la búsqueda a versiones anteriores del archivo en git, al archivo compartido .claude/settings.json y a las transcripciones guardadas de agentes, el número subió a 2.539 secretos repartidos en 1.994 repositorios. Apiiro no comprobó si las credenciales seguían funcionando, pero una de las organizaciones a las que avisó confirmó que una clave seguía activa.

Las cifras de los escáneres son lo que más debería preocuparte. De los 1.122 archivos con un secreto real, TruffleHog detectó 643 y gitleaks detectó 564. 215 archivos pasaron desapercibidos para ambos. Así que “usamos un escáner de secretos” no es prueba de que estés limpio.

La historia llegó a lo más alto de Hacker News con 582 puntos. Boris Cherny, responsable de Claude Code en Anthropic, respondió en el hilo que Claude Code históricamente no ha eliminado secretos y que el equipo está estudiando el problema. Lee la investigación original de Apiiro.

Cómo “Don’t Ask Again” guarda secretos en un archivo

Cuando Claude Code quiere ejecutar un comando Bash, pregunta primero. Si eliges “Yes, and don’t ask again”, Claude Code escribe una aprobación permanente en .claude/settings.local.json como una regla allow, tal como describe la documentación de configuración de Anthropic. La regla registra el texto del comando. Si el comando era este:

psql "postgresql://admin:Sup3rS3cret@db.example.com:5432/prod" -c "select count(*) from users"

entonces tu archivo de configuración contiene ahora algo como esto:

{
  "permissions": {
    "allow": [
      "Bash(psql \"postgresql://admin:Sup3rS3cret@db.example.com:5432/prod\" -c \"select count(*) from users\")"
    ]
  }
}

La contraseña está en la regla porque estaba en el comando. Claude Code guarda lo que aprobaste, palabra por palabra.

Diagrama de cómo una aprobación de Claude Code con una contraseña en línea llega a settings.local.json y luego a un repositorio público de GitHub

Por qué acaba en GitHub

Claude Code intenta mantener este archivo fuera de git. La primera vez que escribe .claude/settings.local.json en un repositorio que aún no lo ignora, añade **/.claude/settings.local.json a tu archivo global de exclusiones de git. Esa protección tiene lagunas:

Revisa tus repositorios con un solo comando

Empieza por el repositorio en el que estás. Esto te indica si el archivo está rastreado ahora y si alguna vez se subió:

git ls-files | grep -E '(^|/)\.claude/settings(\.local)?\.json$'
git log --all --name-only --format= | grep -E '\.claude/settings(\.local)?\.json$' | sort -u

Si la primera línea imprime algo, el archivo está en tu árbol actual. Si la segunda imprime algo, está en tu historial, incluso si lo borraste después.

Para revisar de una vez todos los repositorios bajo una carpeta:

find ~/code -path '*/.claude/settings.local.json' -exec sh -c \
  'cd "$(dirname "$1")/.." && git ls-files --error-unmatch .claude/settings.local.json >/dev/null 2>&1 && echo "TRACKED: $PWD"' _ {} \;

Para una organización de GitHub, la CLI de GitHub puede buscar código por nombre de archivo:

gh search code permissions --owner your-org --filename settings.local.json

Luego mira dentro de cualquier archivo que encuentres. Como los escáneres no detectaron el 19 % de los archivos filtrados, lee las reglas tú mismo en lugar de fiarte de un escaneo en verde:

grep -nE 'Bash\(' .claude/settings.local.json | grep -iE 'pass|token|key|secret|://[^/]*:[^@]*@'

Una cadena de conexión con user:password@, una cabecera Authorization: Bearer, un -p seguido de una contraseña literal, o cualquier cosa que empiece por sk-, ghp_, AKIA o eyJ es una credencial. Ejecuta gitleaks sobre el repositorio como segunda pasada, no como la única.

Cómo corregir un archivo de configuración filtrado

El orden importa. La rotación va primero, porque borrar el archivo no hace nada con la copia que ya está en forks, clones y cachés.

  1. Rota cada credencial que el archivo haya contenido alguna vez. Revisa el historial completo del archivo con git log -p --all -- .claude/settings.local.json, lista cada secreto que contenga y revoca cada uno en su proveedor. Las recomendaciones de Apiiro ponen este paso en primer lugar.
  2. Deja de rastrear el archivo e ignóralo en el propio repositorio:
git rm --cached .claude/settings.local.json
echo '.claude/settings.local.json' >> .gitignore
git add .gitignore && git commit -m "Stop tracking Claude Code local settings"
git check-ignore -v .claude/settings.local.json

El último comando debería imprimir la línea correspondiente del .gitignore. Una regla dentro del repositorio protege cada clon.

  1. Limpia el historial si el repositorio es público o está muy compartido. git-filter-repo elimina el archivo de cada commit:
git filter-repo --invert-paths --path .claude/settings.local.json
git push --force --all

Esto reescribe el historial para todo el mundo, así que avisa antes a tu equipo. Tampoco llega a los forks, así que es una limpieza, no un sustituto del paso 1.

  1. Limpia también el archivo local. Abre .claude/settings.local.json, elimina cualquier regla que contenga un secreto literal y sustitúyela por un patrón (sección siguiente).
  2. Activa la protección de push. La protección de push del escaneo de secretos de GitHub bloquea los push que contienen formatos de secretos reconocidos. No detectará todo, como demuestran los 215 archivos que se escaparon, pero frena los formatos de token más comunes.

Evita que vuelva a ocurrir

La solución es fácil de decir: nunca pongas un secreto literal en un comando que tu agente ejecute, y aprueba patrones en lugar de comandos exactos.

Comparación lado a lado de una regla allow riesgosa de Claude Code que contiene una contraseña literal y una regla segura que hace referencia a una variable de entorno

Haz referencia a los secretos, no los escribas. El shell expande una variable en tiempo de ejecución, pero la regla allow guarda el texto tal como se escribió. Así, psql "$DATABASE_URL" guarda $DATABASE_URL en la regla, no la contraseña:

{
  "permissions": {
    "allow": ["Bash(psql \"$DATABASE_URL\" *)", "Bash(npm run test *)"],
    "deny": ["Read(./.env)", "Read(./.env.*)"]
  }
}

Las reglas deny están tomadas directamente de la documentación de configuración de Anthropic; impiden que Claude Code lea tus archivos .env, así que una clave tampoco puede pegarse desde ahí en un comando.

Mantén las reglas estrechas. Comentaristas de HN que citaban la investigación decían que el 5 % de los archivos permitía cualquier comando de shell y que el 43 % tenía reglas de intérpretes como Bash(python:*). Una regla así permite al agente ejecutar cualquier cosa a través de Python, así que es una regla de permitir-todo con pasos extra.

Considera el modo automático y un sandbox. Cherny recomendó el modo automático en lugar de reglas de permisos escritas a mano y dijo que Anthropic construyó un sandbox de código abierto. Varios comentaristas señalaron que el modo automático es un clasificador, no un límite de seguridad, así que combínalo con los hábitos anteriores.

Mantén a los agentes lejos de los secretos de producción. La clave más segura es la que el agente nunca ve. Cubrimos los patrones para lograrlo en cómo permitir que los agentes de IA usen claves de API sin verlas, y lo que hacen los atacantes con claves de IA filtradas en claves de API de IA robadas y LLMjacking.

Permite que tu agente de programación llame a APIs sin teclear claves, usando Apidog CLI

Una forma habitual de que una clave acaba en un comando aprobado es una llamada rápida a una API: curl -H "Authorization: Bearer sk-..." para comprobar un endpoint. Puedes delegar esa tarea en Apidog CLI. La clave vive en una variable de entorno de Apidog, tu escenario de prueba la referencia como {{API_KEY}} y el comando que ejecuta tu agente contiene solo IDs de proyecto, escenario y entorno. No hay ningún secreto en él que guardar.

Configúralo una vez, en tu propio terminal en lugar de a través del agente, para que tu token de acceso nunca pase por una aprobación:

npm install -g apidog-cli@latest
apidog auth login
npx -y skills add https://apidog.com   # pick Claude Code when prompted

Luego dale a Claude Code un solo prompt:

List the environments in Apidog project 123456, then run test scenario 7890
against the staging environment and summarise any failed assertions.
The API key is stored in that environment as {{API_KEY}}. Never pass a key,
token or password on the command line, and never use --env-var for secrets.

El agente ejecuta comandos como estos, que son seguros para aprobar con “Don’t Ask Again”:

apidog environment list --project 123456
apidog run -t 7890 --project 123456 -e <envId> -r cli,json

Dos advertencias honestas. Primera, apidog run usa el valor compartido de la variable, que se sincroniza con Apidog y es visible para los miembros del proyecto, así que guarda ahí una clave de pruebas con permisos limitados, no una clave raíz de producción. Si prefieres mantener los valores en disco, apidog run también acepta --variables <path>; apúntalo a un archivo fuera del repositorio. Segunda, la opción --env-var "key=value" existe para pasar variables por la línea de comandos, y ese es exactamente el patrón que provoca filtraciones, así que dile a tu agente que no la use para secretos.

Los equipos con el plan Enterprise también pueden extraer secretos de HashiCorp Vault, Azure Key Vault o AWS Secrets Manager con Apidog Vault Secrets, y el Escáner de Secretos del SaaS Enterprise marca las claves guardadas en requests, scripts y variables de Apidog. No escanea repositorios de GitHub, así que mantén también las comprobaciones anteriores. Para ver la guía completa de configuración, consulta cómo usar Apidog CLI en Claude Code y cómo gestionar entornos y variables de secretos en Apidog. El plan gratuito de Apidog cubre hasta 4 usuarios.

button

Preguntas frecuentes

¿El settings.local.json de Claude Code está en .gitignore por defecto?

En parte. Cuando Claude Code crea el archivo en un repositorio git, añade **/.claude/settings.local.json a tus exclusiones globales de git. Eso solo protege tu máquina, y no se aplica si creaste el archivo a mano o lo añadiste a la fuerza. Añade la línea también al .gitignore del propio repositorio.

¿Borrar el archivo elimina la clave filtrada?

No. La clave permanece en el historial de git, en los forks y en cada clon hecho mientras era pública. Rota la clave primero, luego deja de rastrear el archivo y limpia el historial con git-filter-repo.

¿gitleaks o TruffleHog detectarán estas filtraciones?

No de forma fiable. En la muestra de Apiiro, 215 de los 1.122 archivos con un secreto real se les escaparon a ambas herramientas. Ejecuta un escáner y luego lee tú mismo las reglas Bash(...) del archivo.

¿Es un problema del modelo o de Claude Code?

Apiiro lo reprodujo con Opus 5.5 y Fable 5.1 y lo calificó como una característica de Claude Code, no del modelo. La aprobación guarda el texto del comando, sea cual sea el modelo que lo escribió.

¿Cómo apruebo comandos de forma segura en Claude Code?

Aprueba patrones como Bash(npm run test *) y referencia los secretos como variables de entorno, por ejemplo $DATABASE_URL. Añade reglas deny para Read(./.env) y Read(./.env.*).

Practica el diseño de API en Apidog

Descubre una forma más fácil de construir y usar APIs