3Modulo 3

Permisos y Seguridad

40 min

Claude Code opera bajo un sistema de permisos que controla qué acciones puede ejecutar. Este sistema es fundamental para mantener la seguridad mientras permites que Claude sea productivo.

Filosofía del sistema

Claude Code sigue el principio de mínimo privilegio: por defecto, solicita permiso para acciones potencialmente peligrosas. Tú decides qué permitir de forma permanente y qué requiere confirmación cada vez.

┌─────────────────────────────────────────────┐
│              Tu decisión                     │
│                                             │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐     │
│  │ Permitir│  │ Permitir│  │ Denegar │     │
│  │ siempre │  │ una vez │  │ siempre │     │
│  └─────────┘  └─────────┘  └─────────┘     │
│       │            │            │           │
│       ▼            ▼            ▼           │
│   Se añade a   Se ejecuta   Se añade a     │
│   allowlist    solo ahora   denylist       │
└─────────────────────────────────────────────┘

Tools disponibles

Claude Code tiene acceso a herramientas (tools) que le permiten interactuar con tu sistema. Las principales son:

ToolDescripciónEjemplos
ReadLeer archivos del sistemaVer código fuente, configuraciones
WriteCrear o modificar archivosEscribir código, editar configs
EditEditar secciones de archivosModificar funciones específicas
BashEjecutar comandos en terminalgit, npm, scripts
GlobBuscar archivos por patrónEncontrar todos los .ts
GrepBuscar contenido en archivosEncontrar usos de una función

Tool: Read

Permite a Claude leer archivos de tu sistema.

Casos de uso:

  • Leer código fuente para entenderlo
  • Ver archivos de configuración
  • Examinar logs o outputs

Riesgos potenciales:

  • Acceso a archivos sensibles (.env, credenciales)
  • Lectura de datos privados
Read(./src/index.ts)        → Leer archivo específico
Read(./.env)                → ⚠️ Archivo sensible
Read(./package.json)        → Configuración del proyecto

Tool: Write

Permite a Claude crear archivos nuevos o sobrescribir existentes.

Casos de uso:

  • Crear nuevos archivos de código
  • Generar configuraciones
  • Escribir documentación

Riesgos potenciales:

  • Sobrescribir archivos importantes
  • Crear archivos en ubicaciones incorrectas
Write(./src/utils.ts)       → Crear/sobrescribir archivo
Write(./package.json)       → ⚠️ Sobrescribir configuración crítica
Write(./.env.example)       → Crear template

Tool: Edit

Permite a Claude modificar secciones específicas de archivos existentes.

Casos de uso:

  • Modificar una función específica
  • Actualizar imports
  • Corregir bugs puntuales

Ventaja sobre Write:

  • Más preciso: solo cambia lo necesario
  • Menor riesgo de pérdida de código
Edit(./src/api.ts)          → Editar sección del archivo

Tool: Bash

Permite a Claude ejecutar comandos en la terminal.

Casos de uso:

  • Ejecutar git (commits, push, pull)
  • Gestionar paquetes (npm, pip)
  • Correr tests y builds
  • Scripts de automatización

Riesgos potenciales:

  • Comandos destructivos (rm -rf)
  • Acceso a recursos del sistema
  • Comandos con privilegios (sudo)
Bash(git status)            → Seguro, solo lectura
Bash(npm install)           → Modifica node_modules
Bash(rm -rf ./src)          → ⚠️ Destructivo
Bash(sudo apt install)      → ⚠️ Requiere privilegios

Flujo de permisos

Cuando Claude necesita usar una herramienta:

Claude quiere ejecutar: Bash(npm install)


    ¿Está en la lista "allow"?
        │           │
       Sí          No
        │           │
        ▼           ▼
    Ejecutar    ¿Está en la lista "deny"?
                    │           │
                   Sí          No
                    │           │
                    ▼           ▼
                Bloquear    Pedir permiso
                            al usuario

                    ┌───────────┼───────────┐
                    │           │           │
                    ▼           ▼           ▼
                Permitir    Permitir    Denegar
                siempre     una vez

Niveles de permiso

Los permisos se pueden configurar en tres niveles, con prioridad de arriba a abajo:

NivelArchivoAlcancePrioridad
Local.claude/settings.local.jsonSolo tu máquinaMáxima
Proyecto.claude/settings.jsonTodo el equipoMedia
Global~/.claude/settings.jsonTodos tus proyectosMínima

Ejemplo de precedencia

~/.claude/settings.json (global)
  allow: ["Bash(npm *)"]
 
.claude/settings.json (proyecto)
  deny: ["Bash(npm publish)"]
 
.claude/settings.local.json (local)
  allow: ["Bash(npm publish)"]

En este caso, npm publish está permitido porque el archivo local tiene máxima prioridad.

Trust dialogs

Cuando Claude intenta una acción no preconfigurada, aparece un diálogo de confianza:

╭──────────────────────────────────────────────╮
│ Claude wants to run:                         │
│                                              │
│   Bash(npm install lodash)                   │
│                                              │
│ [a] Allow always  [y] Yes, once  [n] No      │
╰──────────────────────────────────────────────╯

Opciones:

  • a (Allow always): Añade a la lista de permitidos
  • y (Yes, once): Permite solo esta vez
  • n (No): Deniega la acción

Las respuestas "Allow always" se guardan en .claude/settings.local.json.

Modo sin confirmación

Para automatización, puedes saltarte las confirmaciones:

# Acepta automáticamente permisos (usa con precaución)
claude --yes
 
