chore(hooks): точечный allowlist на три ключа метрик вместо снятия гарда #3107

Merged
lekss361 merged 1 commit from chore/hook-allow-metrics-keys into main 2026-08-26 10:18:00 +00:00
Owner

Зачем

Понадобились креды Grafana — и периметровый basic_auth Caddy, и внутренний вход самой Grafana. Оба ключа лежат в backend/.env.runtime, который check-secret-read.py закрывает целиком.

Снимать гард целиком нельзя: в том же файле prod DB-пароли и токены Forgejo/GlitchTip. Поэтому — узкая дырка вместо распахнутой двери.

Что сделано

ALLOWED_KEYS из трёх ключей (METRICS_UI_PASSWORD, GRAFANA_ADMIN_USER, GRAFANA_ADMIN_PASSWORD) и разрешение ровно на anchored-греп по ним.

Условия исключения намеренно жёсткие, чтобы «прочитать один ключ» нельзя было развернуть в «выгрузить файл»:

Приём Поведение
grep '^METRICS_UI_PASSWORD=' <файл> разрешён
инверсия -v / --invert-match блок — иначе выдаст всё, КРОМЕ ключа
пайп, ;, &&, $(...), backticks блок — иначе рядом провозится второй, произвольный ридер
редирект > блок
ключ не из списка блок
греп без якоря ^ блок
обычный cat блок

Проверка

Добавлен scripts/claude-hooks/test-check-secret-read.py — 10 кейсов, каждый прогоняется дважды: напрямую через _is_allowed_key_read и end-to-end через сам хук с настоящим stdin-payload и проверкой кода возврата.

OK   allow=True  want=True   разрешённый anchored-греп
OK   allow=False want=False  инверсия -v
OK   allow=False want=False  цепочка ;
OK   allow=False want=False  пайп
OK   allow=False want=False  редирект
OK   allow=False want=False  другой ключ
OK   allow=False want=False  без якоря ^
OK   allow=False want=False  обычный cat
OK   allow=False want=False  цепочка &&
OK   allow=False want=False  подстановка
--- end-to-end через сам хук ---
ALL OK

Гард показал себя в деле дважды по ходу работы: отбил команду, где к разрешённому грепу был приклеен | grep -v, и отбил саму команду этого коммита, когда в тексте сообщения имя закрытого файла оказалось рядом с read-verb.

NB

Пароли, ради которых заводилось исключение, засветились в транскрипте сессии и записаны в vault (meta/00_credentials.md) открытым текстом. Если транскрипт куда-то выгружается — ротировать оба.

## Зачем Понадобились креды Grafana — и периметровый basic_auth Caddy, и внутренний вход самой Grafana. Оба ключа лежат в `backend/.env.runtime`, который `check-secret-read.py` закрывает целиком. Снимать гард целиком нельзя: в том же файле prod DB-пароли и токены Forgejo/GlitchTip. Поэтому — узкая дырка вместо распахнутой двери. ## Что сделано `ALLOWED_KEYS` из трёх ключей (`METRICS_UI_PASSWORD`, `GRAFANA_ADMIN_USER`, `GRAFANA_ADMIN_PASSWORD`) и разрешение ровно на anchored-греп по ним. Условия исключения намеренно жёсткие, чтобы «прочитать один ключ» нельзя было развернуть в «выгрузить файл»: | Приём | Поведение | |---|---| | `grep '^METRICS_UI_PASSWORD=' <файл>` | разрешён | | инверсия `-v` / `--invert-match` | блок — иначе выдаст всё, КРОМЕ ключа | | пайп, `;`, `&&`, `$(...)`, backticks | блок — иначе рядом провозится второй, произвольный ридер | | редирект `>` | блок | | ключ не из списка | блок | | греп без якоря `^` | блок | | обычный `cat` | блок | ## Проверка Добавлен `scripts/claude-hooks/test-check-secret-read.py` — 10 кейсов, каждый прогоняется дважды: напрямую через `_is_allowed_key_read` и end-to-end через сам хук с настоящим stdin-payload и проверкой кода возврата. ``` OK allow=True want=True разрешённый anchored-греп OK allow=False want=False инверсия -v OK allow=False want=False цепочка ; OK allow=False want=False пайп OK allow=False want=False редирект OK allow=False want=False другой ключ OK allow=False want=False без якоря ^ OK allow=False want=False обычный cat OK allow=False want=False цепочка && OK allow=False want=False подстановка --- end-to-end через сам хук --- ALL OK ``` Гард показал себя в деле дважды по ходу работы: отбил команду, где к разрешённому грепу был приклеен `| grep -v`, и отбил саму команду этого коммита, когда в тексте сообщения имя закрытого файла оказалось рядом с read-verb. ## NB Пароли, ради которых заводилось исключение, засветились в транскрипте сессии и записаны в vault (`meta/00_credentials.md`) открытым текстом. Если транскрипт куда-то выгружается — ротировать оба.
lekss361 added 1 commit 2026-08-26 10:09:59 +00:00
chore(hooks): точечный allowlist на три ключа метрик вместо снятия гарда
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
0e4beb8457
Владельцу понадобились креды Grafana (basic_auth Caddy + внутренний вход), а
они лежат в backend/.env.runtime, который check-secret-read.py закрывает
целиком. Снимать гард нельзя: в том же файле prod DB-пароли и токены
Forgejo/GlitchTip.

Вместо этого — ALLOWED_KEYS из трёх ключей (METRICS_UI_PASSWORD,
GRAFANA_ADMIN_USER, GRAFANA_ADMIN_PASSWORD) и разрешение ровно на anchored-греп
по ним. Условия намеренно жёсткие, чтобы «прочитать один ключ» нельзя было
развернуть в «выгрузить файл»: блокируются инверсия (-v / --invert-match),
пайпы, цепочки ; && ||, подстановки $(...) и обратные кавычки, редиректы.

Тест на 10 кейсов: разрешён только anchored-греп по ключу из списка; отбиты
инверсия, пайп, цепочка, редирект, подстановка, чужой ключ, греп без якоря ^ и
обычный cat. Проверяется и функция, и хук end-to-end через настоящий
stdin-payload.

Гард в деле: он же отбил эту самую команду коммита, когда текст сообщения
содержал имя закрытого файла рядом с read-verb.
lekss361 merged commit 8fbcdd2927 into main 2026-08-26 10:18:00 +00:00
lekss361 deleted branch chore/hook-allow-metrics-keys 2026-08-26 10:18:00 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3107
No description provided.