Aller au contenu principal
Avancé9 min

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.json configuré 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écutablechmod +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 printf avec é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)