CVE-2026-111497 — это и номер уязвимости (запись за границы буфера в декодере Google Brotli, CWE-787), и имя репозитория, в котором лежит агент её устранения: три bash-скрипта, которые по очереди проводят аудит Brotli, чинят найденное и проверяют фактический эффект. Смысл набора не в том, чтобы «обновить пакет» — обновление здесь самая простая часть, — а в том, чтобы ответить на четыре вопроса по каждому хосту и вернуть ответ машиночитаемой строкой вместо абзаца текста, который нельзя перепроверить. Версия 1.2.0, вышедшая 27 октября 2026 года, — релиз документации: отчёт об инциденте разложен по девяти страницам вики, чтобы у каждого вопроса был свой дом, а не одна растущая простыня. Код скриптов в этой версии не менялся.

CVE-2026-111497 — обложка обзора
Детектор, устранение и проверка: три скрипта и контракт вердиктов

TL;DR: что это и как проверить за десять минут

  1. Задача. Понять, действительно ли хост уязвим к CVE-2026-111497, устранить причину и доказать эффект, не перезапуская лишнего.
  2. Форма. Три bash-скрипта: detect.sh (только чтение), remediate.sh (единственная точка изменения), verify.sh (только чтение).
  3. Контракт. Коды 0 SAFE, 1 VULNERABLE, 3 NOT_INSTALLED, 10 ERROR; у проверки — VERIFY: PASS или FAIL.
  4. Проверка. Прогнать детектор на хосте и прочитать одну строку вердикта; при VULNERABLE — устранение, затем проверка.
  5. Главное ограничение. Только apt и Debian/Ubuntu: rpm, Alpine и контейнерные образы вне области, статически слинкованная копия Brotli для dpkg невидима.
scp src/common.sh src/detect.sh src/remediate.sh src/verify.sh <host>:/root/cve-2026-111497/
ssh <host> 'bash /root/cve-2026-111497/detect.sh'      # код 0/1/3/10
ssh <host> 'bash /root/cve-2026-111497/remediate.sh'   # только при VULNERABLE
ssh <host> 'bash /root/cve-2026-111497/verify.sh'      # VERIFY: PASS|FAIL

Проблема, которую это решает

Полный аудит Brotli — не проверка версии. По каждому хосту нужно ответить на четыре вопроса: какой пакет установлен, не держат ли работающие процессы уязвимый декодер, какие прикладные пути действительно распаковывают недоверенный Brotli, и является ли backport дистрибутива настоящим исправлением, а не пересборкой того же кода с новым суффиксом. При ручном разборе это десятки вызовов инструментов на хост, а на выходе получается текст, который невозможно воспроизвести.

Самая дорогая ошибка здесь — не пропущенная установка, а ложное «исправлено». Пропатченный файл на диске не патчит уже работающий процесс: библиотека отображена в память, и до перезапуска потребителей хост остаётся уязвимым. Вторая ошибка — принять суффикс версии за доказательство: до обновления security-кармана единственным кандидатом была установленная 1.2.0-3build1, то есть тот же upstream-выпуск, пересобранный дистрибутивом. Пока не прочитан changelog внутри пакета, версия не говорит ничего.

Третья ошибка — перезапустить всё подряд. На гибридных хостах та же libbrotlidec отображается через freetype и fontconfig стеком рабочего стола: GNOME Shell, Xorg, NetworkManager, fwupd, клиент VPN. Их перезапуск обрывает сеанс и туннель, ничего не меняя на серверной поверхности атаки.

Что такое CVE-2026-111497: уязвимость и набор из трёх скриптов

Уязвимость — запись за границы буфера в декодере Google Brotli (c/dec/decode.c, CWE-787). Уязвим именно декодер, а не кодировщик: сервис, который только сжимает ответы, поверхностью атаки не является. Достижимые пути декодирования перечислены в модели угроз:

Путь декодирования Типичный носитель
Тела HTTP-запросов Content-Encoding: br в сторону приложения
Исходящие HTTP-клиенты fetch(), libcurl с пустым CURLOPT_ENCODING
Ответы через прокси декомпрессоры Envoy и Istio
Загружаемые архивы потоковое декодирование Brotli в приложении
Встроенные рантаймы Node.js, Bun, Python Brotli/brotlicffi, декомпрессия запросов в .NET

