CVE-2026-33170 Overview
A Cross-Site Scripting (XSS) vulnerability exists in Ruby on Rails Active Support library where SafeBuffer#% does not properly propagate the @html_unsafe flag to newly created buffers. When a SafeBuffer is mutated in place (e.g., via gsub!) and then formatted with % using untrusted arguments, the result incorrectly reports html_safe? == true, bypassing ERB auto-escaping and potentially leading to XSS attacks.
Critical Impact
This vulnerability allows attackers to bypass Rails' built-in XSS protection mechanisms when applications use specific patterns of SafeBuffer manipulation, potentially enabling injection of malicious scripts into web pages rendered by affected Rails applications.
Affected Products
- Ruby on Rails versions prior to 8.1.2.1
- Ruby on Rails versions prior to 8.0.4.1
- Ruby on Rails versions prior to 7.2.3.1
Discovery Timeline
- 2026-03-24 - CVE CVE-2026-33170 published to NVD
- 2026-03-24 - Last updated in NVD database
Technical Details for CVE-2026-33170
Vulnerability Analysis
This vulnerability stems from improper state management in the Active Support SafeBuffer class, which is a core component of Rails' XSS protection system. The SafeBuffer class is designed to track whether a string contains HTML-safe content that can be rendered directly without escaping, or potentially unsafe content that should be escaped before output.
The flaw occurs in the % (format) method implementation of SafeBuffer. When a SafeBuffer is first mutated in place using methods like gsub!, the buffer is correctly marked as unsafe via the @html_unsafe flag. However, when the % operator is subsequently used to format the buffer with arguments, the method creates a new SafeBuffer instance but fails to copy the @html_unsafe flag to this new instance.
This oversight causes the newly created buffer to incorrectly report html_safe? == true even when it should be considered unsafe. Since ERB templates in Rails rely on this flag to determine whether to auto-escape content, an attacker can exploit this behavior to inject malicious scripts that bypass the escaping mechanism.
Root Cause
The root cause is a missing state propagation in the SafeBuffer#% method. When creating a new SafeBuffer instance from the formatted string result, the method did not preserve the @html_unsafe (or @html_safe) instance variable from the original buffer. This caused the safety status to be reset to the default safe state, incorrectly marking mutated and potentially dangerous content as safe for rendering.
Attack Vector
An attacker can exploit this vulnerability through a network-based attack requiring user interaction. The attack requires:
- The Rails application must use a pattern where a SafeBuffer is mutated in place (using methods like gsub!)
- The mutated buffer is then formatted using the % operator with user-controlled arguments
- The resulting buffer is rendered in an ERB template without additional escaping
When these conditions are met, an attacker can inject malicious JavaScript or HTML through the user-controlled arguments, which will be rendered unescaped in the victim's browser.
# Security patch showing the fix for SafeBuffer#% method
# Source: https://github.com/rails/rails/commit/50d732af3b7c8aaf63cbcca0becbc00279b215b7
new_string = super
new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
if @html_unsafe
- new_safe_buffer.instance_variable_set(:@html_unsafe, true)
+ new_safe_buffer.mark_unsafe!
end
new_safe_buffer
The patch ensures that when a new SafeBuffer is created via the % operator, the unsafe status is properly propagated using the mark_unsafe! method, maintaining the security state across buffer operations.
Detection Methods for CVE-2026-33170
Indicators of Compromise
- Unexpected JavaScript execution in Rails application pages that utilize SafeBuffer string formatting
- XSS payloads appearing in rendered HTML output where ERB auto-escaping should have prevented them
- Application logs showing unusual string patterns containing script tags or event handlers in formatted output
Detection Strategies
- Review application code for patterns that combine gsub! or other in-place mutation methods with subsequent % formatting on SafeBuffer objects
- Implement web application firewall (WAF) rules to detect common XSS payloads in request parameters
- Conduct security audits of views and helpers that manipulate SafeBuffer objects before rendering
Monitoring Recommendations
- Enable Content Security Policy (CSP) reporting to detect attempted XSS attacks
- Monitor application error logs for unexpected HTML or JavaScript content in rendered responses
- Implement runtime application security testing (RAST) to identify XSS attempts in production environments
How to Mitigate CVE-2026-33170
Immediate Actions Required
- Upgrade Rails to patched versions 8.1.2.1, 8.0.4.1, or 7.2.3.1 immediately
- Audit application code for vulnerable SafeBuffer manipulation patterns involving gsub! followed by % formatting
- Review and test all custom helpers that work with SafeBuffer objects for proper escaping behavior
Patch Information
Ruby on Rails has released security patches for all supported version branches:
- Rails v8.1.2.1 - For Rails 8.1.x users
- Rails v8.0.4.1 - For Rails 8.0.x users
- Rails v7.2.3.1 - For Rails 7.2.x users
For detailed information about this vulnerability, refer to the GitHub Security Advisory GHSA-89vf-4333-qx8v.
Workarounds
- Explicitly call html_escape or h helper on any SafeBuffer content that has been mutated in place before using with % formatting
- Avoid using in-place mutation methods like gsub! on SafeBuffer objects that will later be formatted with %
- Use ERB::Util.html_escape explicitly when working with user-controlled content in SafeBuffer operations
# Update Rails to patched version using Bundler
bundle update rails activesupport --conservative
# Verify the installed version
bundle show rails
# Should show: rails (8.1.2.1), (8.0.4.1), or (7.2.3.1)
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