# Alias común
claude -y

Este modo es útil para:

  • Scripts de CI/CD
  • Automatizaciones controladas
  • Entornos de prueba

Precaución: Solo usa --yes en entornos donde confías completamente en lo que vas a pedir.

Auditoría de permisos

Puedes ver qué permisos tiene Claude actualmente:

# En sesión interactiva
/permissions

Esto muestra:

  • Herramientas permitidas
  • Herramientas denegadas
  • Fuente de cada permiso (global, proyecto, local)

Permisos y seguridad

El sistema de permisos es tu primera línea de defensa. Considera:

EscenarioRecomendación
Proyecto personalPermisos más relajados
Proyecto de trabajoPermisos moderados
Datos sensiblesPermisos restrictivos
CI/CDSolo permisos específicos necesarios

Puntos clave

  • Claude Code usa un sistema de permisos para controlar qué puede hacer
  • Las herramientas principales son: Read, Write, Edit, Bash, Glob, Grep
  • Los permisos se configuran en tres niveles: global, proyecto y local
  • El archivo local tiene máxima prioridad
  • Los trust dialogs permiten decidir en tiempo real
  • Usa --yes con precaución, solo en entornos controlados
  • Revisa permisos regularmente con /permissions

Siguiente paso: En el punto 3.2 aprenderás a configurar permisos específicos con patrones.


3.2 Configuración de permisos

Los permisos se configuran en el archivo settings.json. Aquí aprenderás la sintaxis exacta y las mejores prácticas para configurarlos.

Estructura básica

{
  "permissions": {
    "allow": [
      "Tool(patrón)"
    ],
    "deny": [
      "Tool(patrón)"
    ]
  }
}
  • allow: Lista de herramientas/patrones permitidos automáticamente
  • deny: Lista de herramientas/patrones bloqueados siempre

Sintaxis de permisos

Permiso simple (toda la herramienta)

{
  "permissions": {
    "allow": [
      "Read",
      "Write",
      "Edit"
    ]
  }
}

Esto permite todas las operaciones de lectura, escritura y edición sin confirmación.

Permiso con patrón

{
  "permissions": {
    "allow": [
      "Read(./src/*)",
      "Write(./src/*.ts)",
      "Bash(git *)"
    ]
  }
}

Esto permite:

  • Leer cualquier archivo en ./src/
  • Escribir solo archivos .ts en ./src/
  • Ejecutar cualquier comando git

Patrones glob

Los patrones usan sintaxis glob estándar:

PatrónSignificadoEjemplo
*Cualquier cosa excepto /*.tsindex.ts
**Cualquier cosa incluyendo /**/*.tssrc/utils/index.ts
?Un solo carácterfile?.tsfile1.ts
[abc]Uno de los caracteresfile[123].tsfile1.ts
{a,b}Una de las opciones*.{ts,js}index.ts, index.js

Ejemplos de patrones

{
  "permissions": {
    "allow": [
      "Read(**/*.ts)",
      "Read(**/*.js)",
      "Read(**/*.json)",
      "Write(./src/**/*)",
      "Bash(npm run *)",
      "Bash(git {status,log,diff,add,commit,push,pull})"
    ],
    "deny": [
      "Read(./.env*)",
      "Read(**/*.pem)",
      "Read(**/secrets/**)",
      "Write(./package-lock.json)",
      "Bash(rm -rf *)",
      "Bash(sudo *)"
    ]
  }
}

Configuración por herramienta

Read

{
  "permissions": {
    "allow": [
      "Read",
      "Read(./src/**/*)",
      "Read(*.{ts,js,json,md})",
      "Read(./docs/**/*.md)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./.env.local)",
      "Read(**/*.key)",
      "Read(**/*.pem)",
      "Read(**/credentials*)",
      "Read(**/secrets/**)"
    ]
  }
}

Write

{
  "permissions": {
    "allow": [
      "Write(./src/**/*.ts)",
      "Write(./src/**/*.tsx)",
      "Write(./tests/**/*)",
      "Write(./*.md)"
    ],
    "deny": [
      "Write(./.env*)",
      "Write(./package-lock.json)",
      "Write(./yarn.lock)",
      "Write(./.git/**/*)",
      "Write(**/node_modules/**)"
    ]
  }
}

Bash

Los comandos Bash requieren especial atención por su potencial destructivo.

{
  "permissions": {
    "allow": [
      "Bash(git status)",
      "Bash(git log*)",
      "Bash(git diff*)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Bash(git push)",
      "Bash(git pull)",
      "Bash(git branch*)",
      "Bash(git checkout *)",
      "Bash(npm run *)",
      "Bash(npm test*)",
      "Bash(npm install)",
      "Bash(npx prettier *)",
      "Bash(npx eslint *)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(rm -r *)",
      "Bash(sudo *)",
      "Bash(chmod 777 *)",
      "Bash(curl * | sh)",
      "Bash(wget * | sh)",
      "Bash(git push --force*)",
      "Bash(git reset --hard*)",
      "Bash(npm publish*)",
      "Bash(> *)"
    ]
  }
}

Configuración por entorno

Desarrollo personal

Permisos más relajados para máxima productividad:

{
  "permissions": {
    "allow": [
      "Read",
      "Write(./src/**/*)",
      "Write(./tests/**/*)",
      "Edit",
      "Bash(git *)",
      "Bash(npm *)",
      "Bash(node *)"
    ],
    "deny": [
      "Read(./.env)",
      "Bash(rm -rf *)",
      "Bash(sudo *)"
    ]
  }
}

