Если вы self-hostите GitLab, свежая CVE в экосистеме - напоминание об одной очень конкретной ловушке: иногда фикс находится не в том продукте, который вы, как вам кажется, обновляете. CVE-2026-97229 - critical (CVSS 10.0) уязвимость, которая позволяет неаутентифицированному атакующему выполнить произвольный код на вашем GitLab-сервере, отправив специально сформулированное регулярное выражение в CI/CD-конфиге.[1][2] GitLab закрыл её 12 октября в трёх линейках релизов - но настоящий фикс - это набор патчей к regex-движку Ruby (Onigmo), который GitLab применяет только внутри своих официальных пакетов и контейнерных образов.[1][2] Если ваш GitLab работает на Ruby, который вы собрали сами, или на кастомном образе поверх стандартного Ruby - один лишь апгрейд GitLab дыру не закроет.
Это не теоретический крайний случай. Проект Alpine Linux наступил на эти грабли в худшем из возможных вариантов: их инстанс обновили до пропатченной версии GitLab, а уязвимость осталась, потому что в их образе стоял непропатченный стандартный Ruby.[2] Разберём, что это за уязвимость, какие три даты на самом деле важны, что доказывает инцидент Alpine и как по конкретному чек-листу понять, на какой стороне ваш GitLab, - используя только команды из первичной документации GitLab и Ruby.
Уязвимость: регэксп, который не видно, превращается в код
GitLab компилирует регулярные выражения, которые приходят от пользователей - например, паттерны из конфигов CI - через Regexp.new.[3] CVE-2026-97229 - это integer overflow в компиляторе регулярных выражений CI/CD: при определённых условиях неаутентифицированный пользователь мог выполнить произвольный код на сервере GitLab через специально сформированное регулярное выражение в конфигурации CI/CD из-за недостаточной валидации регэкспов.[1]
Переполнение происходит внутри Ruby, а не в GitLab. Движок регэкспов, который использует GitLab, - Onigmo, встроенный в Ruby. Когда Ruby компилирует специально сделанный паттерн, он может повредить память, а это повреждение превращается в удалённое выполнение кода на сервере GitLab.[2][3] Поскольку баг живёт в компиляторе регэкспов интерпретатора, а не в логике GitLab, вырезать его правкой кода GitLab нельзя - это дефект рантайма, который GitLab просто выводит на поверхность.
Две детали из анонса апстрим-Ruby стоит запомнить - они делают эту проблему сложнее для сдерживания, чем обычные regex-DoS:[3]
Regexp.timeoutот этих багов не защищает. Это не проблема исчерпания ресурсов, которую можно решить таймаутом в конфиге.- Ограничение длины паттерна тоже надёжно не защищает - некоторые баги триггерятся короткими паттернами.
Так что привычный план «ограничь ввод, поставь таймаут на регэксп» здесь не работает. Модель угрозы такая: уязвим любой Ruby-сервис, который компилирует регулярное выражение из контролируемого атакующим ввода - через Regexp.new, Regexp.union, Regexp.compile или интерполяцию в literal вида /#{input}/.[3] Приложения, использующие только регэкспы, написанные своими разработчиками, не затронуты; опасность появляется, когда строка паттерна сама может быть внешне изменена.[3]
Три даты, которые на самом деле важны
Путаница вокруг этой CVE - от того, что «фиксы» вышли двумя разными составами в два разных дня, и они не взаимозаменяемы.
12 октября - critical patch release GitLab. GitLab выпустил 19.4.2, 19.3.5 и 19.2.8, настоятельно рекомендовав всем self-managed-установкам обновиться немедленно.[1] В заголовке был CVE-2026-97229, но батч нёс и больше. Таблица security-фиксов содержит High XSS в синтаксической подсветке, High проблему авторизации в сервисе создания downstream-pipeline, High permissive-regex в доступе к файлам репозитория, High проблему авторизации в политике выполнения scheduled pipeline (EE), High LLM-prompting-проблему в GitLab Duo (EE) и Medium утечку чувствительной информации в GitLab Pages - и это не всё.[1]
И главное - посмотрите, чем был сам фикс. В списках баг-фиксов 19.4.2, 19.3.5 и 19.2.8 есть merge request’ы «Add Ruby regexp program size patch for 3.3 and 3.4» и «Add Ruby regexp repeat-range double-free patch for 3.3 and 3.4» - применённые к omnibus-gitlab и CNG (Cloud Native GitLab) container images.[1] GitLab не чинит Onigmo сам; он бэкпортит Ruby-патч в тот Ruby, который ставят его собственные пакеты и образы.
23 октября - фикс апстрим-Ruby. Ruby выпустил 3.3.13, 3.4.12 и 4.0.9 с исправлениями в Onigmo.[2][3] Анонс Ruby прямой: в релизах лежат фиксы багов за CVE-2026-89078, CVE-2026-93577, CVE-2026-97229 (integer overflow в размере скомпилированного паттерна) и CVE-2026-110264, и все они содержатся в 4.0.9, 3.4.12 и 3.3.13.[3] Рекомендация Ruby - обновиться до одной из этих версий или новее.[3]
28 октября - отчёт об инциденте Alpine. Alpine Linux опубликовал историю попытки эксплуатации CVE-2026-97229 на их публичном GitLab-инстансе gitlab.alpinelinux.org.[2] Это реальное доказательство того, что совет «просто обнови GitLab» проваливается, когда Ruby у вас свой.
Инцидент Alpine: апгрейд был, а дыра осталась
20 и 21 октября новозарегистрированный аккаунт отправлял на инстанс Alpine crafted CI-конфиги - через неделю после выхода фикса GitLab. Инстанс работал на 19.3.4, где фикса из релиза 12 октября не было (13 октября Alpine по ошибке обновились до 19.3.4, а не до 19.3.5, где фикс был).[2] Crafted-конфиги восемь раз крашили воркеры GitLab; в четырёх крашах атакующий прошёл часть эксплойта и упал на следующем шаге. Также атакующий скачал container image GitLab’а Alpine - по их оценке, чтобы подогнать эксплойт под их конкретную сборку.[2]
Alpine не нашли доказательств, что атакующий выполнил код, но исключить этого не смогли - и потому обработали инстанс как скомпрометированный: пересобрали с нуля, сбросили все пароли и заменили секретные ключи GitLab.[2]
А вот часть, важная для всех, кто держит свой GitLab: 22 октября Alpine обновили инстанс до GitLab 19.3.6 - версии, в которой фикс был (19.3.6 выше патч-линии 19.3.5). И это всё равно не закрыло дыру:
«Наш апгрейд не починил уязвимость. Обновление до GitLab 19.3.6 22 октября уязвимость не устранило. Фикс не в собственном коде GitLab, а в патче к Ruby, который GitLab применяет только в официальных пакетах. В нашем образе использовался стандартный Ruby, поэтому патч туда не попал».
В этом предложении - весь урок этой CVE. Номер версии GitLab вырос, а уязвимость осталась; инстанс оставался под ударом, пока его не пересобрали с Ruby, где фикс есть (3.3.13 или новее).[2] Поверхность атаки не исчезла только потому, что тег GitLab стал новее.
На какой стороне ваша сборка?
Единственный вопрос, который решает, достаточно ли вам апгрейда GitLab: ваш Ruby - из официальной упаковки GitLab или он свой?
Случай 1 - официальная упаковка GitLab: апгрейд GitLab закрывает всё. Linux package (Omnibus GitLab - «mature, scalable»-метод, который использует сам GitLab.com) и официальные container images / Helm-чарты GitLab бандлят собственный пропатченный Ruby.[1][6] Здесь применение patch-релиза - 19.4.2, 19.3.5 или 19.2.8 или новее - подтягивает и исправленный Ruby. Это тот случай, под который написана рекомендация GitLab «self-managed-установки должны обновиться немедленно».[1]
Случай 2 - свой Ruby: апгрейд GitLab обязателен, но недостаточен. Если вы собрали GitLab из исходников (метод само-компиляции, который GitLab позиционирует для платформ вроде *BSD, где пакетов нет), или встраиваете GitLab в свой контейнерный образ на основе стандартного Ruby, а не базового образа GitLab - апгрейд GitLab движок регэкспов не патчит, потому что ваша сборка тянет Ruby, к которому GitLab не прикасался.[2][6] В этом случае нужно дополнительно обновить Ruby до 3.3.13, 3.4.12 или 4.0.9 или новее.[2][3] Инструкция Alpine именно такая: «Если вы запускаете GitLab со своим Ruby (из исходников или в своём container image), а не из пакетов GitLab - апгрейда GitLab недостаточно: нужен Ruby 3.3.13, 3.4.12, 4.0.9 или новее».[2]
Случай 3 - конкретно образ alpinelinux/gitlab. Если вы используете контейнерный образ GitLab от Alpine, учтите: фикс не распространяется ретроактивно по тегам. Образ alpinelinux/gitlab:19.3.6, опубликованный 27 октября, содержит Ruby 3.3.13; любой образ 19.3.6, Pull-нутый до 27 октября, и все старые теги - уязвимы.[2] Если вы были на этом теге - потяните его заново, и не думайте, что локальная копия автоматически починилась.
Как реально проверить свою сборку
Только команды из первичной документации. Начните с подтверждения версии GitLab и того Ruby, на котором он работает. Обслуживающая rake-задача GitLab делает оба шага сразу - в Linux package это:[4]
sudo gitlab-rake gitlab:env:info
В выводе есть строка Ruby Version (рантайм-Ruby, на котором GitLab) и строка GitLab information → Version (релиз GitLab).[4] Если эта версия Ruby ниже 3.3.13 / 3.4.12 / 4.0.9 в сборке из случая 2 - высокий номер версии GitLab вас не спасает.
Чтобы вывести только константу Ruby внутри работающего GitLab, Rails Runner - задокументированный способ запускать разовый Ruby-код внутри окружения GitLab - даёт прямой ответ:[5]
sudo gitlab-rails runner "puts RUBY_VERSION"
Для кастомного контейнерного образа эквивалент - свой Ruby внутри образа. Тот самый тег 19.3.6, Pull-нутый в конце октября, проверьте, действительно ли в нём Ruby 3.3.13 - версия в строке и есть самый быстрый сигнал, что фикс Onigmo на месте:
docker run --rm your-gitlab-image-tag ruby -v
А на Alpine, где пакетный Ruby уже несёт фикс в актуальных релизах, достаточно обновить пакет (Alpine 3.22/3.23/3.24/edge поставляют Ruby 3.4.12, а 3.21 - 3.3.13):[2]
apk upgrade
Чек-лист после апгрейда
Проходите по порядку; первые два вопроса решают всё, и версия GitLab сама по себе ни на один из них не отвечает.
- Знайте свой метод установки. Linux package, официальные Docker/Helm или своя сборка? Только первые два наследуют Ruby-патч из релиза GitLab.[1][6]
- Проверьте рантайм-Ruby, а не только тег GitLab. Выполните
sudo gitlab-rake gitlab:env:info(илиgitlab-rails runner "puts RUBY_VERSION") и убедитесь, что GitLab реально исполняется на Ruby 3.3.13 / 3.4.12 / 4.0.9 или новее - для сборок из случая 2 это обязательно.[4][5] - Потяните заново образы, которые вы уже тянули. Теги, Pull-нутые до даты их фикса, всё ещё старый уязвимый Ruby - задокументированный пример:
alpinelinux/gitlab:19.3.6, Pull-нутый до 27 октября.[2] - Подтвердите patch-релиз GitLab. Для официальной упаковки цель - 19.4.2 / 19.3.5 / 19.2.8 или новее - эта линия закрывает весь security-батч, а не только эту CVE.[1]
- Любой инстанс, которому нельзя исключить компрометацию, - скомпрометирован. Позиция Alpine - доказательств выполнения кода нет, но машина обработана как скомпрометированная, пересобрана, все пароли сброшены, секретные ключи заменены - правильная планка для сервера, на который прилетали hostile CI-конфиги против известной RCE-дыры.[2] Если такое произошло с вами - не останавливайтесь на бампе версии.
Больший урок
У CVE-2026-97229 два края. Первый - операционный: админам self-hosted GitLab нужно знать, их Ruby - GitLab’овский или свой, потому что эти два пути фикса расходятся. Второй - про то, как вы аудите security-фикс: «пропатченный» релиз настолько пропатчен, насколько пропатчены все зависимости, которые он молча бандлит или не бандлит. Релиз-нота GitLab прямо говорит, что фикс - это Ruby-патч; отчёт Alpine показывает, что бывает, когда оператор обновляет GitLab и считает, что это вся история.
А ещё это расширяет радиус поражения: Onigmo - внутри каждой сборки Ruby, не только GitLab. Любой Ruby-сервис в вашей инфраструктуре, который компилирует регулярные выражения из контролируемых пользователем строк - самописный CI, поиск, валидирующий endpoint - теоретически достижим этим классом багов, пока не работает на Ruby 3.3.13 / 3.4.12 / 4.0.9 или новее.[2][3] Митигация, которую Ruby предлагает для кода, который быстро не обновить, - грубая, но надёжная: перестаньте компилировать регэкспы из ненадёжного ввода.[3]
Self-hosting означает, что ритм патчинга - а теперь и ритм зависимостей - ваш. Эта CVE - чистое напоминание о том, что на самостоятельно управляемой машине строка версии на двери - это ещё не вся security-позиция. Дисциплина, которая держит ваш Nginx на честной патч-линии, - ровно то, о чём на самом деле этот инцидент.
Источники
- GitLab - «GitLab Critical Patch Release: 19.4.2, 19.3.5, 19.2.8» (12.10.2026; critical patch release, таблица security-фиксов, включая CVE-2026-97229 как Integer Overflow в компиляторе регэкспов CI/CD, и Ruby-regexp-патчи GitLab в omnibus-gitlab / CNG). https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-4-2-released/ - checked 2026-10-29
- Alpine Linux - «GitLab security incident: attempted exploitation of CVE-2026-97229» (28.10.2026; таймлайн атаки 20–21 октября, апгрейд до 19.3.6 не починил, инструкция для своего Ruby, образ
alpinelinux/gitlab:19.3.6опубликован 27 октября с Ruby 3.3.13, Ruby-пакеты Alpine,apk upgrade, CVSS 10.0). https://alpinelinux.org/posts/GitLab-security-incident-CVE-2026-97229.html - checked 2026-10-29 - Ruby - «Regexp compilation fixes related to GitLab’s CVE-2026-89078, CVE-2026-93577, CVE-2026-97229 and CVE-2026-110264» (23.10.2026; релизы 4.0.9/3.4.12/3.3.13, первопричины в Onigmo,
Regexp.timeoutне защищает, короткие паттерны триггерят, избегать компиляции регэкспов из ненадёжного ввода). https://www.ruby-lang.org/en/news/2026/10/23/regexp-fixes-for-gitlab-cves/ - checked 2026-10-29 - GitLab Docs - «Maintenance Rake tasks: Gather GitLab and system information» (
sudo gitlab-rake gitlab:env:infoдля Linux package,bundle exec rake gitlab:env:info RAILS_ENV=productionдля self-compiled; вывод показывает Ruby Version и GitLab Version). https://docs.gitlab.com/administration/raketasks/maintenance/ - checked 2026-10-29 - GitLab Docs - «Rails console: Using the Rails Runner» (синтаксис
sudo gitlab-rails runner "RAILS_COMMAND"). https://docs.gitlab.com/administration/operations/rails_console/ - checked 2026-10-29 - GitLab Docs - «Installation methods» (Linux package/omnibus используется на GitLab.com; Docker; Helm chart; self-compiled для неподдерживаемых систем вроде *BSD). https://docs.gitlab.com/install/install_methods/ - checked 2026-10-29