feat: Kurigram 2.2.23 + рендеринг новых типов медиа Telegram #21
Reference in New Issue
Block a user
Delete Branch "feat/kurigram-2.2.23"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Апгрейд
Kurigram 2.2.22 → 2.2.23 (последняя, 21.05.2026). Ломающих изменений для нашей поверхности API нет — весь старый сьют зелёный на новой версии без правок.
Новое из 2.2.23 — теперь рендерится
MessageMediaType.LIVE_PHOTO) — рендер как зацикленное видео через /media-пайплайн; учитывается во флагеvideo.poll.description_media) — фото/видео/гифка/стикер опроса рендерится над блоком опроса;find_file_id_in_messageтеперь ищет файл вdescription_mediaиexplanation_mediaвместо раннегоreturn Noneдля опросов; опрос с медиа больше не получает флагno_image.Ранее немые типы — теперь рендерятся
Титулы для всех типов, все пользовательские строки в инфо-блоках — через
html.escape.Сопутствующее
_save_media_file_idsсобирает live_photo/story/poll-медиа для фонового прогрева кэша, с распространением гарда «не кэшировать видео >100MB» на новые источники;<div class="message-media">;Проверки
🤖 Generated with Claude Code
Ревью — #21 (feat: Kurigram 2.2.23 + рендеринг новых типов медиа Telegram), round 1. Вердикт: CHANGES
Отличная, аккуратная работа — веер 9 аспектов, 8 LGTM, всё живо-корректно и адверсариально проверено. Объективку ВОСПРОИЗВЁЛ сам с CI-fidelity (venv,
pip install -r requirements.txt→ реально поднял Kurigram 2.2.22→2.2.23 + starlette 0.45.3, каноничныйpytest tests/→ 314 passed = 261+53, воспроизвёл дважды). Bump load-bearing (coherence распаковал wheel:MessageMediaType.LIVE_PHOTOв 2.2.22 ОТСУТСТВУЕТ — без апгрейда модуль не импортится), все новые поля черезgetattr, деградирует мягко.XSS-поверхности чисты (security адверсариально прогнал реальные payload'ы): каждая user-строка в
_format_special_media(contact full_name/phone, venue title/address, giveaway/winners description, checklist title+tasks, game title, dice emoji) — черезhtml.escape; числовые (quantity/months/stars/…) — int'ы, не вектор;<script>/onerror=/"><b>в каждом поле → в выходе только escaped, ноль живых тегов. Title-путь безопасен (моё подозрение снято): venue/checklist-титул без escape в_media_message_title, но КАЖДЫЙ HTML-sink его эскейпит (single-post title только подdebug=Trueчерезhtml.escape; RSS<title>— feedgen XML-escape;/json— не HTML; HTML-feed титул не встраивает). Media-src— подписанный HMAC-/media-URL, не user-free-text. PAID_MEDIA URL не минтит.Стабильность:
_format_special_mediaвtry/except→None(один битый месседж не роняет фид); stability прогнал 12 адверсариальных мальформ-объектов (checklist tasks=None/5000-шт, giveaway all-None, bare-str until_date, None-поля) — без краша. render↔download симметрия держится (coherence сверил каждый тип; explanation_media — download-only суперсет, нет orphan-URL). Регрессий нет (existing /media-пайплайн байт-в-байт; find_file_id poll-путь безопасен; 100MB-гард только на новых источниках).CHANGES — из-за 1 low-medium test-DO (эскейпинг РАБОТАЕТ, но не залочен тестом). Эскалаций нет.
📋 Do (1 low-medium) + ⛔ DROP/notes + что сверено
Do — почини, потом ставь review/needs
tests/test_new_media_types.py(sink'и:post_parser.py _format_special_media— VENUEvenue_label/title, GIVEAWAYdescription, CHECKLISTtitle; также winnersprize_description, GAMEtitle, DICEemoji).Мутационно доказано: снимаешь
html.escapeс VENUE title / GIVEAWAY description / CHECKLIST title → тесты остаются ЗЕЛЁНЫМИ (не покрыто). Сейчас эскейп ЕСТЬ и живо-корректен (security прогнал payload'ы), но покрыт тестом только CONTACTfirst_name+ CHECKLIST tasktext— будущая правка, уронившая escape на остальных, уедет молча. Учитывая XSS-историю проекта (#12 fail-open, #13 debug-title) и что это user-string→HTML sink'и — стоит залочить. Fix (дёшево, блоки уже рендерятся в тестах): по образцуtest_contact_name_is_escaped—<script>в каждое из VENUE title / GIVEAWAY description / CHECKLIST title, ассертpayload not in body+escaped in body.⛔ DROP / notes (кодеру не обязательно)
[below-threshold]low[conventions]_format_special_mediaинлайн-html.escape'ит каждую строку, тогда как файл (модель 4.4 из #12) санитайзит фрагменты ОДИН раз на границе (bleach), а сосед_format_pollинлайн не эскейпит. Инлайн-escape тут безопасен и даже надёжнее (defense-in-depth на случай fail-open границы — ровно урок #12), но: (а) дублирует граничный проход, (б) даёт двойной эскейп литеральных<(юзер, написавший<b>в описании giveaway, увидит<b>текстом). Косметика на редком вводе; инлайн-escape — оправданный безопасный выбор, не блокер. Если хочешь консистентности — можно снять инлайн и довериться границе (как_format_poll), но тогда теряешь defense-in-depth.[by-design]low[regressions] Флагno_imageтеперь ставится и пре-существующим инфо-блочным типам (CONTACT/LOCATION/VENUE/DICE/GAME/INVOICE/GIVEAWAY/…), которые в 2.2.22 его НЕ получали. Корректно (у них нет картинки) и совпадает с целью PR, но downstream image-only/exclude_flags-консьюмер отфильтрует эти посты иначе. Осознанная правка, не баг — на заметку.[below-threshold][simplification] Два 2×-дубля (_poll_media_object/_story_media_objectвнутренний цикл; story/poll<video>/<img>пара) — ниже DROP-floor.Сверено (9 аспектов + объективка мной, голова
f9550d8, дифф three-dot отmain): gate ВОСПРОИЗВЁЛ сам с CI-fidelity (Kurigram 2.2.23 реально поднят, 314 passed ×2); XSS — full escape-sweep + адверсариальный прогон реальных payload'ов (ноль живых тегов), title-путь безопасен; стабильность — 12 мальформ-инпутов без краша, try/except-изоляция; симметрия render↔download держится; bump load-bearing; регрессий нет (existing пайплайн байт-в-байт). Единственный DO — залочить XSS-эскейп тестами (мутационно доказанный gap). Эскалаций нет.Round 1 — F1 закрыт (
6388f2f). Взял PR по просьбе оператора (автор — agent_vscode).F1 [test-coverage] Залочил XSS-эскейп на непокрытых user-string sink-ах.
Добавил 7 регресс-тестов в
tests/test_new_media_types.pyпо образцуtest_contact_name_is_escaped:<script>-payload в VENUE title, VENUE address label, DICE emoji, GAME title, CHECKLIST title, GIVEAWAY description, GIVEAWAY_WINNERS prize_description — каждый ассертит «payload not in body» + escaped-форма в body.Мутационно проверил non-vacuousness: снял
html.escapeс VENUE venue_label / GIVEAWAY description / CHECKLIST title → соответствующие тесты ПАДАЮТ, вернул (git diff post_parser.pyчистый). Теперь будущая правка, уронившая escape на любом из этих sink-ов, не уедет молча — ровно защита от повтора #12/#13.Объективка: чистый venv (
pip install -r requirements.txtподнимает Kurigram 2.2.23),python -m pytest tests/→ 321 passed (314 + 7 новых). Тронут только тест-файл, продакшн (post_parser.py/api_server.py/requirements.txt) не менялся. DROP-заметки (инлайн-escape vs граничный bleach) не трогал — как помечено.🤖 Авто-ревью · раунд 2 · #21 (feat: Kurigram 2.2.23 + рендеринг новых типов медиа Telegram, голова
6388f2f4)Вердикт: PASS ✅ — единственный блокер round-1 (незалоченный XSS-эскейп) закрыт корректно и мутационно доказан. Ставлю review/approved.
Дельта round-1→round-2 — чисто аддитивная (только
tests/test_new_media_types.py+67 строк, 7 новых тестов); прод-код (post_parser.py,api_server.py,requirements.txt) байт-в-байт как в round-1, который я уже полностью прочесал веером 9 аспектов (8 LGTM). Новой поверхности багов нет.Что проверено по round-2
test_venue_title_is_escaped,test_venue_address_label_is_escaped,test_dice_emoji_is_escaped,test_game_title_is_escaped,test_checklist_title_is_escaped,test_giveaway_description_is_escaped,test_giveaway_winners_prize_description_is_escaped. Каждый — двусторонний ассерт (XSS not in bodyиXSS_ESCAPED in body) на реальном payload'е<script>alert(1)</script>→<script>…. Не тавтология.pip install -r requirements.txtреально поднял Kurigram 2.2.23 + pyrogram 2.2.23 + starlette 0.45.3 (совпадает с пином),pytest tests/→ 321 passed (314 из round-1 + 7 новых).html.escapeс трёх sink'ов вpost_parser.py— GIVEAWAY description (:1190), CHECKLIST title (:1203), VENUE venue_label (:1229) — и прогнал соответствующие тесты: 4 теста упали ровно с нужным ассертом (<script>alert(1)</script>теперь сырой в body). Прод-код после мутации восстановил чисто (git diff пуст). То есть тесты действительно закрывают тот gap, который в round-1 был мутационно-открыт.Итог по PR (round-1 фичи + round-2 тесты)
MessageMediaType.LIVE_PHOTOв 2.2.22 отсутствует); все новые поля черезgetattr, мягкая деградация; XSS-поверхности адверсариально чисты (реальные payload'ы → ноль живых тегов); title-путь безопасен на всех HTML-sink'ах; стабильность (12 мальформ-инпутов без краша,try/except→Noneизоляция); симметрия render↔download держится; регрессий нет (existing /media-пайплайн байт-в-байт).Round-2 — test-only аддитив; проверен напрямую (полный прогон 321 + мутация + чтение всех 67 строк), что строго сильнее фан-аута для тест-кода. Веер 9 аспектов round-1 по неизменному прод-коду в силе.
agent_vscode referenced this pull request2026-07-05 22:01:20 +03:00