Proyecto de equipo

Permisos moderados para colaboración segura:

{
  "permissions": {
    "allow": [
      "Read(**/*.{ts,tsx,js,jsx,json,md,css,scss})",
      "Write(./src/**/*)",
      "Write(./tests/**/*)",
      "Bash(git status)",
      "Bash(git diff*)",
      "Bash(git log*)",
      "Bash(npm run *)",
      "Bash(npm test*)"
    ],
    "deny": [
      "Read(./.env*)",
      "Read(**/secrets/**)",
      "Write(./*.json)",
      "Write(./*.lock)",
      "Bash(git push*)",
      "Bash(git commit*)",
      "Bash(npm install*)",
      "Bash(npm publish*)",
      "Bash(rm *)",
      "Bash(sudo *)"
    ]
  }
}

CI/CD Pipeline

Solo los permisos estrictamente necesarios:

{
  "permissions": {
    "allow": [
      "Read(**/*.{ts,tsx,js,jsx})",
      "Bash(npm run lint)",
      "Bash(npm run test)",
      "Bash(npm run build)"
    ],
    "deny": [
      "Write",
      "Edit",
      "Bash(git *)",
      "Bash(npm install*)",
      "Bash(npm publish*)",
      "Bash(rm *)",
      "Bash(sudo *)"
    ]
  }
}

Datos sensibles

Configuración restrictiva para proyectos con información confidencial:

{
  "permissions": {
    "allow": [
      "Read(./src/**/*.ts)",
      "Read(./docs/**/*.md)"
    ],
    "deny": [
      "Read(./.env*)",
      "Read(**/config/**)",
      "Read(**/secrets/**)",
      "Read(**/*.key)",
      "Read(**/*.pem)",
      "Read(**/*.cert)",
      "Read(**/credentials*)",
      "Read(**/password*)",
      "Read(**/token*)",
      "Write",
      "Edit",
      "Bash"
    ]
  }
}

Combinación de niveles

Recuerda que puedes combinar configuraciones en diferentes niveles:

~/.claude/settings.json (global):

{
  "permissions": {
    "deny": [
      "Bash(rm -rf *)",
      "Bash(sudo *)",
      "Read(./.env)"
    ]
  }
}

.claude/settings.json (proyecto):

{
  "permissions": {
    "allow": [
      "Read(./src/**/*)",
      "Write(./src/**/*)",
      "Bash(npm run *)"
    ]
  }
}

.claude/settings.local.json (local):

{
  "permissions": {
    "allow": [
      "Bash(npm publish)"
    ]
  }
}

Verificar configuración

Después de configurar, verifica que los permisos son correctos:

claude
/permissions

Deberías ver un listado de todas las reglas activas y su origen.

Errores comunes

Patrón demasiado amplio

// ❌ Malo: permite escribir en cualquier lugar
"allow": ["Write"]
 
// ✅ Mejor: limita a directorios específicos
"allow": ["Write(./src/**/*)", "Write(./tests/**/*)"]

Olvidar archivos sensibles

// ❌ Incompleto
"deny": ["Read(./.env)"]
 
// ✅ Completo
"deny": [
  "Read(./.env)",
  "Read(./.env.*)",
  "Read(./.env.local)",
  "Read(./.env.production)"
]

Patrón incorrecto para Bash

// ❌ No funciona como esperas
"allow": ["Bash(git)"]
 
// ✅ Correcto
"allow": ["Bash(git *)"]
// o específico
"allow": ["Bash(git status)", "Bash(git diff*)"]

Puntos clave

  • Usa allow para permisos automáticos y deny para bloqueos permanentes
  • Los patrones glob permiten configuraciones flexibles (*, **, {a,b})
  • Configura permisos según el contexto: personal, equipo, CI/CD, datos sensibles
  • El archivo local (.claude/settings.local.json) tiene máxima prioridad
  • Verifica tu configuración con /permissions
  • Sé específico: evita permisos demasiado amplios
  • Siempre deniega acceso a archivos sensibles (.env, credenciales, keys)

Siguiente paso: En el punto 3.3 aprenderás patrones avanzados de permisos.


3.3 Patrones de permisos

Dominar los patrones de permisos te permite crear configuraciones precisas que permiten exactamente lo que necesitas y bloquean lo que no.

Anatomía de un patrón

Tool(patrón)
  │     │
  │     └── Qué archivos/comandos afecta
  └── Herramienta (Read, Write, Bash, etc.)

Patrones para Read y Write

Archivo específico

"Read(./package.json)"
"Write(./README.md)"

Solo aplica a ese archivo exacto.

Todos los archivos en una carpeta

"Read(./src/*)"
"Write(./tests/*)"

El * no atraviesa directorios. Solo archivos directamente en esa carpeta.

./src/*  coincide con:
  ✓ ./src/index.ts
  ✓ ./src/app.js
  ✗ ./src/utils/helper.ts    (subcarpeta)

Todos los archivos recursivamente

"Read(./src/**/*)"
"Write(./src/**/*)"

El ** atraviesa todos los subdirectorios.

./src/**/*  coincide con:
  ✓ ./src/index.ts
  ✓ ./src/utils/helper.ts
  ✓ ./src/components/ui/Button.tsx

Por extensión

"Read(*.ts)"
"Read(**/*.ts)"
"Write(**/*.{ts,tsx})"
*.ts           → Solo .ts en raíz
**/*.ts        → Todos los .ts del proyecto
**/*.{ts,tsx}  → Todos los .ts y .tsx

