feat(security): проверка Origin, строгий CSP и лимиты загрузок

- middleware OriginCheck: state-changing запросы с чужого Origin отклоняются
  (AGENT.md 9.7); запросы без Origin пропускаются — cookie уже SameSite=Lax
- CSP перечисляет директивы явно (script-src/worker-src/manifest-src 'self',
  style-src с inline для React, img-src с внешними https для аватаров вебхуков,
  connect-src с доменом файлов и LiveKit); добавлены Permissions-Policy,
  Cross-Origin-Opener-Policy и X-Permitted-Cross-Domain-Policies
- лимиты по AGENT.md 8.6: загрузки 10/мин и 100/сутки (вложения и аватары),
  реакции 20/мин; администратор инстанса лимиты обходит
- step-up: смена прав роли (в теле PATCH) и удаление сервера (перед вызовом
  /auth/step-up) требуют свежего подтверждения личности
- тесты: Origin (свой/чужой/GET), состав CSP, лимит реакций, step-up на права
  роли; исправлен вызов NewRateLimiter (второй аргумент — burst, не окно)
This commit is contained in:
2026-09-22 00:40:10 +03:00
parent 1cb949a7a4
commit af03cfb063
8 changed files with 302 additions and 17 deletions
+4
View File
@@ -760,6 +760,10 @@ func (s *Server) changeReaction(ctx context.Context, rawChannelID, rawMessageID,
if err != nil {
return nil, err
}
// Реакции: 20 в минуту на пользователя (AGENT.md 8.6).
if allowed, retryAfter := s.reactionLimiter.Allow("reaction:" + formatSnowflake(user.ID)); !allowed {
return nil, rateLimitedError(retryAfter)
}
emoji = strings.TrimSpace(emoji)
if emoji == "" || len([]rune(emoji)) > 64 {
return nil, humaErrorStatus(http.StatusUnprocessableEntity, "validation.failed", "emoji is invalid")