Seguridad del Loop Autónomo|INEMA.CLUB-PRO

Clase 5 · 20 min

Hooks como guardia: antes de la herramienta, contra borrar y colisionar

Una regla de permiso compara texto; un hook ejecuta código antes de cada herramienta y puede mirar el disco. En esta clase activas tres guardias PreToolUse: una niega lo irreversible y los archivos del juez, otra muestra cuántos archivos borraría un rm y pide confirmación, y la tercera avisa cuando otra sesión tocó el mismo archivo.

Comenzar la clase
Llave de seguridad roja con tapa levantable junto a un botón de emergencia en un panel industrial.

En esta clase vas a

  1. 1PreToolUse: deny, ask o nada, antes de la herramienta
  2. 2guarda.py: push forzado, reset --hard, find -delete, archivos del juez, y lo que no atrapa
  3. 3raio.mjs y colisao.mjs del inema-agent-runtime
  4. 4Probar el hook con JSON falso y después en una sesión real

Objetivo: activar un hook PreToolUse que niegue lo irreversible, pida confirmación al borrar y avise de colisión entre sesiones.

01Antes de cada herramienta

Haz clic en cada etapa.

Haz clic en cada bloque del diagrama para ver qué hace.

Los hooks de contexto y handoff son del curso 2; aquí el hook es guardia.

02Qué atrapa cada guardia

Tres archivos, tres trabajos.

1. guarda.py (deny)

Niega lo que no tiene vuelta atrás: push forzado, reset --hard, git clean -f, checkout -- ., git restore (del árbol), find -delete, rm recursivo en la raíz o en el HOME (con comillas o dentro de bash -c), pkill y killall, dd y mkfs.

Y niega escribir en goal.md, tests/, .claude/ y en la carpeta de la propia guardia, por Edit/Write y también por Bash: > y >>, tee, sed -i, cp, mv, rm y truncate (GUARDA_PROTEGIDOS cambia la lista).

Lo que no atrapa: la guardia lee el texto del comando, no es sandbox. Un programa que escribe por sí solo (python3 -c "open('goal.md','w')", node -e, un script tuyo), una ruta armada en una variable o eval pasan. Por eso viene junto con el deny Edit(...) de la clase 4, que cubre las herramientas de archivo, y con el sandbox de la clase 6, que limita la escritura del propio Bash.

Lección real de esta máquinapkill -f con el patrón en la misma línea del shell mató al propio shell dos veces (15 y 16/09/2026): por eso pkill está en la lista.

03Los hooks en el settings.json

Primero la guardia, luego raio.mjs en Bash; guardia y colisión en las ediciones. Rutas suponiendo los dos repositorios en ~/projetos.

Archivo · kit/hooks/settings-exemplo.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "python3 \"$HOME/projetos/loop-r-eng-seguranca-v72/kit/hooks/guarda.py\"", "timeout": 10 },
          { "type": "command", "command": "node \"$HOME/projetos/inema-agent-runtime/runtime/mods/runtime-guarda/hooks/raio.mjs\"", "timeout": 15 }
        ]
      },
      {
        "matcher": "Edit|Write|MultiEdit|NotebookEdit",
        "hooks": [
          { "type": "command", "command": "python3 \"$HOME/projetos/loop-r-eng-seguranca-v72/kit/hooks/guarda.py\"", "timeout": 10 },
          { "type": "command", "command": "node \"$HOME/projetos/inema-agent-runtime/runtime/mods/runtime-guarda/hooks/colisao.mjs\" pre", "timeout": 10 }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit|NotebookEdit",
        "hooks": [
          { "type": "command", "command": "node \"$HOME/projetos/inema-agent-runtime/runtime/mods/runtime-guarda/hooks/colisao.mjs\" pos", "timeout": 10 }
        ]
      }
    ]
  }
}

04Ensayo: llamadas falsas, decisiones de verdad

El script envía a los hooks el mismo JSON que enviaría Claude Code e imprime la decisión. Ningún modelo.