Patrones de exclusión comunes

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(**/*.env)",
      "Read(**/*.key)",
      "Read(**/*.pem)",
      "Read(**/*.p12)",
      "Read(**/*.pfx)",
      "Read(**/credentials*)",
      "Read(**/secrets/**)",
      "Read(**/private/**)",
      "Write(**/node_modules/**)",
      "Write(**/.git/**)",
      "Write(*.lock)"
    ]
  }
}

Patrones para Bash

Los patrones de Bash son especiales porque coinciden con comandos, no con archivos.

Comando exacto

"Bash(git status)"
"Bash(npm test)"

Solo permite ese comando exacto, sin argumentos adicionales.

Comando con cualquier argumento

"Bash(git *)"
"Bash(npm run *)"
git *  coincide con:
  ✓ git status
  ✓ git commit -m "mensaje"
  ✓ git push origin main

Comando con opciones específicas

"Bash(git log*)"
"Bash(git diff*)"
git log*  coincide con:
  ✓ git log
  ✓ git log --oneline
  ✓ git log -n 10
  ✗ git status    (no empieza con "git log")

Lista de subcomandos

"Bash(git {status,log,diff,add,commit,push,pull})"

Permite solo esos subcomandos de git.

Comandos con argumentos posicionales

"Bash(npm run *)"
"Bash(npx * --write)"
npm run *  coincide con:
  ✓ npm run dev
  ✓ npm run build
  ✓ npm run test:unit

Patrones avanzados

Negación implícita con deny

Para permitir todo excepto algo específico:

{
  "permissions": {
    "allow": ["Bash(git *)"],
    "deny": [
      "Bash(git push --force*)",
      "Bash(git reset --hard*)",
      "Bash(git clean -fd*)"
    ]
  }
}

Esto permite todos los comandos git excepto los destructivos.

Combinación de extensiones

"Read(**/*.{js,jsx,ts,tsx,json,md,css,scss,html})"

Permite leer todos los archivos de código comunes.

Excluir carpetas específicas

{
  "permissions": {
    "allow": ["Read(**/*.ts)"],
    "deny": [
      "Read(**/node_modules/**)",
      "Read(**/dist/**)",
      "Read(**/.git/**)",
      "Read(**/coverage/**)"
    ]
  }
}

Patrones para múltiples herramientas

{
  "permissions": {
    "allow": [
      "Read(./src/**/*)",
      "Write(./src/**/*)",
      "Edit(./src/**/*)"
    ]
  }
}

Recetas comunes

Proyecto TypeScript/JavaScript

{
  "permissions": {
    "allow": [
      "Read(**/*.{ts,tsx,js,jsx,json,md,css,scss})",
      "Read(./package.json)",
      "Read(./tsconfig.json)",
      "Read(./.eslintrc*)",
      "Read(./.prettierrc*)",
      "Write(./src/**/*.{ts,tsx})",
      "Write(./tests/**/*.{ts,tsx})",
      "Write(./*.md)",
      "Bash(npm run *)",
      "Bash(npm test*)",
      "Bash(npx prettier *)",
      "Bash(npx eslint *)",
      "Bash(git {status,diff,log,add,commit,push,pull})"
    ],
    "deny": [
      "Read(./.env*)",
      "Write(./package*.json)",
      "Write(./*.lock)",
      "Bash(npm install*)",
      "Bash(npm publish*)",
      "Bash(rm *)"
    ]
  }
}

Proyecto Python

{
  "permissions": {
    "allow": [
      "Read(**/*.{py,pyi,json,yaml,yml,md,txt})",
      "Read(./pyproject.toml)",
      "Read(./setup.py)",
      "Read(./requirements*.txt)",
      "Write(./src/**/*.py)",
      "Write(./tests/**/*.py)",
      "Bash(python *)",
      "Bash(pytest *)",
      "Bash(pip list)",
      "Bash(black *)",
      "Bash(ruff *)",
      "Bash(git {status,diff,log,add,commit,push,pull})"
    ],
    "deny": [
      "Read(./.env*)",
      "Read(**/*secret*)",
      "Write(./requirements*.txt)",
      "Bash(pip install*)",
      "Bash(rm *)",
      "Bash(sudo *)"
    ]
  }
}

Proyecto Go

{
  "permissions": {
    "allow": [
      "Read(**/*.{go,mod,sum,json,yaml,md})",
      "Write(**/*.go)",
      "Write(./*.md)",
      "Bash(go build*)",
      "Bash(go test*)",
      "Bash(go run*)",
      "Bash(go fmt*)",
      "Bash(go vet*)",
      "Bash(golangci-lint *)",
      "Bash(git {status,diff,log,add,commit,push,pull})"
    ],
    "deny": [
      "Read(./.env*)",
      "Write(./go.mod)",
      "Write(./go.sum)",
      "Bash(go get*)",
      "Bash(rm *)"
    ]
  }
}

Proyecto Rust

{
  "permissions": {
    "allow": [
      "Read(**/*.{rs,toml,md})",
      "Write(./src/**/*.rs)",
      "Write(./tests/**/*.rs)",
      "Bash(cargo build*)",
      "Bash(cargo test*)",
      "Bash(cargo run*)",
      "Bash(cargo fmt*)",
      "Bash(cargo clippy*)",
      "Bash(git {status,diff,log,add,commit,push,pull})"
    ],
    "deny": [
      "Read(./.env*)",
      "Write(./Cargo.toml)",
      "Write(./Cargo.lock)",
      "Bash(cargo install*)",
      "Bash(cargo publish*)",
      "Bash(rm *)"
    ]
  }
}

