If you self-host GitLab, the CVE that just hit the ecosystem is a reminder of a very specific trap: sometimes the fix is not in the product you think you are updating. CVE-2026-97229 is a critical (CVSS 10.0) vulnerability that lets an unauthenticated attacker run arbitrary code on your GitLab server by submitting a specially crafted regular expression in a CI/CD configuration.[1][2] GitLab patched it on 12 October across three release lines - but the actual fix is a set of patches to Ruby’s regex engine (Onigmo), which GitLab only applies inside its own official packages and container images.[1][2] If your GitLab runs on a Ruby you built yourself, or a custom image over standard Ruby, upgrading GitLab alone will not close the hole.
That is not a theoretical edge case. The Alpine Linux project ran into it in the worst possible way: their instance was upgraded to a patched GitLab version and was still vulnerable, because their image used unpatched standard Ruby.[2] This article walks through what the vulnerability is, the three dates that actually matter, what the Alpine incident proves, and a concrete checklist for finding out which side your own GitLab is on - with only commands that come from GitLab and Ruby primary documentation.
The vulnerability: a regex you can’t see turning into code
GitLab compiles regular expressions that come from its users - patterns in CI configuration, for example - with Regexp.new.[3] CVE-2026-97229 is an integer overflow in the CI/CD Regular Expression Compiler: under certain conditions, an unauthenticated user could execute arbitrary code on the GitLab server via a specially crafted regular expression in a CI/CD configuration, due to insufficient regular expression validation.[1]
The overflow happens inside Ruby, not GitLab. The regex engine GitLab uses is Onigmo, which is built into Ruby. When Ruby compiles a maliciously crafted pattern, it can corrupt memory, and that corruption can be turned into remote code execution on the GitLab server.[2][3] Because the bug lives in the interpreter’s regex compiler, it is not a GitLab-specific logic error that a GitLab code change can strip out - it is a defect in the runtime that GitLab happens to surface.
Two details from the upstream Ruby announcement are worth internalizing, because they make this harder to contain than most regex DoS bugs:[3]
Regexp.timeoutdoes not protect against these bugs. This is not a resource-exhaustion timeout problem you can configure your way around.- Limiting the length of patterns does not reliably protect against them either - some of the bugs can be triggered by short patterns.
So the usual “limit input, time-box the regex” mitigation playbook does not apply. The threat model is: any Ruby application that compiles a regular expression from attacker-controlled input - via Regexp.new, Regexp.union, Regexp.compile, or interpolation into a regexp literal - is the kind of surface this class of bug lives behind.[3] Applications that only use regexps written by their own developers are not affected; the danger is when the pattern string itself can be influenced externally.[3]
The three dates that matter
The confusion around this CVE comes from the fact that two different “fixes” shipped on two different schedules, and they are not interchangeable.
12 October - GitLab’s critical patch release. GitLab published 19.4.2, 19.3.5 and 19.2.8 as a critical patch release, strongly recommending that all self-managed installations upgrade immediately.[1] CVE-2026-97229 was the headline, but the same batch carried more. The security-fixes table lists a High cross-site scripting issue in syntax highlighting, a High improper-authorization issue in the downstream pipeline creation service, a High permissive-regex issue in repository file access, a High incorrect-authorization issue in scheduled pipeline execution policy (EE), a High LLM-prompting issue in GitLab Duo (EE), and a Medium sensitive-information-exposure issue in GitLab Pages, among others.[1]
Crucially, look at what the GitLab fix actually was. The bug-fix lists for 19.4.2, 19.3.5 and 19.2.8 all include merge requests titled “Add Ruby regexp program size patch for 3.3 and 3.4” and “Add Ruby regexp repeat-range double-free patch for 3.3 and 3.4” - applied to the omnibus-gitlab and CNG (Cloud Native GitLab) container images.[1] GitLab does not fix Onigmo itself; it backports the Ruby patch into the Ruby that its own packages and images ship.
23 October - the upstream Ruby fix. Ruby released 3.3.13, 3.4.12 and 4.0.9 with the underlying fixes in Onigmo.[2][3] The Ruby announcement is explicit that these address the bugs behind GitLab’s CVE-2026-89078, CVE-2026-93577, CVE-2026-97229 (an integer overflow in the size of a compiled pattern) and CVE-2026-110264 - and that 4.0.9, 3.4.12 and 3.3.13 contain all of these fixes.[3] Ruby’s recommended action is to upgrade to one of those versions or later.[3]
28 October - the Alpine incident report. Alpine Linux published the account of an attempted exploitation of CVE-2026-97229 on their public GitLab instance, gitlab.alpinelinux.org.[2] It is the real-world proof that the “just upgrade GitLab” advice fails when your Ruby is your own.
The Alpine incident: upgrade happened, hole stayed open
On 20 and 21 October, a newly registered account sent crafted CI configurations to Alpine’s instance - a week after the GitLab fix was released. The instance was running 19.3.4, which did not have the 12 October fix (Alpine had mistakenly upgraded to 19.3.4 on 13 October instead of 19.3.5, which carried it).[2] The crafted configs crashed GitLab worker processes eight times; in four of those crashes the attacker got partway through the exploit and failed at the next step. The attacker also downloaded Alpine’s GitLab container image, which they believe was to tune the exploit to their exact build.[2]
Alpine found no evidence the attacker succeeded in running code, but could not rule it out - so they treated the instance as compromised, rebuilt it from scratch, reset all passwords and replaced GitLab’s secret keys.[2]
Here is the part that matters for everyone running their own GitLab: on 22 October Alpine upgraded the instance to GitLab 19.3.6 - a version that did carry the fix, since 19.3.6 sits above the 19.3.5 patch line. And it still did not close the hole:
“Our upgrade didn’t fix the vulnerability. Upgrading to GitLab 19.3.6 on October 22 didn’t fix the vulnerability. The fix isn’t in GitLab’s own code but in a patch to Ruby, which GitLab applies only in its official packages. Our image used standard Ruby, so it didn’t get the patch.”
That sentence is the entire lesson of this CVE. The GitLab version number went up and the vulnerability stayed; the instance remained exposed until they rebuilt it with a Ruby carrying the fix (3.3.13 or later).[2] The attack surface did not disappear just because the GitLab tag did.
Which side is your build on?
The single question that decides whether your GitLab upgrade is sufficient is: does your Ruby come from GitLab’s official packaging, or is it your own?
Case 1 - official GitLab packaging: upgrade GitLab and you are covered. The Linux package (Omnibus GitLab, the “mature, scalable” method that GitLab.com itself uses) and the official GitLab container images / Helm charts bundle GitLab’s own patched Ruby.[1][6] On these, applying the patch release - 19.4.2, 19.3.5 or 19.2.8 or later - brings in the fixed Ruby with it. This is what GitLab describes when it says its official packages and container images carry the Ruby patch; and it is the case GitLab’s own “self-managed installations should upgrade immediately” recommendation is written for.[1]
Case 2 - your own Ruby: GitLab upgrade is necessary but not sufficient. If you run GitLab compiled from source (the self-compiled installation method, which GitLab positions for platforms like *BSD where packages are unavailable), or you build your GitLab into your own container image that starts from standard Ruby rather than GitLab’s base image - then upgrading GitLab does not patch the regex engine, because your build pulls in a Ruby that GitLab did not touch.[2][6] For this case you must additionally update Ruby to 3.3.13, 3.4.12 or 4.0.9 or later.[2][3] Alpine’s guidance is exactly this: “If you run GitLab with your own Ruby (from source or in your own container image) rather than GitLab’s packages, upgrading GitLab is not enough: you also need Ruby 3.3.13, 3.4.12, 4.0.9 or later."[2]
Case 3 - the alpinelinux/gitlab image specifically. If you run Alpine’s own GitLab container image, note that the fix is not retroactive across tags. The alpinelinux/gitlab:19.3.6 image published on 27 October contains Ruby 3.3.13; any 19.3.6 image you pulled before 27 October, and all older tags, are vulnerable.[2] Pull the tag again if you were on it, and do not assume your existing local copy is fixed.
How to actually check your build
Only commands from primary documentation. Start by confirming your GitLab version and the Ruby it is running on. GitLab’s maintenance rake task does both in one shot - on the Linux package it is:[4]
sudo gitlab-rake gitlab:env:info
The output includes a Ruby Version line (the runtime Ruby GitLab is on) and a GitLab information → Version line (the GitLab release).[4] If that Ruby version is below 3.3.13 / 3.4.12 / 4.0.9 in a Case-2 build, the GitLab version number being high does not save you.
To print just the Ruby constant inside the running GitLab, GitLab’s Rails Runner - the documented way to run one-off Ruby code inside the GitLab environment - gives you a direct answer:[5]
sudo gitlab-rails runner "puts RUBY_VERSION"
For a custom container image, the equivalent surface is your image’s own Ruby. Tag that 19.3.6 you pulled in late October and check whether it actually carries Ruby 3.3.13 - the version string is the fastest signal that the Onigmo fix is present:
docker run --rm your-gitlab-image-tag ruby -v
And on Alpine, where the packaged Ruby already ships the fix in current releases, update the package to get it (Alpine 3.22/3.23/3.24/edge ship Ruby 3.4.12, and 3.21 ships 3.3.13):[2]
apk upgrade
Checklist after an upgrade
Work through these in order; the first two questions decide everything, and the GitLab version alone never answers them.
- Know your install method. Linux package, official Docker/Helm, or your own build? Only the first two inherit the Ruby patch from the GitLab release.[1][6]
- Check the runtime Ruby, not just the GitLab tag. Run
sudo gitlab-rake gitlab:env:info(orgitlab-rails runner "puts RUBY_VERSION") and confirm the GitLab is actually executing on Ruby 3.3.13 / 3.4.12 / 4.0.9 or later for any Case-2 build.[4][5] - Re-pull images you already had. Tags pulled before their fix date are still the old, vulnerable Ruby -
alpinelinux/gitlab:19.3.6pulled before 27 October is the documented example.[2] - Confirm the GitLab patch release. For official packaging you are looking for 19.4.2 / 19.3.5 / 19.2.8 or later - that line is the fix line for the whole security batch, not only this CVE.[1]
- Treat any instance you cannot rule out as compromised. Alpine’s posture - no evidence of code execution, but treated the box as compromised, rebuilt it, reset every password and replaced secret keys - is the correct bar for a server that received hostile CI configs against a known-RCE bug.[2] If that happened to you, do not stop at the version bump.
The bigger lesson
CVE-2026-97229 has two edges. The immediate one is operational: self-hosted GitLab admins must know whether their Ruby is GitLab’s or their own, because the two fix paths diverge. The longer one is about how you audit a security fix: a “patched” release is only as patched as every dependency it silently bundles or fails to bundle. GitLab’s own release note tells you the fix is a Ruby patch; the Alpine report shows what happens when an operator upgrades GitLab and assumes that was the whole story.
This also widens the blast radius: Onigmo is inside every Ruby build, not just GitLab’s. Any Ruby service on your infrastructure that compiles regular expressions from user-controlled strings - whether that is a self-hosted CI, a search box, or a validation endpoint - is theoretically reachable by this class of bug until it runs Ruby 3.3.13 / 3.4.12 / 4.0.9 or later.[2][3] The mitigation Ruby offers for code you cannot update quickly is blunt but reliable: stop compiling regular expressions from untrusted input.[3]
Self-hosting means the patch cadence - and now the dependency cadence - is yours. The GitLab CVE is a clean reminder that on a self-managed box, the version string on the door is not the whole security posture. If you are planning or operating a self-hosted deployment, the same discipline that keeps your Nginx patch level honest is what this kind of incident is really about.
Sources
- GitLab - “GitLab Critical Patch Release: 19.4.2, 19.3.5, 19.2.8” (12 Oct 2026; critical patch release, security-fixes table incl. CVE-2026-97229 as Integer Overflow in CI/CD Regular Expression Compiler, and the GitLab Ruby-regexp patch MRs in 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 Oct 2026; attack timeline Oct 20–21, upgrade to 19.3.6 didn’t fix it, own-Ruby guidance,
alpinelinux/gitlab:19.3.6published 27 Oct with Ruby 3.3.13, Alpine Ruby packages,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 Oct 2026; releases 4.0.9/3.4.12/3.3.13, Onigmo underlying bugs,
Regexp.timeoutdoes not protect, short patterns can trigger, avoid compiling regexps from untrusted input). 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:infofor Linux package,bundle exec rake gitlab:env:info RAILS_ENV=productionfor self-compiled; output shows Ruby Version and 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"syntax). https://docs.gitlab.com/administration/operations/rails_console/ - checked 2026-10-29 - GitLab Docs - “Installation methods” (Linux package/omnibus used on GitLab.com; Docker; Helm chart; self-compiled for unsupported systems like *BSD). https://docs.gitlab.com/install/install_methods/ - checked 2026-10-29