Уязвимые upstream-версии — с 1.0.3 по 1.2.0. Исправленные — 1.0.10, 1.1.1 и 1.2.1, выпущенные 21 октября 2026 года, а также любой backport дистрибутива, в changelog которого назван этот CVE.

Сам репозиторий — это детерминированный агент: три скрипта и общий модуль, контракт вердиктов, документация (модель угроз, плейбук, полевой отчёт), навык для агента SKILL.md и правила репозитория AGENTS.md. Скрипты не «советуют», а возвращают код возврата и одну строку; модель оркестрирует запуск и не исследует хост вручную.

flowchart TB
  DET["src/detect.sh: только чтение"] --> VERDICT["вердикт SAFE / VULNERABLE / NOT_INSTALLED"]
  VERDICT --> REM["src/remediate.sh: единственная точка изменения"]
  REM --> BACKUP["бэкап /root/cve-2026-111497-fix-<метка>"]
  REM --> GATE["гейт подлинности: changelog пакета называет CVE"]
  GATE --> INSTALL["apt-get install кандидата из -security"]
  INSTALL --> RESTART["перезапуск потребителей libbrotlidec"]
  RESTART --> VER["src/verify.sh: только чтение"]
  VER --> RESULT["VERIFY: PASS или FAIL"]
  STATE[("каталог состояния /var/lib/cve-2026-111497")] --- DET
  STATE --- VER
Файл Ответственность
src/common.sh логирование, разбор версий, матрица классификации, чтение состояния
src/detect.sh аудит только для чтения: вердикт, JSON-отчёт, снимок «до»
src/remediate.sh бэкап, гейт подлинности, сухой прогон, установка, перезапуск, сверка inode
src/verify.sh версия, ссылка на CVE, устаревшие отображения, сбойные юниты, round-trip
docs/THREAT-MODEL.md модель экспозиции путей декодирования
docs/PLAYBOOK.md пошаговая процедура для аудитора-человека
docs/FIELD-REPORT.md результаты с трёх боевых хостов и девять найденных дефектов

Возможности: аудит Brotli по контракту вердиктов

Возможности набора удобнее читать как ответы на вопросы аудита, а не как список файлов.

Аудит: что установлено и что реально используется

Что проверяется Механизм Зачем
Установленная и upstream-версия dpkg-query, epoch и ревизия отбрасываются ревизия дистрибутива скрывает upstream-номер
Применённый backport changelog.Debian.gz внутри пакета с упоминанием CVE достоверный признак вместо строки версии
Кандидат apt apt-get update, затем apt-cache policy есть ли поддерживаемое исправление
Живые потребители поиск libbrotlidec по /proc/<pid>/maps пропатченный файл не патчит процесс
Поверхность декодирования Brotli в libcurl, модули Node/Bun/Python/PHP, ngx_brotli, mod_brotli отделяет декодирование от сжатия
Прикладные примитивы число файлов с CURLOPT_ENCODING и CURLOPT_ACCEPT_ENCODING WordPress, SimplePie и Guzzle принимают все кодировки, включая br
Снимок inode «до» find -type f -printf '%i' по реальному .so inode символьной ссылки даёт ложный якорь

Устранение: бэкап, гейт и перезапуск