Debugging de patrones

El patrón no coincide

Si un patrón no funciona como esperas:

  1. Verifica la ruta: ¿Es relativa (./) o absoluta?
  2. Verifica el glob: ¿Necesitas * o **?
  3. Verifica las llaves: {ts,tsx} no es lo mismo que {ts, tsx} (sin espacios)

Probar patrones

Puedes probar si un patrón coincide con un archivo:

# En terminal, no en Claude Code
# Usa el comando find o ls con glob
 
ls ./src/**/*.ts 2>/dev/null
# Si lista archivos, el patrón es correcto

Ver qué regla aplica

/permissions

Muestra todas las reglas activas y puedes identificar conflictos.

Puntos clave

  • * coincide con cualquier cosa excepto /
  • ** coincide con cualquier cosa incluyendo / (recursivo)
  • {a,b,c} coincide con una de las opciones
  • Para Bash, los patrones coinciden con el comando completo
  • Combina allow con deny para permisos precisos
  • Usa recetas específicas para tu stack tecnológico
  • Verifica patrones con /permissions

Siguiente paso: En el punto 3.4 aprenderás sobre el modo peligroso y cuándo usarlo.


3.4 Modo peligroso

El flag --dangerously-skip-permissions elimina todas las confirmaciones de permisos, permitiendo que Claude ejecute cualquier acción sin pedir aprobación. El nombre incluye "dangerously" intencionalmente: es una señal de que debes saber exactamente lo que estás haciendo.

Qué hace exactamente

En modo normal, Claude pide confirmación antes de:

  • Ejecutar comandos en terminal (Bash)
  • Modificar archivos (Write, Edit)
  • Cualquier herramienta que requiera aprobación

Con --dangerously-skip-permissions, todas esas confirmaciones desaparecen. Claude actúa directamente.

# Modo normal: Claude pide permiso antes de cada acción
claude
 
# Modo peligroso: Claude ejecuta todo sin preguntar
claude --dangerously-skip-permissions

Cuándo usarlo

Este modo tiene sentido en entornos donde no hay riesgo real:

Contenedores desechables

# Docker con volumen temporal
docker run --rm -it mi-entorno-dev bash
claude --dangerously-skip-permissions -p "Configura el proyecto y ejecuta los tests"

El contenedor se destruye al terminar. No hay datos que proteger.

Máquinas virtuales de CI/CD

# GitHub Actions
- name: Análisis con Claude
  run: |
    claude --dangerously-skip-permissions -p "Revisa el código y genera reporte"

El runner es efímero y aislado.

Automatización sin supervisión

