Rails Active Support has a possible ReDoS vulnerability in number_to_delimited
Description
Active Support is a toolkit of support libraries and Ruby core extensions extracted from the Rails framework. NumberToDelimitedConverter uses a lookahead-based regular expression with gsub! to insert thousands delimiters. Prior to versions 8.1.2.1, 8.0.4.1, and 7.2.3.1, the interaction between the repeated lookahead group and gsub! can produce quadratic time complexity on long digit strings. Versions 8.1.2.1, 8.0.4.1, and 7.2.3.1 contain a patch.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A quadratic-time regex in Active Support's NumberToDelimitedConverter can cause denial of service via crafted long digit strings.
Vulnerability
CVE-2026-33169 is a denial-of-service vulnerability in Active Support, a Ruby on Rails component. The NumberToDelimitedConverter uses a lookahead-based regular expression with gsub! to insert thousands delimiters. The interaction between the repeated lookahead group and gsub! can produce quadratic time complexity on long digit strings, leading to excessive CPU consumption [1][2].
Exploitation
An attacker can trigger this vulnerability by providing a specially crafted long digit string to any Rails application that uses the number-to-delimited converter (e.g., via number_with_delimiter helper). No authentication is required if the application exposes this functionality to unauthenticated users. The attack vector is network-based, with low attack complexity [2].
Impact
Successful exploitation results in a denial of service (DoS) condition, where the affected Rails process becomes unresponsive due to high CPU usage. This can degrade or completely block the application's ability to handle legitimate requests [1][2].
Mitigation
The vulnerability affects Active Support versions prior to 8.1.2.1, 8.0.4.1, and 7.2.3.1. Patched versions are available, and users should upgrade immediately. The fix improves the performance of NumberToDelimitedConverter by avoiding the quadratic regex behavior [1][3][4].
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
activesupportRubyGems | >= 8.1.0.beta1, < 8.1.2.1 | 8.1.2.1 |
activesupportRubyGems | >= 8.0.0.beta1, < 8.0.4.1 | 8.0.4.1 |
activesupportRubyGems | < 7.2.3.1 | 7.2.3.1 |
Affected products
2- Range: < 8.1.2.1, < 8.0.4.1, < 7.2.3.1
- rails/activesupportv5Range: >= 8.1.0.beta1, < 8.1.2.1
Patches
329154f1097daImprove performance of NumberToDelimitedConverter
2 files changed · +19 −2
activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb+17 −2 modified@@ -16,9 +16,24 @@ def convert private def parts left, right = number.to_s.split(".") - left.gsub!(delimiter_pattern) do |digit_to_delimit| - "#{digit_to_delimit}#{options[:delimiter]}" + if delimiter_pattern + left.gsub!(delimiter_pattern) do |digit_to_delimit| + "#{digit_to_delimit}#{options[:delimiter]}" + end + else + left_parts = [] + offset = left.size % 3 + if offset > 0 + left_parts << left[0, offset] + end + + (left.size / 3).times do |i| + left_parts << left[offset + (i * 3), 3] + end + + left = left_parts.join(options[:delimiter]) end + [left, right].compact end
activesupport/test/number_helper_test.rb+2 −0 modified@@ -139,6 +139,8 @@ def test_to_delimited assert_equal("12,345,678", number_helper.number_to_delimited(12345678)) assert_equal("0", number_helper.number_to_delimited(0)) assert_equal("123", number_helper.number_to_delimited(123)) + assert_equal("1,234", number_helper.number_to_delimited(1234)) + assert_equal("12,345", number_helper.number_to_delimited(12345)) assert_equal("123,456", number_helper.number_to_delimited(123456)) assert_equal("123,456.78", number_helper.number_to_delimited(123456.78)) assert_equal("123,456.789", number_helper.number_to_delimited(123456.789))
b54a4b373c6fImprove performance of NumberToDelimitedConverter
2 files changed · +19 −2
activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb+17 −2 modified@@ -16,9 +16,24 @@ def convert private def parts left, right = number.to_s.split(".") - left.gsub!(delimiter_pattern) do |digit_to_delimit| - "#{digit_to_delimit}#{options[:delimiter]}" + if delimiter_pattern + left.gsub!(delimiter_pattern) do |digit_to_delimit| + "#{digit_to_delimit}#{options[:delimiter]}" + end + else + left_parts = [] + offset = left.size % 3 + if offset > 0 + left_parts << left[0, offset] + end + + (left.size / 3).times do |i| + left_parts << left[offset + (i * 3), 3] + end + + left = left_parts.join(options[:delimiter]) end + [left, right].compact end
activesupport/test/number_helper_test.rb+2 −0 modified@@ -139,6 +139,8 @@ def test_to_delimited assert_equal("12,345,678", number_helper.number_to_delimited(12345678)) assert_equal("0", number_helper.number_to_delimited(0)) assert_equal("123", number_helper.number_to_delimited(123)) + assert_equal("1,234", number_helper.number_to_delimited(1234)) + assert_equal("12,345", number_helper.number_to_delimited(12345)) assert_equal("123,456", number_helper.number_to_delimited(123456)) assert_equal("123,456.78", number_helper.number_to_delimited(123456.78)) assert_equal("123,456.789", number_helper.number_to_delimited(123456.789))
ec1a0e215efdImprove performance of NumberToDelimitedConverter
2 files changed · +19 −2
activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb+17 −2 modified@@ -16,9 +16,24 @@ def convert private def parts left, right = number.to_s.split(".") - left.gsub!(delimiter_pattern) do |digit_to_delimit| - "#{digit_to_delimit}#{options[:delimiter]}" + if delimiter_pattern + left.gsub!(delimiter_pattern) do |digit_to_delimit| + "#{digit_to_delimit}#{options[:delimiter]}" + end + else + left_parts = [] + offset = left.size % 3 + if offset > 0 + left_parts << left[0, offset] + end + + (left.size / 3).times do |i| + left_parts << left[offset + (i * 3), 3] + end + + left = left_parts.join(options[:delimiter]) end + [left, right].compact end
activesupport/test/number_helper_test.rb+2 −0 modified@@ -139,6 +139,8 @@ def test_to_delimited assert_equal("12,345,678", number_helper.number_to_delimited(12345678)) assert_equal("0", number_helper.number_to_delimited(0)) assert_equal("123", number_helper.number_to_delimited(123)) + assert_equal("1,234", number_helper.number_to_delimited(1234)) + assert_equal("12,345", number_helper.number_to_delimited(12345)) assert_equal("123,456", number_helper.number_to_delimited(123456)) assert_equal("123,456.78", number_helper.number_to_delimited(123456.78)) assert_equal("123,456.789", number_helper.number_to_delimited(123456.789))
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-cg4j-q9v8-6v38ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33169ghsaADVISORY
- github.com/rails/rails/commit/29154f1097da13d48fdb3200760b3e3da66dcb11ghsax_refsource_MISCWEB
- github.com/rails/rails/commit/b54a4b373c6f042cab6ee2033246b1c9ecc38974ghsax_refsource_MISCWEB
- github.com/rails/rails/commit/ec1a0e215efd27a3b3911aae6df978a80f456a49ghsax_refsource_MISCWEB
- github.com/rails/rails/releases/tag/v7.2.3.1ghsax_refsource_MISCWEB
- github.com/rails/rails/releases/tag/v8.0.4.1ghsax_refsource_MISCWEB
- github.com/rails/rails/releases/tag/v8.1.2.1ghsax_refsource_MISCWEB
- github.com/rails/rails/security/advisories/GHSA-cg4j-q9v8-6v38ghsax_refsource_CONFIRMWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/activesupport/CVE-2026-33169.ymlghsaWEB
News mentions
3- Object First Fleet Manager simplifies distributed backup storageHelp Net Security · May 8, 2026
- The EOL Blind Spot in Your CVE Feed: What SCA Tools Don't Check.BleepingComputer · May 5, 2026
- The EOL Blind Spot in Your CVE Feed: What SCA Tools MissBleepingComputer · May 5, 2026