Шаг Что делает Инвариант
Бэкап /root/cve-2026-111497-fix-<метка>/ со старым .deb, текущими .so и снимком dpkg до любого изменения
Гейт подлинности читает changelog кандидата и ищет в нём CVE-2026-111497 без совпадения — код 10 и остановка
Сухой прогон показывает, что будет установлено и перезапущено изменение видно до изменения
Установка apt-get install кандидата из -security только apt
Перезапуск серверные службы перезапускаются, рабочий стол — deferred сеанс не обрывается
Сверка сравнение inode и повторное сканирование /proc/*/maps «перезапущено» подтверждается, а не предполагается

Проверка: доказательство эффекта

Проверка verify.sh Что означает
Версия пакета установлена ожидаемая исправленная версия
Ссылка на CVE в changelog исправление настоящее, а не пересборка
Устаревшие отображения ни один серверный процесс не держит старый inode
Сбойные юниты systemctl --failed пуст
Round-trip декодирования ответ с content-encoding: br распаковывается корректно

Как это устроено внутри

Порядок шагов зафиксирован полем, а не привычкой: сначала состояние «до», потом изменение, потом проверка эффекта. remediate.sh отказывается работать без предшествующего detect.sh, потому что сравнение «до/после» — единственное доказательство, что перезапуск что-то изменил.

flowchart LR
  START["хост Debian или Ubuntu"] --> DETECT{"detect.sh: версия в диапазоне 1.0.3-1.2.0?"}
  DETECT -- "нет, backport есть" --> SAFE["код 0: SAFE, закрыть"]
  DETECT -- "пакета нет" --> NI["код 3: NOT_INSTALLED, задокументировать"]
  DETECT -- "да, backport отсутствует" --> VULN["код 1: VULNERABLE"]
  VULN --> REM{"remediate.sh: кандидат называет CVE?"}
  REM -- нет --> ERR["код 10: ERROR, эскалация без изменений"]
  REM -- да --> FIX["бэкап, установка, перезапуск, сверка inode"]
  FIX --> VERIFY{"verify.sh: старая библиотека в /proc отсутствует?"}
  VERIFY -- да --> PASS["VERIFY: PASS"]
  VERIFY -- нет --> FAIL["VERIFY: FAIL, разбираться до закрытия"]
detect.sh    -> VERDICT: SAFE|VULNERABLE|NOT_INSTALLED   (exit 0/1/3/10)
remediate.sh -> REMEDIATED: <старая версия> -> <новая>    (exit 0 или 10)
verify.sh    -> VERIFY: PASS|FAIL                          (exit 0 или 1)

Ключевая деталь — гейт подлинности. В поле конечная точка с changelog дистрибутива отвечала 404, потому что пакет опубликовали несколькими часами ранее; суффикс ubuntu0.1 в версии ничего не доказывал. Доказательство дал сам пакет: security-.deb скачали, распаковали и прочитали usr/share/doc/libbrotli1/changelog.Debian.gz, где нашлась запись с номером CVE и описанием правки декодера. Эта проверка и стала обязательной.

Второй механизм — классификация потребителей. Процессы ищутся по /proc/*/maps; для каждого определяется юнит через cgroup, причём берётся самый внутренний сегмент .service, иначе шаблонные экземпляры вида revoap-worker@1 остаются на старой библиотеке. Демоны панели ISPmanager запускаются загрузчиком панели через временные scope systemd-run, поэтому у них нет собственного юнита и убийство по PID повреждает состояние панели — их перезапускают инструментом панели.

Отдельная тонкость — сравнение версий. dpkg-query отдаёт строку с epoch и ревизией дистрибутива, и если сравнивать её целиком, то 1.2.0-3build1 и 1.2.0-3ubuntu0.1 покажутся разными выпусками upstream, хотя код в них один и тот же с точностью до патча. Поэтому в common.sh версия разбирается по частям: epoch и ревизия отбрасываются, остаётся upstream-номер, который и сравнивается с диапазоном уязвимости. А файл состояния не подключается как shell-скрипт: в строке PRETTY_NAME названия дистрибутивов содержат скобки, и такой файл просто не разберётся — значения читаются по ключу через state_get.

Замеры и цифры

Числа ниже — из полевого отчёта репозитория: три обезличенных боевых хоста и замеры самого релиза 1.2.0. Это выборка, а не перепись парка, и в отчёте это сказано прямо.

CVE-2026-111497: ключевые числа
Ключевые числа набора: три скрипта, четыре вердикта, девять дефектов, найденных полем
Что измерено Значение Где
Хостов в полевом отчёте 3 (Ubuntu 26.04, Debian 12, Ubuntu 24.04) docs/FIELD-REPORT.md
Дефектов, найденных полевым прогоном 9 там же, раздел 3
PHP-файлов с CURLOPT_ENCODING на хосте A 164 инвентаризация декодирования
Устаревших отображений после устранения 0 для серверных потребителей на всех трёх раздел 5
Сайтов, проверенных после устранения (хост A) 10 из 10 вернули HTTP 200 раздел 5
Страниц вики после разделения отчёта 12, все отдают 200 замеры релиза
Гигиена репозитория 100,0 %, 0 находок замеры релиза
Документы и приватность 0 ошибок, 0 предупреждений замеры релиза

Версии в поле: на хосте A libbrotli1 1.2.0-3build1 → 1.2.0-3ubuntu0.1, на Debian 12 1.0.9-2+b6 → 1.0.9-2+deb12u1, на Ubuntu 24.04 1.1.0-2build2 → 1.1.0-2ubuntu0.1. На всех трёх официальный backport присутствовал в -security, и гейт подлинности проходил до каждой установки.

