\
CONTEXT/academy
Lección 02 · ≈ 16 min

Reglas de permisos: allow, ask y deny

Las tres listas, el orden de evaluación y la sintaxis de reglas (con sus trampas).

Reglas de permisos: allow, ask y deny

Aquí decides, con precisión, qué puede hacer Claude Code sin preguntarte. Son reglas que aplica el cliente, no el modelo: ni tu prompt ni tu CLAUDE.md pueden saltárselas. El objetivo de la lección es que termines con un bloque permissions versionado que elimine las preguntas inútiles y cierre lo peligroso.

Las tres listas

Dentro de permissions:

  • allow — Claude usa la herramienta sin pedir aprobación.
  • ask — pide confirmación cada vez.
  • deny — la bloquea, sin más.
{
  "permissions": {
    "allow": ["Bash(pnpm *)", "Bash(git commit *)"],
    "ask":   ["Bash(git push *)"],
    "deny":  ["Bash(git push --force*)", "Read(./.env)", "Read(./secrets/**)"]
  }
}

Gestiónalas también en vivo con /permissions, que además te dice de qué fichero sale cada regla.

El orden importa: deny → ask → allow

Se evalúan en ese orden y gana la primera regla que casa. Conclusión práctica: un deny siempre tiene prioridad, venga del nivel que venga. No puedes "des-denegar" desde un nivel inferior.

En el ejemplo de arriba, git push origin main cae en ask (pregunta), pero git push --force origin main cae antes en deny (bloqueado). El force-push gana al push normal porque deny se evalúa primero.

Sintaxis de reglas

El formato es Herramienta o Herramienta(especificador):

  • Bash (sin paréntesis) casa todos los usos de Bash. Como regla deny, además saca la herramienta del contexto: Claude ni la ve.
  • Bash(pnpm *) casa solo lo que empieza por pnpm .
  • Otros: Read(./.env), WebFetch(domain:example.com), mcp__servidor__*, Agent(Explore).

La trampa del espacio

En reglas Bash, el espacio antes del * marca un límite de palabra:

ReglaCasaNo casa
Bash(ls *)ls -lalsof
Bash(ls*)ls -la, lsof

Además, Claude Code entiende los operadores de shell (&&, ||, ;, |…): una regla Bash(safe-cmd *) no autoriza safe-cmd && rm -rf .. Cada subcomando debe casar por su cuenta. Es una protección, no un bug.

Read y Edit usan patrones tipo gitignore

PatrónSignifica
Read(.env)cualquier .env en este directorio o por debajo
Edit(/src/**/*.ts)TS bajo la raíz del proyecto (el / no es la raíz del disco)
Read(~/.ssh/**)bajo tu home
Read(//tmp/x)ruta absoluta del disco (doble //)

Ojo a esa distinción: /src es relativo al proyecto; para una ruta absoluta de verdad necesitas //.

Una trampa de fondo: no restrinjas argumentos con patrones

Intentar acotar argumentos con patrones Bash es frágil. Bash(curl https://github.com/ *) parece restringir curl a GitHub, pero no cubre curl -L, otra URL, variables, espacios extra… Para filtrar red de verdad:

  • Deniega curl/wget y usa el tool WebFetch con WebFetch(domain:...) para los dominios permitidos.
  • O un hook PreToolUse que valide el comando (lo verás en el curso de hooks deterministas).

Una base sensata para empezar

{
  "permissions": {
    "allow": ["Bash(pnpm *)", "Bash(git status)", "Bash(git diff *)", "Bash(git commit *)"],
    "deny":  ["Bash(git push --force*)", "Bash(rm -rf *)", "Read(./.env)", "Read(./secrets/**)"]
  }
}

Permite lo cotidiano (tests, diffs, commits), bloquea lo destructivo y la lectura de secretos. Commitéalo y todo el equipo lo hereda.

Qué llevas hasta aquí

  • allow / ask / deny, evaluados deny → ask → allow (deny manda).
  • Sintaxis Herramienta(especificador), con la trampa del espacio en Bash y patrones gitignore en Read/Edit.
  • No se acotan argumentos con patrones: para red, deny + WebFetch(domain:...) o un hook.

En la siguiente lección, la otra mitad del control: los permission modes, que ponen la línea base de cuánto te interrumpe Claude.

Fuente oficial: Configure permissions

¿Quieres llevar la cuenta de las lecciones que has terminado?

Entrar para guardar progreso