Перейти к содержимому
Экосистема AI Vibe

Разборы

Guard-hook в Claude Code: три режима — пропустить, залогировать или заблокировать

Редакция 25 октября 2026 г.

Guard-hook в Claude Code: три режима — пропустить, залогировать или заблокировать

У PreToolUse-guard на Bash три исхода: явный deny/ask в JSON, тихий пропуск при таймауте или падении скрипта, жёсткая блокировка с "onFailure": "block". Пока настроен только первый путь, сбой охранника не останавливает агентный цикл; в лаборатории Gabriel Anhaia это ломают дважды и закрывают одной строкой в settings.

Когда охранник молчит, агент не останавливается

Широкий allow на Bash плюс узкий перехват на событии PreToolUse с типом command и matcher Bash — рабочая схема из permissions у Gabriel Anhaia. Пока guard отвечает валидным JSON с deny или ask, он перекрывает allow rules.

Другой класс сбоев. Справка Anthropic по hooks: если command- или http-hook на PreToolUse не уложился в timeout, не стартовал, вернул exit не 0 и не 2, отдал HTTP не 2xx или невалидный JSON, вызов инструмента обычно не блокируется. Plain-text на stdout при падении command-hook не считается. Поле onFailure по умолчанию "continue"; fail closed даёт только "block" (с Claude Code v2.1.295+, в Dev.to-заметке указан релиз 8 октября 2026).

Итог для агентского цикла: ошибка guard видна в транскрипте, опасная команда может выполниться. Модель не получает сигнал «стоп», permission flow идёт дальше.

Таймаут, «левый» exit и режим "onFailure": "block"

Минимальная лаборатория: bare remote, git push на main при allow Bash(git push *), guard на Python читает JSON со stdin и для Bash вытаскивает tool_input.command.

BREAK 1: time.sleep(15) при timeout: 5 в settings. Hook отменяется по таймауту; push на main проходит.

BREAK 2: sys.exit(3) после stderr. Код не 0 и не 2 → non-blocking error; push снова проходит. Тот же сценарий связывают с traceback и exit 1 без валидного JSON на stdout.

Оба раза без "onFailure": "block" guard формально «сломался», политика агента вела себя как fail open.

После "onFailure": "block" в handler оба сценария блокируют вызов. В сообщениях фигурируют failed; blocking because onFailure is "block" или timed out — так описывают логи Claude Code и справка по hooks.

С "block" пять классов сбоя (не старт, exit ≠ 0/2 даже при allow-JSON на stdout, HTTP, timeout, битый JSON) приравниваются к намеренной блокировке через exit 2 на PreToolUse. Решения deny / ask по-прежнему отдаются JSON в hookSpecificOutput с sys.exit(0), как в hooks guide.

Guard-hook не запасной firewall, пока на уровне handler явно не включён fail closed.

Минимальный конфиг и цена широкого matcher

Воспроизводимый минимум из заметки:

  • .claude/hooks/guard.py: regex на git push к main/master → deny, на gh repo create … --public → ask, иначе exit 0 без вывода.
  • .claude/settings.json: permissions.allow для Bash(git push *), цепочка PreToolUse → python3 с ${CLAUDE_PROJECT_DIR}, timeout: 5 (в доке дефолт для command-hook: 600 с).

Проверка вне Claude: pipe с JSON {"tool_input":{"command":"git push origin main"}} в guard.

Узкое место fail-closed: широкий "matcher": "Bash" плюс onFailure: block значит, что синтаксическая ошибка в guard блокирует все Bash-вызовы под matcher (в лабе пример с ls). Смягчение: отдельные handlers с "if": "Bash(git push *)" (синтаксис permission rules, одно правило на handler).

Regex в примере не ловит все формы push (например git push без имени ветки на текущей main); жёсткая защита main на сервере опирается на branch protection, не только на hook.

Границы механизма для агентной настройки

onFailure в официальной справке задаётся только на command и http hooks; на Stop, SubagentStop, TaskCompleted, TeammateIdle, а также на хуки с async / asyncRewake не действует. Промо Hermes IDE в конце Dev.to-заметки к логике onFailure не относится. Упоминание фикса 2.1.299 (14 октября 2026) для PreToolUse без shell на Windows только в тексте Gabriel Anhaia, в обогащении отдельно не перепроверялось.

Открытой статистики инцидентов fail-open нет; вывод строят на воспроизводимой лабе и документации Anthropic. Прямого сопоставления с pre-tool hooks в других IDE в заметке нет; перенос паттерна на свой стек без чтения API того инструмента рискован.


Источники