Конфігурації копії-пасти з виробництва для попереднього підтвердження зобов'язання, сканування PII, захисту розгортання та журналу сеансів Шість місяців тому я тримав на моєму моніторі клейку записку, в якій було сказано: «Ви натякнули? ви перевірили філію? ви подивилися, що вона видалила?» Кожного разу, коли Клод редагував файл, я переходив на інший термінал і запускав форматирувач. Кожен раз, коли він виконував команду Bash, я дивлюся на вихід, дивлячись, чи щось торкалося виробництва. Кожен раз, коли він намагався вчинити, я перевіряв, що я був на правильній гілці спочатку. Проблема полягала в тому, що я робив роботу комп'ютера. Повторний зразок, що відповідає кожній дії, сотні разів на день. Потім я дізнався, що Клод Код викидає події. Кожна окрема дія. Виклики інструментів, редагування файлів, команди Bash, початку сеансу, закінчення сеансу. Всі вони. І ви можете перехопити будь-яку з них за допомогою скрипту оболонки. Залишився на місці, ось що його замінило. Події, про які ніхто не говорить Клод Код - це не просто чат-інтерфейс, який пише код. Під поверхнею він запускає життєвий цикл. Користувач надсилає прохання. Клод вирішує, який інструмент використовувати. Інструмент виконує. Клод читає результат. Повторити, поки не зроблено. При кожному переході в цій ланці Клод Код запускає подію, і ви можете прикріпити драйвер до будь-якого з них. Дві події, які мають найбільше значення: Ваш оператор може перевірити дзвінок і повернути вердикт JSON: дозволити йому, відмовити йому, або мовчати і дозволити нормальному потоку дозволів обробляти його. PostToolUse вистрілює після успішного виклику інструменту. Ваш драйвер отримує повний вихід. Зробіть з ним все, що ви хочете. Зв'яжіть файл. Зареєструйте команду. Надішліть webhook. Інструмент вже запустився, так що це реактивне, а не профілактичне. Клієнти зареєстровані в Вони можуть бути командами shell, кінцевими точками HTTP або запрошеннями LLM. Я використовую команди shell, тому що вони швидкі, дебютуються і не вимагають інфраструктури. .claude/settings.json Весь інтерфейс: Claude Code проправляє JSON до stdin вашого сценарію. Ваш сценарій опціонально пише JSON до stdout. Це те. Якщо ваш сценарій виходить без виходу, Claude Code продовжує нормально. Сценарій 1: Сценарій запобігання катастрофам Я написав це після того, як Клод спробував Будівництво каталогу, що трапилося, щоб мати симлінк в моїй домашній папці. Нічого поганого не сталося, тому що я спіймав його в прохання дозволу. Але я не повинен спіймати його. rm -rf #!/bin/bash # .claude/hooks/guard.sh # Blocks destructive commands before they execute CMD=$(jq -r '.tool_input.command // empty') [ -z "$CMD" ] && exit 0 # Patterns I never want to run unreviewed BLOCKED_PATTERNS=( 'rm -rf' 'rm -r /' 'DROP TABLE' 'DROP DATABASE' 'truncate ' '> /dev/sd' 'mkfs\.' 'dd if=' ) for pattern in "${BLOCKED_PATTERNS[@]}"; do if echo "$CMD" | grep -qi "$pattern"; then jq -n --arg reason "Blocked: command matches dangerous pattern '$pattern'" '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: $reason } }' exit 0 fi done Вісім шаблонів. Може бути вісімдесят. Суть полягає в тому, щоб не перераховувати кожну небезпечну команду. Суть полягає в тому, щоб зловити ті, які ви особисто майже випадково запустили. Мій список відображає мої шрами. Твої будуть відображати ваші. Реєстрація в : .claude/settings.json "PreToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": ".claude/hooks/guard.sh" }] } ] ТІ є регекс на ім'я інструменту. Це означає, що цей гачок виключає лише команди Bash, а не редагує файли або читає. matcher "Bash" Сценарій 2: Форматор, який ніколи не забуває Перед цим гачком мій робочий процес був: Клод редагує файл, я помічаю, що форматування неправильне, я прошу Клода виправити його, Клод запускає форматирувач, я переглядаю форматированную версію. #!/bin/bash # .claude/hooks/fmt.sh # Formats files immediately after Claude edits them TOOL=$(jq -r '.tool_name // empty') [ "$TOOL" != "Edit" ] && [ "$TOOL" != "Write" ] && exit 0 FP=$(jq -r '.tool_input.file_path // .tool_input.path // empty') [ -z "$FP" ] || [ ! -f "$FP" ] && exit 0 case "${FP##*.}" in js|ts|jsx|tsx|mjs) if [ -f node_modules/.bin/prettier ]; then node_modules/.bin/prettier --write "$FP" 2>/dev/null elif command -v npx &>/dev/null; then npx prettier --write "$FP" 2>/dev/null fi ;; rs) rustfmt "$FP" 2>/dev/null ;; py) command -v ruff &>/dev/null && ruff format "$FP" 2>/dev/null command -v ruff &>/dev/null && ruff check --fix "$FP" 2>/dev/null ;; go) gofmt -w "$FP" 2>/dev/null ;; css|scss) command -v stylelint &>/dev/null && stylelint --fix "$FP" 2>/dev/null ;; esac exit 0 Ця версія більш ретельна, ніж мінімальний приклад, який ви знайдете в документації. Вона перевіряє, чи дійсно існує форматист, перш ніж викликати його. та для Python, тому що форматування і обкладинка - це різні речі. ruff format ruff check --fix Зареєструйте його як PostToolUse з Файл форматується, перш ніж Клод читає його назад або переходить до наступного редагування. "Edit|Write" Ефект з'єднання є значним. Протягом тижня я виміряв його. Без гачка я витрачав в середньому 4 хвилини на сесію, просячи Клода виправити проблеми з форматуванням. З гачком, нуль. Протягом 30 сесій на тиждень, тобто дві години відновлювалося. Сценарій 3: Секретний детектор Цей існує через майже пропущену ситуацію. Клод дебютував інтеграцію API, читав файл конфігурації, що містить ключ API-тестування, і включив ключ у свою відповідь. Ключ був для середовища стадіонування і був обертований наступного дня. Але принцип мене турбував. Вихідні інструменти протікають через розмову, отримують кеш, потенційно отримують вхід. #!/bin/bash # .claude/hooks/secrets.sh # Scans tool output for credential-shaped strings OUTPUT=$(jq -r '.tool_output // empty') [ -z "$OUTPUT" ] && exit 0 ALERTS="" # AWS keys (AKIA...) echo "$OUTPUT" | grep -qE 'AKIA[0-9A-Z]{16}' && ALERTS="$ALERTS aws_key" # Generic API key patterns (long hex/base64 after common key names) echo "$OUTPUT" | grep -qiE '(api_key|apikey|secret_key|access_token|auth_token)["\x27: =]+[A-Za-z0-9+/]{20,}' && ALERTS="$ALERTS api_credential" # Private key headers echo "$OUTPUT" | grep -q 'BEGIN.*PRIVATE KEY' && ALERTS="$ALERTS private_key" # JWT tokens echo "$OUTPUT" | grep -qE 'eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.' && ALERTS="$ALERTS jwt_token" # Connection strings with passwords echo "$OUTPUT" | grep -qiE '(postgresql|mysql|mongodb|redis)://[^:]+:[^@]+@' && ALERTS="$ALERTS connection_string" if [ -n "$ALERTS" ]; then TS=$(date '+%H:%M:%S') TOOL=$(jq -r '.tool_name // unknown') echo "[$TS] SECRET_ALERT in $TOOL:$ALERTS" >> .claude/secret-alerts.log echo "WARNING: potential credential in $TOOL output ($ALERTS)" >&2 fi exit 0 Це не заміна для правильного управління таємницями. Це tripwire. Вона охоплює очевидні закономірності: ключі доступу AWS, речі, які виглядають як API-відовідки, блоки приватних ключів, JWT, рядки підключення до бази даних з вбудованими паролями. Коли вогонь горить, воно зникає Він не блокує нічого.Блокування фальшивого позитивного перерватиме ваш потік.Локування на справжньому позитивному дає вам щось досліджувати наприкінці сеансу. .claude/secret-alerts.log І рецензія Один раз на день. Більшість записів є помилковими позитивними (тест-фіктури, приклади документації). Приблизно раз на тиждень, один є достатньо реальним, щоб виправдати обертання акредитації або оновлення .gitignore. secret-alerts.log Сценарій 4: Бланковий поліцейський Короткий, гострий і народжений від справжньої помилки. Він попросив Клода підштовхнути до швидкого виправлення, і воно пройшло. перш ніж я зареєстрував, що відбувалося. Поштовх пройшов. CI був зелений. Ніякого збитку не було зроблено. Але компроміс не мав приземлятися на головній без PR. main git push origin main #!/bin/bash # .claude/hooks/no-push-main.sh # Prevents git push to main/master/production branches CMD=$(jq -r '.tool_input.command // empty') echo "$CMD" | grep -q 'git push' || exit 0 BRANCH=$(git branch --show-current 2>/dev/null) PROTECTED="main master production release" for b in $PROTECTED; do if [ "$BRANCH" = "$b" ]; then jq -n --arg branch "$BRANCH" '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: ("Push to " + $branch + " blocked. Create a feature branch first.") } }' exit 0 fi done # Also block force push to any branch if echo "$CMD" | grep -qE 'push.*(-f|--force)'; then jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "Force push blocked. Use --force-with-lease if you must." } }' fi Дві сторожі в одному сценарії. Без натискання на захищені гілки. Без сили натискання на будь-яку гілку. Друге правило є факультативним, але я зберігаю його, тому що силові натиски є типом речі, яка повинна вимагати від вас ввести команду самостійно, з повною усвідомленням того, що ви робите. Клод лаконічно обробляє заперечення. Він бачить причину, пропонує створити галузь функцій і йде по правильному шляху. Хвіст не перериває сесію. Він перенаправляє його. Сценарій 5: Black Box Recorder Кожен виклик інструменту. кожен вхід. кожен вихід. Тимчасований і прикріплений до файлу журналу. Це гачок, який я найчастіше рекомендую командам, тому що він відповідає на питання, яке ви завжди запитуєте після того, як щось пішло не так: "Що саме зробив Клод?" #!/bin/bash # .claude/hooks/record.sh # Logs every tool call to a structured audit file TS=$(date '+%Y-%m-%d %H:%M:%S') TOOL=$(jq -r '.tool_name // "unknown"') SESSION=$(jq -r '.session_id // "unknown"') # Compact input (one line, no whitespace bloat) INPUT=$(jq -c '.tool_input // {}') # Truncate output to prevent log bloat (first 500 chars) OUTPUT=$(jq -r '.tool_output // empty' | head -c 500) # Tab-separated for easy parsing with awk/cut printf '%s\t%s\t%s\t%s\t%s\n' \ "$TS" "$SESSION" "$TOOL" "$INPUT" "$OUTPUT" \ >> .claude/session-log.tsv exit 0 Файли TSV можна помістити, сортувати і вирізати стандартними інструментами Unix. Чи потрібно бачити кожну команду Bash з сьогоднішнього дня? grep "$(date +%Y-%m-%d)" .claude/session-log.tsv | grep "Bash" | cut -f4 Потрібно рахувати, скільки редагувань файлів Клод зробив цього тижня? grep -c "Edit\|Write" .claude/session-log.tsv Вихідне поле скорочується до 500 символів, щоб запобігти файлам журналу кількох мегабайт, коли Клод читає великі файли. . .claude/logs/session-${SESSION}.tsv Для команд, які потребують централізованого журналу, замініть додаток до файлу на HTTP-кук, який вказує на вашу інфраструктуру журналу. JSON-завантаження Claude Code відправляє містить все, що цей скрипт захоплює, а також додатковий контекст, такий як робочий каталог та конфігурація інструментів. Комбінований файл налаштувань Всі п'ять сценаріїв, пов'язані між собою: { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": ".claude/hooks/guard.sh" }, { "type": "command", "command": ".claude/hooks/no-push-main.sh" } ] } ], "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": ".claude/hooks/fmt.sh" } ] }, { "matcher": "", "hooks": [ { "type": "command", "command": ".claude/hooks/secrets.sh" }, { "type": "command", "command": ".claude/hooks/record.sh" } ] } ] } } Копіюйте JSON в Створюйте п'ять сценаріїв у • біг • Доне. .claude/settings.json .claude/hooks/ chmod +x .claude/hooks/*.sh Попередня статтяУкрзалізниця: обидві сторони в порядку та оцінювати кожну команду Bash. Якщо будь-яка з них повертає заперечення, команда блокується. PostToolUse хаки також працюють в порядку, але вони не можуть блокувати нічого; інструмент вже виконаний. guard.sh no-push-main.sh Порожній матч на другому вході PostToolUse означає та Я хочу сканувати вихід Bash для довіреності і я хочу реєструвати все, незалежно від типу інструменту. secrets.sh record.sh Що я навчився бігати за ці шість місяців Все інше - це страхування. Форматор - це негайна, вимірювана економія часу. Встановіть цей перший, навіть якщо ви ігноруєте решту. The formatter hook pays for itself on day one. Я б краще переглядав десять фальшивих тривог на тиждень, ніж пропустив одну справжню акредитацію. Перегляд файлу журналу займає 30 секунд. False positives in the secret scanner are fine. Кожні кілька тижнів я додаю шаблон після майже пропущеного. Тепер вона має вісім шаблонів. Через шість місяців у неї буде двадцять. Ось що це означає. Вона накопичує інституційні знання вашої команди про те, що ніколи не повинно йти без огляду. The guard script needs to evolve. rm -rf Але коли щось піде не так, повний запис кожної дії Клода перетворює двогодинне розслідування на п'ятихвилинне захоплення. The audit log is most valuable after incidents. Кожен PreToolUse хок додає затримки до кожного виклику інструменту. Мої скрипти Guard і Branch-Cop працюють менш ніж за 5ms кожен. Форматор є найповільнішим при 50-200ms залежно від розміру файлу, але він працює PostToolUse, так що він не блокує трубопровід. Якщо ваш хок викликає зовнішній API, використовуйте PostToolUse або приймайте затримку. Keep hooks fast. The Sticky Note Зник Моніторинг, який колись жив у моїй голові, тепер живе в п'яти шпалерних сценаріях, загалом 150 рядків. Вони працюють на кожному виклику інструменту без моєї участі. Вони спіймають речі, які я б пропустив, коли я втомився наприкінці довгого сеансу. Асистенти кодування штучного інтелекту є потужними.Але потужність без охоронних рейок - це тільки ризик, який ви ще не виміряли.П'ять сценаріїв, десять хвилин налаштування, і ризик знижується майже до нуля. Сліпий запис був замінений чимось кращим. Щось, що не покладається на мене, пам'ятаючи, щоб подивитися на нього.