# Code Audit 2026-06-30 — Новые находки **Контекст**: Полный аудит кодовой базы после исправления предыдущего техдолга. Часть проблем исправлена, но обнаружены новые. ## Суть Аудит выявил 10 новых проблем (1 security, 3 reliability, 6 code quality) в дополнение к уже известным из [[architecture-overview]]. ## Найденные проблемы ### 🔴 Security 1. **Эскалация привилегий через AdminUpdateUser** — `handlers/users.go:166-186` - Модератор может повысить роль любого пользователя до `superadmin` (кроме своей). Ограничение только на собственную роль. - Надо добавить проверку: модератор не может устанавливать роль `superadmin`. ### 🟡 Reliability 2. **Отсутствует статус по умолчанию при создании пользователя** — `repository/users.go:22-29` - INSERT не задаёт `status`, в БД нет DEFAULT. У нового пользователя `Status = ""`. - Миграция должна задавать `status DEFAULT 'active'`, либо код должен явно проставлять. 3. **BookingRepo.Create не проверяет статус места** — `repository/bookings.go:25-63` - Можно забронировать место в статусе `pending_moderation`, `draft` или `rejected`. Проверка только на `deleted_at IS NULL`. - Нужна проверка `status = 'published'`. 4. **RoleMiddleware type assertion без проверки** — `middleware/auth.go:54` - `r.Context().Value(UserRoleKey).(string)` запаникует, если в контексте не строка. - В отличие от безопасного `GetUserRole()`, эта функция делает unsafe type assertion. ### 🔵 Code Quality 5. **N+1 в GetByID для services** — `handlers/services.go:80-85` - Сначала `GetByID`, потом отдельно `GetTags`. Можно объединить в один JOIN-запрос. - Аналогично было с places, но там уже исправлено. 6. **DeleteTag: странный парсинг тела DELETE** — `handlers/tags.go:79-84` - Сначала JSON body, при ошибке — query param. Это не RESTful и путает. - DELETE с body — редкая практика, лучше только query. 7. **broadcastVisitors двойной lock без необходимости** — `handlers/websocket.go:91-127` - Сначала под mutex собирает список, отпускает, потом снова берёт для рассылки. - Между сбором и рассылкой список клиентов может измениться. 8. **logout на фронте подавляет ошибки** — `hooks/useAuth.tsx:65` - `catch {}` скрывает ошибки сети при logout. - Пользователь не видит, что выход не удался. 9. **fetchPlaces подавляет ошибку** — `components/MapView.tsx:37-39` - Ошибка загрузки мест игнорируется, пользователь не видит уведомления. 10. **next.config.js: только cdn.photoplaces.ru и localhost** — `next.config.js:5-8` - В production на тестовом сервере S3/MinIO раздаётся через `api.{DOMAIN}/s3/`, что не добавлено в `remotePatterns`. - Изображения могут не загружаться через Next.js Image Optimization. ## Что было проверено и признано корректным - Валидация на всех эндпоинтах (go-playground/validator) - Безопасность паролей (bcrypt) - Защита от SQL-инъекций (pgx параметризованные запросы) - Refresh token rotation + SHA-256 хранение - Rate limiter с fail-open/closed поддержкой - Проверка сигнатуры файлов при загрузке (magic bytes) - CORS настроен - Мягкое удаление (soft delete) - Graceful shutdown - Консистентность координат (lat, lng) ## Рекомендации 1. **Срочно (P0)**: Добавить проверку в `AdminUpdateUser` — модератор не может ставить роль superadmin 2. **Высоко (P1)**: Добавить DEFAULT 'active' для status в users (миграция 000012) 3. **Высоко (P1)**: Добавить проверку `status = 'published'` при создании брони 4. **Средне (P2)**: Исправить RoleMiddleware, N+1 в services, broadcastVisitors lock, next.config.js ## Связанные заметки - [[architecture-overview]] - [[atomic-toctou-race-registration]] - [[atomic-mutex-defer-panic]] - [[atomic-error-swallowing-frontend]] ## Теги #code-review #security #backend #frontend #bug #2026-06