# Script que procesa múltiples archivos
for file in ./src/*.ts; do
  claude --dangerously-skip-permissions -p "Añade tipos estrictos a $file"
done

Cuando necesitas que Claude trabaje en lote sin interrupciones.

Entornos de desarrollo aislados

# VM de desarrollo sin acceso a producción
# Sin credenciales, sin datos sensibles
claude --dangerously-skip-permissions

Cuándo NO usarlo

Proyectos de producción

# NUNCA hagas esto en un servidor de producción
claude --dangerously-skip-permissions -p "Limpia los logs antiguos"
# Claude podría ejecutar rm -rf en directorios importantes

Máquinas con datos sensibles

Si tu máquina tiene:

  • Archivos .env con credenciales
  • Claves SSH o GPG
  • Tokens de API
  • Bases de datos locales con datos reales

No uses este modo. Un prompt ambiguo podría llevar a Claude a leer o modificar estos archivos.

Entornos compartidos

# Servidor compartido con otros desarrolladores
# Claude podría modificar archivos de otros usuarios
claude --dangerously-skip-permissions  # NO

Cuando no entiendes lo que vas a pedir

Si tu prompt es vago o experimental, el modo normal te protege:

# Prompt vago + modo peligroso = riesgo
claude --dangerously-skip-permissions -p "Arregla todo lo que encuentres mal"
# ¿Qué va a hacer Claude? No lo sabes. Mal momento para saltarse permisos.

Modos de permisos relacionados

Claude Code ofrece varios modos de permisos que ofrecen distintos niveles de control:

ModoComportamiento
defaultPide confirmación la primera vez que usa cada herramienta
acceptEditsAcepta automáticamente ediciones de archivos
planSolo lectura, no puede modificar ni ejecutar
bypassPermissionsSalta todas las confirmaciones (equivalente al flag)
# Solo lectura: Claude analiza pero no toca nada
claude --permission-mode plan
 
# Acepta ediciones automáticamente pero pide permiso para bash
claude --permission-mode acceptEdits

Interacción con hooks

Los hooks siguen ejecutándose incluso con --dangerously-skip-permissions. Esto es importante: los hooks son una capa de seguridad independiente del sistema de permisos.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(rm *)",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'BLOCKED: intento de rm detectado' && exit 1"
          }
        ]
      }
    ]
  }
}

Con esta configuración, aunque uses --dangerously-skip-permissions, el hook bloqueará cualquier intento de ejecutar rm. Los hooks actúan como última línea de defensa.

Buenas prácticas

Combina con prompts específicos

# Mal: modo peligroso + prompt interactivo abierto
claude --dangerously-skip-permissions
 
# Bien: modo peligroso + tarea concreta y acotada
claude --dangerously-skip-permissions -p "Ejecuta npm test y muestra los resultados"

Usa hooks como red de seguridad

Aunque saltes permisos, configura hooks que bloqueen acciones destructivas:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(rm -rf*)",
        "hooks": [{ "type": "command", "command": "exit 1" }]
      },
      {
        "matcher": "Bash(sudo *)",
        "hooks": [{ "type": "command", "command": "exit 1" }]
      }
    ]
  }
}

Limita el alcance cuando sea posible

En lugar de --dangerously-skip-permissions completo, considera si --permission-mode acceptEdits es suficiente para tu caso.

Puntos clave

  • --dangerously-skip-permissions elimina todas las confirmaciones
  • Úsalo solo en entornos aislados y desechables
  • Los hooks siguen funcionando como red de seguridad
  • Combínalo con prompts específicos (-p), no con sesiones interactivas abiertas
  • Considera --permission-mode acceptEdits como alternativa menos agresiva
  • Si tienes dudas sobre si usarlo: no lo uses

Siguiente paso: En el punto 3.5 aprenderás sobre los Trust Dialogs y cómo Claude gestiona la confianza por directorio.


3.5 Trust Dialogs

Cada vez que Claude necesita hacer algo que podría tener impacto en tu sistema, muestra un diálogo de confirmación. Estos "Trust Dialogs" son el mecanismo principal de seguridad interactiva de Claude Code.

Verificación de confianza por directorio

La primera vez que ejecutas claude en un directorio nuevo, Claude Code establece una relación de confianza con ese proyecto. Esto implica:

  1. Reconocer el directorio como espacio de trabajo
  2. Cargar los archivos de configuración del proyecto (.claude/settings.json, CLAUDE.md)
  3. Aplicar las reglas de permisos definidas para ese proyecto

La confianza es por directorio. Los permisos que concedes en un proyecto no aplican a otro.

~/proyecto-a/   → Permisos concedidos aquí
~/proyecto-b/   → Requiere sus propios permisos

Tipos de aprobación según la herramienta

No todas las acciones requieren el mismo nivel de aprobación:

Tipo de acciónEjemploAprobaciónDuración
Solo lecturaLeer archivos, GrepNo requiere
Comandos BashEjecutar en terminalPermanente por proyecto y comando
Modificación de archivosWrite, EditSolo durante la sesión

Solo lectura: sin confirmación

> ¿Qué hace la función validateUser?
 
# Claude lee archivos libremente para responder
# No aparece ningún diálogo

Las operaciones de lectura (Read, Grep, Glob) nunca piden permiso. Claude necesita leer para entender tu código.

Comandos Bash: aprobación permanente por proyecto

> Ejecuta los tests
 
┌─────────────────────────────────────────────────┐
│ Claude quiere ejecutar:                         │
│                                                 │
│   npm test                                      │
│                                                 │
│ [Permitir] [Permitir siempre] [Denegar]         │
└─────────────────────────────────────────────────┘

Si eliges "Permitir siempre", el comando npm test queda aprobado permanentemente para ese directorio. La próxima vez que Claude necesite ejecutarlo, no preguntará.

Modificación de archivos: aprobación por sesión

> Añade validación al formulario de login
 
┌─────────────────────────────────────────────────┐
│ Claude quiere modificar:                        │
│                                                 │
│   src/components/LoginForm.tsx                  │
│                                                 │
│ [Permitir] [Denegar]                            │
└─────────────────────────────────────────────────┘

La aprobación de edición de archivos dura solo hasta que termina la sesión. En la siguiente sesión, Claude volverá a pedir permiso.

Modos de permisos interactivos

Puedes cambiar el modo de permisos durante una sesión con Shift+Tab:

Default

El comportamiento estándar descrito arriba. Pide confirmación según el tipo de acción.

Accept edits

claude --permission-mode acceptEdits

Acepta automáticamente todas las ediciones de archivos, pero sigue pidiendo confirmación para comandos Bash. Útil cuando confías en que Claude editará correctamente pero quieres controlar qué ejecuta.

Plan

claude --permission-mode plan

Modo solo lectura. Claude puede analizar tu código pero no puede modificar archivos ni ejecutar comandos. Ideal para:

  • Explorar un codebase nuevo
  • Pedir explicaciones sin riesgo
  • Revisar antes de dar permiso para cambios

Don't ask

claude --permission-mode dontAsk

Deniega automáticamente cualquier acción que no esté pre-aprobada en la configuración. Claude solo puede hacer lo que allowedTools permita explícitamente.

Pre-aprobar acciones con configuración

En lugar de responder diálogos uno a uno, puedes pre-aprobar acciones en .claude/settings.json:

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git *)",
      "Write(./src/**/*)"
    ]
  }
}

Con esta configuración, Claude nunca preguntará antes de ejecutar comandos npm/git o editar archivos en src/. Los diálogos solo aparecerán para acciones no cubiertas por estas reglas.

Orden de evaluación

Cuando Claude quiere ejecutar una acción, el sistema evalúa en este orden:

1. ¿Está en "deny"?          → Bloqueado, sin diálogo
2. ¿Está en "allow"?         → Permitido, sin diálogo
3. ¿El modo es plan?         → Bloqueado, sin diálogo
4. ¿El modo es acceptEdits?  → Permitido si es edición
5. ¿El modo es dontAsk?      → Bloqueado, sin diálogo
6. Modo default              → Muestra Trust Dialog

Trust y MCP servers

Los servidores MCP también requieren verificación de confianza. La primera vez que añades un MCP nuevo, Claude Code pide confirmación antes de activarlo:

