Skip to main content

svg-sanitizer CVE-2026-107381

| EUVDEUVD-2026-101125 MEDIUM
Improper Handling of Case Sensitivity (CWE-178)
2026-10-08 https://github.com/darylldoyle/svg-sanitizer GHSA-m9xh-6747-9r6f
6.5
CVSS 3.1 · Vendor: https://github.com/darylldoyle/svg-sanitizer
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Craft nesting-bomb SVG with mixed-case xlink:HrEf
  2. Delivery
    Upload or paste SVG to target application
  3. Exploit
    Case-sensitive reference graph skips use nullification
  4. Execution
    Sanitizer re-canonicalizes attributes, output stays live
  5. Impact
    Downstream renderer exhausts memory/CPU (DoS)

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Case sensitivity handling
Exploitation Conditions

Requires that the target application uses enshrined/svg-sanitize ≤ 0.22.0 to sanitize an attacker-supplied SVG and then serves or renders the sanitized result. The specific trigger is an SVG whose <use> nesting-bomb references are written with mixed-case href attribute names such as xlink:HrEf or HrEf; these evade the case-sensitive XPath predicate use[@href or @xlink:href] in Resolver::processReferences() that builds the reference graph, while Sanitizer::cleanHrefAttributes() later rewrites them to canonical xlink:href, leaving a live nesting bomb. No authentication or privileges are required beyond the ability to upload/paste an SVG (PR:N). Limiting factors: the DoS only materializes when a downstream consumer actually renders the served SVG and expands the <use> references (UI:R); a mixed-case namespace *prefix* like XLINK:href is instead dropped wholesale and does not trigger the bug. No special non-default configuration is needed - default sanitization is affected.

Impact

This is a medium-severity (CVSS 6.5) availability-only issue - a sanitizer-bypass that resurrects a known <use> nesting-bomb DoS. The CVSS vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H is internally consistent with the description: an attacker only needs to submit an SVG (AV:N, PR:N, AC:L), but the damage requires a downstream consumer to render the sanitized output (UI:R), and the impact is purely A:H (DoS) with no confidentiality or integrity effect. Exploitation status: publicly available exploit code exists - a complete, working PoC is published inside the GHSA-m9xh-6747-9r6f advisory. No EPSS score, no CISA KEV listing, and no SSVC decision data were provided, so there is no signal of active or automatable exploitation; this should be prioritized as a defense-in-depth sanitizer regression rather than an urgent in-the-wild threat. One important caveat the reporter states explicitly: they measured only that the nullification was skipped and the attribute re-canonicalized, NOT the actual renderer memory/CPU cost - so the real-world DoS magnitude inherits whatever cost you already assign to the project's useDosTest.svg fixture and depends on the downstream renderer actually expanding the <use> references. The 'Information Disclosure' input tag contradicts the C:N/I:N/A:H vector and appears mislabeled.

Exploit ScenarioDerived

An attacker uploads or pastes a crafted SVG whose <use> nesting-bomb structure uses mixed-case href attribute names (e.g. xlink:HrEf) into an application that sanitizes SVGs with enshrined/svg-sanitize ≤ 0.22.0 and then serves them. Because the reference graph is built case-sensitively before casing is normalized, the nesting-DoS nullification is skipped and cleanHrefAttributes() rewrites the attributes back to canonical xlink:href, so the sanitizer returns a fully live nesting bomb. A working proof-of-concept is published in the GHSA advisory; the DoS manifests when a victim or backend renderer processes the served SVG (UI:R, AC:L).

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.4% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Oct 08, 2026

Sanitizer bypass in enshrined/svg-sanitize 0.22.0 and earlier lets an attacker smuggle a fully live SVG <use> nesting bomb past the library's useNestingLimit defenses by writing the href attribute with mixed casing, for example xlink:HrEf or HrEf. The resolver builds its reference graph with the case-sensitive XPath predicate use[@href or @xlink:href], so the mixed-case nodes never enter the graph and are never nullified, while Sanitizer::cleanHrefAttributes() runs later in the same pass and re-canonicalizes the attribute to xlink:href - handing back output that would have been stripped entirely had the input used canonical casing. Impact is availability only (CVSS 6.5, CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H): unauthenticated attackers able to upload or paste an SVG into an application that sanitizes and then serves it can cause a denial of service when the output is rendered (UI:R); publicly available exploit code exists (a reproducible proof of concept against tag 0.22.0 ships with the vendor advisory), but no CISA KEV entry indicates active exploitation.

RemediationAI

