CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-126206

CVE-2026-126206: Cross-Site Scripting via HTML5 Named Solidus Entity in Loofah URI Validator

Alon Barad
Alon Barad
Software Engineer

Nov 12, 2026·9 min read·3 visits

Executive Summary (TL;DR)

Loofah 2.25.2 fails to decode the HTML5 named character reference `&sol;` (`/`) inside `Loofah::HTML5::Scrub.allowed_uri?` (also exposed via `Rails::HTML::Sanitizer.allowed_uri?`). Attackers can supply `data:text&sol;html,<script>...` to bypass URI validation as `text/plain` and achieve Cross-Site Scripting (XSS) when the URI is rendered into an HTML attribute.

CVE-2026-126206 (GHSA-jcff-cj73-4w4f) is a Cross-Site Scripting (XSS) vulnerability in version 2.25.2 of the Loofah Ruby gem. The vulnerability stems from an incomplete character reference normalization routine in the `Loofah::HTML5::Scrub.allowed_uri?` method, which fails to decode the HTML5 named character reference `&sol;` (representing the solidus character `/`). Consequently, a crafted `data:` URI such as `data:text&sol;html,<script>alert(1)</script>` bypasses mediatype validation by triggering the WHATWG MIME parser fallback to `text/plain`, while web browsers later resolve `&sol;` to `/` and execute the payload as `text/html`.

Vulnerability Overview

Loofah is a widely deployed Ruby library built on top of Nokogiri for manipulating, transforming, and sanitizing HTML and XML documents and fragments. Within the Ruby on Rails ecosystem, Loofah serves as the underlying sanitization engine for rails-html-sanitizer. In addition to full DOM-tree scrubbing, Loofah provides standalone utility methods for validating individual strings, notably Loofah::HTML5::Scrub.allowed_uri?, which is re-exported directly as Rails::HTML::Sanitizer.allowed_uri?.

In Loofah version 2.25.2, a parser differential and input neutralization flaw (classified under CWE-79 and CWE-184) exists in Loofah::HTML5::Scrub.allowed_uri?. Applications invoke this method to verify whether a user-supplied URI string uses a permitted scheme and, in the case of data: URIs, a permitted MIME mediatype before embedding the string into HTML attributes such as href or src.

Because allowed_uri? relies on Ruby's standard library CGI.unescapeHTML for entity decoding, it only resolves legacy HTML4/XML named entities and numeric character references. It fails to decode the HTML5 named character reference &sol;, which represents the solidus character / (U+002F). When an attacker supplies a data: URI whose mediatype separator is encoded as &sol; (for example, data:text&sol;html,<script>alert(1)</script>), Loofah treats the mediatype as malformed and falls back to the safe text/plain default. When the validated string is rendered into an HTML attribute, the browser's HTML5 parser decodes &sol; to / prior to URI processing, resulting in arbitrary JavaScript execution in the context of the victim's browser session.

> [!NOTE] > This vulnerability only affects applications that call Loofah::HTML5::Scrub.allowed_uri? or Rails::HTML::Sanitizer.allowed_uri? directly on unparsed strings. Standard DOM sanitization workflows using Loofah.scrub_fragment or Loofah.fragment(html).scrub! are not affected because Nokogiri's HTML parser decodes all character references before invoking URI scrubbers.

Root Cause Analysis

To understand the root cause of CVE-2026-126206, it is necessary to examine both the WHATWG data: URL parsing specification and how HTML5 parsers handle named character references inside attribute values. According to the WHATWG Data URL specification, a data: URI follows the syntax data:[<mediatype>][;base64],<data>, where <mediatype> is structured as type/subtype*(;parameter). If the <mediatype> string does not contain a literal solidus (/) separating the type and subtype, the MIME parser rejects the mediatype as malformed and defaults to text/plain;charset=US-ASCII.

In Loofah::HTML5::Scrub.allowed_uri?, data: URIs are inspected to ensure their mediatype matches an explicit safelist (such as image/png, image/gif, or text/plain). Mediatypes capable of active script execution, specifically text/html and image/svg+xml, are rejected. Before parsing the scheme and mediatype, allowed_uri? normalizes the input string to prevent attackers from smuggling prohibited schemes or mediatypes using HTML character references that a browser would later decode when parsing an HTML attribute.

