Dev.to AI 🤖 Ai 👁 0 📖 3 min read

Hooks en Claude Code: bloquea comandos peligrosos con un script de 20 líneas

Si usas Claude Code a diario, seguramente tienes reglas en tu CLAUDE.md del tipo "nunca leas el .env" o "no hagas git push --force". El problema es que una instrucción en texto es una sugerencia: el modelo la sigue casi

Si usas Claude Code a diario, seguramente tienes reglas en tu CLAUDE.md del tipo "nunca leas el .env" o "no hagas git push --force". El problema es que una instrucción en texto es una sugerencia: el modelo la sigue casi siempre, pero "casi" no basta cuando hablamos de secretos o de borrar carpetas.

Para eso existen los hooks: comandos de shell que Claude Code ejecuta automáticamente en momentos concretos. A diferencia de un prompt, un hook siempre se ejecuta.

Los cuatro eventos que más vas a usar

  • PreToolUse: antes de que Claude use una herramienta (Bash, Read, Edit...). Ideal para validar o bloquear.
  • PostToolUse: después de que la herramienta termina. Ideal para formatear o pasar el linter.
  • Notification: cuando Claude necesita tu atención.
  • Stop: cuando Claude termina de responder.

El hook recibe por entrada estándar un JSON con, entre otros campos, tool_name y tool_input. Y la regla clave: si el script sale con código 2, la acción se bloquea y lo que escribas en stderr le llega a Claude como explicación, para que busque otra alternativa.

El script

Guárdalo como .claude/hooks/proteger.py en tu proyecto:

#!/usr/bin/env python3
"""Bloquea comandos destructivos y el acceso a archivos de secretos."""
import json, re, sys

datos = json.load(sys.stdin)
herramienta = datos.get("tool_name", "")
entrada = datos.get("tool_input", {})

PELIGROSOS = [r"\brm\s+-[a-zA-Z]*r[a-zA-Z]*f", r"\bgit\s+push\s+.*--force",
              r"\bmkfs\b", r"\bdd\s+if=", r">\s*/dev/sd"]
if herramienta == "Bash":
    comando = entrada.get("command", "")
    for patron in PELIGROSOS:
        if re.search(patron, comando):
            print(f"Bloqueado por política: '{comando}'. Propón una alternativa "
                  "segura o pide al usuario que lo ejecute manualmente.", file=sys.stderr)
            sys.exit(2)

if herramienta in ("Read", "Edit", "Write"):
    ruta = entrada.get("file_path", "")
    if re.search(r"(^|/)\.env(\.|$)|/secrets/|\.pem$", ruta):
        print(f"Acceso denegado a archivo sensible: {ruta}", file=sys.stderr)
        sys.exit(2)

sys.exit(0)

Registrarlo

En .claude/settings.json (o de forma interactiva con /hooks):

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Read|Edit|Write",
        "hooks": [
          { "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/proteger.py" }
        ]
      }
    ]
  }
}

matcher filtra por nombre de herramienta y $CLAUDE_PROJECT_DIR apunta a la raíz del proyecto, así que funciona aunque Claude cambie de carpeta.

Pruébalo sin abrir Claude

Como el hook solo lee JSON por stdin, puedes probarlo directamente en la terminal:

echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf build"}}' \
  | python3 .claude/hooks/proteger.py; echo "salida=$?"
# Bloqueado por política: 'rm -rf build'. ...
# salida=2

Haz lo mismo con {"tool_name":"Read","tool_input":{"file_path":"/app/.env"}} y con un comando inocente como ls -la (este debe devolver 0).

Tres consejos

  1. No es un sandbox. Un patrón puede esquivarse con un comando creativo. Combínalo con reglas deny en los permisos y, si quieres autonomía total, trabaja dentro de un contenedor.
  2. Mantén los hooks rápidos. Se ejecutan en cada llamada a la herramienta.
  3. Revisa los hooks de repos ajenos. Viajan con el proyecto y se ejecutan con tus permisos.

Si una regla de tu CLAUDE.md es crítica, conviértela en hook. Es la diferencia entre "por favor, no lo hagas" y "no puedes hacerlo".

¿Te sirvió? Este ejemplo es parte del Guía Claude Code de Cero a Experto (PDF en español): paga lo que quieras, desde 1 USD. Más recursos gratis en GitHub.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.