┌─────────────────────────────────────────────────┐
│ Nuevo servidor MCP detectado:                   │
│                                                 │
│   github (@modelcontextprotocol/server-github)  │
│                                                 │
│ ¿Confiar en este servidor?                      │
│ [Sí] [No]                                       │
└─────────────────────────────────────────────────┘

Gestionar permisos concedidos

Para ver qué permisos están activos en tu sesión:

/permissions

Esto muestra todas las reglas activas: las definidas en configuración y las concedidas interactivamente.

Para resetear permisos concedidos a un proyecto:

# Eliminar configuración local del proyecto
rm .claude/settings.local.json
 
# Eliminar toda la configuración del proyecto
rm -rf .claude/

Ejecución no interactiva

Cuando usas Claude con el flag -p (prompt directo), la verificación de confianza del directorio se desactiva. Esto es necesario para que funcione en scripts y pipelines:

# No hay Trust Dialog en modo no interactivo
claude -p "Lista los archivos TypeScript del proyecto"

Sin embargo, los permisos de herramientas siguen aplicando. Si un comando no está pre-aprobado y no usas --dangerously-skip-permissions, Claude no podrá ejecutarlo.

Puntos clave

  • La confianza es por directorio: cada proyecto tiene sus propios permisos
  • Las lecturas nunca piden permiso; los comandos Bash se aprueban permanentemente por proyecto; las ediciones se aprueban por sesión
  • Usa Shift+Tab para cambiar entre modos de permisos durante la sesión
  • Pre-aprueba acciones frecuentes en .claude/settings.json para evitar diálogos repetitivos
  • Los servidores MCP también requieren verificación de confianza
  • En modo no interactivo (-p) se desactiva la verificación del directorio

Siguiente paso: En la práctica del módulo aplicarás todo lo aprendido configurando permisos para un proyecto real.


Práctica del módulo 3: Gestión de permisos y seguridad

En esta práctica configurarás permisos restrictivos para un proyecto, probarás los límites del sistema de permisos y crearás una configuración de seguridad adaptada a tu flujo de trabajo.

Objetivos

  • Configurar permisos restrictivos para un proyecto
  • Probar qué pasa cuando Claude intenta acceder a archivo denegado
  • Crear lista de comandos bash permitidos para tu workflow
  • Experimentar con los modos de permisos

Ejercicio 1: Preparar el entorno

Paso 1.1: Crear proyecto de prueba

Crea un proyecto con estructura realista para practicar:

mkdir -p ~/practica-permisos/src
mkdir -p ~/practica-permisos/tests
mkdir -p ~/practica-permisos/.claude
touch ~/practica-permisos/.env
touch ~/practica-permisos/.env.production
touch ~/practica-permisos/src/app.ts
touch ~/practica-permisos/src/config.ts
touch ~/practica-permisos/tests/app.test.ts

Paso 1.2: Añadir contenido de prueba

Añade contenido al archivo .env:

DB_PASSWORD=super_secreto_123
API_KEY=sk-fake-key-para-practica

Y al archivo src/app.ts:

export function hello(): string {
  return "Hola desde la práctica de permisos";
}

Verificación: La estructura del proyecto existe con archivos que contienen datos.


Ejercicio 2: Permisos restrictivos

Paso 2.1: Crear configuración de permisos

Crea el archivo ~/practica-permisos/.claude/settings.json:

{
  "permissions": {
    "allow": [
      "Read(./src/**/*)",
      "Read(./tests/**/*)",
      "Write(./src/**/*)",
      "Write(./tests/**/*)",
      "Bash(npm test*)",
      "Bash(npx tsc*)",
      "Bash(git {status,diff,log})"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Write(./.env*)",
      "Write(./package*.json)",
      "Bash(rm *)",
      "Bash(sudo *)",
      "Bash(curl *)",
      "Bash(wget *)"
    ]
  }
}

Paso 2.2: Verificar que la configuración se carga

cd ~/practica-permisos
claude

Dentro de la sesión:

/permissions

Verificación: Ves las reglas de allow y deny que configuraste.


Ejercicio 3: Probar los límites

Paso 3.1: Intentar leer archivo protegido

Dentro de la sesión de Claude, pide:

Muéstrame el contenido del archivo .env

Claude debería no poder leer el archivo porque está en la lista deny.

Paso 3.2: Verificar que la lectura permitida funciona

Muéstrame el contenido de src/app.ts

Claude debería poder leerlo sin problemas.

Paso 3.3: Intentar comando denegado

Ejecuta curl https://example.com

Claude debería rechazar la ejecución.

Paso 3.4: Verificar comando permitido

Ejecuta git status

Claude debería ejecutarlo sin diálogo de confirmación.

Paso 3.5: Probar comando no listado

Ejecuta ls -la

Como ls no está en allow ni en deny, Claude debería mostrar un Trust Dialog pidiendo confirmación.

Verificación: Entiendes la diferencia entre acciones denegadas, permitidas y no configuradas.


Ejercicio 4: Modos de permisos

Paso 4.1: Modo plan (solo lectura)

Inicia Claude en modo plan:

cd ~/practica-permisos
claude --permission-mode plan

Intenta pedir una modificación:

Añade un comentario al archivo src/app.ts

Claude debería explicar qué haría pero no ejecutar la modificación.

Paso 4.2: Modo acceptEdits

Sal y reinicia con:

claude --permission-mode acceptEdits

Pide una modificación:

Añade un comentario al inicio de src/app.ts

Claude debería editar el archivo sin pedir confirmación para la escritura.