Отдельно про релиз 1.2.0: это выпуск документации. Отчёт об инциденте разложен по девяти страницам вики — матрица экосистемы, инвентаризация, плейбуки устранения, промежуточные меры, — а дайджест рекомендаций переписан вокруг записи GHSA-3wvj-j6qm-62mr и трёх публичных псевдонимов (Node2Shell, HTTP2Shell, Broccoli2Shell). Между 1.1.0 и 1.2.0 два коммита и тринадцать изменённых файлов; код скриптов не менялся.

Самые дорогие находки поля — те, что давали ложный зелёный результат. Первая: список юнитов на перезапуск формировался правильно, но выводился через printf '%s' без перевода строки, имена склеивались в один несуществующий юнит, systemctl restart получал его, отказ не был фатальным — и каждая служба оставалась на уязвимой библиотеке без единой тревожной строки в выводе. Вторая: проверка «ни один процесс не отображает старый inode» сравнивала с inode символьной ссылки, который по построению не отображается ни в одном процессе, поэтому проверка сообщала об успехе на хосте, который не перезапускался. Обе видны только перекрёстной сверкой утверждения с /proc/*/maps на самом хосте — ни код возврата, ни чтение кода их не выдают.

Почему это круто

  • Вердикт вместо рассказа. Агент читает одну строку и код возврата, а не разбирает вывод десятка команд — расход контекста падает на порядок.
  • Строка версии не считается доказательством. Гейт читает changelog внутри .deb, потому что пересборка и настоящее исправление выглядят одинаково, пока не посмотрена ревизия.
  • Проверяется процесс, а не файл. Утверждение «устранено» подтверждается через /proc/*/maps и сравнение inode, а не через dpkg.
  • Рабочий стол не страдает. Процессы freetype, Xorg, NetworkManager и VPN помечаются deferred: пропатченная библиотека подхватится при следующем входе.
  • У панельного ПО свой путь перезапуска. mgrctl -m core --restart srvparam вместо kill — состояние панели не повреждается.
  • Отказ громкий. Если кандидат не называет CVE или совпадает с установленной версией, скрипт останавливается с кодом 10 до любого изменения системы.
  • Инварианты записаны, а не подразумеваются. AGENTS.md фиксирует «бэкап до изменения», запрет форсировать установку и запрет запускать устранение без аудита.
  • Полевые дефекты опубликованы. Девять найденных ошибок, включая два случая ложного «успеха», описаны с воспроизведением — это редкость и главная ценность отчёта.
  • Вердикты годятся для автоматизации. Четыре кода различают «уязвим», «нет пакета» и «не удалось» — такую задачу можно ставить в cron и разбирать результат машиной, не читая текст вывода.

Установка

Требование Значение
Целевой хост Debian 12, Ubuntu 24.04 или Ubuntu 26.04 с apt
Доступ root по SSH-ключу (алиас из ~/.ssh/config)
Обязательное bash 5.x, dpkg, apt-get, systemd
Необязательное curl и brotli — расширяют функциональный round-trip
Сеть только зеркала пакетов дистрибутива
Состояние /var/lib/cve-2026-111497/: report.json, state.before, state.after
Бэкапы /root/cve-2026-111497-fix-<метка времени>/
Лицензия MIT

Скрипты доставляются один раз в каталог на хосте; ставить пакеты на сам хост заранее не нужно.

git clone https://github.com/paulmann/CVE-2026-111497.git
cd CVE-2026-111497
bash -n src/common.sh src/detect.sh src/remediate.sh src/verify.sh
scp src/common.sh src/detect.sh src/remediate.sh src/verify.sh <host>:/root/cve-2026-111497/

