|
@@ -0,0 +1,134 @@
|
|
|
|
|
+---
|
|
|
|
|
+tags: [audit, bug, security, frontend, backend, code-review]
|
|
|
|
|
+date: 2025-06-29
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+# Code Audit: 35 найденных проблем (2025-06-29)
|
|
|
|
|
+
|
|
|
|
|
+## Критические (3)
|
|
|
|
|
+
|
|
|
|
|
+### 1. Отсутствует go-playground/validator в go.mod
|
|
|
|
|
+- `backend/go.mod` и `backend/internal/validator/validator.go:11`
|
|
|
|
|
+- Пакет импортируется, но отсутствует в `go.mod`.
|
|
|
|
|
+- **Фикс:** `go mod tidy` или ручное добавление.
|
|
|
|
|
+
|
|
|
|
|
+### 2. Data race на WSHub.clients
|
|
|
|
|
+- `backend/internal/handlers/websocket.go:83-109`
|
|
|
|
|
+- `broadcastVisitors()` итерация по `h.clients` без мьютекса из двух горутин.
|
|
|
|
|
+- **Фикс:** `sync.RWMutex` при чтении/записи clients.
|
|
|
|
|
+
|
|
|
|
|
+### 3. Утечка маркеров Leaflet
|
|
|
|
|
+- `frontend/src/lib/map.ts:105-122`
|
|
|
|
|
+- `addMarker` не удаляет старый маркер с тем же ID — дубликаты в DOM.
|
|
|
|
|
+- **Фикс:** `removeMarker(id)` перед `addMarker`.
|
|
|
|
|
+
|
|
|
|
|
+## Высокие (8)
|
|
|
|
|
+
|
|
|
|
|
+### 4. Утечка внутреннего URL MinIO
|
|
|
|
|
+- `backend/internal/handlers/upload.go:159-164`
|
|
|
|
|
+- При ошибке `url.Parse` клиент получает `http://minio:9000/...`.
|
|
|
|
|
+
|
|
|
|
|
+### 5. Игнорирование ошибки GetByEmail
|
|
|
|
|
+- `backend/internal/services/auth.go:85`
|
|
|
|
|
+- `backend/internal/handlers/users.go:113`
|
|
|
|
|
+- При недоступности БД дубликаты email не проверяются.
|
|
|
|
|
+
|
|
|
|
|
+### 6-8. Stale closures в MapView
|
|
|
|
|
+- `frontend/src/components/MapView.tsx:57-69` — геолокация
|
|
|
|
|
+- `frontend/src/components/MapView.tsx:100-117` — маркеры мест
|
|
|
|
|
+- `frontend/src/components/MapView.tsx:121-133` — маркеры посетителей
|
|
|
|
|
+- Пропущен `user` в deps, stale `user` в замыкании.
|
|
|
|
|
+
|
|
|
|
|
+### 9. WebSocket reconnect после unmount
|
|
|
|
|
+- `frontend/src/hooks/useWebSocket.ts:74-104`
|
|
|
|
|
+- `onclose` может сработать после cleanup и запустить reconnect.
|
|
|
|
|
+
|
|
|
|
|
+### 10. Presigned upload без проверки ответа
|
|
|
|
|
+- `frontend/src/app/services/add/page.tsx:39-48`
|
|
|
|
|
+- `fetch()` на presigned URL без `res.ok`.
|
|
|
|
|
+
|
|
|
|
|
+### 11. Логи без контекста запроса
|
|
|
|
|
+- `backend/internal/handlers/errors.go:27`
|
|
|
|
|
+- Глобальный `slog` без request_id/user_id.
|
|
|
|
|
+
|
|
|
|
|
+## Средние (10)
|
|
|
|
|
+
|
|
|
|
|
+### 12. Игнорирование ошибки тегов сервиса
|
|
|
|
|
+- `backend/internal/handlers/services.go:79-82`
|
|
|
|
|
+
|
|
|
|
|
+### 13. Теги сервиса не обновляются
|
|
|
|
|
+- `backend/internal/handlers/services.go:96-103,167-193`
|
|
|
|
|
+
|
|
|
|
|
+### 14. Неиспользуемый атомарный rotate token
|
|
|
|
|
+- `backend/internal/repository/refresh_tokens.go:66-125`
|
|
|
|
|
+
|
|
|
|
|
+### 15. Нет LIMIT в запросах броней
|
|
|
|
|
+- `backend/internal/repository/bookings.go:83-107`
|
|
|
|
|
+
|
|
|
|
|
+### 16. Опчатка `detail` → `details`
|
|
|
|
|
+- `frontend/src/lib/api.ts:86`
|
|
|
|
|
+
|
|
|
|
|
+### 17. `deleted` не в PlaceStatus
|
|
|
|
|
+- `frontend/src/app/admin/page.tsx:7`
|
|
|
|
|
+
|
|
|
|
|
+### 18. Falsy-zero для hourlyRate
|
|
|
|
|
+- `frontend/src/components/PlaceForm.tsx:98-99`
|
|
|
|
|
+
|
|
|
|
|
+### 19. `type` в PATCH
|
|
|
|
|
+- `frontend/src/components/PlaceForm.tsx:93`
|
|
|
|
|
+
|
|
|
|
|
+### 20. Глотание ошибки API
|
|
|
|
|
+- `frontend/src/app/places/my/page.tsx:330-336`
|
|
|
|
|
+
|
|
|
|
|
+### 21. Нет валидации NaN координат
|
|
|
|
|
+- `frontend/src/components/PlaceForm.tsx:88-89`
|
|
|
|
|
+
|
|
|
|
|
+## Низкие (14)
|
|
|
|
|
+
|
|
|
|
|
+### 22. useSSL захардкожен
|
|
|
|
|
+- `backend/cmd/api/main.go:64`
|
|
|
|
|
+
|
|
|
|
|
+### 23. Неиспользуемая ErrPlaceNotFound
|
|
|
|
|
+- `backend/internal/services/places.go:47`
|
|
|
|
|
+
|
|
|
|
|
+### 24. Неиспользуемые валидаторы
|
|
|
|
|
+- `backend/internal/validator/validator.go:34-35`
|
|
|
|
|
+
|
|
|
|
|
+### 25. Игнорирование ошибок парсинга query
|
|
|
|
|
+- `backend/internal/handlers/places.go:62-95`
|
|
|
|
|
+
|
|
|
|
|
+### 26. cleanup горутина не останавливается
|
|
|
|
|
+- `backend/internal/middleware/ratelimit.go:29`
|
|
|
|
|
+
|
|
|
|
|
+### 27. Soft delete сервиса без смены статуса
|
|
|
|
|
+- `backend/internal/repository/services.go:103`
|
|
|
|
|
+
|
|
|
|
|
+### 28. Лишний ?. на getBounds
|
|
|
|
|
+- `frontend/src/components/MapView.tsx:76`
|
|
|
|
|
+
|
|
|
|
|
+### 29. JSDoc: Яндекс вместо Leaflet
|
|
|
|
|
+- `frontend/src/components/MapView.tsx:13`
|
|
|
|
|
+
|
|
|
|
|
+### 30. Избыточный ref
|
|
|
|
|
+- `frontend/src/components/MapView.tsx:41-42`
|
|
|
|
|
+
|
|
|
|
|
+### 31. fetchPlaces не в deps
|
|
|
|
|
+- `frontend/src/app/places/my/page.tsx:338`
|
|
|
|
|
+
|
|
|
|
|
+### 32. Двойной revokeObjectURL
|
|
|
|
|
+- `frontend/src/components/PlaceForm.tsx:55-61`
|
|
|
|
|
+
|
|
|
|
|
+### 33. SetupHandler без интерфейса
|
|
|
|
|
+- `backend/internal/handlers/setup.go:13-17`
|
|
|
|
|
+
|
|
|
|
|
+### 34. PlaceHandler хранит authSvc для парсинга
|
|
|
|
|
+- `backend/internal/handlers/places.go:19-26`
|
|
|
|
|
+
|
|
|
|
|
+### 35. pointer.Str("") → nil
|
|
|
|
|
+- `backend/internal/pointer/pointer.go:3-7`
|
|
|
|
|
+
|
|
|
|
|
+## Связанные заметки
|
|
|
|
|
+- [[MOC-security-patterns]]
|
|
|
|
|
+- [[MOC-backend-patterns]]
|
|
|
|
|
+- [[atomic-refresh-token-race-condition]]
|
|
|
|
|
+- [[atomic-error-swallowing-frontend]]
|