Vendor-released patch: upgrade composer/enshrined/svg-sanitize to 1.0.0 (https://github.com/darylldoyle/svg-sanitizer/releases/tag/1.0.0), which drops the case-sensitive XPath predicate and resolves href through a case-insensitive local-name lookup in Helper::getElementHref(); if a full upgrade is not immediately possible, cherry-pick or backport upstream commit 2dff6628314de8519155b7feb218bbe132785757, and verify the fix by testing the mixed-case variants xlink:HrEf, xlink:HREF, HrEf and HREF against your own useDosTest-style fixture. Where the library cannot be changed at all, the most direct compensating control is to pre-normalize input before sanitization - rewrite href attribute names case-insensitively to canonical href/xlink:href (or reject any SVG containing an href attribute whose name is not exactly href or xlink:href) so the resolver and the cleaner can no longer disagree; the trade-off is that aggressive name rewriting can mangle unrelated attributes or namespace prefixes, and the strict-reject variant will refuse spec-legal mixed-case SVGs that some producers emit. A second, blast-radius-only control is to keep treating the sanitized output as hostile: serve it from a separate origin inside a sandboxed iframe with a strict Content-Security-Policy, and render it in a process with hard memory and CPU limits, which does not remove the nesting bomb but bounds the resource cost if <use> expansion still occurs; note this is a mitigation, not a fix, and it leaves the sanitizer's advertised nesting-DoS guarantee broken. Because the trigger requires a downstream consumer to actually render the served SVG (UI:R), avoiding server-side rasterization or preview generation of user-supplied SVGs removes the immediate denial-of-service path until the upgrade lands.

Affected VersionsENISA

svg-sanitizer < 1.0.0

Affected Packages

composer enshrined/svg-sanitize fix: 1.0.0
euvd svg_sanitizer fix: 1.0.0

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 2 packages in composer, euvd

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

Root cause is CWE-178, improper handling of case sensitivity, in the PHP library enshrined/svg-sanitize (Composer package, CPE pkg:composer/enshrined_svg-sanitize). The library uses PHP's DOM/XPath stack: attribute-name matching in XPath is case sensitive, so the predicate built at src/ElementReference/Resolver.php:97-118 (use[@href or @xlink:href]) silently skips <use> elements whose href attribute is written as HrEf, xlink:HrEf or xlink:HREF. Those nodes are therefore absent from the element-reference graph that powers the nesting-DoS nullification and the useThreshold recursion counting. Ordering is the entire bug: Sanitizer::sanitize() builds the reference graph first and only afterwards runs Sanitizer::cleanHrefAttributes() (src/Sanitizer.php:479-504), which lowercases any attribute name that matches an allowed attribute case-insensitively and rewrites it via removeAttribute/setAttribute back to canonical href or xlink:href. The net effect is that the sanitizer emits a live nesting bomb whose attributes are canonical, i.e. exactly the input the useNestingLimit machinery exists to defuse. This is the mirror image of CVE-2025-55166, whose fix made href *value* checking case-insensitive but left node selection in the reference graph case-sensitive. The upstream fix removes the href predicate from the XPath query entirely and instead extends Helper::getElementHref() (Helper.php:10-19) with a case-insensitive local-name lookup that only accepts unprefixed href or an xlink-prefixed href, adding an XLINK_NAMESPACE_URI constant. Test coverage was extended with data providers for xlink:HrEf, xlink:HREF, href, HrEf and HREF; notably a mixed-case *prefix* such as XLINK:href is not bound to the xlink namespace and is dropped wholesale by cleanXlinkHrefs(), so it leaves inert <use> leftovers rather than a live reference and does not trigger the bug.

Know when PHP gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/darylldoyle/svg-sanitizer) PRIMARY 6.5 MEDIUM
AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
vuln.today AI 6.5 MEDIUM

SVG is submitted remotely with no privileges (AV:N/PR:N/AC:L), but impact needs a consumer to render the output (UI:R); effect is DoS only, so C:N/I:N/A:H.

3.1 AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
4.0 AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/darylldoyle/svg-sanitizer).

CVSS VectorVendor: https://github.com/darylldoyle/svg-sanitizer

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

DescriptionCVE.org

Summary

Resolver::processReferences() collects <use> elements with the XPath predicate use[@href or @xlink:href], which is case sensitive. A <use> element written as xlink:HrEf is therefore never added to the reference graph, so the nesting-DoS nullification never marks it for removal. Sanitizer::cleanHrefAttributes() then runs later in the same pass and rewrites xlink:HrEf back to the canonical xlink:href. The sanitizer hands back a fully live nesting bomb that it would have stripped completely had the input used canonical casing.

This is the mirror image of CVE-2025-55166: that fix made href *value* checking case insensitive, but the <use> reference graph still selects nodes case sensitively.

Where it is

src/ElementReference/Resolver.php:97-118, the case-sensitive node selection:

