Detección de secrets
Ahora el primer muro de verdad: un hook que impide que Claude escriba una API key o un token en un fichero. A diferencia del formateo, este corre antes de la acción y la cancela si detecta algo. Es el patrón PreToolUse + exit 2.
El script
Los hooks con lógica van mejor en un fichero. Guarda esto en .claude/hooks/block-secrets.sh:
#!/bin/bash
INPUT=$(cat)
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // empty')
# Patrones de lo que parece un secreto.
if echo "$CONTENT" | grep -Eq \
'sk-[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z]+ PRIVATE KEY-----|ghp_[A-Za-z0-9]{36}'; then
echo "Bloqueado: el contenido parece contener un secreto (API key / token / clave privada). Usa variables de entorno." >&2
exit 2
fi
exit 0
Hazlo ejecutable:
chmod +x .claude/hooks/block-secrets.sh
El registro en settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-secrets.sh" }
]
}
]
}
}
Cómo funciona, paso a paso
- Claude intenta un
WriteoEdit. - Como es
PreToolUse, el hook corre antes de escribir nada. - El script lee el JSON de stdin y saca el contenido:
tool_input.content(enWrite) otool_input.new_string(enEdit). - Un
grep -Ebusca patrones típicos: claves OpenAI (sk-…), AWS (AKIA…), tokens de GitHub (ghp_…), bloques de clave privada PEM. - Si casa, escribe el motivo en
stderry haceexit 2: la escritura se cancela y Claude recibe el mensaje, así que sabe por qué y puede corregir (p.ej. usar una env var).
El
// .tool_input.new_string // emptydejqes un detalle que importa: cubre tantoWrite(campocontent) comoEdit(camponew_string), y si no hay nada devuelve vacío en vez de romper.
Ajusta los patrones a tu realidad
Esos regex son un punto de partida. Añade los formatos de los proveedores que uses (Stripe sk_live_…, Slack xox…, etc.). Y recuerda la lección 1: esto es un invariante determinista (¿el texto casa este patrón?), justo lo que un hook hace bien — no le pidas que juzgue si "tiene pinta de secreto" con criterio, dale patrones.
Falsos positivos: un
sk-…de ejemplo en un test o fixture también se bloquearía. Crea una excepción leyendo la ruta del fichero (jq -r '.tool_input.file_path') y dejando pasar las que casen*.test.*ofixtures/, o afina el patrón para no atrapar valores de muestra.
Defensa en profundidad
Esto se combina con lo del curso de permisos: un deny de Read(./.env) impide que lea secretos; este hook impide que los escriba en el código. Capas distintas, mismo objetivo.
Pruébalo
Con el hook registrado, pídele a Claude que escriba una clave de ejemplo en un fichero:
"Crea
config.jscon la líneaconst KEY = "sk-abc123def456ghi789jkl012";"
El grep casa el patrón sk-…, así que el script hace exit 2: la escritura se cancela y Claude recibe el mensaje del stderr ("Bloqueado: el contenido parece contener un secreto…"). Comprueba que el fichero no se creó y que Claude reacciona al motivo (típicamente, proponiendo una variable de entorno). Ese bloqueo observable es la verificación de que el muro funciona.
Qué llevas hasta aquí
- Un hook
PreToolUsecon matcherEdit|Writeintercepta antes de escribir. - El script saca el contenido con
jq(contentonew_string), busca patrones de secreto y haceexit 2para bloquear. - Es un invariante por patrón; ajústalo a los proveedores que uses.
En la siguiente lección, el guard clásico: bloquear git push --force, rm -rf y compañía.
Fuente oficial: Hooks reference
¿Quieres llevar la cuenta de las lecciones que has terminado?
Entrar para guardar progreso