On 12 October 2026 GitLab shipped a critical patch release, versions 19.4.2, 19.3.5 and 19.2.8, that fixes CVE-2026-97229, an integer overflow in what GitLab calls its CI/CD Regular Expression Compiler. GitLab scores it CVSS 10.0. Its advisory says that, under certain conditions, the flaw could have let an unauthenticated user run arbitrary code on the GitLab server through a specially crafted regular expression in a CI/CD configuration.
Two weeks later the story got harder. On 23 October the Ruby project shipped Ruby 3.3.13, 3.4.12 and 4.0.9 with fixes to Onigmo, the regular expression engine built into Ruby, and explained that the GitLab CVEs come from bugs in that engine. On 28 October the Alpine Linux project published a post about an attempt to exploit CVE-2026-97229 against its own GitLab instance. Alpine wrote that upgrading GitLab alone did not close the hole on its server, because the fix is a patch to Ruby that GitLab applies in its own packages.
That one detail decides what "patched" means for you. If you run GitLab from GitLab's official packages or images, the GitLab version number tells you most of the story. If you run GitLab on a Ruby you supply yourself, from a source install, a distribution package or a custom image, the GitLab version number is not enough. You also need a fixed Ruby.
This post is for the people who run self-hosted GitLab and the CI runners attached to it. It covers what GitLab, Ruby and Alpine have each said, with dates; how to find the exact GitLab and Ruby versions you run on every install type; the upgrade path notes GitLab publishes; how to think about CI exposure, sign-ups and runners; and when to rotate tokens and secrets. Every fact below is stated as of 29 October 2026 and is tied to the source that published it.
What GitLab published on 12 October
GitLab's patch release page for 19.4.2, 19.3.5 and 19.2.8 opens with the line that matters for planning: "On October 12, 2026, we released versions 19.4.2, 19.3.5, 19.2.8 for GitLab Community Edition (CE) and Enterprise Edition (EE)." The release fixes more than twenty CVEs. CVE-2026-97229 is the one rated critical.
The CVE entry reads, in GitLab's words: "GitLab has remediated an issue that, under certain conditions, could have allowed an unauthenticated user to execute arbitrary code on the GitLab server via a specially crafted regular expression in a CI/CD configuration." The title is "Integer Overflow issue in CI/CD Regular Expression Compiler impacts GitLab CE/EE." The affected ranges are "all versions from 19.2 before 19.2.8, 19.3 before 19.3.5, and 19.4 before 19.4.2." GitLab credits the researcher joaxcar, who reported it through GitLab's HackerOne bug bounty program.
Three more details on the same page are worth reading closely.
First, the bug fix list for each release line includes "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." The page does not tie those entries to a specific CVE, but they line up with what the Ruby project later described. This is how GitLab delivered the fix to its own packages: by patching the Ruby it ships.
Second, GitLab lists six threat detections for self-managed customers, so that administrators can look for attempts. The names are: Onigmo native worker crash; Graphql cilint hostile regexp; ci regexp oversized non ascii path; ci regexp guard rejection; ci lint workhorse worker crash; and ci lint scripted client error burst. We come back to these under technical details.
Third, there is a version number trap. Some GitLab blog and forum titles for this release say 19.3.4. The docs page says plainly: "19.3.4 had a bug that affected merge request pages when the Ona integration was enabled. This is fixed in 19.3.5." The affected range in the CVE entry is "19.3 before 19.3.5". Treat 19.3.5 as the first fixed 19.3 release, and treat 19.3.4 as vulnerable. Alpine's post shows why this matters in practice: Alpine says that on 13 October it upgraded to 19.3.4 "instead of 19.3.5, which had the fix, by mistake."
GitLab gives no workaround on the page. Its instruction is that self-managed installations be "upgraded to the latest version as soon as possible."
What Ruby published on 23 October
On 23 October 2026 the Ruby project posted "Regexp compilation fixes related to GitLab's CVE-2026-89078, CVE-2026-93577, CVE-2026-97229 and CVE-2026-110264", written by k0kubun, alongside the Ruby 3.3.13, 3.4.12 and 4.0.9 release notes.
The post explains the link plainly. GitLab found that specially crafted patterns could corrupt memory while Ruby's regular expression engine, Onigmo, compiled them, and that this could be turned into remote code execution on GitLab servers. GitLab patched the Ruby it ships, assigned the CVEs, reported the issues to Ruby and coordinated the fixes. Ruby credits Johan Carlsson (joaxcar) with finding the integer overflow fixed by Ruby change #19020 through GitLab's bug bounty program.
Ruby's table maps each GitLab CVE to a Ruby bug and the first fixed release on each Ruby branch.
| GitLab CVE | GitLab release named by Ruby | Ruby bug | Ruby 4.0 | Ruby 3.4 | Ruby 3.3 |
|---|---|---|---|---|---|
| CVE-2026-89078 | 19.4.1 | Double free when a pattern parse fails after an alternation (#13332) | 4.0.0 | 3.4.12 | 3.3.13 |
| CVE-2026-93577 | 19.4.1 | Integer overflow in the size of a compiled pattern (#19020) | 4.0.8 | 3.4.12 | 3.3.13 |
| CVE-2026-97229 | 19.4.2 | Integer overflow in the size of a compiled pattern (#19020) | 4.0.8 | 3.4.12 | 3.3.13 |
| CVE-2026-110264 | 19.5.1 | Overflow of internal IDs in a pattern with many repeats (#16801) | 4.0.9 | 3.4.12 | 3.3.13 |
Ruby's conclusion is that Ruby 4.0.9, 3.4.12 and 3.3.13 contain all of these fixes. On the 4.0 branch, 4.0.8 already covers CVE-2026-97229, but only 4.0.9 covers all four. The simple instruction is to run 4.0.9, 3.4.12, 3.3.13 or later on the matching branch.
The Ruby post also says what does not help, which saves time during an incident:
Regexp.timeoutdoes not protect against these bugs. A timeout bounds matching time; these bugs happen while the pattern is being compiled.- Limiting pattern length does not reliably protect either, because Ruby says some of the bugs are triggered by short patterns.
- If you cannot upgrade, Ruby's advice is to avoid compiling regular expressions from untrusted input.
Ruby also scopes the risk for everyone who is not running GitLab. The bugs are reached only when an application compiles a regular expression from an attacker-controlled string, for example through Regexp.new, Regexp.union, Regexp.compile, or interpolation into a literal such as /#{input}/. Applications that use only developer-written regular expressions are not affected. Ruby notes that it has generally considered compiling untrusted regular expressions unsafe, that it has not requested CVEs of its own for these fixes yet, and that it plans to discuss whether to treat such issues as vulnerabilities.
Ruby 3.3 is in its security maintenance phase. The Ruby post says 3.3.13 includes these fixes as an exception, because GitLab assigned CVEs to issues that rely on them.
What Alpine Linux reported on 28 October
The Alpine Linux project runs its own GitLab instance for its package repository work. On 28 October 2026 it published "GitLab security incident: attempted exploitation of CVE-2026-97229". Everything in this section is Alpine's own account, as stated in that post.
Alpine describes CVE-2026-97229 as a critical, CVSS 10.0 flaw in how GitLab evaluates regular expressions in CI configuration, with the bug in Onigmo. Alpine was running GitLab 19.3.4 at the time of the attack. It upgraded to 19.3.6 on 22 October, and says that upgrade did not fix the flaw on its server. Alpine's explanation: "The fix is a patch to Ruby, which GitLab applies only in its own packages." Alpine runs GitLab on Ruby from its own distribution, so the GitLab upgrade did not change the Ruby underneath.
Alpine's timeline, as published:
| Date (2026) | Event, per Alpine |
|---|---|
| 12 Oct | GitLab releases the fix |
| 13 Oct | Alpine upgrades to 19.3.4 instead of 19.3.5 by mistake |
| 20 to 21 Oct | A newly registered account sends crafted CI configurations; GitLab worker processes crash 8 times, 4 of them partway through the exploit; the attacker also downloads the container image |
| 22 Oct | Upgrade to GitLab 19.3.6, which does not fix the flaw on Alpine's install |
| 23 Oct | Attacker's account blocked; upstream Ruby releases the fix |
| 24 Oct | Alpine verifies aports commits, packages and the 21 October releases against records outside GitLab |
| 25 to 26 Oct | Attacker tries to return and regain access; attempts fail; Alpine finds the instance still vulnerable on 26 Oct |
| 27 Oct | GitLab closed to everyone except developers and build servers; rebuild starts |
| 28 Oct | Rebuild complete; passwords reset; GitLab secret keys replaced |
Alpine's findings, again in its own account: releases 3.21.9, 3.22.7, 3.23.7 and 3.24.3, their images, and packages built from 20 to 24 October were not tampered with, based on checks against outside records. Package signing keys were never stored on the GitLab server. Alpine found no evidence that the attacker's code ran, though it says logging gaps mean this cannot be fully confirmed. It treated the instance as compromised and rebuilt it.
Alpine also published what it ships. Alpine 3.22, 3.23, 3.24 and edge ship Ruby 3.4.12; Alpine 3.21 ships Ruby 3.3.13. For people who use Alpine's GitLab container image, Alpine says to re-pull alpinelinux/gitlab:19.3.6: the image published on 27 October contains Ruby 3.3.13, and earlier pulls of 19.3.6 and older tags remain vulnerable.
Alpine's own follow-up list is a useful checklist for any team: apply critical GitLab security releases within a day and check whether component patches are also needed; log outbound and database connections and keep logs longer than 14 days; add signed release tags. Alpine asks its users to change any reused password elsewhere and to set a new GitLab password through "Forgot your password?" when the instance reopens.
We draw one plain lesson from Alpine's account and attribute nothing else to it. A version string on the application is a statement about the application. When the vulnerable code sits in a runtime the application is built on, you have to check the runtime too.
Technical details
This section collects the defender-useful specifics from the three primary sources, in one place.
GitLab's vector is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H, scored 10.0 for Community and Enterprise Editions.
The vector is worth reading term by term. PR:N means GitLab rates the attack as needing no privileges. S:C means a successful attack reaches beyond the vulnerable component, which for a GitLab server means the code execution lands in the server context that holds repositories, CI/CD variables and integration secrets. The advisory's own qualifier, "under certain conditions", is not explained on the page. Do not read it as "my instance is probably fine". Read it as "patch first, then work out whether the conditions applied to you".
As of 29 October 2026, GitLab lists 19.2 before 19.2.8, 19.3 before 19.3.5 and 19.4 before 19.4.2 as affected by CVE-2026-97229. 19.3.4 is still affected, and some titles mislabel the release. Older lines are not listed for this CVE but are covered by the follow-up below.
The follow-up patch on 21 October: CVE-2026-110264
On 21 October 2026 GitLab released another critical patch: 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10 and 18.11.13. Its one critical entry is CVE-2026-110264, "Integer Overflow issue in Regular Expression Compiler impacts GitLab CE/EE", also scored CVSS 10.0. GitLab's page says it could let an unauthenticated user potentially run arbitrary code on the GitLab server by compiling a specially crafted regular expression, and that it affects all releases from 7.0 up to the fixed version on each branch. The fix is a Ruby regexp repeat ID overflow patch for Ruby 3.3, 3.4 and 4.0, which matches Ruby's bug #16801. GitLab says GitLab.com is patched and that Dedicated tenants on 19.3.5-internal0 or 19.4.2-internal0 and later already include the fixes.
This changes the practical target. If you are upgrading now, go to the newest patch on your line, not to the first release that fixed CVE-2026-97229. On 19.3 that means 19.3.6 or later; on 19.4, 19.4.3 or later. Older lines that were outside the CVE-2026-97229 range, 19.1, 19.0 and 18.11, have backports of the CVE-2026-110264 fix in 19.1.9, 19.0.10 and 18.11.13. Anything older than 18.11 is outside the lines GitLab patched and needs an upgrade to a maintained version.
This patch also includes database migrations, with downtime on single node installs, and it lists an Onigmo native worker crash detection again.
Threat detections named by GitLab
GitLab published six detection names for self-managed customers. The patch page gives only the names, not the logic behind them, so read them as pointers to where to look rather than as a description of the exploit.
| Detection name (GitLab) | Where to start looking |
|---|---|
| Onigmo native worker crash | Puma and Sidekiq logs for workers that died in native code |
| Graphql cilint hostile regexp | GraphQL request logs for CI lint calls |
| ci regexp oversized non ascii path | Rails request logs for CI configuration validation |
| ci regexp guard rejection | Rails logs after patching, for patterns the patched version refuses |
| ci lint workhorse worker crash | Workhorse logs for upstream errors on CI lint requests |
| ci lint scripted client error burst | API logs for bursts of errors from one client on CI lint |
Step 1: find exactly which GitLab and Ruby you run
Do this for every GitLab instance you own, including the test and staging instances people forget. For each one, record two numbers: the GitLab version and the Ruby version that GitLab actually runs on.
Linux package (Omnibus) installs
The Linux package bundles its own Ruby under /opt/gitlab/embedded. That bundled Ruby is the one GitLab patched.
# GitLab and the Ruby it runs on, in one report
sudo gitlab-rake gitlab:env:info
# The embedded Ruby directly
/opt/gitlab/embedded/bin/ruby -v
# Every bundled component and version
grep -iE '^(gitlab-rails|ruby) ' /opt/gitlab/version-manifest.txt
# Package version as the OS sees it
dpkg -l | grep -E 'gitlab-(ce|ee)' # Debian and Ubuntu
rpm -q gitlab-ee gitlab-ce # RHEL family
If the package version is 19.4.2, 19.3.5, 19.2.8 or a later patch on the same line, you have GitLab's fix for CVE-2026-97229 in the bundled Ruby. Upgrading the system Ruby on the host does nothing for an Omnibus install, because GitLab does not use it.
Official Docker images
# Version of the running container
docker exec -it gitlab cat /opt/gitlab/embedded/service/gitlab-rails/VERSION
docker exec -it gitlab /opt/gitlab/embedded/bin/ruby -v
# Image tag the container was started from
docker inspect --format '{{.Config.Image}}' gitlab
Official GitLab images are built from the Linux package, so the same reasoning applies: the GitLab version tells you whether the patched Ruby is inside. Pin the tag you deploy to an exact version, for example gitlab/gitlab-ee:19.4.2-ee.0 or later, rather than latest, so you can prove what is running.
Helm chart and Operator installs
The cloud native chart runs GitLab Rails in the webservice and Sidekiq pods, and those images carry their own Ruby.
helm list -n gitlab
kubectl -n gitlab get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{" "}{end}{"\n"}{end}' | grep -E 'webservice|sidekiq|toolbox'
kubectl -n gitlab exec deploy/gitlab-toolbox -- ruby -v
Check that the webservice, Sidekiq and toolbox images all moved to the fixed GitLab version. A partial rollout leaves the vulnerable code running in whatever pods did not restart.
Source installs, distribution packages and custom images
This is the group Alpine's account warns about. If GitLab runs on a Ruby you installed, through your distribution, a version manager, or a base image, then GitLab's patch to its own bundled Ruby never reached you.
# From the GitLab source directory
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
sudo -u git -H ruby -v
which -a ruby
# What the distribution ships
apk version ruby # Alpine
dpkg -l | grep -E '^ii +ruby[0-9.]* ' # Debian and Ubuntu
rpm -q ruby # RHEL family
You need both conditions: GitLab on a fixed release, and Ruby at 3.3.13, 3.4.12, 4.0.9 or later on its branch. If Ruby is older, upgrade it, restart every GitLab Ruby process (Puma and Sidekiq), and check ruby -v again as the git user.
From the API, across many instances
When you have many instances, the version endpoint gives a quick inventory. It reports the GitLab version, not Ruby, so use it to find candidates and then check Ruby on hosts that are not Omnibus.
curl --silent --header "PRIVATE-TOKEN: $TOKEN" https://gitlab.example.com/api/v4/version
Keep the output. It is evidence for the follow-up questions you will get from auditors and from your own leadership.
Step 2: upgrade the right way
GitLab's patch page includes notes that change how you schedule the work:
- "This patch includes database migrations that may impact your upgrade process."
- On a single node: "This patch will cause downtime during the upgrade as migrations must complete before GitLab can start."
- On multiple nodes: "With proper zero-downtime upgrade procedures, this patch can be applied without downtime."
- Post-deploy migrations are listed for 19.3.5.
Plan the path
If you are already on 19.2, 19.3 or 19.4, the move to the latest patch on your line is a patch upgrade inside one minor version. If you are further behind, GitLab publishes required upgrade stops, and its Upgrade Path tool lists the versions you must pass through. Do not skip stops to save time on a critical patch; a failed migration costs more time than the stops.
A workable order for a single node Linux package install:
- Read the patch release page for your target version and the upgrade notes for your line.
- Take a backup with
sudo gitlab-backup create, and copy/etc/gitlab/gitlab-secrets.jsonand/etc/gitlab/gitlab.rbsomewhere safe. The backup does not contain the secrets file, and you cannot decrypt CI/CD variables without it. - Confirm that background migrations from the previous upgrade have finished (Admin area, Monitoring, Background migrations).
- Install the exact package version, for example
sudo apt-get install gitlab-ee=19.4.3-ee.0or the newest patch on your line, or theyumordnfequivalent. As of 29 October 2026 the newest patches GitLab has published are 19.5.1, 19.4.3, 19.3.6 and 19.2.9. - Run
sudo gitlab-ctl statusandsudo gitlab-rake gitlab:check SANITIZE=trueafter the upgrade. - Re-run the version checks from Step 1, including
/opt/gitlab/embedded/bin/ruby -v.
For Docker, pull the exact patched tag, stop and remove the old container, and start the new one with the same volumes. For Helm, helm upgrade with the matching chart version and watch every webservice and Sidekiq pod roll.
Source installs
Follow GitLab's source upgrade guide for your version and also upgrade Ruby. Order matters: install the fixed Ruby, rebuild gems against it with bundle install, run migrations, then restart. Afterwards run sudo -u git -H ruby -v and confirm that Puma and Sidekiq processes started after the Ruby upgrade (ps -o lstart= -p <pid>). A long running process keeps the old interpreter in memory until it restarts.
If you cannot upgrade today
GitLab's page offers no workaround. What you can do while you schedule the window is reduce who can reach the vulnerable path, which the next section covers. Treat it as risk reduction, not a fix. Ruby's own guidance that Regexp.timeout and length limits do not protect is a reason not to rely on clever filtering in front of GitLab.
Step 3: review CI exposure and who can trigger pipelines
GitLab rates this as reachable without privileges, under certain conditions. Alpine's account describes a newly registered account sending crafted CI configurations. Put those together and the exposure question becomes: who can get a CI configuration in front of your GitLab server's regex engine?
Sign-ups
If anyone on the internet can register an account on your instance, then "unauthenticated" and "authenticated" are close to the same thing for a bug like this. GitLab's documentation is direct about the default on self-managed: "By default, any user visiting your GitLab domain can create an account." The settings live under Admin, Settings, General, New user account restrictions:
- Clear Allow new user accounts if your instance does not need public registration.
- If you need it, select Require admin approval for new user accounts, and use the email domain allowlist so only your domains can register.
- Review accounts created since 12 October that have little or no history, especially any that touched CI lint or created projects with pipeline configuration and nothing else.
CI lint and configuration validation
GitLab's detection names point at CI lint, including the GraphQL path. CI lint validates .gitlab-ci.yml content, which is exactly where the regular expressions in rules:if and only/except live. Look for:
- Spikes in requests to the CI lint API and the GraphQL CI config endpoints, especially from new accounts.
- Puma or Sidekiq workers exiting with signals in native code, which GitLab's detection calls an Onigmo native worker crash.
- Workhorse logs showing upstream errors on CI lint requests in bursts.
On a Linux package install, the logs to read are under /var/log/gitlab/: puma/, sidekiq/, gitlab-workhorse/, gitlab-rails/production_json.log and gitlab-rails/api_json.log. Default log rotation may have removed the October entries already. Alpine's own lesson about keeping logs longer than 14 days applies to most self-hosted teams.
Shared runners
A crucial distinction first: CVE-2026-97229 is code execution on the GitLab server, not on GitLab Runner. The regex is compiled by GitLab Rails when it reads the CI configuration. Runners matter here for two other reasons.
- A compromised server holds runner authentication tokens and sends jobs to runners. Anyone who controls the server can schedule work on your runners, including the privileged ones that build images or deploy.
- Shared and instance runners accept jobs from many projects. If an outside account can create a project and get a pipeline to run on a shared runner, that is a separate exposure you should limit regardless of this CVE.
Practical controls:
- Check whether instance runners are enabled for all new projects. GitLab's docs say that on self-managed "an administrator can enable them for all new projects"; if that is on and sign-ups are open, any new account's project can use them. Enable them per group or project instead.
- Mark deployment and release runners as protected. GitLab's docs: "To ensure runners don't reveal sensitive information, you can configure them to only run jobs on protected branches." In the runner's edit page, select the Protected checkbox and save.
- Use job tags so sensitive runners pick up only the jobs meant for them, and clear Run untagged on those runners.
- Avoid running privileged Docker executors on runners that accept jobs from untrusted projects.
- Keep runners that hold production credentials separate from runners that run community contributions.
Protected branches and protected variables
CI/CD variables marked protected are exposed only to pipelines on protected branches and tags. Masked variables are hidden in job logs. Neither setting protects a variable from someone who controls the GitLab server, because the server decrypts variables to send them to jobs. That is why the question after patching is not only "did someone get code execution?" but "if they did, what could they read?"
Step 4: decide whether to rotate, and what
If your instance ran an affected version and was reachable by accounts you do not fully trust, decide whether you can rule out compromise. Alpine could not fully rule it out because of logging gaps, so it rebuilt and rotated. That is a reasonable default when the evidence is thin.
If you suspect compromise, GitLab's own page on responding to security incidents is the place to start. For a suspected compromised instance it says to review audit events for suspicious account behaviour, review all users including the root administrator, review the Credentials Inventory where available, and "Change any sensitive credentials, variables, tokens, and secrets." It also lists host steps: preserve server state and logs in a write-once location, look for unrecognised processes and open ports, rebuild from a known-good backup or from scratch with the latest patches, and route logs to an independent datastore. It tells administrators to "Update to the latest version of GitLab and adopt a plan to update after every security patch release."
The usual scope for a self-hosted instance includes:
| Credential | Why it is at risk | Where to rotate |
|---|---|---|
| Runner authentication tokens | The server holds them; GitLab's docs warn that an exposed token lets an attacker "clone the runner" | Delete the runner and create a new one so it gets a new token, or reset through the Runners API or the gitlab-runner reset command, then update config.toml |
| CI/CD variables (cloud keys, deploy keys, registry passwords) | Decrypted by the server for jobs | Rotate at the issuing system first, then update the variable |
| Personal, project and group access tokens | Stored and checked by the server | Revoke and reissue; consider forcing expiry |
| Deploy keys and deploy tokens | Grant repository and registry access | Revoke and reissue |
| Integration secrets (webhooks, chat, issue trackers) | Stored by the server | Rotate at the integrated service |
| User passwords and sessions | Server compromise exposes hashes and sessions | Force password reset, as Alpine did |
GitLab secret keys (gitlab-secrets.json) | GitLab's backup docs say "the secrets file contains your database encryption key", which protects CI/CD variables and two-factor data | Alpine replaced its GitLab secret keys during its rebuild; plan this with GitLab's documentation and support, because the same key decrypts existing data |
For runners specifically: do the deployment runners first, and check the runner list for runners you do not recognise. GitLab also supports automatic rotation: an administrator can set a runner token expiration time in the CI/CD settings, and GitLab's docs say that "Before the interval expires, runners automatically request a new runner authentication token." Turning that on after the incident shortens the life of any token that did leak in future.
Alpine's approach is the model: verify commits, packages and releases against records held outside GitLab, such as signed tags, SBOMs or provenance.
Open questions
- GitLab's advisory says "under certain conditions" without listing them. As of 29 October 2026 we have not seen GitLab publish the conditions.
- Whether other applications that compile user-supplied regular expressions in Ruby are exposed. Ruby says such applications are affected and that it has not requested its own CVEs yet.
- How many self-hosted instances ran vulnerable versions with open sign-ups. No primary source has published this.
What would have helped
Nothing here would have stopped a CVSS 10.0 bug in a server you run. What changes the outcome is how fast you know where you are exposed, how fast you patch, and how much an attacker could reach if the patch came late.
- Knowing every place GitLab and Ruby run. Most organisations can name their main GitLab instance. Fewer can list the staging instance, the test container a team runs on a laptop, and the image that bakes GitLab onto a custom Ruby. An inventory that records the runtime under the application is what turns Alpine's lesson into a five minute check.
- Treating runtimes as dependencies. Ruby, Node.js, Python and the JVM ship fixes that applications rely on. Track them like any other component.
- Closing sign-ups you do not need. Many instances leave registration open by default or by habit.
- Separating runners by trust. Deployment runners reachable only from protected branches limit the blast radius when something upstream fails.
- Keeping logs long enough. Alpine's post names logging gaps as the reason it cannot fully confirm what happened.
- Holding release evidence outside the server. Signed tags, SBOMs and provenance stored elsewhere let you prove that what you shipped is what you built.
Where Safeguard fits
Safeguard does not patch your GitLab server, and nothing in this post suggests Safeguard discovered CVE-2026-97229 or had a fix for it before GitLab and Ruby published theirs. Where it helps is the inventory and the follow-through.
See what is inside the images you build. Safeguard's documentation describes a CI step that runs after your pipeline builds an image: it "scans that image on the runner and records it in Safeguard under the image's repository", with a version per environment, and "Only the results leave the runner." The documented command looks like this:
safeguard ci scan --image "registry.example.com/platform/gitlab-custom:$TAG" --environment prod --wait --fail-on critical
If your team builds its own GitLab image on a distribution Ruby, or ships Ruby services in containers, this is how each build's components land in one place you can review. Safeguard's software composition analysis page lists Ruby Bundler among its ecosystems and says components are covered when they are "discovered inside container image layers." For this particular question, confirm the Ruby and GitLab versions in your image SBOMs and on the host.
Know which Ruby services depend on what. For your own Ruby applications, composition analysis "resolves the complete dependency graph from your manifests and lockfiles, including deeply nested transitive dependencies." The Ruby advisory says the Onigmo bugs are reached only when an application compiles a pattern from untrusted input. That is a question about your code, and it is worth answering by code review for every Ruby service that takes search patterns, filters or rules from users.
Keep a record outside the server. Safeguard records each scan per image and environment, and SBOMs can be exported in formats including SPDX and CycloneDX. Records held outside your GitLab server are the kind of outside evidence Alpine used to show its releases were not tampered with.
For CVE-2026-97229 the fix exists in both GitLab and Ruby, so the work is moving every image and service to a fixed version and proving you did. The section below is how Safeguard helps with that work for the open source components and images you build yourself.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Griffin, Safeguard's security model, opens a policy gated pull request that moves the affected dependency to the safe version, and your own CI runs your tests on it before anyone merges. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Auto-merge is a separate switch and stays off by default, so your team decides what merges on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Safeguard Gold rebuilds patched versions of open-source packages and images from source, signs each build, attaches an SBOM, SLSA provenance and VEX, and serves them from registry.safeguard.sh to your organization. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
FAQ
Is GitLab.com affected?
GitLab says GitLab.com is already running the patched version and that all GitLab Dedicated tenants were patched before the release was published. The work is for self-managed instances.
I upgraded to 19.3.4. Am I safe?
No. GitLab's affected range is "19.3 before 19.3.5". Upgrade to 19.3.5 or, better, the newest patch on 19.3.
I run the Linux package. Do I need to upgrade Ruby on the host?
No. The Linux package uses its own embedded Ruby, and GitLab patched that Ruby in the fixed releases. Check /opt/gitlab/embedded/bin/ruby -v only to confirm the package upgrade completed.
I run GitLab from source or a distribution package. What do I need?
A fixed GitLab release and a fixed Ruby: 3.3.13, 3.4.12 or 4.0.9 or later on the matching branch, per Ruby's advisory. Restart all GitLab Ruby processes afterwards.
Was CVE-2026-97229 exploited?
Alpine Linux reports an attempted exploitation of its instance on 20 to 21 October 2026. Alpine says it found no evidence the attacker's code ran but cannot rule it out because of logging gaps, and it rebuilt the instance. As of 29 October 2026 that is the primary source account we have read.
Does this affect GitLab Runner?
The vulnerable code runs on the GitLab server, where the CI configuration is parsed. Runners matter because a compromised server can send jobs to them and holds their tokens. If you suspect compromise, reset runner tokens and review runner registrations.
Does Regexp.timeout help?
No. Ruby says Regexp.timeout does not protect against these bugs, and that limiting pattern length does not reliably protect either.
Are my own Ruby apps affected?
Only if they compile regular expressions from attacker-controlled input, for example with Regexp.new(params[:q]) or /#{input}/, per Ruby's advisory. Upgrading Ruby is the fix; avoiding untrusted pattern compilation is the workaround.
Should I rotate everything?
If you ran an affected version, had open sign-ups or untrusted users, and cannot rule out compromise from your logs, rotating runner tokens, CI/CD variables at their source, access tokens and user passwords is a reasonable default. Alpine did all of that and replaced its GitLab secret keys.
Which GitLab version should I target?
The newest patch release on your line. On 21 October 2026 GitLab shipped 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10 and 18.11.13 for CVE-2026-110264, a second CVSS 10.0 regex compiler flaw. Target those or later.
Is CVE-2026-110264 the same bug?
No. GitLab and Ruby list it separately. Ruby maps CVE-2026-97229 to an integer overflow in the size of a compiled pattern (#19020) and CVE-2026-110264 to an overflow of internal IDs in a pattern with many repeats (#16801). Both are fixed in Ruby 4.0.9, 3.4.12 and 3.3.13.
See how Safeguard would have helped
These demos show real Safeguard sessions on other examples. None of them shows this incident. Each paragraph explains how the same flow applies to CVE-2026-97229.
The CLI demo shows a dependency fix done from the terminal. For this incident you would run the same kind of scan against the image that bakes GitLab or a Ruby runtime, ask which components carry Ruby below the fixed versions, and have it bump the base image tag.
Click through it at your own pace, or open it full screen.
The desktop app demo shows the same work in a desktop session, where you pick the folder and then Approve or Deny each command and file edit. For a Dockerfile that pins an old Ruby, that is the upgrade, the rebuild and the test run, each one approved by you.
Click through it at your own pace, or open it full screen.
The Claude demo shows Safeguard connected to Claude. During an incident like this one, you can ask in the chat you already use which of your projects and images carry a Ruby runtime and which versions, and get the answer from your own Safeguard data.
Click through it at your own pace, or open it full screen.
Quick check
Which GitLab 19.3 release is the first fixed one for CVE-2026-97229?
19.3.5. GitLab's affected range is "19.3 before 19.3.5", and 19.3.4 is still affected.
Which Ruby versions contain all four Onigmo fixes?
Ruby 4.0.9, 3.4.12 and 3.3.13, per the Ruby project's 23 October 2026 advisory.
Why did Alpine's upgrade to GitLab 19.3.6 not fix its server?
Alpine says the fix is a patch to Ruby that GitLab applies only in its own packages, and Alpine runs GitLab on its own Ruby.
Does Regexp.timeout protect against these bugs?
No. Ruby says it does not, because the bugs happen while a pattern is compiled.
Sources
- GitLab Critical Patch Release: 19.4.2, 19.3.5, 19.2.8, GitLab Docs, 12 October 2026
- GitLab Critical Patch Release: 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10, 18.11.13, GitLab Docs
- Regexp compilation fixes related to GitLab's CVE-2026-89078, CVE-2026-93577, CVE-2026-97229 and CVE-2026-110264, Ruby, 23 October 2026
- Ruby 3.4.12 Released, Ruby, 23 October 2026
- Ruby 3.3.13 Released, Ruby, 23 October 2026
- Ruby 4.0.9 Released, Ruby, 23 October 2026
- GitLab security incident: attempted exploitation of CVE-2026-97229, Alpine Linux, 28 October 2026
- GitLab release and maintenance policy, GitLab Docs
- Responding to security incidents, GitLab Docs
- Configure runners, GitLab Docs
- Manage runners (runner scope), GitLab Docs
- Sign-up restrictions, GitLab Docs
- Back up GitLab, GitLab Docs
- Scan container images in CI/CD, Safeguard Docs
- Software composition analysis, Safeguard Docs
- Explore your SBOM, Safeguard Docs
Last reviewed: 29 October 2026.