Nov 13, 2026·10 min read·3 visits
Action View's strip_tags helper marks its output as html_safe without encoding quotation marks, allowing attackers to break out of quoted HTML attributes and achieve Cross-Site Scripting (XSS). Upgrade to Ruby on Rails 8.0.5.2 or 8.1.4.1.
CVE-2026-126070 is a Cross-Site Scripting (XSS) vulnerability in the Action View component of Ruby on Rails. Prior to versions 8.0.5.2 and 8.1.4.1, the strip_tags helper returned sanitized output marked as html_safe without encoding single or double quotation marks. When applications render the output of strip_tags inside quoted HTML attributes in ERB templates, attackers can break out of the attribute context and inject arbitrary HTML attributes or JavaScript event handlers.
Action View is the core Ruby on Rails framework component responsible for handling view template lookup, template rendering, and providing helpers for constructing HTML documents. Among these utilities is the SanitizeHelper module, which exposes methods such as sanitize, strip_links, and strip_tags to process untrusted user input before rendering it within application views.
A Cross-Site Scripting (XSS) vulnerability, tracked as CVE-2026-126070 (and GHSA-rq8q-p985-fjq7), exists in the strip_tags helper method in Ruby on Rails versions prior to 8.0.5.2 and 8.1.4.1. This flaw is classified under CWE-79 (Improper Neutralization of Input During Web Page Generation). The vulnerability stems from a discrepancy between the character encoding performed by the underlying HTML sanitizer and the safety guarantees expected of strings marked as html_safe in Rails.
When strip_tags processes a string, it removes HTML elements and encodes ampersands (&), less-than signs (<), and greater-than signs (>). However, it leaves single quotes (') and double quotes (") unencoded while marking the returned string as html_safe. Because the Rails ERB template engine bypasses automatic entity escaping for html_safe strings, interpolating the output of strip_tags directly inside a quoted HTML attribute allows untrusted input to prematurely close the attribute delimiter.
An attacker who controls input passed through strip_tags and rendered inside an HTML attribute (such as <meta content="...">, <input value="...">, or <div title="...">) can inject arbitrary HTML attributes, including inline JavaScript event handlers like onfocus or onmouseover. When triggered in a victim's browser, this results in arbitrary JavaScript execution within the context of the vulnerable application's origin.
To understand the root cause of CVE-2026-126070, it is necessary to examine how Ruby on Rails manages output safety during template compilation. Rails utilizes the ActiveSupport::SafeBuffer class (instantiated by calling .html_safe on a standard Ruby String) to distinguish between raw untrusted strings and strings that have already been sanitized or escaped. When an ERB template evaluates an expression using <%= ... %>, Rails inspects the return value: standard strings undergo full HTML entity escaping via ERB::Util.html_escape, whereas SafeBuffer instances are concatenated directly into the output buffer without further modification.
In Rails convention, any string marked as html_safe that is intended for general text interpolation must neutralize all five HTML-significant characters: &, <, >, ", and '. Neutralizing < and > prevents tag injection in element body contexts, while neutralizing " and ' prevents attribute breakout when the string is interpolated inside single- or double-quoted HTML attribute values.
Prior to the patch, ActionView::Helpers::SanitizeHelper#strip_tags delegated sanitization to rails-html-sanitizer via self.class.full_sanitizer.sanitize(html) and immediately invoked &.html_safe on the result. The full_sanitizer (backed by Loofah and Nokogiri) parses the input as an HTML fragment, strips all element nodes, and serializes the remaining text nodes. During text node serialization, Nokogiri/Loofah encodes &, <, and > into &, <, and > respectively, as required by the HTML specification for element content. Because quotation marks are completely valid inside HTML text nodes, the serializer leaves " and ' as literal characters.
By unconditionally appending .html_safe to the output of full_sanitizer.sanitize, strip_tags promoted a string that was only safe for element content into a SafeBuffer that ERB treated as safe for all HTML contexts, including quoted attribute values. When a developer wrote <div title="<%= strip_tags(untrusted_input) %>">, ERB observed the SafeBuffer flag, skipped attribute escaping, and emitted the unescaped quotation marks directly into the rendered HTML document.
The vulnerability resides in actionview/lib/action_view/helpers/sanitize_helper.rb. In vulnerable versions of Rails, strip_tags was implemented as a single-line method that called full_sanitizer.sanitize and marked any non-nil return value as html_safe.
# Vulnerable implementation in actionview/lib/action_view/helpers/sanitize_helper.rb
def strip_tags(html)
self.class.full_sanitizer.sanitize(html)&.html_safe
endIn commits 568b19cb860322885593e308a7f27aceb779e9d7 and c3772a10f405f9644125cbb484adfd2aa7a25a59, the Rails core team modified strip_tags to pass the sanitized string through ERB::Util.html_escape_once rather than calling .html_safe directly.
diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb
--- a/actionview/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionview/lib/action_view/helpers/sanitize_helper.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+require "active_support/core_ext/string/output_safety"
require "rails-html-sanitizer"
module ActionView
@@ -33,6 +34,9 @@ module SanitizeHelper
# Please note that sanitizing user-provided text does not guarantee that the
# resulting markup is valid or even well-formed.
#
+ # The output is markup, which is safe only as element content. Do not use it in an attribute
+ # value; use +strip_tags+ or a tag helper such as <tt>tag.a(title: ...)</tt> instead.
+ #
# ==== Options
#
# [+:tags+]
@@ -137,7 +141,8 @@ def sanitize_css(style)
# strip_tags("> A quote from Smith & Wesson")
# # => > A quote from Smith & Wesson
def strip_tags(html)
- self.class.full_sanitizer.sanitize(html)&.html_safe
+ stripped = self.class.full_sanitizer.sanitize(html)
+ ERB::Util.html_escape_once(stripped) if stripped
endThe replacement of &.html_safe with ERB::Util.html_escape_once(stripped) resolves the issue through two specific mechanisms:
html_escape_once converts " to " and ' to ' while also ensuring &, <, and > are encoded. Because full_sanitizer.sanitize already converts &, <, and > into &, <, and >, using html_escape_once (instead of standard html_escape) prevents double-encoding existing HTML entities (e.g., avoiding &amp;).ERB::Util.html_escape_once returns an ActiveSupport::SafeBuffer instance, preserving backward compatibility with callers that expect strip_tags to return an html_safe string.Fix Completeness and Variant Analysis: The patch also updates the documentation for the sibling sanitize helper to explicitly warn developers that sanitize returns markup intended strictly for element content and must not be placed inside HTML attributes. Because sanitize intentionally allows HTML tags and attributes through an allowlist, encoding quotes across its entire return string would break allowed HTML tag attributes. Furthermore, strip_tags remains unsafe if placed inside an unquoted HTML attribute (e.g., <div title=<%= strip_tags(input) %>>), as whitespace characters are sufficient to break out of unquoted attributes. However, unquoted dynamic attributes violate standard HTML authoring practices regardless of the escaping function used.
Exploiting CVE-2026-126070 requires an application to accept untrusted input, pass that input through strip_tags, and interpolate the result directly inside a quoted HTML attribute within an ERB template. Common real-world patterns where developers use strip_tags inside attributes include populating SEO <meta> tags, tooltip title attributes, data-* attributes, or form <input value="..."> fields from user-generated rich text or Markdown summaries.
Consider a vulnerable Rails ERB template that renders user-supplied profile data or article summaries inside a <meta> tag and an <input> field:
<!-- Vulnerable ERB Template -->
<meta name="description" content="<%= strip_tags(@user.bio) %>">
<input type="text" name="headline" value="<%= strip_tags(@user.headline) %>">Because the attacker cannot use < or > to close the enclosing tag or inject a <script> block (as full_sanitizer.sanitize strips tags and encodes angle brackets), exploitation relies on attribute injection. If the target element is an interactive form control such as <input>, the attacker can supply the following payload for @user.headline:
x" onfocus="alert(document.domain)" autofocus="When strip_tags executes on this payload, full_sanitizer detects no HTML tags or angle brackets and returns the string unmodified, marked as html_safe. ERB inserts the string directly into the value attribute:
<input type="text" name="headline" value="x" onfocus="alert(document.domain)" autofocus="">When the page loads in the victim's browser, the autofocus attribute immediately directs browser focus to the <input> element, firing the onfocus event handler and executing the attacker's JavaScript without requiring direct user clicks.
If the target element is non-interactive by default (such as a <div> with a title attribute) or a hidden element that can be styled to become visible and interactive, the attacker can combine inline style attributes with mouse event handlers:
x" onmouseover="alert(document.domain)" style="position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9999;" x="When rendered inside <div title="<%= strip_tags(input) %>">, this payload breaks out of the title attribute, overlays the element across the entire browser viewport using fixed CSS positioning, and executes the onmouseover payload as soon as the user moves their cursor anywhere on the page.
CVE-2026-126070 is rated as Medium severity with a CVSS v3.1 base score of 6.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N). The attack vector is Network (AV:N) with Low attack complexity (AC:L) and requires no privileges (PR:N) in scenarios where unauthenticated input (such as search queries, public comments, or URL parameters) is reflected or stored. User Interaction is Required (UI:R), as a victim must view the rendered page or interact with the injected element. The Scope is Changed (S:C) because the vulnerability resides in the server-side web framework but executes within the security context of the client browser.
Successful exploitation grants the attacker arbitrary JavaScript execution in the victim's browser session within the origin of the vulnerable Rails application. Depending on the application's architecture and session management configuration, an attacker can:
fetch or XMLHttpRequest calls, bypassing Cross-Site Request Forgery (CSRF) protections by reading the <meta name="csrf-token"> directly from the DOM.localStorage, sessionStorage, or cookies lacking the HttpOnly flag.Applications that only render strip_tags output as standard HTML element content (e.g., <p><%= strip_tags(user_input) %></p>) are not affected, because < and > were already properly encoded into < and > by the sanitizer. Furthermore, applications that pass strip_tags output into Rails tag builder helpers (such as tag.div(title: strip_tags(input)) or content_tag) are confirmably unaffected or have reduced exposure depending on how attribute hashes are escaped.
According to threat intelligence data, CVE-2026-126070 has an EPSS score of 0.00269 (19.25th percentile), indicating a low probability of widespread automated exploitation within 30 days. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no weaponized exploits or mass-scanning templates have been observed in the wild.
To remediate CVE-2026-126070, upgrade Ruby on Rails (rails / actionview gems) to a patched release that encodes quotation marks in strip_tags output:
Update the Gemfile to specify the patched version and run bundle update rails:
# Gemfile
gem "rails", "~> 8.1.4", ">= 8.1.4.1"
# or for the 8.0 branch:
gem "rails", "~> 8.0.5", ">= 8.0.5.2"> [!NOTE]
> Behavioral Change in Non-HTML Contexts: Because strip_tags now encodes " and ' as " and ', applications that use strip_tags to clean strings for non-HTML outputs—such as plain-text emails, JSON API payloads, or CSV exports—will now see HTML entity strings where literal quotation marks previously appeared. If plain text is required after stripping tags, wrap the call in CGI.unescapeHTML(strip_tags(input)) and ensure the resulting plain string is not marked html_safe.
If an immediate framework upgrade is not feasible, apply one of the following code-level mitigations in ERB templates:
Explicitly call html_escape_once when interpolating inside HTML attributes:
<meta name="description" content="<%= html_escape_once(strip_tags(user_input)) %>">
<div title="<%= html_escape_once(strip_tags(user_input)) %>">Content</div>Use Rails tag helpers instead of raw HTML attribute interpolation:
<%= tag.meta(name: "description", content: html_escape_once(strip_tags(user_input))) %>Audit for unquoted attributes and improper sanitize usage: Ensure all HTML attributes containing dynamic content are enclosed in single or double quotes, and verify that the output of sanitize(...) (which permits HTML markup) is never interpolated inside an HTML attribute value.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
rails (actionview) Ruby on Rails | < 8.0.5.2 | 8.0.5.2 |
rails (actionview) Ruby on Rails | >= 8.1.0, < 8.1.4.1 | 8.1.4.1 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-126070 (GHSA-rq8q-p985-fjq7) |
| Vulnerability Class | CWE-79: Cross-Site Scripting (XSS) |
| Affected Component | Action View (ActionView::Helpers::SanitizeHelper#strip_tags) |
| CVSS v3.1 Score | 6.1 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N) |
| EPSS Score | 0.27% (19.25th Percentile) |
| Exploit Status | PoC Confirmed in Unit Tests (Not in CISA KEV) |
| Patched Versions | Rails 8.0.5.2, Rails 8.1.4.1 |
The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVE-2026-126078 is a Cross-Site Scripting (XSS) vulnerability classified under CWE-79 affecting the Action View component of Ruby on Rails in versions prior to 8.0.5.2 and 8.1.4.1. The vulnerability resides in the ActionView::Helpers::TextHelper#highlight method when invoked with a custom :highlighter template that places capture group placeholders inside HTML attribute values. Because matched substrings are substituted without HTML escaping after initial sanitization preserves literal quotation marks, an attacker controlling the input text or target search phrase can break out of the surrounding attribute delimiter and inject arbitrary HTML attributes or JavaScript event handlers.
An information exposure vulnerability in Ruby on Rails Action Pack allows sensitive session cookies to be attached to public, cacheable responses served by Active Storage proxy controllers. When cookie secret rotation or a hybrid cookie serializer migration is enabled, reading a legacy-format cookie triggers an automatic cookie rewrite that ignores the session-skipping directive. If an intermediate shared cache or CDN caches the resulting response along with its Set-Cookie header, subsequent users requesting the same URL receive the leaked session token.
CVE-2026-126072 (GHSA-rwvh-fw6r-pwvp) is a concurrency deadlock vulnerability in the Ruby on Rails Action Pack framework affecting applications that utilize ActionController::Live response streaming. The defect occurs due to improper synchronization within ActionController::Live::Buffer when handling full bounded queues during stream closure and connection abortion. An unauthenticated remote attacker can trigger this condition by stalling TCP socket reads on streaming endpoints—including Active Storage proxy mode—resulting in permanent worker thread exhaustion and application-wide denial of service.
CVE-2026-126192 is an algorithmic complexity vulnerability in Soup Sieve (soupsieve), the default CSS selector engine for Beautiful Soup 4 in Python. Evaluating a CSS selector that contains a general sibling combinator (~) nested inside a :has() relational pseudo-class against attacker-controlled HTML markup triggers O(n^2) quadratic evaluation complexity when the right-hand sibling target is absent. An unauthenticated attacker can supply small HTML documents containing thousands of sibling elements to exhaust CPU resources and stall application worker threads.
CVE-2026-126190 (GHSA-mmhr-3c5m-f64f) is an inefficient algorithmic complexity vulnerability (CWE-407) in Soup Sieve, the default CSS selector library integrated into Beautiful Soup 4. In versions prior to 4.0, evaluating CSS selectors that contain descendant combinators (whitespace between compound selectors) executes an unmemoized upward traversal of the document tree. When evaluated against deeply nested HTML or XML documents, the time complexity scales polynomially at O(n^(k+1)), where n is the tree depth and k is the number of descendant combinators. Remote attackers can supply compact, deeply nested markup (1 KB to 4 KB) to trigger prolonged CPU starvation and denial of service in Python applications.
CVE-2026-122006 is a critical vulnerability in the Ruby on Rails Active Storage framework affecting versions prior to 8.0.5.2 and 8.1.4.1. When an application processes uploaded file attachments using the ImageMagick variant processor (:mini_magick), the framework invokes ImageMagick's metadata analyzer without applying a restrictive security policy. An unauthenticated attacker can upload a crafted file containing directives for unrestricted ImageMagick coders, such as Magick Scripting Language (MSL), resulting in arbitrary server-side file read and write operations. In deployments using the Disk storage service, attackers can exfiltrate sensitive application configuration files, including secret_key_base and master encryption keys, which enables remote code execution via signed payload forgery.