← Artículos

Cómo auditar los permisos de Claude Code en 2 minutos

· 9 min de lectura

Para auditar los permisos de Claude Code, corre un script de Python que lee la lista permissions.allow de settings.local.json (el del proyecto y el de tu usuario) y devuelve tres números: cuántas reglas Bash tienes, cuántas llevan comodín y en qué posiciones hay algo con forma de credencial. Nunca imprime el valor de la clave, solo su índice.

Claude Code guarda un archivo que casi nadie abre y que crece solo: el de permisos. Cada vez que apruebas un comando con la opción de no volver a preguntar, ese comando queda escrito ahí como regla permanente. Esta guía te da el comando que lo audita, cómo leer lo que devuelve y qué hacer con cada caso.

Tarda dos minutos y no necesitas instalar nada más que Python 3.

Dónde vive el archivo y por qué crece solo

En la carpeta .claude conviven dos archivos con nombre parecido y propósito distinto. El archivo settings.json es el compartido: lo que el equipo acuerda, y sí se sube al repositorio. El archivo settings.local.json es el tuyo: lo que tú aprobaste en tu máquina.

El segundo es el que se llena. La documentación oficial de Claude Code lo describe así: "When Claude asks permission to run a Bash command and you choose 'Yes, and don't ask again', Claude Code saves that approval as an allow rule in .claude/settings.local.json".

Dos detalles que explican el volumen:

Hay uno por proyecto, dentro de la carpeta del proyecto, y uno de usuario en tu carpeta personal. No es un archivo: son varios, y la auditoría hay que repetirla en cada proyecto.

El comando que cuenta tus reglas

Copia esta línea completa y pégala en la terminal, parado en la carpeta del proyecto que quieras revisar. Mira las dos rutas a la vez, la del proyecto y la de tu usuario, y si alguna no existe la salta sin quejarse.

python3 -c "import json,re,os;K=re.compile(r'AIza[\w-]{15,}|sk-[\w-]{20,}|\w*(KEY|TOKEN|SECRET|PASS)\w*=\S{8,}');B=[r for p in ('$HOME/.claude/settings.local.json','.claude/settings.local.json') if os.path.exists(p) for r in json.load(open(p)).get('permissions',{}).get('allow',[]) if r[:4]=='Bash'];print('BASH',len(B),'| COMODIN',sum('*' in r for r in B),'| CLAVE',[i for i,r in enumerate(B) if K.search(r)])"

Lo importante de este comando es lo que no hace: nunca imprime el valor de una credencial. Solo devuelve conteos y la posición dentro de la lista. Puedes correrlo con la pantalla compartida.

Cómo leer los tres números

La salida tiene esta forma:

BASH 284 | COMODIN 40 | CLAVE [29, 30, 37]

Si CLAVE viene como lista vacía, no tienes nada urgente que hacer. Si trae índices, ve a la sección de rotación antes que a la de borrado: el orden importa.

Para ver las reglas una por una con las credenciales tapadas, sin abrir el archivo en el editor:

python3 -c "import json,re,os;K=re.compile(r'AIza[\w-]{6,}|sk-[\w-]{6,}|(?<==)[\w./+-]{8,}');B=[r for p in ('$HOME/.claude/settings.local.json','.claude/settings.local.json') if os.path.exists(p) for r in json.load(open(p)).get('permissions',{}).get('allow',[]) if r[:4]=='Bash'];[print(str(i).rjust(3), K.sub('<OCULTO>',r)[:96]) for i,r in enumerate(B)]"

Cada línea sale como índice más regla, con lo que parezca credencial sustituido por un marcador y recortado a 96 caracteres. Los índices coinciden con los que te devolvió el comando corto.

Las tres clases de regla peligrosa

No todas las reglas gordas son iguales. Estas son las tres que conviene mirar, de más grave a menos:

  1. Credencial dentro de la regla. La clave quedó escrita en texto plano. Es la única de las tres que obliga a rotar algo. Ejemplo inventado: Bash(MI_CLAVE=AIzaSyEJEMPLO_NO_REAL_123 python3 subir.py).
  2. Comodín antes del subcomando. Ejemplo: Bash(git * main). La documentación de permisos lo explica sin ambigüedad: ahí el asterisco "stands in for the subcommand, so Claude Code matches every git subcommand and every option before it. That includes -c, which makes git run a program you name". Desde la versión 2.1.246, publicada el 25 de agosto de 2026, Claude Code te avisa de estas al arrancar.
  3. Regla demasiado amplia. Ejemplo: Bash(python3 *). Cubre familias enteras de comandos. No dispara el aviso de arranque, y la documentación de errores lo dice explícitamente: no advierte sobre "rules in which no word other than an option follows the *, such as Bash(git *)". Es una decisión tuya sobre cuánto quieres delegar, no un fallo.

La clase 2 es la que se arregla reescribiendo la regla: pon el asterisco después del subcomando, una regla por subcomando que quieras permitir. La clase 1 es la que se arregla rotando.

Borrar una regla sin romper tu flujo

Antes de tocar nada, cierra Claude Code. Si el programa reescribe el archivo encima mientras editas, pierdes el cambio.

