Hook: агент не читает секреты и не делает необратимое
PreToolUse-hook для Claude Code на Python: блокирует чтение .env, ключей и .pem, вывод переменных окружения, git push, reset --hard и массовое удаление. Правило, которое нельзя забыть.
опубликовано
9171d00Работает сclaude-code
Установка
mkdir -p .claude/hooksИсходники — в тексте ниже, отдельного репозитория у набора нет.

Содержание
Зачем он нужен
«Не читай .env» в CLAUDE.md — просьба, и модель может её не выполнить. Hook — код: он срабатывает до вызова инструмента и при коде выхода 2 блокирует действие, а текст из stderr возвращается агенту. Это второй барьер после permissions.deny, и его нельзя «забыть» посреди длинной сессии.
Набор — упрощённая версия хука из моего проекта DeepRisk. Проектные правила (защита каталогов с исходниками книг) я убрал, остались универсальные.
Что блокирует
| Что агент пытается сделать | Реакция |
|---|---|
Прочитать или изменить .env, *.pem, *.key, id_rsa* |
блок |
cat .env, type .env, Get-Content .env |
блок |
printenv, env с конвейером |
блок |
git push |
блок: пушит человек |
git reset --hard, git clean -f… |
блок |
rm -rf /, rm -rf ~, rm -rf ., rm -rf * |
блок |
Как установить
- Сохраните
guard.pyиз блока ниже в.claude/hooks/guard.py. - Добавьте в
.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Read|Edit|Write",
"hooks": [{ "type": "command", "command": "python .claude/hooks/guard.py" }]
}
]
}
}
- Проверьте руками: попросите агента прочитать
.envи убедитесь, что он получил сообщение «Заблокировано правилами проекта».
guard.py
"""PreToolUse hook: детерминированные запреты проекта.
Код выхода 2 блокирует действие, а stderr уходит агенту. Это второй барьер после permissions.deny:
просьбы в CLAUDE.md можно забыть, а hook срабатывает всегда.
"""
import json
import re
import sys
for _s in (sys.stdout, sys.stderr):
try:
_s.reconfigure(encoding="utf-8")
except Exception:
pass
SECRET_PATH = re.compile(r"(^|[\\/])(\.env(\..+)?|.*\.pem|id_rsa.*|.*\.key)$", re.I)
BAD_CMD = [
(re.compile(r"\b(cat|type|more|less|head|tail|Get-Content)\b[^|;&]*\.env\b", re.I), "чтение .env"),
(re.compile(r"\bprintenv\b|\benv\s*\|", re.I), "вывод переменных окружения"),
(re.compile(r"\bgit\s+push\b", re.I), "git push (пушит только человек)"),
(re.compile(r"\bgit\s+(reset\s+--hard|clean\s+-[a-z]*f)", re.I), "необратимая git-команда"),
(re.compile(r"\brm\s+-rf?\s+(/|~|\.|\*)", re.I), "массовое удаление"),
]
def block(msg: str) -> None:
print(f"Заблокировано правилами проекта: {msg}. См. CLAUDE.md.", file=sys.stderr)
sys.exit(2)
def main() -> None:
try:
data = json.load(sys.stdin)
except Exception:
sys.exit(0)
tool = data.get("tool_name", "")
inp = data.get("tool_input", {}) or {}
path = inp.get("file_path") or inp.get("path") or ""
if tool in ("Read", "Edit", "Write") and path and SECRET_PATH.search(path):
block(f"доступ к секретному файлу {path}")
if tool == "Bash":
cmd = inp.get("command", "")
for rx, why in BAD_CMD:
if rx.search(cmd):
block(why)
sys.exit(0)
if __name__ == "__main__":
main()
Ограничения
- Это защита от случайных действий агента, а не от целенаправленного обхода: команду можно запутать (
base64, переменные, скрипты). Хук работает вместе сpermissions.deny, изоляцией окружения и тем, что реальные секреты вообще не лежат в рабочей копии. - Регулярные выражения под Windows и POSIX я проверял на своём проекте; на вашем наборе команд добавьте свои запреты и прогоните тесты.
- Команда
pythonдолжна быть в PATH. Если нет, укажите полный путь к интерпретатору.


