Когда HTTP 200 отдаёт чужой промпт: KV-cache на Workers AI, баг SGLang и canary для вашего inference

Три ловушки для соло-разработчика, который гоняет агента через облачный OpenAI-compatible API без своего GPU: ответ с кодом 200, но внутри чужие фрагменты; изоляция сессий держится на учёте KV-страниц, а не на «чистой» памяти; smoke-тест на утечку между запросами ловит класс симптома, но не заменяет аудит провайдера. По отчёту Cloudflare, на Workers AI в августе–сентябре 2026 модель GLM-5.3 дала 155 ответов с чужими данными при промптах длиннее 681 280 токенов; механизм, таймлайн и canary-скрипт разобраны в разборе slabb на Dev.to и в отчёте Cloudflare от 26 октября 2026.
HTTP 200 и агенты: «успех» без гарантии изоляции
Агентный цикл в Cursor или другой IDE часто упирается не в локальный inference, а в шлюз провайдера: rules, логи, MCP-контекст и длинные промпты уезжают одним запросом. Если API отвечает кодом 200 и валидным JSON, клиент и оркестратор обычно считают ход успешным.
Инцидент с GLM-5.3 на Workers AI показал обратное: устаревшие страницы KV-cache отдавались как нормальный ответ, без явной ошибки на стороне клиента. Для vibe-coding это не абстрактный security headline: в ответ могли попасть чужие промпты и генерации из параллельных сессий, то есть секреты из rules и контекста агента теоретически оказываются в чужом потоке.
Проверяйте не только статус HTTP и схему ответа, но и смысл содержимого, особенно после апгрейда endpoint или смены модели.
Как смешались тенанты: конфигурация и баг SGLang
Затронута была только модель GLM-5.3 (@cf/zai-org/glm-5.3) на Workers AI; остальные модели на платформе, по словам Cloudflare, этим багом не затронуты. Окно сбоя: с 27 августа по 9 сентября 2026 (UTC).
Два фактора накладывались друг на друга.
Конфигурация. Контекстное окно объявляли как 1 048 576 токенов, а prefill KV pool как 681 280 (~65% окна). Cloudflare ожидала, что движок отклонит слишком длинный промпт; утечки случались, когда промпт превышал размер пула, то есть 681 280 токенов.
Баг в disaggregated prefill/decode (SGLang). Промпт длиннее пула помечался как отклонённый и подменялся one-token placeholder, но вместо abort и ошибки клиенту prefill всё равно передавал KV decode-узлу. Decode зарезервировал 8 KV-страниц, записал только первую (P0), а attention читал P1–P7 со старыми данными других запросов на том же узле.
KV-страницы не обнуляются между запросами ради производительности; безопасность завязана на корректный учёт владения страницами, а не на «свежую» память по умолчанию.
Upstream: issue SGLang #30233; фиксы в линии v0.5.22 и новее; Cloudflare рекомендует операторам v0.5.22+, плюс merged follow-ups в своей цепочке патчей (подробности в отчёте Cloudflare).
Что сделали Cloudflare и когда стало публично
Краткий таймлайн по UTC (таблица в отчёте Cloudflare):
- 8 сентября 2026, 23:11: отчёт через HackerOne (исследователь Gadi Evron / Knostic).
- 9 сентября: воспроизведение у Cloudflare примерно за 25 минут двумя независимыми аккаунтами; инцидент отмечен около 13:22.
- 14:00: GLM-5.3 перевели в private, для запросов вернули HTTP 400.
- 14:35: root cause: pool меньше заявленного context.
- 14:52: rollout исправленной конфигурации на prefill.
- 19:40: модель снова публична после валидации (repro исследователя и canary-marker tests).
До восстановления расширили prefill KV pool примерно до 2,04 млн токенов (около двух контекстных окон 1 048 576), чтобы валидный промпт помещался в pool. В движке отклонённые запросы должны retire в admission queue до bootstrap и передачи KV.
Координированное раскрытие с SGLang и публичный отчёт вышли 26 октября 2026. Cloudflare не нашла признаков целенаправленной эксплуатации; паттерн запросов похож на обычное long-context использование.
В дорожной карте hardening: KV checksums на prefill→decode, canaries, per-page ownership tags; для self-host SGLang упоминают флаг --disaggregation-enable-kv-checksum.
Canary, self-host SGLang и ваш шлюз
slabb предлагает не внешний аудит KV pool, а детекцию класса симптома: данные запроса A попали в ответ на запрос B, по той же логике, что post-incident canaries у Cloudflare.
Стек минимальный:
- Python 3, только stdlib (
urllib,json,os). - OpenAI-compatible endpoint:
BASE/chat/completions(без завершающего/вBASE). - Переменные:
BASE,MODEL(обязательно),KEY(опционально, по умолчаниюnone).
Сценарий:
- Запрос A: случайный маркер
CANARY-<16 chars>, просьба ответить толькоnoted. - Запросы B1–B5: короткие несвязанные промпты; в теле ответа ищут подстроку canary.
- Итог:
CROSS-REQUEST LEAK DETECTEDилиno leak in this sample (not a proof).
Баг GLM-5.3 требовал промптов за пределами misconfigured KV pool; короткий canary ловит грубые cross-request leaks, но не обязательно воспроизводит тот же порог длины. Гоняйте проверку после апгрейдов, на новых endpoint, в CI на своём inference; провайдерам стоит повторять серию B на новой session/conversation id (same-account barrier слабый). Скрипт целиком лежит у slabb на Dev.to (секция «The lab: a canary test for YOUR provider»).
Если поднимаете disaggregated inference сами, а не только бьётесь в managed API, из постмортема GLM-5.3 остаются три шага:
- Обновить SGLang до v0.5.22+, чтобы отклонённые запросы не доходили до KV handoff.
- Держать KV pool строго больше context length и сверять измеренный pool с context до деплоя (у GLM-5.3 на Workers AI этого не хватило).
- Включить
--disaggregation-enable-kv-checksumдля проверки передачи prefill→decode.
На managed-стеке переносимый шаг тот же: canary против вашего BASE/MODEL. Один вечер после смены модели или региона endpoint дешевле, чем разбирать утечку контекста агента постфактум.
Источники
- Пост slabb на Dev.to (механизм, repro, canary, ссылки на upstream): Dev.to, доступ 2026-10-27 UTC.
- Официальный отчёт Cloudflare KV cache exposure & remediation: https://blog.cloudflare.com/kv-cache-exposure-remediation/, доступ 2026-10-27 UTC.
- SGLang issue #30233: https://github.com/sgl-project/sglang/issues/30233, ссылка из поста slabb на Dev.to; доступ 2026-10-27 UTC.