Файл дня

Хук для Claude Code, который не даст агенту стереть базу данных

Слава Сажин 23.09.2026 2 мин чтения 6 просмотров

PreToolUse-хук, который останавливает Claude Code перед DROP TABLE, TRUNCATE и DELETE без WHERE. Один скрипт, ставится за минуту.

Начинаем новую рубрику, «Файл дня»: один файл из нашей собственной настройки, который можно в тот же день поставить себе. Хук, промпт, блок для CLAUDE.md. Несколько абзацев о том, что он делает, а ниже сам файл целиком: читать и скачивать бесплатно.

Что делает

Claude Code запускает этот скрипт перед каждой командой в терминале. Если команда уничтожает данные в базе, хук её не пропускает и объясняет Claude причину, и тот ищет другой путь. Хук останавливает:

  • DROP TABLE, TRUNCATE TABLE и DROP DATABASE
  • ALTER TABLE, который удаляет, переименовывает или меняет колонку (ADD COLUMN проходит)
  • DELETE FROM без WHERE

Почему хук, а не строчка в CLAUDE.md

Правило в CLAUDE.md остаётся просьбой: модель его читает и обычно выполняет. Хук срабатывает до команды, что бы модель ни решила, и это ровно то, что нужно, когда Claude работает без подтверждений и рядом никого нет.

Если таблицу правда нужно удалить

Поставьте перед командой CLAUDE_USER_APPROVED=1, и она пройдёт. Чтобы Claude ставил этот префикс только после вашего «да» в разговоре, добавьте одну строку в ~/.claude/CLAUDE.md, точная формулировка есть в инструкции ниже. Префикс не замок, а лежачий полицейский: удалить таблицу Claude всё ещё может, но только намеренно и на виду, в логе разговора. DELETE FROM без WHERE не проходит никогда, даже с префиксом.

Как поставить

Нужны jq и perl (на macOS: brew install jq). Скрипт кладётся в ~/.claude/hooks/, делается исполняемым и прописывается в разделе PreToolUse файла ~/.claude/settings.json. Все шаги и проверка одной командой есть в инструкции ниже. Можно проще: отдайте оба файла Claude и попросите поставить.

Файлы

hook-bez-udaleniya-dannyh.sh 4 КБ Скачать
#!/bin/bash
# PreToolUse hook: не даёт Claude выполнить в bash команду, которая уничтожает данные в базе.
# Отбивает DROP TABLE, TRUNCATE TABLE, DROP DATABASE, разрушающий ALTER TABLE и DELETE FROM без WHERE.
# Пропуск с префиксом CLAUDE_USER_APPROVED=1 в самой команде; DELETE FROM без WHERE не проходит никогда.

# Без jq команду не прочитать. Защитный хук тогда не пропускает ничего молча:
# exit 2 отбивает вызов, и Claude видит причину в stderr.
if ! command -v jq >/dev/null 2>&1; then
    echo "BLOCKED: хуку против удаления данных нужен jq (brew install jq)" >&2
    exit 2
fi

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Отказ с причиной: модель получает permissionDecisionReason, понимает, почему команда
# не прошла, и перестраивается сама.
hook_deny() {
    jq -cn --arg r "${1}" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}'
    exit 0
}

if [ -z "$COMMAND" ]; then
    exit 0
fi

# bash убирает пару «обратная косая + перевод строки» при чтении, а grep сверяет одну
# физическую строку. Поэтому сверка идёт по двум видам сразу: склеенному и сырому.
SKLEENO=$(printf '%s' "$COMMAND" | perl -0pe 's/\\\n//g')
[ -z "$SKLEENO" ] && SKLEENO=$COMMAND
COMMAND="$SKLEENO
$COMMAND"

approved() {
    echo "$COMMAND" | grep -q 'CLAUDE_USER_APPROVED=1'
}

# DROP TABLE / TRUNCATE TABLE
if echo "$COMMAND" | grep -qiE 'DROP\s+TABLE|TRUNCATE\s+TABLE'; then
    approved || hook_deny "BLOCKED: DROP TABLE/TRUNCATE запрещены без CLAUDE_USER_APPROVED=1 (потеря данных)"
fi

# DROP DATABASE
if echo "$COMMAND" | grep -qiE 'DROP\s+DATABASE'; then
    approved || hook_deny "BLOCKED: DROP DATABASE запрещён без CLAUDE_USER_APPROVED=1 (удаление базы целиком)"
fi

# Разрушающий ALTER TABLE: DROP/RENAME/ALTER COLUMN. ADD COLUMN и ADD CONSTRAINT проходят свободно.
if echo "$COMMAND" | grep -qiE 'ALTER\s+TABLE'; then
    if echo "$COMMAND" | grep -qiE '\b(DROP\s+COLUMN|DROP\s+CONSTRAINT|RENAME\s+(TO|COLUMN)|ALTER\s+COLUMN)\b'; then
        approved || hook_deny "BLOCKED: разрушающие ALTER TABLE (DROP/RENAME/ALTER COLUMN) запрещены без CLAUDE_USER_APPROVED=1. ADD COLUMN - разрешено свободно."
    fi
fi

# DELETE FROM без WHERE. Через perl, потому что grep на macOS не умеет заглядывать вперёд.
if echo "$COMMAND" | perl -ne 'BEGIN{$f=0} $f=1 if /DELETE\s+FROM(?!\s+.*WHERE)/i; END{exit !$f}'; then
    hook_deny "BLOCKED: DELETE FROM без WHERE запрещён"
fi

exit 0
kak-postavit.md 2 КБ Скачать
# Как поставить хук против удаления данных

Хук срабатывает перед каждой командой, которую Claude запускает в терминале, и не пускает команды, уничтожающие данные в базе: DROP TABLE, TRUNCATE TABLE, DROP DATABASE, ALTER TABLE с DROP/RENAME/ALTER COLUMN и DELETE FROM без WHERE.

Что нужно: `jq` и `perl`. На macOS perl уже есть, jq ставится через `brew install jq`. Без jq хук отбивает любую команду в терминале.

Куда положить:
```
mkdir -p ~/.claude/hooks
cp hook-bez-udaleniya-dannyh.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/hook-bez-udaleniya-dannyh.sh
```

Что вписать в `~/.claude/settings.json` (если раздел `hooks` уже есть, добавьте блок внутрь `PreToolUse`):

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "$HOME/.claude/hooks/hook-bez-udaleniya-dannyh.sh" }
        ]
      }
    ]
  }
}
```

Что вписать своё: строку в `~/.claude/CLAUDE.md` - «Префикс CLAUDE_USER_APPROVED=1 ставится только после моего прямого разрешения в этом разговоре».

Как убедиться, что заработало:
```
echo '{"tool_input":{"command":"psql -c \"DROP TABLE users\""}}' | ~/.claude/hooks/hook-bez-udaleniya-dannyh.sh
```

В ответ придёт строка с `"permissionDecision":"deny"`. Потом в новой сессии Claude попросите выполнить `echo "DROP TABLE test"`: команда не пойдёт, Claude покажет причину отказа. Список подключённых хуков виден командой `/hooks`.

Статья стоила вашего времени?

Все мастер-классы по одной подписке

Записи мастер-классов и файлы к ним, новые выходят каждую неделю. €10 в месяц.

Все мастер-классы

Связаться с командой Combobulating