Three releases from the past ten days, three different reasons the version number ran ahead of the fix.

The fix is a Ruby patch, and only GitLab's packages carry it

CVE-2026-97229 was covered here on October 21 as a CI/CD regex-compiler problem — unauthenticated, CVSS 10.0, fixed October 12 in GitLab 19.2.8 / 19.3.5 / 19.4.2, with database migrations in the same release. What the October post could not have known is where the fix actually lives. Alpine's incident statement is the first public post-mortem that spells it out:

The underlying bug is in Onigmo, the regular expression engine built into Ruby. GitLab published fixed releases on October 12, but the fix itself is a set of patches to the Ruby that GitLab bundles in its own packages and container images. Upstream Ruby shipped the fix on October 23, in Ruby 3.3.13, 3.4.12 and 4.0.9.

Everything after "The underlying bug is in Onigmo..." is the part worth rereading: the fix and the runtime are not the same artifact. The timeline from Alpine's post, compressed to the lines that matter:

  • Oct 12 — GitLab ships the fix
  • Oct 13 — CVE published; Alpine upgrades to 19.3.4 instead of 19.3.5 by mistake
  • Oct 20–21 — a fresh-registered account sends crafted CI configs; eight worker crashes, four partial exploit attempts, the attacker's image download
  • Oct 22 — Alpine upgrades to 19.3.6 — which does not fix the bug
  • Oct 25–26 — attacker returns; attempts fail
  • Oct 26 — Alpine finds the instance still vulnerable after the upgrade
  • Oct 27 — instance closed except to developers and build servers; rebuild begins
  • Oct 28 — rebuild complete; passwords reset; secret keys replaced
  • Oct 29–30 — back online on 19.3.7

Note which line is the kicker. Not the attack — the October 26 line. A community operations team did the right thing, four days slower than the attack clock but in the right order, and their instance was still vulnerable afterward, because the patch they needed was never in the GitLab release at all. The bug is in Onigmo, and GitLab's fix was a distribution of Ruby, not a distribution of GitLab. GitLab's official packages and images carry patched Ruby; anything built on the distro's or the operator's own Ruby got nothing until October 23.

That means the honest threat model for CVE-2026-97229 was wider than "GitLab with a stale release":

  • GitLab on self-managed Ruby (from source, or any container image not built on the official packages): upgrading alone is not the fix; Ruby ≥ 3.3.13 / 3.4.12 / 4.0.9 is.
  • The alpinelinux/gitlab image: any pull before October 27 is vulnerable, including every 19.3.6-tagged image predating that date; the October 27 rebuild carries Ruby 3.3.13.
  • Not just GitLab: as Alpine's FAQ states plainly, the bug is Ruby's regex engine — "any program that compiles regular expressions from untrusted input is exposed, not only GitLab." Every Ruby application that accepts user-supplied patterns was inside the blast radius, with or without a forge in the picture.

CISA closed the theoretical question on October 29 by adding the CVE to the KEV catalog, alongside the Cisco firewall DoS — the exploitation is confirmed in the field, and Alpine's incident post documents what one of those field campaigns looked like from the inside: probe, crash, retry, pull the victim's image to tune the exploit to its exact build. That last step deserves a moment. The attacker reverse-engineered Alpine's packaging as part of the attack — not to plant malware, but to figure out whether the Ruby underneath was a patched one before committing more crash attempts to a target that might be wasting their time. Build provenance is attack intelligence now.

If you cannot rule out code execution and the evidence needed to confirm it does not exist — Alpine found attack-day background-job logs and the old container filesystem gone, no outbound-connection logging anywhere in their stack, and their hosting provider keeps none — then the operational answer is the one they took: rebuild from scratch, reset every credential, replace the application's secret keys, and verify the supply chain against records the attacker could not reach. Their verification method is worth stealing: every aports commit from the attack window checked against a developer's offline clone and all 45 build servers, every package built October 20–24 on every architecture traced to its verified commit, the October 21 stable releases (3.21.9, 3.22.7, 3.23.7, 3.24.3) confirmed untampered, and the signing keys provably never on the GitLab host at all. That is what "no evidence of compromise" means when it is earned instead of hoped for.

Their permanent fixes are equally transferable: apply critical GitLab security releases within a day; check each release for Ruby or other component patches your own build must apply; log outbound connections from the GitLab host; sign release tags so future incidents can be verified independently of the GitLab server. That second item is the whole lesson: the release you upgrade is a bundle, and bundling is where the fix hides. When a vendor's fix is "we patched the runtime we ship," anyone running the app on their own runtime has to maintain a component patch floor themselves — the version jump buys them nothing.

The same trap class hit larger scale the same week: Nextcloud's built-in CODE app, exploited in the wild with a fix that shipped a month before anyone said so. Packaging decisions become patch decisions.

WordPress 7.1.4 — the attack window was the upgrade itself

