Nov 13, 2026·10 min read·2 visits
Action View's highlight and simple_format helpers perform regex and string splitting on HTML sanitized by Rails::HTML5::Sanitizer. Because Nokogiri < 1.19.5 leaves '<' and '>' unescaped inside HTML5 attribute values, attackers can inject crafted attributes that break during string post-processing, resulting in Cross-Site Scripting (XSS). Upgrade to Rails 8.0.5.2 or 8.1.4.1 and Nokogiri >= 1.19.5.
A Cross-Site Scripting (XSS) vulnerability exists in Ruby on Rails Action View prior to versions 8.0.5.2 and 8.1.4.1. When applications use the default HTML5 sanitizer (Rails::HTML5::Sanitizer) with Nokogiri versions prior to 1.19.5, angle brackets (< and >) inside HTML attribute values are serialized unescaped. Action View text helpers—specifically highlight and simple_format—perform string-based manipulations on the sanitized HTML rather than operating on the DOM tree. An unescaped '>' character or a double newline ('\n\n') inside an attribute value causes these helpers to misidentify tag or paragraph boundaries, breaking out of the attribute context and emitting inert attribute contents as live HTML elements in the final html_safe output.
Action View is the view template lookup and rendering framework within Ruby on Rails. It provides built-in view helpers designed to format text, construct HTML elements, and sanitize untrusted user input before rendering it to the client browser. Among these utilities are ActionView::Helpers::TextHelper#highlight and ActionView::Helpers::TextHelper#simple_format, which are routinely used to format user-generated content such as search results, comments, and forum posts.
In Rails applications configured with config.load_defaults 7.1 or later running on CRuby, the default sanitization backend is Rails::HTML5::Sanitizer. This sanitizer relies on rails-html-sanitizer, loofah, and nokogiri (specifically Nokogiri's Libgumbo-based HTML5 parser and serializer) to parse and clean untrusted HTML fragments.
CVE-2026-126079 (GHSA-2p2f-4rq4-gjwc) is a Cross-Site Scripting (CWE-79) vulnerability caused by a structural mismatch between how Nokogiri (< 1.19.5) serializes HTML5 attribute values and how Action View text helpers subsequently manipulate the serialized HTML string. Because the text helpers mark their final output as html_safe (ActiveSupport::SafeBuffer), any structural corruption introduced after the sanitization step is rendered directly by the victim's browser without further escaping.
Applications are affected if they use the HTML5 sanitizer and pass untrusted input to highlight, or to simple_format when html_options are supplied. Exploitation allows an unauthenticated remote attacker to execute arbitrary JavaScript within the security context of a victim's browser session.
The root cause of CVE-2026-126079 is a parser-serializer desynchronization between DOM-based HTML5 sanitization and regex/string-based post-processing. The vulnerability requires the interaction of two distinct behaviors across the dependency stack: permissive HTML5 attribute serialization in Nokogiri and naive string tokenization in Action View.
According to the WHATWG HTML5 specification for serializing HTML fragments, the characters < and > do not strictly need to be entity-encoded (< and >) when they appear inside double-quoted or single-quoted attribute values. When an HTML5 parser reads <a title="x > y">, it remains in the attribute value state until it encounters the matching closing quote ("), treating the > character as inert text. Prior to version 1.19.5, Nokogiri's HTML5 serializer followed this specification literally, emitting raw < and > characters inside quoted attribute values after sanitization.
While the output produced by Nokogiri is safe when parsed directly by a standards-compliant browser, ActionView::Helpers::TextHelper does not treat the sanitized result as an immutable string or manipulate it via DOM APIs. Instead, both highlight and simple_format perform raw string transformations on the sanitized HTML:
highlight: After sanitizing the input string, highlight splits the HTML on tag boundaries using regular expressions (scanning for <...> patterns) so that it can wrap matching text nodes in <mark> tags without corrupting HTML tag names or attributes. Because the regular expression does not implement a full HTML5 state machine, it treats the first raw > encountered inside an attribute value as the closing delimiter of the opening tag. Everything following that >—including the remainder of the attribute value and subsequent attributes—is misclassified as an inner text node.simple_format: The simple_format helper sanitizes its input and then splits the resulting string on blank lines (double newlines, \n\n) to wrap text blocks in <p> and </p> tags, as well as converting single newlines to <br /> tags. When an allowed HTML tag contains an attribute with embedded blank lines, simple_format injects closing and opening paragraph tags (</p><p ...>) directly into the middle of the quoted attribute string.In both helpers, the post-processing step alters the syntactic structure of the HTML fragment after the sanitizer has already validated it. Markup strings that were previously trapped inside a quoted attribute value are pushed outside the tag or quote boundaries, becoming active HTML elements in the returned ActiveSupport::SafeBuffer.
Rather than rewriting highlight and simple_format to operate on parsed Nokogiri DOM trees—which would introduce performance overhead and potentially break existing string-handling semantics in legacy applications—the Rails security team resolved the issue by updating the underlying sanitization dependency chain to always escape < and > inside attribute values.
In Nokogiri 1.19.5, the HTML5 serializer behavior was modified to entity-encode < as < and > as > within attribute values, matching the defensive serialization behavior already used in modern browser implementations (such as Chromium and Gecko) to prevent mutation XSS (mXSS) and string-processing bugs. Loofah 2.25.3 and rails-html-sanitizer 1.7.2 were subsequently released to enforce the minimum Nokogiri version and tighten handling around attribute serialization.
In Rails commits adc3a32d4abea77ed3aacf6f5fb71cfc005bef73 (for 8.1.4.1) and 009ea68342d05d02087c04dae7faaa4e4c1def11 (for 8.0.5.2), actionview.gemspec was updated to require rails-html-sanitizer >= 1.7.2, and regression tests were added to verify that both highlight and simple_format keep attribute payloads inert:
# File: actionview/actionview.gemspec
s.add_dependency "builder", "~> 3.1"
s.add_dependency "erubi", "~> 1.11"
- s.add_dependency "rails-html-sanitizer", "~> 1.6"
+ s.add_dependency "rails-html-sanitizer", "~> 1.6", ">= 1.7.2" # Enforces Loofah >= 2.25.3 and Nokogiri >= 1.19.5
s.add_dependency "rails-dom-testing", "~> 2.2"# File: actionview/test/template/text_helper_test.rb
class TextHelperHTML5SanitizerTest < ActionView::TestCase
tests ActionView::Helpers::TextHelper
self.safe_list_sanitizer = Rails::HTML5::SafeListSanitizer.new
def test_highlight_keeps_markup_inside_attribute_values_inert
# Before patch: '>' in title breaks tag scanning; matching on "class" injects <mark> tags
# that unbalance quotes, exposing the <img> tag as live HTML.
html = highlight(%(<a title="x > y" class="<img src=x onerror=alert(1)>">z</a>), "class")
assert_empty Nokogiri::HTML5.fragment(html).css("img")
end
def test_simple_format_keeps_markup_inside_attribute_values_inert
# Before patch: "\n\n" splits the attribute across <p class="post"> tags,
# closing the quote early and exposing the <img> payload.
html = simple_format(%(<a title="x\n\n<img src=x onerror=alert(1)>">link</a>), class: "post")
assert_empty Nokogiri::HTML5.fragment(html).css("img")
end
end if Rails::HTML::Sanitizer.html5_support?By ensuring that < and > are always serialized as < and > inside attributes at the Nokogiri layer, any downstream string manipulation—whether in highlight, simple_format, or custom application helpers—cannot accidentally expose raw angle brackets from inside an attribute value. Even if simple_format splits an attribute across paragraph boundaries due to embedded newlines, any <img ...> payload inside that attribute has already been converted to <img ...> by the sanitizer. Consequently, when the browser parses the broken tag structure, the injected text is rendered as harmless character data rather than executable HTML elements.
To exploit CVE-2026-126079, an attacker must supply crafted HTML input to an application endpoint that renders the input through highlight or simple_format (with html_options) while using Rails::HTML5::Sanitizer on Nokogiri < 1.19.5.
highlightConsider a search or content display view that highlights a target keyword (either static or user-controlled) within user-supplied text:
<%= highlight(user_comment, "class") %>The attacker submits the following payload as user_comment:
<a title="x > y" class="<img src=x onerror=alert(1)>">z</a>Rails::HTML5::SafeListSanitizer parses the fragment. The <a> element and its title and class attributes are permitted by the default allowlist. The <img src=x onerror=alert(1)> string is inside the quoted class attribute value, so the sanitizer considers it safe attribute text rather than an HTML element. Nokogiri (< 1.19.5) serializes the sanitized DOM back into an HTML string without escaping < or >, returning the exact input string.highlight splits the sanitized string into tags and text segments to wrap occurrences of the phrase "class" in <mark> tags. Because its tokenizer stops at the first > inside title="x > y", the substring " class="<img src=x onerror=alert(1)>">z is treated as text outside of an HTML tag.highlight replaces class with <mark>class</mark>, producing:
<a title="x > y" <mark>class</mark>="<img src=x onerror=alert(1)>">z</a><a title="x > y" as a valid attribute, encounters <mark>class</mark>=" as malformed attribute names, and uses the opening quote " before <img to start an attribute value for the = attribute, or closes the <a> tag at the > of <mark>. Because the <mark> tags inject unquoted > characters before the class attribute value, the <a> start tag is prematurely closed in the browser's parser before <img src=x onerror=alert(1)> is reached. The <img> tag is parsed as a top-level DOM element, firing the onerror handler and executing JavaScript.simple_formatConsider a view that formats user input into paragraphs with custom HTML attributes (html_options):
<%= simple_format(user_bio, class: "post") %>The attacker provides a payload containing double newlines (\n\n) and an XSS vector inside an allowed attribute:
<a title="x
<img src=x onerror=alert(1)>">link</a><a> tag and its title attribute. In Nokogiri < 1.19.5, the < and > characters inside title remain unescaped.simple_format splits the sanitized string on \n\n and wraps each paragraph in <p class="post">...</p>. The injected wrapper tags contain double quotes (class="post").</p>\n<p class="post"> is inserted into the middle of title="x...", the first " inside class="post" closes the title attribute quote. The > at the end of <p class="post"> closes the opening <a> tag. As a result, the <img src=x onerror=alert(1)> string that follows the double newline is pushed completely outside the <a> tag and rendered as a live DOM element.CVE-2026-126079 is rated 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 vulnerability is network-exploitable without authentication (AV:N/AC:L/PR:N) wherever untrusted input is passed to the affected helpers, though it requires a victim user to view the rendered page (UI:R).
Successful exploitation results in stored or reflected Cross-Site Scripting (XSS), changing the security scope (S:C) from the vulnerable web server component to the client browser environment. Once arbitrary JavaScript executes within the victim's browser origin, an attacker can:
C:L).HttpOnly flag is not set.fetch or XMLHttpRequest calls (I:L).The Exploit Prediction Scoring System (EPSS) assigns this vulnerability a score of 0.00338 (0.34% probability of exploitation in the next 30 days, ~27.57th percentile), and it is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. The real-world exposure is constrained to applications that meet the specific configuration preconditions (CRuby, HTML5 sanitizer enabled, and direct usage of highlight or simple_format with html_options on untrusted data).
To remediate CVE-2026-126079, upgrade Ruby on Rails and its underlying HTML sanitization dependencies to the patched versions:
rails / actionview): Upgrade to 8.0.5.2 (for the 8.0 release series) or 8.1.4.1 (for the 8.1 release series).Gemfile.lock resolves to the following minimum versions:
rails-html-sanitizer >= 1.7.2loofah >= 2.25.3nokogiri >= 1.19.5Run the following Bundler command to update the dependency chain:
bundle update actionview rails-html-sanitizer loofah nokogiri> [!NOTE]
> Applications running on older Rails branches (such as Rails 7.1 or 7.2) that have enabled Rails::HTML5::Sanitizer can also mitigate the underlying serialization flaw directly by upgrading rails-html-sanitizer to >= 1.7.2, loofah to >= 2.25.3, and nokogiri to >= 1.19.5 via Bundler.
If an immediate gem upgrade cannot be deployed to production, apply one of the following workarounds:
< and > in attribute values during serialization:
# config/initializers/sanitizer.rb
Rails.application.config.action_view.sanitizer_vendor = Rails::HTML4::Sanitizerhighlight or simple_format, strip all tags or pass the input through ERB::Util.html_escape before invoking the helper:
<%= highlight(strip_tags(user_input), search_term) %>
<%= simple_format(ERB::Util.html_escape(user_input), { class: "post" }, sanitize: false) %>CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Ruby on Rails (actionview) Rails | < 8.0.5.2 | 8.0.5.2 |
Ruby on Rails (actionview) Rails | >= 8.1.0, < 8.1.4.1 | 8.1.4.1 |
rails-html-sanitizer Rails | < 1.7.2 | 1.7.2 |
loofah sparklemotion | < 2.25.3 | 2.25.3 |
nokogiri sparklemotion | < 1.19.5 | 1.19.5 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Cross-Site Scripting (CWE-79) |
| Affected Component | Action View (highlight & simple_format helpers) |
| CVSS v3.1 Score | 6.1 Medium (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N) |
| EPSS Score | 0.34% (27.57th percentile) |
| Root Cause | Unescaped < and > in HTML5 attribute serialization combined with string-based helper post-processing |
| Patched Versions | Rails 8.0.5.2 / 8.1.4.1 (Nokogiri >= 1.19.5) |
| Exploit Status | Proof-of-Concept (Regression Test Vectors) |
| CISA KEV Status | Not Listed |
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.
A vulnerability classified under CWE-345 (Insufficient Verification of Data Authenticity) exists in the Active Storage component of Ruby on Rails prior to versions 8.1.4.1 and 8.0.5.2. When Google Cloud Storage (GCS) is configured as the storage backend with the cache_control option enabled, Active Storage switches from GCS V2 signing to V4 signing for direct upload URLs. Because the V4 signing implementation only binds the Cache-Control and Host headers, the Content-Type and Content-MD5 headers remain unsigned. An attacker can modify the Content-Type header during a direct upload request to store HTML or SVG files with active JavaScript on the GCS bucket origin, enabling Service Worker registration and interception of subsequent signed URL responses within the bucket path.
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.
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.