Hooks — Automatiser et sécuriser les sessions Claude Code
Ce que tu vas apprendre
- Bloquer une commande dangereuse avant qu’elle ne s’exécute
- Scanner les fichiers écrits pour détecter des secrets
- Formater le code automatiquement après chaque modification
- Logger les commandes bash pour audit
Prérequis
- Claude Code v2.1+
- Bash et Python 3 installés
- Un projet avec un dossier
.claude/
Le concept en 30 secondes
Un hook est un script qui s’exécute automatiquement quand un événement se produit dans Claude Code. Il reçoit des données JSON sur stdin et retourne une décision (autoriser, bloquer, avertir).
@diagram:flow
JSON sur stdin :: Claude transmet l'événement et son contexte au script.
Le hook s'exécute :: Le script analyse les données reçues.
Décision :: Autoriser, bloquer ou avertir.
Les événements les plus utiles, dans l’ordre d’une session :
@diagram:flow
UserPromptSubmit :: Quand tu envoies un message.
PreToolUse :: Avant chaque action d'un outil.
PostToolUse :: Après l'action de l'outil.
Stop :: Quand Claude a fini de répondre.
Étapes
1. Créer la structure des hooks
mkdir -p .claude/hooks
2. Bloquer les commandes dangereuses (PreToolUse)
Crée .claude/hooks/pre-tool-check.sh :
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | sed -n 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
# Bloque les commandes destructrices
BLOCKED_PATTERNS=(
"rm -rf /([[:space:]]|$)"
"rm -rf \*"
"dd if=/dev/zero"
"mkfs\."
)
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qE "$pattern"; then
echo "Blocked: commande destructrice détectée" >&2
exit 2
fi
done
exit 0
Rends-le exécutable :
chmod +x .claude/hooks/pre-tool-check.sh
Configure dans .claude/settings.json :
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-check.sh",
"timeout": 10
}
]
}
]
}
}
Teste en demandant à Claude :
Exécute rm -rf /
Le hook doit bloquer la commande avec un message d’erreur.
3. Scanner les secrets après écriture (PostToolUse)
Crée .claude/hooks/security-scan.sh :
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
exit 0
fi
ISSUES=""
if grep -qiE '"password"[[:space:]]*:[[:space:]]*"[^"]+"' "$FILE_PATH" 2>/dev/null; then
ISSUES="${ISSUES}- Mot de passe en dur détecté\n"
fi
if grep -qE "AKIA[0-9A-Z]{16}" "$FILE_PATH" 2>/dev/null; then
ISSUES="${ISSUES}- Clé AWS détectée\n"
fi
if [ -n "$ISSUES" ]; then
printf '{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Scan sécurité :\n%s"}}' "$ISSUES"
fi
exit 0
Ajoute dans .claude/settings.json :
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.sh",
"timeout": 10
}
]
}
]
}
}
4. Formater le code automatiquement
Crée .claude/hooks/format-code.sh :
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
exit 0
fi
case "$FILE_PATH" in
*.js|*.jsx|*.ts|*.tsx)
command -v prettier &>/dev/null && prettier --write "$FILE_PATH" 2>/dev/null
;;
*.py)
command -v black &>/dev/null && black "$FILE_PATH" 2>/dev/null
;;
*.go)
command -v gofmt &>/dev/null && gofmt -w "$FILE_PATH" 2>/dev/null
;;
esac
exit 0
Ajoute-le aussi dans PostToolUse avec Write|Edit comme matcher.
5. Logger les commandes bash
Crée .claude/hooks/log-bash.sh :
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | sed -n 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
if [ -n "$COMMAND" ]; then
LOGFILE="$HOME/.claude/bash-commands.log"
mkdir -p "$(dirname "$LOGFILE")"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $COMMAND" >> "$LOGFILE"
fi
exit 0
Configure dans PostToolUse avec matcher Bash.
6. Valider les prompts utilisateur
Crée .claude/hooks/validate-prompt.sh :
#!/bin/bash
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | sed -n 's/.*"user_prompt"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
if echo "$PROMPT" | grep -qiE "(rm -rf /|drop database|delete database)"; then
printf '{"decision": "block", "reason": "Opération dangereuse détectée"}'
exit 0
fi
exit 0
Configure dans UserPromptSubmit :
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.sh"
}
]
}
]
}
}
Vérification
- Dossier
.claude/hooks/créé - Scripts rendus exécutables (
chmod +x) -
.claude/settings.jsonconfiguré avec les événements - Test
PreToolUse: commande bloquée avec exit 2 - Test
PostToolUse: warning affiché après écriture - Test
UserPromptSubmit: prompt bloqué si dangereux
Pièges courants
- Script non exécutable →
chmod +x .claude/hooks/*.sh - Pas de lecture stdin → le hook doit lire
cat, pas$1 - Message de blocage sur stdout → les raisons de blocage doivent aller sur stderr (
>&2) - Timeout trop court → un scan semgrep peut prendre 30s. Ajuste
timeout. - JSON mal formé en sortie → utilise
printfavec échappement des guillemets.
Récapitulatif
| Événement | Quand ça se déclenche | Peut bloquer ? |
|---|---|---|
PreToolUse |
Avant chaque outil | Oui |
PostToolUse |
Après succès d’un outil | Non (warning) |
UserPromptSubmit |
Quand tu envoies un message | Oui |
Stop |
Quand Claude a fini | Oui |
SessionStart |
Au démarrage de la session | Non |
SessionEnd |
À la fermeture | Non |
| Code de sortie | Signification |
|---|---|
| 0 | Succès, continue |
| 2 | Erreur bloquante, arrête |
| Autre | Erreur non bloquante |
Pour aller plus loin
Inspiré du guide claude-howto de luongnv89 (MIT)