WordPress 7.1.4 shipped October 28 with a headline bug whose precondition deserves to be read twice: remote code execution during the upgrade window, reported by Umut Bayram via the Wordfence disclosure. A full-package WordPress core update unpacks into a predictable path under wp-content/upgrade; the staging surface is reachable by an unauthenticated visitor from the internet while that unpack is in flight; an attacker who wins the race against the cleanup routines executes code. CVSS 8.1. Laboratory tests found the window open only seconds at a time — Wordfence's notes say the staging directory was available briefly. It recurs on every full-package core update, which WordPress performs automatically on a schedule across most of the installed base. The door opens on every upgrade, closes again when the update finishes.

This is the same shape as the CODE-app story: exploitation keyed to a maintenance state, not to a standing misconfiguration. The difference is that here the maintenance state is not operator-created at all — it is the auto-updater opening and closing the door on its own, site by site, day after day. Wordfence's count of what shows up at that door: more than 18,500 requests probing the vulnerable setup-config.php installation paths in the first 21 hours after their signature rule landed. The two most active sources polled small groups of WordPress sites in rotation for about 14.5 hours at a stretch — patience-based scheduling built to catch each target's next auto-upgrade window. Other sources varied the staging paths and version names probed. The exploit attempts began while most administrators had not yet read the announcement.

For the fire halls, city halls and small businesses this site exists for — the demographic where WordPress is the default CMS and "auto-updates on" gets counted as good hygiene — the honest summary is that the hygiene did not cover this one. What does:

  • Verify each instance you are responsible for runs 7.1.4 or its branch backport (the fix was backported to every maintained branch, 4.7.39 through 7.0.8).
  • If any instance was below the fix when 7.1.4 shipped: check access logs for setup-config.php and staging-path probes, and hunt for unexpected PHP files under wp-content/upgrade, unknown admin accounts, and plugins nobody can attribute. Wordfence's post documents the exact request shapes.
  • The remaining two dozen fixes in 7.1.4 are lower-flash but worth the five minutes: a batch of stored XSS across the Interactivity API, block markup, the categories and playlist/image blocks and the customizer, a stored denial-of-service in the Interactivity API, unpublished customizer changesets discoverable by unauthenticated users, and post notes readable by users without edit rights on the attached post.

Two reporting credits in that list are worth naming: Anthropic reported the stored XSS in user login names and the one in privacy data removal requests. The same week their research model found the zero-click kernel bug this site led with yesterday — an eleven-year-old race in the IPv4 stack. A machine that finds decade-old races in kernel code in the same week it files CMS-level XSS reports is scaling vulnerability discovery in both directions at once, and the queue on the other side is every community's maintenance backlog. Budget patch cycles accordingly — heise's risk review called it security debt falling due with interest, and for weekend-maintained community sites that is not a metaphor.

And one line in that release closes a loop this site opened on October 22: 7.1.4 updates the bundled root certificates because of the eMudhra revocation — the CA whose WebTrust-audited intermediates included private keys you could type on a keyboard, whose trust the browsers pulled and whose cleanup became a server-side task. Within four days the consequence propagated from the browser trust store into WordPress shipping updated CA bundles. Trust-anchor hygiene is not a browser project — it reaches every application that bundles its own root set, and 7.1.4 is that arriving in the single most-installed CMS.

Drupal Commerce Core — actively exploited, check your orders

SA-CONTRIB-2026-254: access bypass in Drupal Commerce Core, the shopping-cart and checkout module behind a significant share of community stores, co-op shops and municipal payment forms. The Drupal security team states the module does not adequately protect certain routes and entity fields — and flags that the vulnerability is being actively exploited with potential financial impact on the seller. The fix is Commerce Core 3.3.13, or 8.x-2.41 or newer on the 2.x branch.

The unusual part of this advisory is the homework. Because the module's protection failure was route-level and attack traffic is confirmed, patching does not erase what already happened; the advisory guidance is to audit order records for inconsistencies before treating accounts as clean. A community store should handle this the way Alpine handled its forge: reconcile against evidence that predates the attacker's access, then trust the record.

What to actually do

  • GitLab on official packages: never this week's problem — current releases carry patched Ruby. On 19.3.7+ you are through it. Note Alpine reopened on 19.3.7, one release past the one everyone quoted.
  • GitLab on your own Ruby, or any non-official image: the Ruby patch level must be 3.3.13 / 3.4.12 / 4.0.9 or newer, independently of the GitLab version shown in your admin panel. If you run the alpinelinux/gitlab image, re-pull: anything built before October 27 is vulnerable regardless of its tag.
  • Any Ruby application that compiles user-supplied regexes: the bug was in Onigmo, not in GitLab. This was never only a forge's problem, and it will not be the last CVE whose fix is a runtime rather than an application.
  • Adopt Alpine's checklist verbatim: apply critical releases within a day; read each release for component patches your own build must apply; log outbound connections from the service host; sign release tags.
  • WordPress: 7.1.4 or branch backport now; log review and wp-content/upgrade scan for anything that was live during the past week; treat auto-update as an exposure schedule, not a defense.
  • Drupal Commerce: 3.3.13 / 8.x-2.41+, then the order audit before any account is declared clean.
  • KEV as a watchlist, not a forecast: October 29's additions were GitLab and Cisco; October 30's were two Ivanti Connect Secure entries from a chain first analyzed October 13. The catalog records exploitation after it started, sometimes weeks after. Its entries are histories, not warnings you have days to act on.