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
+17 -2
View File
@@ -332,7 +332,7 @@ func (s *Server) registerGuildCoreRoutes(api huma.API) {
GuildID string `path:"guild_id"`
},
) (*okOutput, error) {
user, _, err := requireUser(ctx)
user, session, err := requireUser(ctx)
if err != nil {
return nil, err
}
@@ -343,6 +343,11 @@ func (s *Server) registerGuildCoreRoutes(api huma.API) {
if !resolved.IsOwner && !user.IsInstanceAdmin {
return nil, humaErrorStatus(http.StatusForbidden, "perm.denied", "only the owner can delete the guild")
}
// Удаление сервера требует свежего подтверждения личности (AGENT.md 9.3).
// Тело у DELETE не спрашиваем: клиент сначала вызывает /auth/step-up.
if err := s.auth.RequireStepUp(ctx, user, session, "", ""); err != nil {
return nil, humaError(err)
}
if err := s.deleteGuild(ctx, user, guildID); err != nil {
return nil, err
}
@@ -1274,10 +1279,13 @@ func (s *Server) registerRoleRoutes(api huma.API) {
CosmeticBadgeID *string `json:"cosmetic_badge_id,omitempty"`
NickColor *int64 `json:"nick_color,omitempty"`
NickEffect *string `json:"nick_effect,omitempty"`
// Step-up обязателен при смене прав роли (AGENT.md 9.3).
StepUpPassword string `json:"step_up_password,omitempty"`
StepUpTOTP string `json:"step_up_totp,omitempty"`
}
},
) (*roleOutput, error) {
user, _, err := requireUser(ctx)
user, session, err := requireUser(ctx)
if err != nil {
return nil, err
}
@@ -1285,6 +1293,13 @@ func (s *Server) registerRoleRoutes(api huma.API) {
if err != nil {
return nil, err
}
// Смена набора прав роли — чувствительная операция: подтверждаем
// личность свежим паролем (AGENT.md 9.3).
if input.Body.Permissions != nil {
if err := s.auth.RequireStepUp(ctx, user, session, input.Body.StepUpPassword, input.Body.StepUpTOTP); err != nil {
return nil, humaError(err)
}
}
roleID, err := parseID("role_id", input.RoleID)
if err != nil {
return nil, err