Files
TeleWave/REVIEW.md
T

7.5 KiB

Ревью TeleWave — план исправлений

Статус чекбоксов: [ ] не сделано · [x] исправлено · [~] в работе.

Дата ревью: 2026-07-25. Область: backend (C#/.NET 10), frontend (React 19), инфра. Сборка чистая, 144 теста зелёные, typecheck чистый.


🔴 Критичное

  • C1. Утечка слота семафора → медиа-обработка навсегда встаёт MediaProcessingBackgroundService.cs:52 — слот берётся до ClaimNextAsync; бросок из claim уводит во внешний catch без Release(). После N ошибок диспетчер зависает навсегда. Fix: try/finally вокруг claim либо Release() в ветке ошибки.

  • C2. Слабый JWT-ключ по умолчанию без fail-fast appsettings.json:8 + DependencyInjection.cs:65 — placeholder-ключ, нет проверки на переопределение и длину; тот же ключ у stream-токенов. Забыли → обход авторизации. Fix: на старте кидать, если ключ = placeholder или < 32 байт.

  • C3. Инъекция/поломка ffmpeg-фильтра из пользовательских подписей FfmpegBumperRenderer.cs:306EscapeText не экранирует , ; [ ] % перевод строки. Подпись с запятой рвёт filtergraph; [/] — инъекция звеньев. Валидатор проверяет только длину. Fix: экранировать полный набор метасимволов filtergraph.


🟠 Среднее

Безопасность

  • M1. Cookie без Secure за TLS-проксиAuthEndpoints.cs:207, StreamingEndpoints.cs:63.
  • M2. Ключи TMDb/OMDb в логахTmdbMetadataProvider.cs:27, OmdbMetadataProvider.cs:25 (System.Net.Http не приглушён в Serilog).
  • M3. Stream-токен неотзывной, TTL 6ч, игнорирует userId/блокировкуStreamTokenService.cs:15, StreamingEndpoints.cs:98,123.
  • M4. Rate-limiter глобальный (не партиционирован), только на /api/authProgram.cs:66.

Архитектура / транзакции

  • M5. ExecuteDeleteAsync ломает границу UnitOfWork + файлы удаляются до коммитаDeleteShowMediaCommandHandler.cs:36, ClearAllMediaCommandHandler.cs, DeleteAllShowsCommandHandler.cs.
  • M6. Query с побочным эффектом на ФСRenderBumperPreviewQueryHandler.cs:18 (рендер файлов под видом запроса).
  • M7. Файловый/HTTP I/O до коммита — Delete/Register/RefreshEpisodes хендлеры (orphan-файлы при откате).

Планировщик / медиа

  • M8. Нет таймаута на ffmpeg/ffprobeProcessRunner.cs:63 (зависший процесс держит слот).
  • M9. Гонка при конкурентной генерации расписания каналаScheduleGenerator.cs:62 (тик + regenerate → дубли записей).
  • M10. Рендер заставок синхронно внутри тика планировщикаScheduleBumperResolver.cs:174.
  • M11. Override через полночь не работаетSchedulePlannerModels.cs:40 (EndMinute <= StartMinute → пустое окно).

Фронтенд

  • M12. Молчаливое проглатывание ошибки → вечный скелетонAirPage.tsx:43.
  • M13. Повторный 401 после refresh не разлогиниваетclient.ts:80.
  • M14. Клиентская пагинация поверх усечённого ответаShowDetail.tsx:42 (pageSize:500), ChannelDetail.tsx:32 (pageSize:100).
  • M15. Дедуп загрузок только по имени файлаupload-store.ts:143.

🟡 Низкое / прочее

  • L1. Синхронный I/O без CancellationToken в портах удаления (IMediaStorage/IImageStore/IBumperTemplateStorage).
  • L2. ValidationBehavior вызывает Validate вместо ValidateAsyncValidationBehavior.cs:24.
  • L3. Инвариант «Single = 1 серия» проверяется в хендлере, а не в агрегатеShow.AddEpisode.
  • L4. Двойной SaveChanges в командном пути ScheduleGeneratorScheduleGenerator.cs:79,132.
  • L5. HLS-плеер не восстанавливается после fatal network/media errorChannelPlayer.tsx:106.
  • L6. Бесконечная повторная регистрация «падающего» файла из inboxInboxScannerBackgroundService.cs:92.
  • L7. Возможное переполнение int в весах шоуSchedulePlanner.cs:256,265.
  • L8. OpenAPI/Scalar мапятся всегда, без гейта по окружениюProgram.cs:108.
  • L9. AllowedHosts: "*" и dev-креды БД в appsettings.json.
  • L10. SSRF-поверхность в ImageDownloader (без allowlist схемы/хоста) — ImageDownloader.cs:16.
  • L11. Документация (CLAUDE.md/README) отстала: заявлено «каталог каналов и раздача видео не реализованы», хотя реализованы.
  • L12. Пробелы в тестах: фоновые сервисы, ScheduleGenerator-оркестрация, media-конвейер, эндпоинты без тестов.

Журнал исправлений

2026-07-25 — критические (C1–C3)

  • C1MediaProcessingBackgroundService.cs: ClaimNextAsync обёрнут в try/catch, слот освобождается (slots.Release()) при любом сбое захвата перед throw. Теперь транзиентная ошибка БД не «съедает» слот семафора — диспетчер не зависает.
  • C2Infrastructure/DependencyInjection.cs: после загрузки JwtOptions добавлен fail-fast: старт падает с понятным сообщением, если Jwt:SigningKey короче 32 байт или содержит change-me (значение-заглушка). Тот же ключ подписывает stream-токены, поэтому это закрывает и их.
  • C3FfmpegBumperRenderer.cs: подписи «Сейчас/Далее» (NowLabel/NextLabel) переведены с инлайнового text= на textfile= (nowlabel.txt/nextlabel.txt, expansion=none), как уже сделано для названий шоу. Метасимволы filtergraph в подписи больше не ломают/не инъектируют цепочку. Неиспользуемый EscapeText удалён. Файлы чистятся в finally.

Проверка: dotnet build — 0 warnings/0 errors (при TreatWarningsAsErrors); тесты 95 + 49 зелёные.