php
$useNodeName = $this->xPath->createNodeName('use');
foreach ($this->subjects as $subject) {
    $useElements = $this->xPath->query(
        $useNodeName . '[@href or @xlink:href]',
        $subject->getElement()
    );

src/Sanitizer.php:479-504, the normalization that happens afterwards and makes the attribute live again:

php
// in case the attribute name is `HrEf`/`xlink:HrEf`, adjust it to `href`/`xlink:href`
if (!in_array($attribute->nodeName, $this->allowedAttrs, true)
    && in_array(strtolower($attribute->nodeName), $this->allowedAttrs, true)
) {
    $element->removeAttribute($attribute->nodeName);
    $element->setAttribute(strtolower($attribute->nodeName), $attribute->value);
}

Ordering is the whole bug. Sanitizer::sanitize() builds the resolver graph first and normalizes attribute casing second, so the graph is built against names the sanitizer is about to change.

Impact

Anyone who can submit an SVG to an application that sanitizes it with this library and then serves it. No privileges beyond being able to upload or paste an SVG. The output is the project's own tests/data/useDosTest.svg nesting structure, intact, with canonical xlink:href attributes, which is exactly the input the useNestingLimit machinery exists to defuse.

Proof of concept

Against a clean clone of tag 0.22.0. The only change to the input is rewriting xlink:href to xlink:HrEf in the project's own DoS fixture.

php
<?php
spl_autoload_register(function ($class) {
    $p = 'enshrined\\svgSanitize\\';
    if (strpos($class, $p) !== 0) return;
    $rel = str_replace('\\', '/', substr($class, strlen($p)));
    $f = __DIR__ . '/svgsan/src/' . $rel . '.php';
    if (file_exists($f)) require $f;
});
$orig = file_get_contents(__DIR__ . '/svgsan/tests/data/useDosTest.svg');
$mut  = str_replace('xlink:href', 'xlink:HrEf', $orig);

function run($label, $input) {
    $s = new \enshrined\svgSanitize\Sanitizer();
    $s->removeRemoteReferences(true);
    $out = $s->sanitize($input);
    echo "[$label]\n";
    echo "  output <use  count : ", substr_count($out, '<use '), "\n";
    echo "  output xlink:href  : ", (strpos($out,'xlink:href')!==false?'YES':'NO'), "\n";
    echo "  output xlink:HrEf  : ", (strpos($out,'xlink:HrEf')!==false?'YES':'NO'), "\n";
}
run('canonical xlink:href (control)', $orig);
run('mixed-case xlink:HrEf (attack)', $mut);

Observed on PHP 8.5.9:

text
orig use count: 201
mutated input has xlink:HrEf: YES

[canonical xlink:href (control)]
  output <use  count : 0
  output xlink:href  : NO
  output xlink:HrEf  : NO
[mixed-case xlink:HrEf (attack)]
  output <use  count : 201
  output xlink:href  : YES
  output xlink:HrEf  : NO

201 to 0 on canonical input, 201 to 201 on mixed-case input, and the output attribute name comes back canonical either way. To be precise about what I measured: I measured the nullification being skipped and the attribute being re-canonicalized. I did not measure renderer memory or CPU on the resulting file, so the downstream cost is whatever you already assign to useDosTest.svg.

Suggested fix

Stop matching attribute names case sensitively when the reference graph is built. Either select every descendant use and resolve the href in PHP with a case-insensitive local-name lookup (an extension of Helper::getElementHref() at Helper.php:10-19 is the smallest change), or move the cleanHrefAttributes() canonicalization so it runs before $this->elementReferenceResolver->collect() rather than after, so the resolver only ever sees href and xlink:href. The second option also removes the general class of bug where the graph and the cleaner disagree about a node's identity.

Threat Intelligence Overview

EPSS Score
0.4%
Top 65% exploitable
Prevalence
Medium
popular PHP SVG-sanitization library, common web-app dependency
Threat Intel
1
1 source
Data Quality
5 sources
Confidence: Medium

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC No
EPSS 0.4%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
None
System Exposure· derived
Open
Automatable· derived
No
Human Impact· derived
Medium

Priority Score

32
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +32
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

More in PHP

View all
CVE-2019-11043 CRITICAL POC
9.8 Oct 28

In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it

CVE-2012-1823 CRITICAL POC
9.8 May 11

sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not

CVE-2016-1555 CRITICAL POC
9.8 Apr 21

(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear

CVE-2018-11138 CRITICAL POC
9.8 May 31

The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by

CVE-2024-11680 CRITICAL POC
9.8 Nov 26

ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C

CVE-2025-49113 CRITICAL POC
9.9 Jun 02

Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au

CVE-2017-9841 CRITICAL POC
9.8 Jun 27

Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c

CVE-2025-0108 HIGH POC
8.8 Feb 12

Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers

CVE-2021-25298 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2021-25296 HIGH POC
8.8 Feb 15

Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re

CVE-2013-4983 CRITICAL POC
10.0 Sep 10

The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows

CVE-2023-6553 CRITICAL POC
9.8 Dec 15

The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy