Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 4 min read

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

CVE-2026-126206: Cross-Site Scripting via HTML5 Named Solidus Entity in Loofah URI Validator Vulnerability ID: CVE-2026-126206 CVSS Score: 5.3 Published: 2026-11-12 CVE-2026-126206 (GHSA-jcff-cj73-4w4f) is a Cross-S

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

Vulnerability ID: CVE-2026-126206
CVSS Score: 5.3
Published: 2026-11-12

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.

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.

⚠️ Exploit Status: POC

Technical Details

  • CVE / Advisory ID: CVE-2026-126206 / GHSA-jcff-cj73-4w4f
  • Vulnerability Class: CWE-79 (Cross-Site Scripting) / CWE-184 (Incomplete List of Disallowed Inputs)
  • CVSS v4.0 Score: 5.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 Component: Loofah::HTML5::Scrub.allowed_uri? (loofah gem = 2.25.2)
  • Patched Version: loofah >= 2.25.3
  • Attack Vector: Network (Crafted data: URI using / HTML5 entity)
  • Exploit Status: Proof-of-Concept (PoC) Publicly Available
  • CISA KEV Status: Not Listed

Affected Systems

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

Code Analysis

Commit: ad92002

Ensure Loofah::HTML5::Scrub.allowed_uri? recognizes the named character reference / and rejects data: URIs that disguise a disallowed mediatype with it.

diff --git a/lib/loofah/html5/scrub.rb b/lib/loofah/html5/scrub.rb
index 2a35090..e8b524a 100644
--- a/lib/loofah/html5/scrub.rb
+++ b/lib/loofah/html5/scrub.rb
@@ -53,6 +53,15 @@ module Scrub
       # URIs. CGI.unescapeHTML does not decode these, so they are handled explicitly.
       WHITESPACE_CHARACTER_REFERENCES = /&(Tab|NewLine);/

+      # 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)
+
       class << self
         def allowed_element?(element_name)
           ::Loofah::HTML5::SafeList::ALLOWED_ELEMENTS_WITH_LIBXML2.include?(element_name)
@@ -234,12 +243,12 @@ def allowed_uri?(uri_string)
           # also decode the semicolon-less ones, which browsers still decode and execute. Normalizing
           # more aggressively than a browser only rejects more, which is safe. Control characters are
           # stripped both before and after decoding, since decoding can produce them. That strip must
-          # precede WHITESPACE_CHARACTER_REFERENCES: removing a control character can reveal a named
-          # whitespace reference.
+          # 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, "")
-          uri_string.gsub!("&colon;", ":")
+          uri_string.gsub!(DELIMITER_CHARACTER_REFERENCE, DELIMITER_CHARACTER_REFERENCES)
           uri_string.downcase!
           if URI_PROTOCOL_REGEX.match?(uri_string)
             protocol = uri_string.split(SafeList::PROTOCOL_SEPARATOR)[0]

Exploit Details

Mitigation Strategies

  • Upgrade the loofah Ruby gem to version 2.25.3 or newer across all environments.
  • Prefer full DOM fragment sanitization (Loofah.scrub_fragment or Rails sanitize helper) over standalone string validation via allowed_uri?.
  • Ensure all user-supplied URI strings rendered into HTML attributes pass through standard attribute escaping helpers (such as Rails link_to or tag.a) rather than raw .html_safe string interpolation.

Remediation Steps:

  1. Audit project Gemfile.lock files using bundle list | grep loofah or bundle exec bundler-audit check --update to identify instances of loofah version 2.25.2.
  2. Search application source code (app/, lib/, config/) for direct invocations of Loofah::HTML5::Scrub.allowed_uri? or Rails::HTML::Sanitizer.allowed_uri?.
  3. Update Gemfile to require gem "loofah", ">= 2.25.3" and execute bundle update loofah.
  4. Run the application test suite and redeploy affected services.

References

Read the full report for CVE-2026-126206 on our website for more details including interactive diagrams and full exploit analysis.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.