[CL-SEC-Q2] config: fail-closed на ENABLE_ADMIN_API_KEY_AUTH=true в prod #797

Merged
andrei merged 1 commit from feature/claude-sec-q2-admin-apikey-prod-guard into master 2026-06-20 17:30:04 +00:00
Owner

Что сделано

Добавлена fail-closed startup-проверка в validateConfigImpl (api/src/config/validate.ts): в production при ENABLE_ADMIN_API_KEY_AUTH=true boot падает с понятной ошибкой. Escape hatch — ALLOW_ADMIN_API_KEY_AUTH_UNTIL=<future ISO date> для осознанного time-boxed окна (override логируется через logger.error + warning, сам истекает).

Новый тест validate-admin-api-key.test.ts (TDD): throw без override, no-throw при unset / future-override, throw при expired-override, no-op вне prod.

Зачем

MEDIUM-finding аудита (SEC-Q2-2026). requireAdminSecretVerified освобождает API-key запросы (req.apiKeyId) от step-up secret-гейта — это документированное поведение (API-ключ = отдельный сильный путь). Флаг ENABLE_ADMIN_API_KEY_AUTH выключен по умолчанию, но startup-проверки не было. Если в prod он случайно =true (env-drift / копипаста), ВСЕ admin-secret гейты обходятся через API-ключ — включая только что добавленные в #792 (admin.users money/destructive) и #794 (admin.agents control). Эта проверка замыкает периметр тех PR.

Идиома override зеркалит существующие ALLOW_SHARED_MINTER_KEY_UNTIL / ALLOW_PROD_CI_BYPASS_UNTIL — fail-closed по умолчанию, громкий самоистекающий override, не может тихо задрейфить. Подход (validate-at-startup, fail-fast) — канонический 12-factor config.

AUDIT: SEC-Q2-2026.

План тестирования

  • TDD: новый тест RED (2 throw-кейса падали) -> GREEN (5/5).
  • validate.test.ts + validate-noise.test.ts зелёные (нет ложных warning без флага).
  • tsc --noEmit чисто; eslint 0 ошибок; pre-push 189 integration + build green.

Где могу ошибаться

  • Если в prod легитимно используется admin-API-key auth (например автоматизация), этот PR потребует выставить ALLOW_ADMIN_API_KEY_AUTH_UNTIL либо постоянное решение. Если такой кейс есть — нужно решить, делать ли постоянный ack-флаг вместо time-boxed окна.
## Что сделано Добавлена fail-closed startup-проверка в `validateConfigImpl` (`api/src/config/validate.ts`): в production при `ENABLE_ADMIN_API_KEY_AUTH=true` boot падает с понятной ошибкой. Escape hatch — `ALLOW_ADMIN_API_KEY_AUTH_UNTIL=<future ISO date>` для осознанного time-boxed окна (override логируется через `logger.error` + warning, сам истекает). Новый тест `validate-admin-api-key.test.ts` (TDD): throw без override, no-throw при unset / future-override, throw при expired-override, no-op вне prod. ## Зачем MEDIUM-finding аудита (SEC-Q2-2026). `requireAdminSecretVerified` освобождает API-key запросы (`req.apiKeyId`) от step-up secret-гейта — это документированное поведение (API-ключ = отдельный сильный путь). Флаг `ENABLE_ADMIN_API_KEY_AUTH` выключен по умолчанию, но **startup-проверки не было**. Если в prod он случайно `=true` (env-drift / копипаста), ВСЕ admin-secret гейты обходятся через API-ключ — включая только что добавленные в #792 (admin.users money/destructive) и #794 (admin.agents control). Эта проверка замыкает периметр тех PR. Идиома override зеркалит существующие `ALLOW_SHARED_MINTER_KEY_UNTIL` / `ALLOW_PROD_CI_BYPASS_UNTIL` — fail-closed по умолчанию, громкий самоистекающий override, не может тихо задрейфить. Подход (validate-at-startup, fail-fast) — канонический 12-factor config. AUDIT: SEC-Q2-2026. ## План тестирования - TDD: новый тест RED (2 throw-кейса падали) -> GREEN (5/5). - `validate.test.ts` + `validate-noise.test.ts` зелёные (нет ложных warning без флага). - `tsc --noEmit` чисто; `eslint` 0 ошибок; pre-push 189 integration + build green. ## Где могу ошибаться - Если в prod легитимно используется admin-API-key auth (например автоматизация), этот PR потребует выставить `ALLOW_ADMIN_API_KEY_AUTH_UNTIL` либо постоянное решение. Если такой кейс есть — нужно решить, делать ли постоянный ack-флаг вместо time-boxed окна.
[CL-SEC-Q2] config: fail-closed на ENABLE_ADMIN_API_KEY_AUTH=true в prod
All checks were successful
CI / Contracts (pull_request) Successful in 2m0s
CI / Telegram Mini App (pull_request) Successful in 1m24s
CI / Python SDK (pull_request) Successful in 25s
CI / Secrets Scan (pull_request) Successful in 12s
CI / Prisma Migrate Gate (pull_request) Successful in 1m44s
PR Janitor / janitor (pull_request) Successful in 9s
CI / API (pull_request) Successful in 14m8s
CI / App (pull_request) Successful in 16m24s
2f4cec8a6b
AUDIT: SEC-Q2-2026 — MEDIUM hardening sweep.

Что:
requireAdminSecretVerified освобождает API-key запросы (req.apiKeyId) от
step-up secret-гейта. Флаг ENABLE_ADMIN_API_KEY_AUTH выключен по умолчанию, но
не было startup-проверки. Если в prod он случайно =true (env-drift), ВСЕ
admin-secret гейты (включая новые из #792/#794) обходятся через API-ключ.

Добавлена fail-closed проверка в validateConfigImpl: в prod при
ENABLE_ADMIN_API_KEY_AUTH=true boot падает с понятной ошибкой. Escape hatch —
ALLOW_ADMIN_API_KEY_AUTH_UNTIL=<future ISO> для осознанного time-boxed окна
(зеркалит идиому ALLOW_SHARED_MINTER_KEY_UNTIL / ALLOW_PROD_CI_BYPASS_UNTIL):
override логируется громко и сам истекает, не может тихо задрейфить.

Тесты: validate-admin-api-key.test.ts (TDD RED->GREEN) — throw без override,
no-throw при unset / future-override, throw при expired-override, no-op вне prod.
validate.test.ts + validate-noise.test.ts зелёные; tsc + eslint чисто.
forgejo-actions scheduled this pull request to auto merge when all checks succeed 2026-06-20 17:12:10 +00:00
andrei merged commit 4f296af3c6 into master 2026-06-20 17:30:04 +00:00
andrei deleted branch feature/claude-sec-q2-admin-apikey-prod-guard 2026-06-20 17:30:06 +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
europa-tech-srl/europatech!797
No description provided.