[CL-AUDIT-EP] Endpoint audit fixes: model-rollback owner-gate, magic-link throttle, dashboard-token header #321

Merged
andrei merged 4 commits from fix/claude-arnold-endpoint-audit into master 2026-06-11 11:11:25 +00:00
Owner

Что сделано

Фиксы трёх находок аудита всех 197 endpoints (46 routers) от 2026-06-11:

  1. HIGH — POST /api/ai-signals/model-rollback owner-gate (64ca08f). ModelRegistry process-global: любой авторизованный юзер мог откатить ML-модель для всех. Теперь is_owner_account → 403, по образцу feature_flags.
  2. MEDIUM — per-IP throttle на magic-link endpoints (d7763ce). magic-link-login — единственный credential-issuing auth-роут без Pass 91/95 ip_throttle (оракул для брута токенов). request-magic-link — только per-email throttle: один IP ротацией email флудил Telegram DM. Оба закрыты существующим ip_throttle.check_and_record; generic-200 no-leak контракт сохранён.
  3. MEDIUM — X-Dashboard-Token header (f8d1610). Query-string token утекал в access-логи/Referer/history, включая деструктивные live-close-all/live-grid-rebuild. Header приоритетен, query fallback сохранён для /live HTML-навигации. slowapi limiter вынесен в app/rate_limit.py.

Зачем

Закрытие P0/P1 находок endpoint-аудита: privilege escalation на глобальную ML-модель, брут-форс окно на входе, утечка дашборд-секрета через логи.

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

  • Новый backend/tests/test_endpoint_audit_fixes.py: 9 тестов (owner 403/200, 429 на magic-link брут, IP-flood стоп с no-leak, header/query token, wrong header 401).
  • Полный suite: 5972 passed, 1 failed → исправлен (test_magic_link_login_returns_access_state — новая сигнатура), повторный прогон затронутых файлов: 110 passed.
  • Post-deploy: scripts/ops/post-deploy-validate.sh после merge.

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

  • Лимит ip_throttle (10/60s) на magic-link-login: легитимные ретраи Mini App за одним NAT-IP мобильного оператора теоретически могут упереться в лимит (как и на остальных 4 роутах — поведение консистентно).
  • request-magic-link отвечает generic-200 и при IP-throttle (не 429) — осознанно, чтобы не давать флудеру сигнал; мониторить нечем кроме логов.
  • Если внешние клиенты дёргали /api/live-* строго query-token — поведение не сломано (fallback), но если где-то стоит WAF, режущий кастомные заголовки, header-путь не заработает.
## Что сделано Фиксы трёх находок аудита всех 197 endpoints (46 routers) от 2026-06-11: 1. **HIGH — `POST /api/ai-signals/model-rollback` owner-gate** (`64ca08f`). ModelRegistry process-global: любой авторизованный юзер мог откатить ML-модель для всех. Теперь `is_owner_account` → 403, по образцу feature_flags. 2. **MEDIUM — per-IP throttle на magic-link endpoints** (`d7763ce`). `magic-link-login` — единственный credential-issuing auth-роут без Pass 91/95 `ip_throttle` (оракул для брута токенов). `request-magic-link` — только per-email throttle: один IP ротацией email флудил Telegram DM. Оба закрыты существующим `ip_throttle.check_and_record`; generic-200 no-leak контракт сохранён. 3. **MEDIUM — `X-Dashboard-Token` header** (`f8d1610`). Query-string token утекал в access-логи/Referer/history, включая деструктивные `live-close-all`/`live-grid-rebuild`. Header приоритетен, query fallback сохранён для /live HTML-навигации. slowapi limiter вынесен в `app/rate_limit.py`. ## Зачем Закрытие P0/P1 находок endpoint-аудита: privilege escalation на глобальную ML-модель, брут-форс окно на входе, утечка дашборд-секрета через логи. ## План тестирования - Новый `backend/tests/test_endpoint_audit_fixes.py`: 9 тестов (owner 403/200, 429 на magic-link брут, IP-flood стоп с no-leak, header/query token, wrong header 401). - Полный suite: 5972 passed, 1 failed → исправлен (`test_magic_link_login_returns_access_state` — новая сигнатура), повторный прогон затронутых файлов: 110 passed. - Post-deploy: `scripts/ops/post-deploy-validate.sh` после merge. ## Где могу ошибаться - Лимит ip_throttle (10/60s) на `magic-link-login`: легитимные ретраи Mini App за одним NAT-IP мобильного оператора теоретически могут упереться в лимит (как и на остальных 4 роутах — поведение консистентно). - `request-magic-link` отвечает generic-200 и при IP-throttle (не 429) — осознанно, чтобы не давать флудеру сигнал; мониторить нечем кроме логов. - Если внешние клиенты дёргали `/api/live-*` строго query-token — поведение не сломано (fallback), но если где-то стоит WAF, режущий кастомные заголовки, header-путь не заработает.
ModelRegistry process-global, не tenant-scoped: rollback одним юзером
менял inference для всех. Теперь is_owner_account обязателен (403),
тот же гейт что у feature_flags.
magic-link-login был единственным credential-issuing /api/auth/* без
Pass 91/95 ip_throttle — нетормозимый оракул для брута magic-link
токенов. request-magic-link имел только per-email throttle: один IP
ротацией email мог флудить Telegram DM и user lookup. Оба получили
ip_throttle.check_and_record по существующему контракту; generic-200
no-leak ответ request-magic-link сохранён.
[CL-AUDIT-EP3] X-Dashboard-Token header для /live и /api/live-*
Some checks failed
Arnold Forgejo CI / secret-scan (pull_request) Successful in 30s
Forgejo Smoke Test / Smoke (pull_request) Successful in 3s
Arnold Forgejo CI / frontend-audit (pull_request) Successful in 39s
Arnold Forgejo CI / backend-tests (pull_request) Has been cancelled
f8d16102c2
Query-string token утекал через access-логи, Referer и browser history,
включая деструктивные live-close-all и live-grid-rebuild. Новый
dependency _dashboard_token: заголовок X-Dashboard-Token приоритетен,
query param остаётся как fallback (ссылки /live HTML навигируют по URL).
slowapi limiter вынесен в app/rate_limit.py (доступен роутерам без
циклического импорта app.main). Тесты на все три находки аудита.
[CL-AUDIT-EP3] live_dashboard.html: fetch через X-Dashboard-Token header
All checks were successful
Arnold Forgejo CI / secret-scan (pull_request) Successful in 4s
Forgejo Smoke Test / Smoke (pull_request) Successful in 2s
Arnold Forgejo CI / frontend-audit (pull_request) Successful in 13s
Arnold Forgejo CI / backend-tests (pull_request) Successful in 4m31s
9d7f75cf71
Все 6 fetch-вызовов шаблона слали ?token= в URL — каждый refresh
дашборда печатал секрет в nginx access-лог. Теперь заголовок TKH;
остался только навигационный <a href> (headers неприменимы к
window-навигации, документировано в _dashboard_token).
andrei merged commit 719b547b0e into master 2026-06-11 11:11:25 +00:00
Sign in to join this conversation.
No reviewers
No labels
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/arnold-trader-app!321
No description provided.