Hook de seguridad para bloquear comandos peligrosos en Claude Code
Hola, bienvenido. En esta lección vamos a crear nuestro primer hook: guard.sh, un script que se ejecuta en PreToolUse, antes de cada lectura, edición o comando de Bash, y bloquea lo que no debe pasar nunca en el proyecto. En la anterior vimos cómo se escribe un hook en settings.json, qué recibe, qué devuelve y cómo se comprueba con /hooks. Vamos a escribirlo, a probarlo a mano, a registrarlo y a probarlo dentro de Claude Code, y después veremos 2 cosas que tienes que tener claras antes de fiarte de él: qué pasa si el propio hook falla y cómo convive con auto mode.
Ya tenemos reglas deny y el sandbox, pero cada una de esas capas tiene un hueco que ya conoces. En la lección de permisos vimos que una regla de Bash se compara con el texto del comando, y que Bash(./vendor/bin/sail artisan migrate:fresh *) para ese comando, pero no ./vendor/bin/sail php artisan migrate:fresh, que hace exactamente lo mismo. Y al configurar el sandbox dejamos Sail fuera, así que un comando que se ejecuta dentro del contenedor, donde está montado el proyecto entero con su .env, solo lo frenan los permisos. Un hook PreToolUse cubre justo eso: ve el comando completo antes de que se ejecute y le aplica tu propia lógica, escribas el comando como lo escribas.
Es el tercero de los límites que te propuse en la lección de modos de permiso para trabajar con auto mode en el día a día, junto a las reglas deny y el sandbox. Eso sí, guard.sh impide acciones antes de que ocurran, pero no revisa el código que escribe Claude. Una inyección SQL o una ruta sin autorización viven en ese código, y revisarlo es otra capa, con su propia lección más adelante.
Para seguir esta lección necesitas el .claude/settings.json de la lección del sandbox, jq instalado como vimos en la lección anterior y Claude Code actualizado.
Qué va a bloquear guard.sh
Un hook de seguridad tiene que bloquear poco y claro. Si bloquea demasiado, acabas quitándolo el día que te estorba, y si su lista crece sin criterio, nadie sabe ya qué protege. guard.sh va a cubrir 4 cosas, y cada una tiene su motivo:
- Los archivos
.env, que tienen las credenciales de la base de datos, del correo y de cualquier API. Para las herramientas de archivos ya tenemos las reglasRead(./.env)yRead(./.env.*), y el hook las repite como segunda capa. Lo que aporta de verdad es Bash: va a bloquear cualquier comando que nombre un archivo.env, también los que se ejecutan dentro del contenedor de Sail, donde el sandbox no llega. - Los archivos de lock,
composer.lockypackage-lock.json. Solo los deben tocar Composer y npm, y una edición a mano deja el lock desincronizado de lo que hay instalado. Leerlos sí está permitido, porque Claude los consulta para saber qué versiones tienes. - Los comandos de Git que descartan trabajo:
git reset --hard,git push --forceygit clean -f.reset --hardyclean -fpierden cambios locales sin vuelta atrás, y el force push reescribe el historial del remoto. - Los comandos de Artisan que vacían la base de datos:
migrate:fresh,migrate:refresh,migrate:resetydb:wipe. Son los 3 que ya bloquean las reglasdeny, másmigrate:refresh, que revierte todas las migraciones y las vuelve a lanzar, y el hook los para aunque vayan conphpdelante o dentro de un comando compuesto.
Fíjate en que ninguna de estas cosas es algo que Claude necesite hacer para trabajar en el proyecto. Si alguna vez te hace falta vaciar la base de datos o descartar cambios, lo haces tú desde tu terminal, que es el mismo criterio que usamos con las reglas deny.
- 02El script guard.sh
- 03Probarlo a mano
- 04Registrarlo en settings.json
- 05Probarlo dentro de Claude Code
- 06Qué pasa si el hook falla
- 07Cómo convive con auto mode