In version 2.25.2, the normalization pipeline executed the following sequence: strip control characters, decode numeric character references and legacy named entities via CGI.unescapeHTML, strip any newly decoded control characters, strip HTML5 whitespace character references (&Tab; and &NewLine;), and replace &colon; with :. However, CGI.unescapeHTML only recognizes the classic HTML4 entity table (&amp;, &lt;, &gt;, &quot;, &apos;) and numeric references. It leaves modern HTML5 named character references intact.

Across the entire HTML5 named character reference specification, exactly four named references resolve to characters that act as structural delimiters or stripped whitespace during URI parsing:

  • &colon; (U+003A :): Delimits the URI scheme.
  • &sol; (U+002F /): Separates the MIME type and subtype in data: URIs and delimits path segments.
  • &Tab; (U+0009): Stripped by WHATWG URL parsers.
  • &NewLine; (U+000A): Stripped by WHATWG URL parsers.

While Loofah 2.25.2 explicitly accounted for &colon;, &Tab;, and &NewLine;, it omitted &sol;. When data:text&sol;html,<script>alert(1)</script> was evaluated, the mediatype substring text&sol;html lacked a / character, causing Loofah's mediatype check to evaluate it as text/plain and approve the URI.

Code Analysis & Patch Evaluation

The vulnerability resides in lib/loofah/html5/scrub.rb within the allowed_uri? class method. In Loofah 2.25.2, the colon replacement was hardcoded as a single string substitution (uri_string.gsub!("&colon;", ":")), leaving &sol; unhandled during string normalization.

