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:306—EscapeTextне экранирует,;[]%перевод строки. Подпись с запятой рвёт 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/auth—Program.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/ffprobe —
ProcessRunner.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вместоValidateAsync—ValidationBehavior.cs:24. - L3. Инвариант «Single = 1 серия» проверяется в хендлере, а не в агрегате —
Show.AddEpisode. - L4. Двойной
SaveChangesв командном пути ScheduleGenerator —ScheduleGenerator.cs:79,132. - L5. HLS-плеер не восстанавливается после fatal network/media error —
ChannelPlayer.tsx:106. - L6. Бесконечная повторная регистрация «падающего» файла из inbox —
InboxScannerBackgroundService.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)
- C1 —
MediaProcessingBackgroundService.cs:ClaimNextAsyncобёрнут вtry/catch, слот освобождается (slots.Release()) при любом сбое захвата передthrow. Теперь транзиентная ошибка БД не «съедает» слот семафора — диспетчер не зависает. - C2 —
Infrastructure/DependencyInjection.cs: после загрузкиJwtOptionsдобавлен fail-fast: старт падает с понятным сообщением, еслиJwt:SigningKeyкороче 32 байт или содержитchange-me(значение-заглушка). Тот же ключ подписывает stream-токены, поэтому это закрывает и их. - C3 —
FfmpegBumperRenderer.cs: подписи «Сейчас/Далее» (NowLabel/NextLabel) переведены с инлайновогоtext=наtextfile=(nowlabel.txt/nextlabel.txt,expansion=none), как уже сделано для названий шоу. Метасимволы filtergraph в подписи больше не ломают/не инъектируют цепочку. НеиспользуемыйEscapeTextудалён. Файлы чистятся вfinally.
Проверка: dotnet build — 0 warnings/0 errors (при TreatWarningsAsErrors); тесты 95 + 49 зелёные.