Why Untrusted Regex Patterns Are a Code Execution Risk
Security Intermediate 5 min read

Why Untrusted Regex Patterns Are a Code Execution Risk

Untrusted regex patterns can corrupt memory while they compile, as GitLab CVE-2026-97229 showed. Learn how to design features that accept them safely.

Published: October 30, 2026 • Updated: October 30, 2026
regular expressionssecure designthreat modelingredos

Untrusted regex patterns are a code execution risk because a regular expression engine is a compiler. It parses a small program written by whoever supplied the pattern, and that compiler is usually written in C. For years teams treated user-supplied regular expressions as a denial-of-service problem and fixed it with timeouts. GitLab's CVE-2026-97229, now in CISA's Known Exploited Vulnerabilities catalog, showed that a crafted pattern can corrupt memory before matching even starts. Plan for that.

Why are untrusted regex patterns dangerous?

Untrusted regex patterns are dangerous because the engine has to parse and compile them, and bugs in that compiler can corrupt memory. In the worst case an attacker who controls the pattern controls code execution on the server.

A regular expression looks like data, but the engine turns it into an internal program and runs it. Accepting a pattern from a user is closer to accepting a script than to accepting a search string. The parser, compiler and matcher are all attack surface, and in most mainstream languages that surface is native code.

The old threat model: slow matching

The familiar risk is regular expression denial of service (ReDoS). Some patterns force a backtracking engine into exponential work on certain input, pinning a CPU. The standard answers are match timeouts, input length limits and pattern review.

Those controls work against ReDoS. They do nothing against the newer problem. The Ruby core team said so directly in its October 2026 advisory: Regexp.timeout does not protect against the Onigmo bugs behind GitLab's CVEs, and limiting pattern length does not reliably protect either, because some of the bugs trigger on short patterns.

The new threat model: hostile compilation

GitLab compiles patterns that users put in CI/CD configuration. Crafted patterns caused memory corruption while Ruby's Onigmo engine compiled them, and GitLab turned that into a CVSS 10.0 remote code execution finding. The details are in our report on GitLab CVE-2026-97229. Four GitLab CVEs came out of the same engine: a double free and three overflow bugs. The mechanics of the overflow bugs are explained in what an integer overflow vulnerability is.

This was not the first time. The Ruby advisory notes that Ruby treated a double free in regexp compilation as a vulnerability in CVE-2022-28738, and did the same for CVE-2024-27282. It also says Ruby has generally considered it unsafe to compile a regular expression created from untrusted input, while its documentation has only discussed untrusted patterns in terms of denial of service. That gap between what the maintainers believed and what the documentation said is how a design choice in a large product became a KEV entry.

Why does this keep happening?

It keeps happening because accepting user patterns is a convenient feature, the documentation of most engines frames the risk as performance only, and the dangerous code sits in a library that application teams never review.

Product teams add regex support because users ask for flexible filters. Security reviews check the feature for ReDoS and approve it with a timeout. Nobody asks whether the compiler itself is safe against hostile input, because the language runtime is assumed to be trustworthy.

Where untrusted patterns hide in your estate

Before you can decide anything, find the features. Common places:

  • CI/CD and automation platforms that read patterns from repository configuration, where anyone who can push a branch can supply one
  • search boxes with a "regex" toggle
  • log, SIEM and alerting tools with user-defined match rules
  • mail, proxy and WAF products with admin-defined filter patterns
  • chat bots, ticket routing and webhook filters with custom triggers
  • multi-tenant SaaS features where one customer's pattern runs on shared infrastructure

The last case is the worst. A pattern from one tenant compiling inside a process that serves every tenant turns a single malicious account into a platform compromise.

Design options, from strongest to weakest

  1. Remove the feature. Many regex features exist because they were easy to build. A substring match, a prefix match or a short list of predefined patterns often covers the real need.
  2. Accept literals, not patterns. If users need to match their text, escape it before building the expression so it can never contain operators.
  3. Use a fixed allowlist. Offer named patterns that your developers wrote and tested, and let users pick from them.
  4. Use a linear-time engine. Engines built without backtracking, such as RE2, remove the ReDoS class and restrict the syntax users can supply. They are still native parsers, so they reduce risk rather than eliminate it.
  5. Isolate compilation. Compile and run user patterns in a separate, low-privilege process with tight memory limits, so a crash or corruption cannot reach application secrets.
  6. Patch fast and track the engine. If you must accept arbitrary patterns in-process, the regex engine becomes a component you patch on an emergency schedule, with its own entry in the asset inventory.

Most teams should aim for options 1 to 3 and use 5 when a product requirement truly needs arbitrary patterns.

Questions to ask vendors

When a product accepts patterns from users, ask the vendor:

  • Which regex engine and version does the product use, and how quickly do you ship its security fixes?
  • Who can supply patterns: anonymous users, any authenticated user, or administrators only?
  • Are patterns compiled in the main application process or in an isolated worker?
  • Can the regex feature be disabled or restricted to administrators?

How should security teams plan for this?

Security teams should classify any feature that compiles user-supplied patterns as code execution exposure, not as a performance risk, and review it with the same scrutiny as a file upload or a template engine.

In practice that means adding regex input to threat models and design reviews, tracking the engine as a dependency, and including those features when you scope emergency patching. For the immediate Ruby fix, follow our guide on how to fix the Ruby Onigmo regexp bugs. For GitLab specifically, reducing who can push to public projects, as described in locking down public projects on self-managed GitLab, shrinks the set of people who can hand the server a pattern in the first place.