Maintainer Mike Dalessio (@flavorjones) resolved the issue in commit ad92002966053309f83b550861e2ef04bc714744 (Pull Request #324) by introducing a constant mapping for all URI delimiter character references (DELIMITER_CHARACTER_REFERENCES) and applying a unified regular expression substitution.

# File: lib/loofah/html5/scrub.rb
 
# HTML5 named character references for the delimiters that decide how a URI parses:
# ":" ends the scheme, and "/" separates the type and subtype of a data: mediatype.
# CGI.unescapeHTML does not decode these either. Together with
# WHITESPACE_CHARACTER_REFERENCES these are every named character reference that
# decodes to U+0009, U+000A, U+002F, or U+003A, so no other named reference can
# change how a URI parses.
DELIMITER_CHARACTER_REFERENCES = { "&colon;" => ":", "&sol;" => "/" }.freeze
DELIMITER_CHARACTER_REFERENCE = Regexp.union(DELIMITER_CHARACTER_REFERENCES.keys)
 
def allowed_uri?(uri_string)
  # Control characters are stripped both before and after decoding, since decoding can
  # produce them. That strip must precede the named reference substitutions: removing a
  # control character can reveal a named reference.
  uri_string = decode_numeric_character_references(CGI.unescapeHTML(uri_string.gsub(CONTROL_CHARACTERS, "")))
  uri_string.gsub!(CONTROL_CHARACTERS, "")
  uri_string.gsub!(WHITESPACE_CHARACTER_REFERENCES, "")
  # PATCHED: Replaces both &colon; and &sol; with their literal delimiter characters
  uri_string.gsub!(DELIMITER_CHARACTER_REFERENCE, DELIMITER_CHARACTER_REFERENCES)
  uri_string.downcase!
  # ... scheme and data: mediatype validation continues ...
end

The ordering of operations in allowed_uri? is critical to the completeness of this fix. First, raw control characters are removed, and CGI.unescapeHTML alongside decode_numeric_character_references resolves any numeric or double-encoded entities (such as &amp;sol; or &#0;). Second, control characters and named whitespace references (&Tab;, &NewLine;) are stripped before DELIMITER_CHARACTER_REFERENCE is evaluated.

If delimiter substitution occurred prior to control character or whitespace stripping, an attacker could split the entity name (for example, data:text&s&#0;ol;html or data:text&s&Tab;ol;html) so that it would coalesce into &sol; only after the delimiter replacement pass had already completed. Furthermore, uri_string.downcase! is intentionally called after delimiter replacement; HTML5 named character references are case-sensitive, meaning browsers do not decode uppercase &SOL; as /, and preserving case until after entity replacement matches browser tokenization behavior.

Exploitation Methodology

Exploiting CVE-2026-126206 requires an application to accept a user-controlled URL string, validate it via Loofah::HTML5::Scrub.allowed_uri? or Rails::HTML::Sanitizer.allowed_uri?, and subsequently render that string into an HTML attribute without re-encoding the ampersand (&) character. This pattern occurs in custom view helpers, markdown link renderers, or rich-text link validators where developers validate URLs prior to constructing raw HTML tags.

Consider a vulnerable Ruby on Rails helper method that validates user-supplied profile URLs:

def render_user_link(user_url)
  if Rails::HTML::Sanitizer.allowed_uri?(user_url)
    "<a href=\"#{user_url}\">Website</a>".html_safe
  else
    "<span>Invalid URL</span>".html_safe
  end
end

An attacker can supply any of several payload variants to bypass the allowed_uri? check in Loofah 2.25.2:

  1. Standard Plaintext Payload:
    data:text&sol;html,<script>alert(document.domain)</script>
  2. Base64-Encoded Payload:
    data:text&sol;html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
  3. Combined Scheme and Mediatype Named Entities:
    data&colon;text&sol;html,<script>alert(1)</script>
  4. Nested Whitespace / Control Character Coalescing Payloads:
    data:text&s&#0;ol;html,<script>alert(1)</script>
    data:text&s&Tab;ol;html,<script>alert(1)</script>
    data:text&s&NewLine;ol;html,<script>alert(1)</script>

When the application renders <a href="data:text&sol;html,<script>alert(document.domain)</script>">Website</a> (or embeds the URI into an <iframe> or <object> src/data attribute), the browser's HTML parser processes the attribute value and resolves &sol; to /. If rendered in an <iframe> or <embed>, the script executes automatically upon page load; if rendered in an <a href> tag, the script executes when a user clicks the link (depending on browser navigation context).

Impact Assessment

The security impact of CVE-2026-126206 is Cross-Site Scripting (XSS) within the client browser session. The vulnerability is scored at 5.3 (Medium) under CVSS v4.0 (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N). Because exploitation occurs within the user's browser rather than on the server, the Vulnerable System Impact metrics (VC/VI/VA) are rated as None (N), while the Subsequent System Impact metrics for Confidentiality and Integrity (SC/SI) are rated as Low (L).

Successful exploitation allows an unauthenticated remote attacker (AV:N/PR:N) with low attack complexity (AC:L/AT:N) and passive user interaction (UI:P) to execute arbitrary JavaScript in the context of the victim's session. Depending on where the data: URI is placed in the DOM, an attacker can steal session identifiers not protected by HttpOnly flags, read sensitive DOM content, perform unauthorized state-changing requests via Cross-Site Request Forgery (CSRF) bypass, or present fraudulent authentication prompts.

The scope of affected environments is constrained by two factors. First, the flaw was introduced in Loofah 2.25.2 (released July 15, 2026) and remediated in 2.25.3, meaning earlier versions of the library are not affected by this specific regression. Second, standard Rails view helpers (such as link_to) HTML-escape attribute values by default (turning &sol; into &amp;sol;), and standard Loofah DOM scrubbers decode entities via Nokogiri prior to calling allowed_uri?. Consequently, only codebases that invoke allowed_uri? directly and insert the unescaped string into an HTML attribute are vulnerable.

Remediation & Mitigation

Engineering and security teams should first determine whether Loofah 2.25.2 is present in their dependency tree and whether allowed_uri? is invoked directly within application or library code.

To audit the installed version and search for direct callers, execute the following commands in the project root:

# Check Gemfile.lock for Loofah 2.25.2
bundle list | grep loofah
bundle exec bundler-audit check --update
 
# Search application code for direct calls to allowed_uri?
grep -rn "Loofah::HTML5::Scrub.allowed_uri?" app/ lib/ config/
grep -rn "Rails::HTML::Sanitizer.allowed_uri?" app/ lib/ config/

Primary Fix: Upgrade Loofah

Upgrade the loofah gem to version 2.25.3 or later, which includes commit ad92002966053309f83b550861e2ef04bc714744:

# Gemfile
gem "loofah", ">= 2.25.3"

Then update the locked dependency:

bundle update loofah

Temporary Workaround (Monkey-Patch)

If an immediate gem upgrade is blocked by deployment freezes, apply an initializer patch that runs after Loofah::HTML5::Scrub is loaded to ensure &sol; is decoded during URI validation:

# config/initializers/loofah_cve_2026_126206_patch.rb
require "loofah"
 
 selected_constants = Loofah::HTML5::Scrub
if Loofah::VERSION == "2.25.2"
  Loofah::HTML5::Scrub.singleton_class.prepend(
    Module.new do
      def allowed_uri?(uri_string)
        return super if uri_string.nil?
        # Pre-normalize control chars, whitespace refs, and &sol; before delegating
        patched = uri_string.gsub(Loofah::HTML5::Scrub::CONTROL_CHARACTERS, "")
        patched = Loofah::HTML5::Scrub.send(:decode_numeric_character_references, CGI.unescapeHTML(patched))
        patched.gsub!(Loofah::HTML5::Scrub::CONTROL_CHARACTERS, "")
        patched.gsub!(Loofah::HTML5::Scrub::WHITESPACE_CHARACTER_REFERENCES, "")
        patched.gsub!("&sol;", "/")
        super(patched)
      end
    end
  )
end

Additionally, developers should avoid concatenating validated URI strings directly into .html_safe interpolation blocks; instead, use standard Rails tag helpers (tag.a, link_to) or full DOM fragment sanitization via Loofah.scrub_fragment.

Official Patches

flavorjones (Loofah)Official GitHub Security Advisory for GHSA-jcff-cj73-4w4f / CVE-2026-126206
flavorjones (Loofah)GitHub commit resolving the &sol; character reference bypass
flavorjones (Loofah)Loofah v2.25.3 release tag

Fix Analysis (1)

Technical Appendix

CVSS Score
5.3/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N

Affected Systems

loofah Ruby gem (version 2.25.2)rails-html-sanitizer (when invoking Rails::HTML::Sanitizer.allowed_uri? with loofah 2.25.2)

Affected Versions Detail

Product
Affected Versions
Fixed Version
loofah
flavorjones
= 2.25.22.25.3
AttributeDetail
CVE / Advisory IDCVE-2026-126206 / GHSA-jcff-cj73-4w4f
Vulnerability ClassCWE-79 (Cross-Site Scripting) / CWE-184 (Incomplete List of Disallowed Inputs)
CVSS v4.0 Score5.3 Medium (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N)
Affected ComponentLoofah::HTML5::Scrub.allowed_uri? (loofah gem = 2.25.2)
Patched Versionloofah >= 2.25.3
Attack VectorNetwork (Crafted data: URI using &sol; HTML5 entity)
Exploit StatusProof-of-Concept (PoC) Publicly Available
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1185Browser Session Hijacking
Collection
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

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.

Known Exploits & Detection

GitHub Security AdvisoryOfficial advisory containing functional proof-of-concept `data:text&sol;html` payloads.
GitHub Fix Commit & Unit TestsUpstream unit tests demonstrating plaintext, base64, double-encoded, and control-character-injected `&sol;` bypass vectors.

Vulnerability Timeline

Loofah v2.25.2 released, introducing the vulnerable URI normalization logic.
2026-07-15
Fix commit ad92002966053309f83b550861e2ef04bc714744 authored by maintainer Mike Dalessio.
2026-09-13
CVE-2026-126206 and GitHub Security Advisory GHSA-jcff-cj73-4w4f published; Loofah v2.25.3 released.
2026-11-12

References & Sources

  • [1]CVE-2026-126206 Official CVE Record
  • [2]GitHub Security Advisory GHSA-jcff-cj73-4w4f
  • [3]Loofah Fix Commit ad92002966053309f83b550861e2ef04bc714744
  • [4]Loofah Pull Request #324
  • [5]Loofah v2.25.3 Release Notes
  • [6]RubySec Advisory Database Entry for GHSA-jcff-cj73-4w4f
  • [7]Loofah GitHub Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•24 minutes ago•CVE-2026-126188
3.7

CVE-2026-126188: Local Media Information Disclosure in Ruby on Rails Active Storage via FFmpeg Demuxer Selection

CVE-2026-126188 (GHSA-3f4q-8x5m-76v8) is an information disclosure vulnerability in the Active Storage component of Ruby on Rails prior to versions 8.0.5.2 and 8.1.4.1. When generating previews or extracting metadata from uploaded media files, Active Storage invokes the external command-line tools ffmpeg and ffprobe without specifying a format whitelist. An attacker who can upload a crafted HTTP Live Streaming (HLS) playlist while declaring a standard video or audio MIME type can cause FFmpeg to engage the HLS demuxer, resolve file:// segment URIs, and disclose visual frames or metadata from local media files readable by the Rails application process.

Amit Schendel
Amit Schendel
1 views•10 min read
•about 2 hours ago•CVE-2026-125325
5.9

CVE-2026-125325: Cross-Request Information Disclosure via Shared BM25 Index Race Condition in pgEdge RAG Server

pgEdge RAG Server versions prior to 2.0.0 contain a race condition in the hybrid search pipeline orchestrator (CWE-362, CWE-488). The server maintains a single shared, in-memory BM25 index instance across all incoming HTTP requests on a pipeline. Concurrent queries to the unauthenticated HTTP REST API interleave non-atomic clear, document addition, and search operations on this singleton index. As a result, documents fetched using one caller's search filter can be queried and returned to another concurrent caller, bypassing multi-tenant data isolation without generating any anomalous audit trail in server or database logs.

Amit Schendel
Amit Schendel
2 views•10 min read
•about 4 hours ago•CVE-2026-118740
9.1

CVE-2026-118740: Missing Authorization in Microsoft Aspire Dashboard Blazor Router

CVE-2026-118740 is a Missing Authorization (CWE-862) vulnerability in the Microsoft Aspire Dashboard and Aspire.Dashboard.Sdk.linux-x64 packages. In versions 13.6.1 prior to 13.6.5, an architectural flaw in the Blazor routing configuration (Routes.razor) causes unmapped (404 / Not-Found) routes to bypass the AuthorizeRouteView component. By navigating to an arbitrary non-existent route, an unauthenticated remote attacker forces the application into the NotFound template, which unconditionally renders the primary MainLayout component and initializes global user interface services, interaction providers, and underlying SignalR/Blazor circuits without verifying authentication credentials.

Alon Barad
Alon Barad
5 views•9 min read
•1 day ago•CVE-2026-120006
7.5

CVE-2026-120006: Elevation of Privilege in .NET Aspire via Unintended Dashboard Endpoint Tunneling

An elevation of privilege vulnerability exists in .NET Aspire versions prior to 13.6.5 due to the insertion of sensitive information into sent data (CWE-201). When the Developer Control Plane (DCP) container tunneling feature is enabled, the orchestration engine implicitly exposes all endpoints of the Aspire Dashboard—including its administrative frontend and management APIs—to containerized workloads instead of restricting tunnel access solely to OpenTelemetry Protocol (OTLP) collection endpoints. This exposure allows an attacker operating within a compromised or untrusted container to query the dashboard frontend, extract sensitive environment variables and connection strings, and elevate privileges across the host orchestration boundary.

Amit Schendel
Amit Schendel
6 views•10 min read
•1 day ago•CVE-2026-118750
7.5

CVE-2026-118750: Uncontrolled Resource Allocation in ASP.NET Core FileBufferingReadStream

CVE-2026-118750 is an uncontrolled resource allocation vulnerability (CWE-770) affecting the Microsoft.AspNetCore.WebUtilities component in ASP.NET Core. Specifically, the FileBufferingReadStream class eagerly allocates large memory buffers from the shared ArrayPool<byte> inside its constructor prior to reading any incoming request payload data. An unauthenticated remote attacker can exploit this behavior by opening numerous concurrent connections and stalling or omitting request body transmission, exhausting shared memory pools and causing a server-wide Denial of Service (DoS).

Amit Schendel
Amit Schendel
6 views•10 min read
•1 day ago•CVE-2026-114155
7.5

CVE-2026-114155: Local Elevation of Privilege via TOCTOU Race Condition in Microsoft .NET dotnet-watch

CVE-2026-114155 is a local elevation of privilege vulnerability affecting Microsoft .NET on Linux and macOS platforms. The vulnerability exists within the dotnet-watch (Hot Reload) component and is caused by a Time-of-Check Time-of-Use (TOCTOU) race condition (CWE-367) during the generation and execution of temporary XML solution files (.slnx) in shared system temporary directories. A local attacker with low privileges can intercept and modify the temporary solution file before MSBuild evaluates it, achieving arbitrary code execution in the security context of the user running dotnet-watch.

Alon Barad
Alon Barad
6 views•9 min read