Ahora pide ejecutar un comando no pre-aprobado:

Ejecuta ls -la

Claude debería pedir confirmación para el comando Bash.

Paso 4.3: Cambiar modo en sesión

Inicia una sesión normal y cambia el modo con Shift+Tab. Observa cómo cambia el comportamiento sin reiniciar.

Verificación: Entiendes cómo cada modo afecta el comportamiento de los diálogos.


Ejercicio 5: Configuración para tu workflow real

Paso 5.1: Identificar tu stack

Piensa en un proyecto real tuyo. Identifica:

  • ¿Qué lenguaje/framework usas?
  • ¿Qué comandos ejecutas frecuentemente? (build, test, lint, format)
  • ¿Qué archivos contienen secretos?
  • ¿Qué acciones destructivas quieres bloquear?

Paso 5.2: Crear configuración personalizada

Basándote en las recetas del punto 3.3, crea un .claude/settings.json para tu proyecto real. Ejemplo para un proyecto Node.js:

{
  "permissions": {
    "allow": [
      "Read(./src/**/*)",
      "Read(./tests/**/*)",
      "Read(./package.json)",
      "Read(./tsconfig.json)",
      "Write(./src/**/*)",
      "Write(./tests/**/*)",
      "Bash(npm run *)",
      "Bash(npm test*)",
      "Bash(npx prettier *)",
      "Bash(npx eslint *)",
      "Bash(git {status,diff,log,add,commit,push,pull})"
    ],
    "deny": [
      "Read(./.env*)",
      "Read(**/*secret*)",
      "Read(**/*.pem)",
      "Write(./package*.json)",
      "Write(./*.lock)",
      "Bash(npm install*)",
      "Bash(npm publish*)",
      "Bash(rm -rf*)",
      "Bash(sudo *)"
    ]
  }
}

Paso 5.3: Probar la configuración

Inicia Claude en tu proyecto real y trabaja normalmente. Observa:

  • ¿Aparecen demasiados Trust Dialogs? → Añade más reglas a allow
  • ¿Claude hace algo que no debería? → Añade reglas a deny
  • ¿El flujo es cómodo? → Tu configuración está bien calibrada

Verificación: Tienes una configuración de permisos funcional para tu proyecto real.


Ejercicio 6: Hooks como red de seguridad

Paso 6.1: Añadir hook protector

Añade al settings.json del proyecto de prueba un hook que bloquee comandos destructivos incluso si alguien usa --dangerously-skip-permissions:

{
  "permissions": {
    "allow": ["..."],
    "deny": ["..."]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(rm *)",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'BLOQUEADO: intento de rm' && exit 1"
          }
        ]
      }
    ]
  }
}

Paso 6.2: Probar el hook

Inicia Claude con permisos saltados:

cd ~/practica-permisos
claude --dangerously-skip-permissions -p "Ejecuta rm -rf ./src"

El hook debería bloquear la ejecución a pesar de --dangerously-skip-permissions.

Verificación: Los hooks funcionan como última línea de defensa.


Checklist de validación

Marca cada punto cuando lo hayas completado:

  • He creado un proyecto con configuración de permisos restrictiva
  • He verificado que Claude no puede leer archivos en deny
  • He verificado que Claude puede leer archivos en allow sin diálogo
  • He probado que comandos no listados generan un Trust Dialog
  • He usado el modo plan y confirmado que es solo lectura
  • He usado acceptEdits y confirmado que edita sin preguntar
  • He creado una configuración de permisos para mi proyecto real
  • He configurado un hook como red de seguridad

Troubleshooting común

Claude ignora mis reglas de deny

  • Verifica que el path es correcto (relativo con ./)
  • Revisa que no haya una regla en allow más específica que la contraiga
  • Recuerda: deny tiene prioridad sobre allow

Demasiados Trust Dialogs

Cada vez que Claude necesita un permiso no pre-aprobado, pregunta. Soluciones:

  • Añade los comandos frecuentes a allow
  • Usa --permission-mode acceptEdits si confías en las ediciones
  • Usa patrones con * para cubrir variantes de un comando

El hook no se ejecuta

  • Verifica que el matcher coincide con el patrón exacto
  • Recuerda que el matcher usa el formato Tool(patrón)
  • Comprueba que el comando del hook tiene permisos de ejecución

"Permission denied" en modo no interactivo

Con -p, los Trust Dialogs no aparecen. Si una acción no está en allow, Claude no puede ejecutarla. Pre-aprueba lo necesario o usa --dangerously-skip-permissions en entornos seguros.


Reto adicional (opcional)

Crea un "perfil de seguridad" multinivel para tu equipo:

  1. Perfil junior — Solo lectura y tests:
{
  "permissions": {
    "allow": ["Read(**/*)", "Bash(npm test*)"],
    "deny": ["Read(./.env*)", "Write(**/*)", "Bash(git push*)"]
  }
}
  1. Perfil senior — Lectura, escritura en src, git completo:
{
  "permissions": {
    "allow": [
      "Read(**/*)",
      "Write(./src/**/*)",
      "Bash(npm *)",
      "Bash(git *)"
    ],
    "deny": ["Read(./.env*)", "Bash(rm -rf*)", "Bash(sudo *)"]
  }
}
  1. Perfil CI — Todo permitido en entorno aislado:
claude --dangerously-skip-permissions -p "Ejecuta build y tests"

Guarda cada perfil como archivo separado y cárgalos según el contexto.


Siguiente módulo: Comandos slash y personalización