GitLab CVE-2026-97229: Patch Critical RCE Today
If you run your own (self-managed) GitLab and it is on 19.2 before 19.2.8, 19.3 before 19.3.5, or 19.4 before 19.4.2, update it today: CVE-2026-97229 is rated critical and could let someone with no account run code on your GitLab server.
This post is for owners and admins of self-managed GitLab (Community Edition or Enterprise Edition) who are not full-time security people. You will get the affected and fixed versions, then three steps in order: update, check for unknown users and tokens, and rotate secrets if you think anything was exposed.
A note on sources. The version numbers and severity below come from the NVD record for CVE-2026-97229, which points to GitLab's own patch release notes. Before you act, open GitLab's 19.4.2 patch release page and confirm your version against it. If the two ever differ, GitLab's page wins.
What CVE-2026-97229 is, in plain English
A CVE is a public ID number for a known security flaw. This one is in GitLab, the popular tool for storing code and running automated build jobs (called CI/CD, short for continuous integration and delivery).
Per the NVD description, GitLab fixed an issue where, "under certain conditions," an unauthenticated user could run arbitrary code on the GitLab server. Unauthenticated means no login is needed. Remote code execution (RCE) means an outsider can make your server run commands of their choosing.
The NVD description says the route is a specially crafted regular expression in a CI/CD configuration, caused by insufficient regular expression validation. A regular expression (regex) is a text pattern, like a search rule. GitLab did not check these patterns well enough.
The record lists a CVSS score of 10, the top of the scale, with the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H. In plain terms: reachable over the network, low effort, no login, no user interaction. The listed weakness type is CWE-190.
That is serious. It is not a reason to panic. It is a reason to schedule the update now instead of next month.
Affected and fixed versions
The NVD record says all versions in these three release lines are affected, for both Community Edition (CE) and Enterprise Edition (EE):
| Your release line | Affected if you run | Fixed in |
|---|---|---|
| 19.2 | Any 19.2.x before 19.2.8 | 19.2.8 |
| 19.3 | Any 19.3.x before 19.3.5 | 19.3.5 |
| 19.4 | Any 19.4.x before 19.4.2 | 19.4.2 |
If you run a version older than 19.2, the NVD text does not say whether you are affected. Do not read that as safe. Older releases are out of the normal patch window, so plan an upgrade to a current, supported release and check GitLab's release notes for your version.
The NVD record also lists Ruby versions before 3.3.13 and before 3.4.12 in its affected products. Ruby is the language GitLab is built on. The record links a Ruby announcement about regexp fixes for GitLab CVEs. If you installed GitLab with the official packages or containers, the Ruby it needs ships inside the GitLab update. If you built GitLab from source with your own Ruby, read that announcement too.
GitLab.com (the hosted service) is run by GitLab. This post is about self-managed installs. If you only use gitlab.com, there is nothing for you to patch.
Step 1: Update GitLab first
A Metasploit pull request appears among the NVD references. We cannot tell from that whether working exploit code exists. We also have no source saying this bug is being used in attacks, so we are not claiming that. Still, treat a no-login, score-10 bug on a server that holds your code as urgent.
Find your current version
Sign in as an admin and open Admin Area > Overview > Dashboard, or open /help on your GitLab address while signed in. On the server, you can run:
sudo gitlab-rake gitlab:env:info
Back up before you upgrade
Take a backup of data and configuration. This is quick insurance:
sudo gitlab-backup create
sudo mkdir -p /root/gitlab-config-backup
sudo cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab.rb /root/gitlab-config-backup/
sudo chmod 700 /root/gitlab-config-backup
The config copy goes into a root-owned folder that only root can open, because gitlab-secrets.json holds encryption keys. Keep a copy somewhere safe and off the server too.
Upgrade the package (Linux package installs)
On Debian or Ubuntu with the Enterprise Edition package, first list the exact package strings your repository offers:
sudo apt-get update
apt-cache madison gitlab-ee
Then install a pinned version. This example is for the 19.4 line:
sudo apt-get install gitlab-ee=19.4.2-ee.0
Copy the exact package string from the apt-cache madison output. On the 19.2 or 19.3 line, pin 19.2.8 or 19.3.5 instead, unless GitLab's upgrade path (next section) tells you to stop somewhere else first.
The plain command sudo apt-get install gitlab-ee installs the newest version in the repository. That is only safe if you are already on the latest required upgrade stop. Otherwise it can jump past stops you need.
On RHEL-style systems, list the available versions, then install a pinned one:
dnf list --showduplicates gitlab-ee
sudo dnf install gitlab-ee-19.4.2-ee.0.el9
The el9 part must match your OS release, so copy the exact string from the dnf list output.
Use gitlab-ce instead of gitlab-ee if you run Community Edition. Docker and Helm users should move to the patched image or chart for their release line and restart.
Watch the upgrade path
GitLab sometimes requires stops on the way between versions. If you are several releases behind, enter your current and target versions in GitLab's upgrade path tool and install each stop it lists, in order. The official upgrade docs cover the details. Do not skip required stops just to move fast.
If you run a multi-node setup or Gitaly Cluster, do not use a plain apt-get install on each server. Follow the zero-downtime upgrade docs in GitLab's official upgrade documentation instead.
Confirm it worked
Run the version command again. You want 19.2.8, 19.3.5, 19.4.2, or newer, in whichever line you are on. Then open a project and run a pipeline to confirm things still work.
If you truly cannot update today
Reduce who can reach the server. Put GitLab behind a VPN or an IP allowlist at your firewall until you can patch. This is a stopgap, not a fix. The NVD text names no configuration switch that removes the risk, so the update is the real answer.
Step 2: Check for unknown users and tokens
Updating closes the door. It does not tell you whether someone already walked through it. Spend 20 minutes checking.
- Look at users. Go to Admin Area > Overview > Users and sort by newest. Any account you do not recognize, or any new admin, needs a close look.
- Look at tokens. Personal access tokens, deploy tokens, and runner tokens can all give access without a password. Runner tokens live under Admin Area > CI/CD > Runners, or under Settings > CI/CD > Runners in a group or project.
- Look at SSH keys and runners. A new key or an unfamiliar CI runner is a classic way to keep access.
- Skim logs. Look at
/var/log/gitlab/gitlab-rails/for odd requests around your last unpatched period.
These read-only commands list recent items on a Linux package install:
sudo gitlab-rails runner 'User.order(created_at: :desc).limit(20).each { |u| puts [u.id, u.username, u.email, u.admin, u.created_at].join(" | ") }'
sudo gitlab-rails runner 'User.where(admin: true).each { |u| puts [u.id, u.username, u.email].join(" | ") }'
sudo gitlab-rails runner 'PersonalAccessToken.order(created_at: :desc).limit(20).each { |t| puts [t.id, t.user_id, t.name, t.created_at, t.last_used_at].join(" | ") }'
sudo gitlab-rails runner 'Key.order(created_at: :desc).limit(20).each { |k| puts [k.id, k.user_id, k.title, k.created_at].join(" | ") }'
Also review Admin Area > CI/CD > Runners for runners you did not add. And look at recent changes to .gitlab-ci.yml files, since this bug involves CI/CD configuration.
No output from these checks does not prove nothing happened. An attacker with code execution can tidy up after themselves. But finding something odd tells you to move to Step 3 right away.
Step 3: Rotate secrets if you think they were exposed
If your server was reachable from the internet on an affected version, and you saw anything suspicious, assume secrets stored on that server could have been read. A secret is any password, key, or token a program uses to log in somewhere.
Rotate in this order, most powerful first:
- Admin and root passwords. Change them and turn on two-factor authentication.
- Personal access tokens, deploy tokens, and SSH keys. Revoke anything unknown, and recreate the ones you still need.
- Runner tokens. In Admin Area > CI/CD > Runners (or Settings > CI/CD > Runners for a group or project), reset the runner authentication tokens and re-register your runners.
- CI/CD variables. Every cloud key, database password, and API token stored in project or group variables should be replaced at the source (AWS, your registrar, your payment provider), not just edited in GitLab.
- Server config secrets. Mail (SMTP) passwords, LDAP or OAuth client secrets, and anything else in
/etc/gitlab/gitlab.rb. - Encryption secrets.
gitlab-secrets.jsonis harder to rotate safely. Follow GitLab's official docs, and consider rebuilding on a clean server if you suspect real compromise.
If you find signs of a break-in, switch to our what to do if your website was hacked guide. Preserve logs before you change things.
Quick checklist
- [ ] Confirm version against GitLab's patch release page
- [ ] Back up data and
/etc/gitlab - [ ] Upgrade to 19.2.8, 19.3.5, 19.4.2 or newer
- [ ] Re-check the version
- [ ] Review new users, admins, tokens, keys, runners
- [ ] Rotate secrets if anything looks off
- [ ] Limit public access to GitLab where you can
Where our free tools fit
Be clear about what they do. Our free website security check looks at your public site from the outside. It will not tell you whether your GitLab is on a vulnerable version. You can still run it on the domain where GitLab lives to spot exposed basics. For this issue, the version check above is the test that matters.
If you want to know how a human audit differs from a scan, read manual vs automated penetration testing.
Key takeaways
- Self-managed GitLab on 19.2 before 19.2.8, 19.3 before 19.3.5, or 19.4 before 19.4.2 is affected by CVE-2026-97229, rated critical (CVSS 10) in the NVD.
- Update first, after a backup, and confirm the new version number afterward.
- Then check for new users, admins, tokens, SSH keys, and runners you do not recognize.
- Rotate secrets, starting with admin credentials and CI/CD variables, if you see anything suspicious or cannot rule out exposure.
- Confirm versions on GitLab's own patch release page, since the final word on fixes is theirs.
Want a human to look at the rest?
Patching one app is only part of the picture. The rest of your public footprint matters too, like login pages, old subdomains, and exposed files. Our Circuit audit is $49 one-time: a real person reviews your site by hand and gives you a written report with severity, evidence, and exact fixes. It is a small-site audit, not an enterprise pentest or a compliance certificate. See pricing if that sounds useful, or start with the free check above.
A real human security engineer audits your whole site by hand and sends a full report: every issue, its severity, and the exact fix. From $49, with a 14-day money-back guarantee.
See pricing