Files
transcriber/tasks/items/journal-fields-bounded-by-service.md
T
av b4b19db6e4 tasks: заведён урожай ревью remove-telegram-intake
- шесть записей по кластерам причин: журнал под внешним значением, нулевой
  код ответа в журнале, затирание вложения бедным ответом, открытый анониму
  адрес подтверждения почты, рубеж расшифровки без работы, пределы длительности
- находка про код 500 у отказа приёма дописана в json-api-for-spa: там живёт
  единая точка отображения доменной ошибки
- telegram-account-link и bot-api-only-through-bot-client оставлены с оговоркой,
  что предмета у них нет до возвращения входа
2026-08-15 07:46:16 +03:00

5.2 KiB
Raw Blame History

🐞 Не давать отправителю задавать, что уедет в журнал

  • Тип: fix
  • Категория: Очередь — Заведена урожаем ревью и встала в конец машинально: место в плане стройки назначает человек.
  • Зачем: Путь запроса уезжает в журнал целиком: аноним пишет туда до мегабайта своего текста одной строкой, а хвост имени файла приезжает внутри чужого текста ошибки вместо своего поля.
  • Теги: review-2026-08-15

Длину и содержимое строки журнала задаёт тот, кто прислал запрос, а не сервис.

Мест два, причина одна. Слой журнала пишет путь запроса полем http.route целиком: приведение режет только последний сегмент и только у ссылок на файлы. Отказ подготовки рабочей копии пересказывает отказ библиотеки целиком, а тот несёт полное имя временного файла — вместе с расширением, взятым из имени отправителя.

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

Класс проект уже назвал недопустимым в приведении отказа провайдера входа (conventions/logging.md): «без приведения аноним пишет в журнал что угодно и сколько угодно». Здесь тот же канал шире и дешевле — сессия для него не нужна.

Воспроизведение

Путь запроса. Поднять сервис и послать запрос, чей путь длиной 900 000 байт:

python3 -c 'socket … GET /<900000×"A"> HTTP/1.1'

Ответ — 404, журнал вырастает с 1048 до 901 194 байт одной строкой. Потолок замерен: 1 048 000 байт ещё проходят, 1 100 000 отвергаются кодом 431. Внедрения строк при этом нет — библиотека журнала квотирует значение.

Хвост имени. Принять запись, чьё имя несёт 280 байт после последней точки. Отказ подготовки рабочей копии приезжает в журнал уровня ERROR строкой вида error="failed to create work file: open /tmp/transcriber-<число>.<280 байт>: file name too long", и поля file_ext в ней нет вовсе.

Затрагивает

  • слой журнала запроса в main.go и приведение пути journalRoute;
  • отказ подготовки рабочей копии в internal/service, шаг приёма записи;
  • docs/conventions/logging.md — правило о длине поля журнала;
  • умолчание MaxHeaderBytes у сервера: сегодня оно единственное неназванное рядом с названным сроком чтения.

Критерии приёмки

  • Путь запроса в журнале не длиннее объявленного предела, и усечение видно в самой строке. Оракул: тот же запрос с путём в 900 000 байт — прирост журнала не превышает предела, а маршрут по первым байтам по-прежнему различим.
  • Отказ подготовки рабочей копии не пересказывает отказ библиотеки: расширение идёт своим полем. Оракул: проверка приёма с длинным хвостом имени — строки с этим хвостом в журнале нет, поле file_ext есть.
  • Правило записано конвенцией: длину поля журнала не задаёт вызывающий. Оракул: строка в docs/conventions/logging.md и ссылка на неё из обеих правок.

Рамки

Приведение пути не должно делать маршрут неразличимым: по журналу отличают /api/audio от /api/status/:id. Найдено прогоном ревью задачи remove-telegram-intake 2026-08-15, враждебный проход; дефект существовал до той правки, и владелец решил вынести его задачей.