- metadata-title-sanitize: название из метабазы подставляется в план мимо санитизации и уезжает в имя каталога дословно - long-title-to-review: имя длиннее ~237 байт роняет раскладку в failed вместо отправки на ревью
5.8 KiB
🐞 Санитизировать название из метабазы перед подстановкой в план
- Тип: fix
- Категория: Ядро продукта
- Зачем: plan.Title = match.Title подставляется ПОСЛЕ sanitizePlan — название из TMDB/TVDB уезжает в имя каталога Jellyfin дословно, с невидимыми символами и гомоглифами, и авто-раскладка это пропускает
- Теги: sprint:2026-08-06
Название, пришедшее из метабазы, подставляется в план после того, как план
прошёл санитизацию: plan.Title = match.Title стоит ниже sanitizePlan.
Значение уезжает в имя каталога библиотеки, не сверенное ни с чем — гейт
авто-раскладки сверяет normalize(c.Title) || normalize(c.OriginalTitle) и
проходит по второму полю, а в путь идёт первое.
layout.sanitizeComponent ниже по потоку снимает /\:*?"<>| и байты < 0x20,
но категорию Cf (невидимые управляющие) не трогает и гомоглифы не сворачивает.
Итог — два визуально неотличимых каталога в медиатеке.
Класс пре-существующий, и это важно для рамок: тот же путь у TMDB, задача
tvdb-title-locale его только распространила на второго провайдера. Инвариант
«целевой путь строго под библиотекой» держится — проверено на Dune/../../etc,
" .. ", "..." и на 400 символах, выхода из песочницы нет. Поэтому major,
а не critical.
Провенанс: ревью изменения tvdb-title-locale, находка 2 —
отчёт триажа.
Воспроизведение
Падающий тест на реальном Recognizer (оракул добыт триажом, не рассуждением).
Подать кандидата метабазы с названием и посмотреть на plan.Title и решение
гейта:
| Вход | Что происходит |
|---|---|
три символа ZWSP (U+200B) |
auto=true, в план уезжают невидимки; sanitizeTitle дал бы пустую строку |
Dune<RLO>gnp.mkv (U+202E) |
auto=true, имя каталога переворачивается при отображении |
Dune\nHACK |
auto=true, перевод строки доезжает до плана |
Dunа с кириллической а |
auto=true, каталог визуально неотличим от латинского Dune |
Во всех четырёх случаях значение в плане отличается от того, что дал бы санитайзер, и ни одно не остановлено гейтом.
Затрагивает
internal/recognize/recognize.go— порядок подстановкиmatch.Titleиmatch.OriginalTitleотносительноsanitizePlan;internal/layout— составsanitizeComponent, если решим закрывать категорию Cf и гомоглифы здесь, а не на подстановке (выбор места — часть задачи);- поведение обоих провайдеров, TMDB и TVDB: правка меняет уже работающий TMDB, и это её главный риск;
openspec/specs/metadata-match/spec.mdлибоfile-layout— чьим требованием станет «название из метабазы санитизируется перед попаданием в путь».
Критерии приёмки
- Все четыре входа из «Воспроизведения» дают либо санитизированный
plan.Title, либо уход в review — но не авто-раскладку с исходным значением. Оракул: тот самый падающий тест из отчёта триажа, перенесённый в дерево. - Название, схлопывающееся санитизацией в пустую строку, не порождает каталог с пустым именем и не роняет раскладку. Оракул: табличный тест на границе, случай «три ZWSP».
- Поведение TMDB на нормальных названиях не изменилось. Оракул: существующие
тесты
internal/recognizeиinternal/metadataзелёные без правок ожиданий. - Место санитизации названо требованием спеки, а не только кодом. Оракул:
openspec validate --strictна дельте.
Рамки
Гомоглифы сворачивать не обязательно — достаточно сделать значение в пути предсказуемым и сверяемым; полноценная нормализация Unicode это отдельный разговор. Инвариант «целевой путь строго под библиотекой» уже держится, ломать его правкой нельзя. Правка задевает работающий TMDB — регресс на нём дороже самого дефекта.