Files
av 42d5b73a04 docs: перевод документации на канон av-dev
- Раскладка docs/ приведена к канону 2: заведены passport/architecture/
  database/security/review и research; docs/specs, drafts, backlog, review/
  и BRIEF.md разобраны и удалены, беклог переехал в docs/tasks (34 задачи,
  6 целей, слаги на английский).
- Нарративы specs удалены как дубли openspec-спек после поимённой сверки;
  остаток заведён задачами (редактор маппинга ревью, крайние случаи
  именования), отказ от сущности title промоутнут в ADR.
- Проектные копии агентов и скиллов ревью/пайплайна удалены в пользу
  плагинов av-dev-pm и av-dev-pipeline; в task gate добавлен шаг canon
  вместо er-schema.
2026-08-04 09:27:26 +03:00

9.5 KiB

Модель угроз

Периметр

Контур доверенный: домашняя LAN, публичного интернета здесь нет — не выдумывай его. Сервис слушает :8080 на хосте umbar внутри локальной сети, наружу не проброшен, доменного имени и обратного прокси у него нет. Веб-UI и REST API работают без авторизации осознанно; поле [http].trusted_subnets зарезервировано, но не применяется.

Целевой периметр — тот же: выставлять jellybit в интернет не планируется. Если это когда-нибудь изменится, первым шагом идёт задача «Авторизация веб-UI», и модель угроз пересматривается целиком, а не дополняется.

Находки строятся против сегодняшнего периметра. «Любой может открыть страницу и удалить загрузку» — это принятое решение, а не дефект; чтобы стать дефектом, ему нужен путь снаружи LAN.

Недоверенный вход

Что приходит извне и каким каналом. Всё перечисленное контролируется не нами и может быть враждебным по содержанию, даже когда канал доверенный.

Что Канал Чем опасно
Имена файлов и каталогов раздачи qBittorrent API разделители пути, .., управляющие символы, юникод-омоглифы, длина сверх лимита ФС
Имя торрента, поля magnet (dn, tr) приём то же плюс подстановка в промпт
Байты .torrent приём (файл на форме) bencode-разбор недоверенных данных, размер, вложенность
Текстовый контекст человека все транспорты попадает в промпт LLM целиком
Сообщение торрент-бота Telegram (пересылка) чужой формат, парсер, ссылки; текст автора бота, а не отправителя
Ответ LLM HTTP к эндпоинту целиком под влиянием входа выше; названия, годы, номера сезонов и серий, из которых строится целевой путь
Ответы метабаз HTTP к TMDB/TVDB/TVMaze канонические названия, из которых тоже строится путь
Ответы qBittorrent HTTP пути, состояния, размеры
Запросы веб-UI и REST LAN идентификаторы, параметры действий

Выход LLM не отвечает за безопасность. Инъекция в промпт считается состоявшейся по умолчанию; защита стоит ниже — на валидации целевого пути.

Из чего строятся пути и ключи

Отсюда строится выход за пределы песочницы — самое ценное место для враждебного прохода.

  • Целевой путь = paths.movies/paths.series + имя папки тайтла + (для сериала) Season NN + имя файла + расширение. Имя папки и файла собираются в internal/naming из полей распознавания: title, original_title, year, season, episode, provider-тег вида [tmdbid-…]. Все эти поля — недоверенный вход.
  • Правило: компоненты санитизируются (убираются разделители пути, .., управляющие символы), финальный filepath.Clean-путь обязан быть строго под соответствующей библиотекой, иначе операция отклоняется. Проверка на результате, а не на входе.
  • Исходный путь = save_path из qBittorrent + относительное имя файла из /torrents/files. Тоже недоверенный, но по нему мы только читаем и линкуем; писать в paths.downloads нельзя вообще.
  • Ключ идентичности загрузки — инфохэш (v1 SHA-1 / v2 SHA-256), нормализуется в lowercase hex фиксированной длины. Крафт-магнет с чужим или подставным хешем — известное направление атаки на владение (задача в беклоге).
  • Идентификаторы сущностей — ULID, ident.Parse на каждой входной границе: строка из запроса не доходит до SQL непроверенной.
  • Владение целевым путём — один путь, один владелец-file_link; смена владельца возможна только на свободном пути.

Что разграничивает доступ

  • Telegram — allowlist telegram.allowed_user_ids, fail-closed: пустой список запрещает всем. Это единственное реальное разграничение в системе.
  • Веб-UI и REST — не разграничивают ничего: любой в LAN может всё. Осознанно, см. «Периметр».
  • Файловая система — контейнер под 1000:1000, смонтирована только песочница /srv/media и собственные каталоги /config (ro) и /data. /srv/applications целиком в контейнер не попадает.
  • qBittorrent — логин и пароль WebUI; docker-подсеть намеренно не входит в его LAN-whitelist.

Что чувствительнее чего

  1. Медиафайлы в раздаче — единственное, что невосстановимо. Отсюда инвариант «источник неприкосновенен» и гард последней копии в Undo (nlink <= 1 → отказ целиком).
  2. База /data/jellybit.db — восстановима только перезапуском всей работы: теряется всё in-flight состояние и история привязок.
  3. Секреты: пароль qBittorrent, ключи LLM и метабаз, токен Telegram, API-ключ Jellyfin. Живут только в config.toml (0600, рендерит деплой) и никогда не попадают в логи, в диагностику состояния и в ответы API — правило в conventions/logging.md.
  4. Библиотечные хардлинки — восстановимы повторной раскладкой, поэтому ниже по шкале, хотя видны пользователю первыми.

Что вне модели

Перечислено явно: против этого находки не строятся.

  • Злонамеренный участник LAN. Сеть считается доверенной; «сосед по вайфаю удалил загрузку через веб-UI» — не дефект в сегодняшнем периметре.
  • Злонамеренный оператор. Владелец может всё по определению, включая удаление раздачи вместе с файлами.
  • Компрометация соседних сервисов — qBittorrent, Jellyfin, LLM-эндпоинта, хоста umbar. Если qBittorrent врёт про пути, мы проиграли раньше.
  • Отказ в обслуживании изнутри контура. Огромная раздача, тысяча файлов, бесконечный ответ LLM — это вопросы устойчивости и ресурсов (architecture.md → «Эксплуатация»), а не безопасности. Отсутствие лимита на размер ответа LLM — известный пробел, задача в беклоге.
  • Целостность содержимого медиафайлов. Что в контейнере mkv — не наша забота.
  • Цепочка поставки — модули Go, базовый образ distroless, плагины тулинга.
  • Приватность запросов к внешним сервисам. Названия раздач уезжают в LLM и метабазы; это принято сознательно, прокси в конфиге есть.
  • Физический доступ к серверу и бекапам.