Este comando hace un respaldo, quita todas las reglas que contengan el texto que le pases y deja el JSON válido. Cambia el texto del final por el que identifique tus reglas: el nombre de tu variable de entorno suele ser el más limpio.

python3 -c "import json,os,shutil,sys;p=os.path.expanduser('~/.claude/settings.local.json');t=sys.argv[1];shutil.copy(p,p+'.bak');d=json.load(open(p));a=d.get('permissions',{}).get('allow',[]);n=[x for x in a if t not in x];d['permissions']['allow']=n;json.dump(d,open(p,'w'),indent=2);print('BORRADAS',len(a)-len(n),'de',len(a),'| respaldo:',p+'.bak')" MI_CLAVE=

Tres avisos que salen de haberlo hecho mal:

Rotar una credencial que ya quedó escrita

Este es el paso que la gente se salta, y es el único que de verdad sirve. Borrar la regla no desactiva la credencial: si estuvo escrita, sigue siendo válida hasta que la cambies en su origen.

  1. Crea primero la clave nueva. Si borras la vieja antes, cortas lo que tengas corriendo.
  2. Entra a la consola donde nació. Si empieza por AIza es de Google, y se gestiona en la sección de claves de API de Google AI Studio.
  3. Borra allí la clave expuesta. Solo este paso la deja inservible.
  4. Guarda la nueva fuera del comando, en un archivo de entorno (siguiente sección).
  5. Restringe la clave nueva a la API que de verdad usas, si el proveedor lo permite.
  6. Recién entonces limpia las reglas viejas, y borra también el archivo .bak: ese respaldo guarda la clave vieja tal cual.

Los nombres de los botones de cada consola cambian cada pocos meses. Lo que no cambia es el orden: crear, borrar la vieja, sacarla del comando, limpiar el archivo.

Cómo no volver a llenarlo

Como la regla guarda el comando entero, la solución no es acordarse de limpiar cada mes: es que la clave nunca esté escrita en el comando.

mkdir -p ~/.config/miapp && chmod 700 ~/.config/miapp

echo 'GEMINI_API_KEY=AIzaSyEJEMPLO_NO_REAL_123' > ~/.config/miapp/.env

chmod 600 ~/.config/miapp/.env

Y a partir de ahí el comando carga la clave justo antes en vez de llevarla dentro:

set -a; . ~/.config/miapp/.env; set +a

python3 subir.py

La regla que Claude Code guardará ahora es Bash(python3 subir.py), limpia. El archivo de entorno vive fuera del repositorio; si tiene que vivir dentro, va al .gitignore antes de que escribas la primera clave. Y el chmod 600 deja el archivo legible solo para tu usuario.

Si ya usabas archivos de entorno, revisa igual: basta un comando suelto con la variable pegada delante para volver a escribir la clave en los permisos.

Qué no arregla esto

La parte honesta, sin suavizar.

Corre el comando en tus proyectos, apunta los tres números y repite dentro de un mes. Si un comando necesita una credencial, la credencial no va en el comando.

Preguntas frecuentes

¿Dónde está el archivo de permisos de Claude Code?
Hay más de uno. El del proyecto vive en .claude/settings.local.json dentro de la carpeta del proyecto, y el de usuario en la carpeta .claude de tu carpeta personal. El archivo settings.json que está al lado es distinto: ese es el compartido del equipo y sí se sube al repositorio. El comando de esta guía mira las dos rutas a la vez y salta la que no exista.
¿El comando de auditoría muestra mis claves en pantalla?
No. Devuelve solo tres conteos y las posiciones dentro de la lista de reglas, nunca el contenido de la credencial. La variante que lista las reglas una por una sustituye por un marcador cualquier cosa con forma de clave antes de imprimirla. Aun así, el tapado es una heurística: no copies esa salida a un chat o a un issue sin releerla línea por línea.
¿Basta con borrar la regla que contiene la clave?
No. Borrar la regla quita el texto del archivo, pero la credencial sigue siendo válida en el servicio que la emitió. Hay que rotarla: crear la nueva primero para no cortar lo que tengas corriendo, borrar la expuesta en la consola del proveedor, guardar la nueva fuera del comando y solo después limpiar las reglas. Y borra el respaldo .bak, que guarda la clave vieja.
¿Por qué Claude Code guarda el comando completo?
Porque necesita saber qué fue exactamente lo que autorizaste para volver a reconocerlo. Si guardara solo el nombre del programa, tu permiso cubriría muchas más cosas de las que aprobaste, que es justo lo contrario de lo que quieres de un sistema de permisos. Es comportamiento documentado, no un fallo: el problema aparece cuando el comando que aprobaste llevaba una credencial dentro.
¿Qué es el aviso de comodín de la versión 2.1.246?
Es un aviso al arrancar sobre reglas allow de Bash cuyo asterisco va antes del subcomando, como Bash(git * main). Claude Code no cambia nada de cómo funciona la regla: solo la nombra para que la puedas estrechar. Existe porque ese asterisco también cubre opciones metidas antes del subcomando, incluida -c, con la que git puede acabar ejecutando un programa que el comando nombre.

Fuentes

MÁQUINA IA

La comunidad donde dejas de usar IA y empiezas a dirigirla.

Entrar a la comunidad →