Перед доставкой полезно прогнать проверки, которые проект считает обязательными: bash -n, shellcheck src/*.sh и ghagent doc --root ..

Отдельный шаг, который экономит время: убедиться, что хост вообще в области. Дешёвая проверка до копирования файлов снимает половину будущих вопросов.

lsb_release -ds                 # Debian 12, Ubuntu 24.04 или 26.04
command -v apt-get dpkg systemctl
id -u                           # 0 — root
apt-cache policy libbrotli1     # есть ли кандидат из -security

Если вместо apt стоит rpm или apk, дальше идти незачем: матрица классификации переносима, а путь устранения — нет. Если libbrotli1 в системе не установлен вообще, детектор вернёт NOT_INSTALLED с кодом 3 — это не ошибка, а состояние, которое надо задокументировать и перепроверить при появлении Brotli в зависимостях.

Настройка

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

flowchart TD
  HOST["целевой хост: Debian 12, Ubuntu 24.04 или 26.04"] --> STATE["/var/lib/cve-2026-111497: report.json, state.before, state.after"]
  HOST --> BACKUP["/root/cve-2026-111497-fix-<метка>: старый .deb, .so, снимок dpkg"]
  HOST --> PAT{"процесс держит libbrotlidec?"}
  PAT -- "серверная служба" --> RESTART["перезапуск: nginx, php-fpm, apache2, zabbix, панель"]
  PAT -- "рабочий стол, VPN, шрифты" --> DEFER["статус deferred: без перезапуска, до перезагрузки"]
  HOST --> PANEL{"демон панели ISPmanager?"}
  PANEL -- да --> MGRCTL["mgrctl -m core --restart srvparam"]
  PANEL -- нет --> SYSTEMCTL["systemctl restart нужного юнита"]
Что Значение по умолчанию Когда меняют
Каталог состояния /var/lib/cve-2026-111497/ при нестандартной раскладке хоста
Каталог бэкапов /root/cve-2026-111497-fix-<метка>/ если /root на отдельном малом разделе
Список отложенных шаблонов DESKTOP_INFRA_PAT если в нём оказалась нужная серверная служба
Порог классификации der версий матрица в common.sh при появлении нового backport
Список потребителей вычисляется по /proc при нестандартных cgroup-раскладках

Проверка кодов возврата — единственный «язык» этого инструмента, поэтому его стоит держать в голове:

bash detect.sh ; echo "exit=$?"     # 0 SAFE, 1 VULNERABLE, 3 NOT_INSTALLED, 10 ERROR
bash remediate.sh ; echo "exit=$?"  # 10 до изменения системы, если гейт не пройден
bash verify.sh ; echo "exit=$?"     # 1 и строка VERIFY: FAIL — разбираться до закрытия

Подводные камни

  • Только apt. Хосты на rpm, Alpine и базовые образы контейнеров вне области: матрица классификации переносима, путь устранения — нет.
  • Статическая линковка невидима. Копия Brotli, вкомпилированная в бинарник или образ, для dpkg не существует; детектор сообщает только о версиях рантаймов, которые может прочитать.
  • Слоя оркестрации нет. Envoy, Istio, .NET, Bun и Deno описаны в модели угроз, но скрипты их не исправляют — для них нужны свои процедуры.
  • needrestart может сработать раньше. На Ubuntu 24.04 пакетный хук перезапускает серверные службы во время установки, и явному шагу перезапуска может быть уже нечего делать; в отчёте такие перезапуски отнесены к хуку.
  • Рабочий стол и VPN откладываются. Это правильное поведение, но означает, что до перезагрузки часть процессов остаётся на старой библиотеке; при требовании «ноль отображений» нужен перезагрузочный сценарий.
  • Полевые данные — три хоста. Отчёт является выборкой, а не переписью парка; поведение на экзотических раскладках не проверено.

Чек-лист внедрения

  1. Убедиться, что хост в области: Debian 12, Ubuntu 24.04 или 26.04, есть apt и root по ключу.
  2. Доставить скрипты и прогнать bash -n плюс shellcheck — синтаксис и линтер до, а не после.
  3. Запустить detect.sh, зафиксировать вердикт и сохранить report.json со снимком «до».
  4. При VULNERABLE запустить remediate.sh и проверить, что гейт подлинности нашёл CVE в changelog кандидата.
  5. Запустить verify.sh и добиться VERIFY: PASS; отдельно посмотреть строку про отложенные процессы.
  6. Записать в тикет: версию «до/после», число перезапущенных потребителей, число отложенных и что осталось до перезагрузки.

Критерий «работает»: verify.sh печатает VERIFY: PASS, systemctl --failed пуст, серверные процессы не держат старый inode, а сервис, отдающий content-encoding: br, отвечает корректной распаковкой.

FAQ

Это точно про уязвимость в Brotli, а не про сжатие ответов?

Уязвим декодер, а не кодировщик. Сервис, который только сжимает ответы, поверхностью атаки не является; важны пути, где распаковывается недоверенный Brotli: тела запросов, исходящие HTTP-клиенты, прокси-декомпрессоры и встроенные рантаймы.

Почему нельзя просто проверить версию пакета?

Потому что пересборка и настоящее исправление в поле версии выглядят одинаково. Дистрибутив может выпустить тот же upstream-код с новым суффиксом; доказательство даёт только changelog внутри пакета, где назван CVE, — его и читает гейт подлинности.

Что будет, если запустить устранение на хосте с рабочим столом?

Скрипт классифицирует процессы рабочего стола и VPN как deferred: отчёт их назовёт, перезапуска не будет, пропатченная библиотека загрузится при следующем входе или перезагрузке. Сеанс и туннель не обрываются.

Почему демоны панели перезапускаются отдельной командой?

Потому что ISPmanager запускает их собственным загрузчиком через временные scope systemd-run: у них нет юнита, и остановка по PID повреждает состояние панели. Поддерживаемый путь — mgrctl -m core --restart srvparam.

Подходит ли набор для контейнеров и rpm-хостов?

Нет. Область — Debian и Ubuntu с apt; для rpm и Alpine переносима матрица классификации, но не путь устранения. Статически слинкованная копия Brotli не видна dpkg в любом случае.

Что нового в версии 1.2.0, если код не менялся?

Документация: отчёт об инциденте разложен на девять страниц вики (матрица экосистемы, инвентаризация, плейбуки устранения, промежуточные меры), переписан дайджест рекомендаций вокруг GHSA-3wvj-j6qm-62mr и трех публичных псевдонимов, обновлены разделы операций и ловушек.

Чем это отличается от обычного apt upgrade?

apt upgrade меняет файл на диске и ничего не знает о том, кто уже держит старую библиотеку в памяти, и не проверяет, что кандидат действительно исправляет CVE, а не пересобран из того же кода. Набор добавляет к обновлению три вещи: доказательство подлинности пакета, перезапуск именно тех потребителей, которые можно перезапустить, и проверку эффекта в /proc.

Итог

Набор решает узкую задачу так, как её и надо решать в эксплуатации: сначала состояние «до», потом единственная точка изменения, потом доказательство эффекта. Главная ценность здесь даже не в трёх скриптах, а в двух решениях — читать changelog вместо версии и проверять /proc/*/maps вместо dpkg. Полевой отчёт добавляет третье: девять дефектов, два из которых давали ложный «успех», найдены запуском, а не чтением кода. Если у вас есть парк Debian/Ubuntu-хостов с Brotli в декодерах, начинать стоит с одного хоста и detect.sh — это ничего не меняет в системе и сразу показывает, есть ли работа. Дальше решение принимается по вердикту, а не по интуиции: код 0 закрывает вопрос, код 1 даёт право на устранение, код 3 требует перепроверки, код 10 — эскалации без самодеятельности.

