Ruby 4.0.9 Regex Security Fix Tutorial: Upgrade Ruby 3.3 and 3.4
Learn how to upgrade Ruby to 4.0.9, 3.4.12, or 3.3.13, identify risky dynamic regular-expression compilation, and verify the fix in development and production.
On this page
An application that turns user-submitted text into a Ruby regular expression can expose a much deeper problem than a slow search: a specially crafted pattern may trigger memory corruption while the expression is being compiled. On October 23, 2026, the Ruby project released Ruby 4.0.9, 3.4.12, and 3.3.13 with fixes for regular-expression compilation bugs identified through GitLab security research. This tutorial shows how to identify your Ruby version, upgrade the correct maintenance branch, test existing patterns, and check whether your application compiles expressions from untrusted input.
Understand which Ruby regex security fixes your application needs
Ruby uses Onigmo, its regular-expression engine, to interpret patterns used for searching and matching text. The engine must parse a pattern and compile it into an internal representation before Ruby can use it. GitLab reported several defects in this process that could corrupt memory, including a double-free condition and integer overflows. A double free occurs when software releases the same memory allocation more than once; an integer overflow happens when a calculation exceeds the range a numeric type can represent, potentially producing an incorrect allocation size or identifier.
The Ruby project's October 23 advisory connects these problems to four vulnerabilities assigned by GitLab: CVE-2026-89078, CVE-2026-93577, CVE-2026-97229, and CVE-2026-110264. The releases containing all the listed fixes are Ruby 4.0.9, 3.4.12, and 3.3.13. The important distinction is that the vulnerabilities affect the regular-expression compilation process, not every ordinary use of a regular expression. Applications that compile expressions built from attacker-controlled strings deserve particular attention.
Check the Ruby version your application actually runs
Before installing an update, open a terminal in the environment where the application runs and execute the following command. It reports the Ruby interpreter available through that terminal's current executable search path, which may differ from the version used by a web server, container, or deployment service.
ruby --version
which ruby
On Windows, use where ruby instead of which ruby. If the project uses a version manager such as rbenv or RVM, check its selected version as well. For an application managed by Bundler, inspect the project's Gemfile and lockfile to understand its Ruby version requirements before changing the runtime. Record the current version and run the existing test suite so you have a baseline for comparison.
Match the update to the maintenance branch you already use. Ruby 4.0 users should move to 4.0.9 or later, Ruby 3.4 users to 3.4.12 or later, and Ruby 3.3 users to 3.3.13 or later. Do not assume that moving to a newer major release is necessary: a patched release within the existing branch is generally the more controlled first step for an established application.
Upgrade Ruby without changing the application's major version
The exact installation command depends on how Ruby was installed. The Ruby project lists package managers and platform-specific installation options in its official release information. If you use rbenv, install the patched version supported by your operating system and build environment, then select it for the project. If you use RVM, install the appropriate patched interpreter through RVM. Teams using containers should update the Ruby base image or runtime package rather than upgrading only the developer's local machine.
For a project using rbenv, the workflow can look like this, provided your installed rbenv and ruby-build versions offer the requested release:
rbenv install 3.4.12
rbenv local 3.4.12
ruby --version
bundle install
Replace 3.4.12 with the patched version for your application's existing branch. The installation command downloads and builds the interpreter, the local selection tells rbenv which version the project should use, and the final commands confirm the selected runtime and reinstall dependencies as needed. If the version is unavailable in your local ruby-build definitions, update those definitions or follow the installation method supported by your environment rather than silently switching to another Ruby branch.
For a containerized application, update the Ruby version in the Dockerfile or equivalent build configuration, rebuild the image, and deploy that image through your normal release process. Updating a laptop does not patch a production container that continues to run its previous interpreter. Likewise, installing a new Ruby version does not automatically change a service's runtime if its process manager or deployment configuration still points to the old executable.
Find code that compiles regular expressions from user input
After the interpreter is updated, inspect the application for code that creates regular expressions dynamically. Ruby's Regexp.new, Regexp.compile, and Regexp.union can all be relevant when their arguments include data controlled by a user. Interpolating input into a regular-expression literal, such as /#{input}/, deserves the same scrutiny because the resulting string becomes part of a pattern rather than remaining ordinary search text.
For example, a search endpoint might accept a query parameter and pass it directly into a dynamically compiled expression. That design gives the caller control over the pattern's syntax and complexity, not just the text being searched. A safer design often treats the query as literal text and escapes regular-expression metacharacters before using it in a pattern. The following example illustrates that approach:
def find_matches(text, user_query)
escaped_query = Regexp.escape(user_query)
pattern = Regexp.new(escaped_query)
text.scan(pattern)
end
Regexp.escape makes characters that would otherwise have special meaning in a regular expression behave as literal characters. This is appropriate when the feature is intended to search for the exact text supplied by the user. It is not a substitute for every security control: if your product deliberately allows users to submit regular-expression syntax, you need a carefully designed policy for supported patterns and must still upgrade Ruby.
The Ruby project's advisory explicitly warns that Regexp.timeout does not protect against the compilation bugs discussed in the report. A timeout can help limit certain expensive matching operations, but it does not repair memory-management or integer-overflow defects triggered while compiling a pattern. Limiting input length is also not a reliable standalone fix because some of the reported problems can be triggered by short patterns.
Test the patched runtime and preserve expected search behaviour
Once the application uses the updated interpreter, run the test suite in the same environment used for deployment. Start with tests covering search, filtering, validation, routing, and any feature that accepts patterns from users. Include ordinary strings, empty queries, punctuation, parentheses, brackets, repetition operators, and malformed patterns where these inputs are valid for your application. The goal is to check that the update closes the runtime gap without unexpectedly changing the application's intended behaviour.
You can also run a small smoke test against literal-search behaviour:
query = "price (before tax)"
pattern = Regexp.new(Regexp.escape(query))
raise "match failed" unless pattern.match?("The price (before tax) is shown")
raise "unexpected match" if pattern.match?("The price before tax is shown")
puts "Regex smoke test passed"
This example checks that punctuation in the query is treated literally rather than as regular-expression syntax. It does not prove that the runtime is secure, and it does not reproduce the reported vulnerabilities. Version verification, dependency review, application tests, and deployment checks remain separate tasks. A successful smoke test is useful evidence that the intended search behaviour still works, not a replacement for the security update.
Verify the version in production and check dependency constraints
After deployment, verify the Ruby version from the actual application process or a diagnostic command executed inside the deployed container. Confirm that the lockfile and runtime configuration agree, and rebuild any image that still contains the old interpreter. If your deployment platform manages Ruby for you, check its available runtime versions and rebuild or redeploy according to the platform's documented process.
Dependency constraints can complicate a patch update. A Gemfile may specify a Ruby version, a deployment image may pin a particular interpreter, or continuous integration may test against a different version from production. Update these declarations deliberately and rerun the test suite across the environments your project supports. If the application cannot move immediately, follow the Ruby project's interim recommendation: avoid compiling regular expressions from untrusted input until the patched runtime is deployed.
The Ruby project recommends moving to Ruby 4.0.9, 3.4.12, or 3.3.13, or a later patched release. These fixes matter most to applications that accept user-controlled regular-expression patterns, but the safest operational response is to confirm the interpreter version everywhere the application runs and remove unnecessary trust in externally supplied pattern syntax. Start with your production runtime inventory, identify every affected maintenance branch, and make the upgrade part of the next controlled deployment rather than relying on a timeout or input-length limit.
Share this post
Written by