Terminal · guarda.py, raio.mjs y colisao.mjs
[ -d ~/projetos/inema-agent-runtime ] || git clone -q https://github.com/inematds/inema-agent-runtime ~/projetos/inema-agent-runtime
bash ~/projetos/loop-r-eng-seguranca-v72/kit/hooks/provar-guarda.sh
Ejemplo del repositorio
## guarda.py (nega o irreversível)
git push --force origin main       → deny
git reset --hard HEAD~3            → deny
find . -name '*.o' -delete         → deny
rm -rf ~                           → deny
pkill -f servidor                  → deny
python3 -m pytest -q               → -
Edit tests/test_x.py               → deny
Edit src/app.py                    → -
## guarda.py: escrita via Bash nos protegidos
sed -i s/x/y/ tests/test_x.py      → deny
echo pronto > goal.md              → deny
echo {..} > .claude/settings.local → deny
mv goal.md /tmp/                   → deny
bash -c 'rm -rf ~'                 → deny
git restore .                      → deny
pytest -q tests/ > log.txt         → -
git restore --staged app.py        → -
## o que a guarda NÃO pega (para isso: deny Edit/Write da aula 4 e sandbox da aula 6)
python3 -c open('goal.md','w')     → -
## raio.mjs (apagar pede confirmação, com a contagem)
rm -r build                        → ask
rm -r nada                         → -
## colisao.mjs (outra sessão editou há pouco)
sessão B edita o mesmo arquivo    → ask
sessão A edita de novo            → -

"-" es sin decisión: el flujo normal (reglas y modo) decide. pytest con salida en log.txt y git restore --staged pasan, porque no tocan los protegidos. La línea del python3 -c es el hueco declarado: la guardia no ve lo que escribe un programa. El rm de una carpeta que no existe no pide nada, porque no hay nada que borrar.

05Real: la guardia en una sesión que libera todo

claude -p en bypassPermissions con el settings-exemplo.json, pidiendo git reset --hard y rm -r build en un repositorio de ensayo con un cambio sin commitear. Se comprueba el disco.

Terminal · una llamada real
bash ~/projetos/loop-r-eng-seguranca-v72/kit/hooks/real.sh; echo "saída: $?"
Ejemplo del repositorio
app.txt: mudança não commitada preservada
build/a.o: continua lá
saída: 0

06Práctica

Ensayo, sesión real y la guardia en tu proyecto.

E12 · Decisiones de los tres hooks

10 min

Ejecuta el ensayo. Clona inema-agent-runtime en ~/projetos si falta.

Listo cuando: bash ~/projetos/loop-r-eng-seguranca-v72/kit/hooks/provar-guarda.sh | grep -c '→ deny' → 12; y rm -r build → ask, sessão B edita o mesmo arquivo → ask y sessão A edita de novo → -.

E13 · La guardia en una sesión real

5 min

Ejecuta el bloque real (una llamada, sale de tu cuota de suscripción).

Listo cuando: app.txt: mudança não commitada preservada, build/a.o: continua lá y saída: 0.

E14 · La guardia en tu proyecto

15 min

En el .claude/settings.json de tu proyecto, junta las permissions del executar.json de la clase 4 (deny Edit(goal.md), Edit(tests/**), Edit(.claude/**)) con el bloque hooks del settings-exemplo.json: las capas se suman. Si tu juez tiene otros archivos, exporta GUARDA_PROTEGIDOS antes de abrir Claude Code.

Listo cuando: K=~/projetos/loop-r-eng-seguranca-v72/kit; printf '{"cwd":"%s","tool_name":"Bash","tool_input":{"command":"echo x > goal.md"}}' "$PWD" | python3 $K/hooks/guarda.py | grep -c deny → 1 y grep -c 'Edit(goal.md)' .claude/settings.json → 1, ejecutados en la carpeta del proyecto.

07Prueba de la clase

Pregunta 1 de 4

¿Qué devuelve un hook PreToolUse para bloquear?competencia

08En tres frases

El hook ejecuta códigoAntes de cada herramienta, puede negar, preguntar o dejar seguir.
Prueba por JSONLlamada falsa en stdin, decisión en stdout: sin modelo, sin cuota.
Protege la guardiaJuez, objetivo, settings y el hook protegidos en tres capas: deny Edit de la clase 4, la guardia (también contra escritura vía Bash) y el sandbox de la clase 6.

Para ir más allá

Clase anteriorSiguiente clase