Цитата: Уязвимость закрывается обновлением пакета, а инцидент — только доказательством того, что ни один процесс не держит старую библиотеку в памяти.

Связанные материалы

  • CVE-2026-111497 — репозиторий — скрипты, модель угроз, плейбук, полевой отчёт и правила репозитория.
  • Релиз v1.2.0 — разделение отчёта об инциденте на девять страниц и замеры перед выпуском.
  • Вики проекта — кейсы, дайджест рекомендаций и операционный runbook.
  • docs/FIELD-REPORT.ru.md — три хоста, девять дефектов и свидетельства проверки.
  • docs/THREAT-MODEL.ru.md — модель экспозиции путей декодирования.

LLM Summary

  • Тема: CVE-2026-111497 — набор из трёх bash-скриптов для аудита, устранения и проверки уязвимости в декодере Google Brotli (запись за границы буфера, CWE-787).
  • Что делает: detect.sh только читает и выдаёт вердикт, remediate.sh — единственная точка изменения с бэкапом и гейтом подлинности, verify.sh доказывает фактический эффект.
  • Форма: shell-скрипты плюс документация (модель угроз, плейбук, полевой отчёт), навык SKILL.md и правила AGENTS.md; лицензия MIT.
  • Контракт: коды 0 SAFE, 1 VULNERABLE, 3 NOT_INSTALLED, 10 ERROR; проверка печатает VERIFY: PASS или FAIL.
  • Ключевые команды: bash src/detect.sh, bash src/remediate.sh, bash src/verify.sh, bash -n src/*.sh, shellcheck src/*.sh.
  • Требования: Debian 12, Ubuntu 24.04 или 26.04 с apt, root по SSH-ключу, bash 5.x, dpkg, systemd; состояние в /var/lib/cve-2026-111497/, бэкапы в /root/cve-2026-111497-fix-<метка>/.
  • Замеры: три боевых хоста, 164 PHP-файла с CURLOPT_ENCODING на одном из них, ноль устаревших серверных отображений после устранения, десять из десяти сайтов ответили 200; релиз 1.2.0 проверен на 12 страницах вики.
  • Ограничения: только apt, нет обнаружения статической линковки, нет слоя оркестрации для Envoy/Istio/.NET/Bun, процессы рабочего стола помечаются deferred, полевые данные — выборка из трёх хостов.
  • Ссылки: https://github.com/paulmann/CVE-2026-111497, https://github.com/paulmann/CVE-2026-111497/releases/tag/v1.2.0

Промт для агента (установка и настройка)

Готовый промт для AI-агента: скопируйте блок целиком кнопкой и отдайте своему ассистенту. Шаги и команды взяты из AGENTS.md и «Быстрого старта» README репозитория — это инструкция автора для агентов, а не пересказ.

Задача: провести аудит, устранение и проверку CVE-2026-111497 на хосте Debian или Ubuntu, не ломая рабочий сеанс.

Материалы:
- репозиторий: https://github.com/paulmann/CVE-2026-111497
- релиз: https://github.com/paulmann/CVE-2026-111497/releases/tag/v1.2.0
- правила репозитория (прочитать первой): https://github.com/paulmann/CVE-2026-111497/blob/main/AGENTS.ru.md
- модель экспозиции: docs/THREAT-MODEL.ru.md
- полевые уроки: docs/FIELD-REPORT.ru.md

Приоритет: безопасность важнее скорости; любому необратимому действию предшествуют резервная копия и проверка той же операции в песочнице.

Границы ответственности из AGENTS.md:
- src/detect.sh — только чтение, не меняет пакеты, службы и конфигурацию;
- src/remediate.sh — единственная точка изменения: бэкап, гейт подлинности, сухой прогон, установка, перезапуск;
- src/verify.sh — только чтение, подтверждает фактически достигнутый эффект.

Подготовка и проверки перед доставкой:
bash -n src/common.sh src/detect.sh src/remediate.sh src/verify.sh
shellcheck src/*.sh
ghagent doc --root .

Доставка и порядок (порядок фиксирован: detect -> remediate -> verify):
scp src/common.sh src/detect.sh src/remediate.sh src/verify.sh <host>:/root/cve-2026-111497/
ssh <host> 'bash /root/cve-2026-111497/detect.sh' ; echo "exit=$?"
ssh <host> 'bash /root/cve-2026-111497/remediate.sh'
ssh <host> 'bash /root/cve-2026-111497/verify.sh'

Трактовка кодов возврата detect.sh: 0 SAFE — задокументировать и закрыть; 1 VULNERABLE — запускать устранение; 3 NOT_INSTALLED — задокументировать и перепроверить при появлении Brotli; 10 ERROR — прочитать сообщение и эскалировать, не форсировать.

Запрещено (AGENTS.md):
- запускать remediate.sh без предшествующего detect.sh: состояние «до» обязательно;
- форсировать установку пакета, когда проверка подлинности не пройдена;
- удалять файлы, которые задача не упоминает, без явного согласия владельца;
- включать автоматическое обновление или перезагрузку без присмотра на целевом хосте;
- перезапускать демоны панели через kill: путь панели — mgrctl -m core --restart srvparam.

Ловушки, о которых предупреждает репозиторий:
- подключение файла состояния как shell даёт синтаксическую ошибку — читать значения через state_get;
- inode по glob даёт inode символьной ссылки и ложно проходит проверку — find -type f -printf '%i';
- printf '%s' без перевода строки в цикле по юнитам склеивает имена и делает перезапуск холостым — printf '%s\n';
- systemctl restart юнита рабочего стола рвёт сеанс и VPN — такие процессы в DESKTOP_INFRA_PAT и помечаются deferred.

Что считать успехом: detect.sh вернул 0 или 1 с понятным вердиктом; remediate.sh прошёл гейт подлинности и создал бэкап; verify.sh напечатал VERIFY: PASS; systemctl --failed пуст; серверные процессы не держат старый inode; сервис с content-encoding: br отвечает корректной распаковкой.