- json-api-for-spa: адреса приложения уехали в своё пространство /app/, приём стал POST /app/audiorecords, опрос /api/status/:id убран, заведены список, карточка, текст, /app/me и /app/config; имя файла отправителя легло своей колонкой рядом с заголовком - три действия над записью — правка заголовка, возврат в работу и журнал событий — собраны задачей audiorecord-actions - голова очереди: контракт, каркас, экран загрузки, список, действия - в прежних задачах поправлены адреса, рубежи конвейера и остатки Telegram
5.3 KiB
🐞 Не давать отправителю задавать, что уедет в журнал
- Тип: 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и ссылка на неё из обеих правок.
Рамки
Приведение пути не должно делать маршрут неразличимым: по журналу отличают приём
записи от чтения одной записи — POST /app/audiorecords от
GET /app/audiorecords/{id}. Найдено прогоном ревью задачи
remove-telegram-intake 2026-08-15, враждебный проход; дефект существовал до той
правки, и владелец решил вынести его задачей.