Every CVE Search/ Get API key

CVE-2026-46360

phpMyFAQ - Stored XSS via Entity Decoding Depth Limit Bypass in SVG Sanitizer

5.1 MediumFix available

phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in SvgSanitizer::decodeAllEntities() that limits recursive entity decoding to 5 iterations, allowing attackers to bypass sanitization. Authenticated users with FAQ_EDIT permission can upload malicious SVG files with deeply nested ampersand encoding around numeric HTML entities to reconstruct javascript: URLs, which execute arbitrary JavaScript when clicked by other users viewing the uploaded SVG.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
Passive

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
None

Exploit likelihood

0.19%Up 0.2 points since May 16, 2026
90%50%10%1%

What each source reports

CVE List Score used 5.1

phpMyFAQ - Stored XSS via Entity Decoding Depth Limit Bypass in SVG Sanitizer

phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in SvgSanitizer::decodeAllEntities() that limits recursive entity decoding to 5 iterations, allowing attackers to bypass sanitization. Authenticated users with FAQ_EDIT permission can upload malicious SVG files with deeply nested ampersand encoding around numeric HTML entities to reconstruct javascript: URLs, which execute arbitrary JavaScript when clicked by other users viewing the uploaded SVG.

Record
CVE-2026-46360
CVSS 4.0
5.1 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-08
EUVDEUVD-2026-30597 5.1

phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in SvgSanitizer::decodeAllEntities() that limits recursive entity decoding to 5 iterations, allowing attackers to bypass sanitization. Authenticated users with FAQ_EDIT permission can upload malicious SVG files with deeply nested ampersand encoding around numeric HTML entities to reconstruct javascript: URLs, which execute arbitrary JavaScript when clicked by other users viewing the uploaded SVG.

Record
EUVD-2026-30597
CVSS 4.0
5.1 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N
Updated
2026-11-08
GitHub AdvisoriesGHSA-whqh-9pq5-c7r3 5.4

phpMyFAQ has a SVG Sanitizer Entity Decoding Depth Limit Bypass Leading to Stored XSS

Summary

The SvgSanitizer::decodeAllEntities() method limits recursive entity decoding to 5 iterations. By wrapping each character of javascript in an href attribute value with 5 levels of & encoding around numeric HTML entities (e.g., j for j), an attacker can bypass both isSafe() detection and sanitize() removal. The uploaded SVG is served from the application origin with image/svg+xml content type, and the browser's XML parser fully decodes the remaining &#NNN; entities, resulting in a clickable javascript: link that executes arbitrary JavaScript.

Details

Root cause: decodeAllEntities() at phpmyfaq/src/phpMyFAQ/Helper/SvgSanitizer.php:223-249 limits entity decoding to maxIterations=5. Each iteration: (1) decodes &#NNN; numeric entities, (2) decodes &#xHH; hex entities, (3) calls html_entity_decode() which resolves one level of & → &. With 5 levels of & wrapping, all 5 iterations are consumed unwinding the & nesting, leaving the final &#NNN; numeric entities unresolved.

Code path:

  1. Authenticated user with FAQ_EDIT permission uploads SVG via POST /admin/api/content/images (ImageController::upload() at line 39)
  2. File extension is svg → SvgSanitizer::isSafe() called (line 114)
  3. isSafe() calls decodeAllEntities() — 5 iterations resolve & nesting but leave ja... (numeric entities for javascript)
  4. Pattern matching at line 47 (/href\s*=\s*["\'][\s]*javascript\s*:/i) does not match ja...
  5. isSafe() returns true — file saved without any sanitization
  6. SVG served directly by web server from content/user/images/ with image/svg+xml MIME type
  7. Browser's XML parser decodes j → j, a → a, etc., reconstructing javascript:alert(document.domain)
  8. User clicks the SVG link → JavaScript executes in the phpMyFAQ origin

The bypass is even simpler than initially described — no <script> decoy tag is needed. Since isSafe() itself is bypassed, the file is stored without sanitization and the sanitize() code path is never reached.

Relevant code in decodeAllEntities():

// phpmyfaq/src/phpMyFAQ/Helper/SvgSanitizer.php:223-249
private function decodeAllEntities(string $content): string
{
    $previous = '';
    $decoded = $content;
    $maxIterations = 5;  // <-- insufficient for 5 levels of &amp; + numeric entity

    while ($decoded !== $previous && $maxIterations-- > 0) {
        $previous = $decoded;
        // Step 1: Decode decimal entities (&#106; → j)
        $decoded = preg_replace_callback('/&#(\d+);/', ...);
        // Step 2: Decode hex entities (&#x6A; → j)
        $decoded = preg_replace_callback('/&#x([0-9a-fA-F]+);/', ...);
        // Step 3: Decode named HTML entities (&amp; → &)
        $decoded = html_entity_decode($decoded, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    }
    // After 5 iterations with 5 &amp; levels: &#106; remains undecoded
    return preg_replace('/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/', '', $decoded);
}

PoC

Upload an SVG file containing a javascript: href where each character of javascript is entity-encoded with 5 levels of &amp; nesting around numeric entities. No <script> decoy is required — isSafe() itself is bypassed.

Step 1: Create malicious SVG file (xss.svg):

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
  <a href="&amp;amp;amp;amp;amp;#106;&amp;amp;amp;amp;amp;#97;&amp;amp;amp;amp;amp;#118;&amp;amp;amp;amp;amp;#97;&amp;amp;amp;amp;amp;#115;&amp;amp;amp;amp;amp;#99;&amp;amp;amp;amp;amp;#114;&amp;amp;amp;amp;amp;#105;&amp;amp;amp;amp;amp;#112;&amp;amp;amp;amp;amp;#116;:alert(document.domain)">
    <circle cx="100" cy="100" r="80" fill="red"/>
    <text x="100" y="110" text-anchor="middle" fill="white" font-size="20">Click me</text>
  </a>
</svg>

Step 2: Upload via admin image upload endpoint:

curl -b…
Record
GHSA-whqh-9pq5-c7r3
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
GitHub AdvisoriesGHSA-wj3q-vw2v-3rj3 5.1

Duplicate Advisory: phpMyFAQ: SVG Sanitizer Entity Decoding Depth Limit Bypass Leading to Stored XSS

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-whqh-9pq5-c7r3. This link is maintained to preserve external references.

Original Description

phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in SvgSanitizer::decodeAllEntities() that limits recursive entity decoding to 5 iterations, allowing attackers to bypass sanitization. Authenticated users with FAQ_EDIT permission can upload malicious SVG files with deeply nested ampersand encoding around numeric HTML entities to reconstruct javascript: URLs, which execute arbitrary JavaScript when clicked by other users viewing the uploaded SVG.

Record
GHSA-wj3q-vw2v-3rj3
CVSS 4.0
5.1 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
NVD 5.1

phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in SvgSanitizer::decodeAllEntities() that limits recursive entity decoding to 5 iterations, allowing attackers to bypass sanitization. Authenticated users with FAQ_EDIT permission can upload malicious SVG files with deeply nested ampersand encoding around numeric HTML entities to reconstruct javascript: URLs, which execute arbitrary JavaScript when clicked by other users viewing the uploaded SVG.

Record
CVE-2026-46360
CVSS 4.0
5.1 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
OSV 5.1

phpMyFAQ - Stored XSS via Entity Decoding Depth Limit Bypass in SVG Sanitizer

phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in SvgSanitizer::decodeAllEntities() that limits recursive entity decoding to 5 iterations, allowing attackers to bypass sanitization. Authenticated users with FAQ_EDIT permission can upload malicious SVG files with deeply nested ampersand encoding around numeric HTML entities to reconstruct javascript: URLs, which execute arbitrary JavaScript when clicked by other users viewing the uploaded SVG.

Record
CVE-2026-46360
CVSS 4.0
5.1 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N
Weakness
CWE-79
Updated
2026-11-02
OSVGHSA-whqh-9pq5-c7r3 5.4

phpMyFAQ has a SVG Sanitizer Entity Decoding Depth Limit Bypass Leading to Stored XSS

Summary

The SvgSanitizer::decodeAllEntities() method limits recursive entity decoding to 5 iterations. By wrapping each character of javascript in an href attribute value with 5 levels of &amp; encoding around numeric HTML entities (e.g., &amp;amp;amp;amp;amp;#106; for j), an attacker can bypass both isSafe() detection and sanitize() removal. The uploaded SVG is served from the application origin with image/svg+xml content type, and the browser's XML parser fully decodes the remaining &#NNN; entities, resulting in a clickable javascript: link that executes arbitrary JavaScript.

Details

Root cause: decodeAllEntities() at phpmyfaq/src/phpMyFAQ/Helper/SvgSanitizer.php:223-249 limits entity decoding to maxIterations=5. Each iteration: (1) decodes &#NNN; numeric entities, (2) decodes &#xHH; hex entities, (3) calls html_entity_decode() which resolves one level of &amp; → &. With 5 levels of &amp; wrapping, all 5 iterations are consumed unwinding the &amp; nesting, leaving the final &#NNN; numeric entities unresolved.

Code path:

  1. Authenticated user with FAQ_EDIT permission uploads SVG via POST /admin/api/content/images (ImageController::upload() at line 39)
  2. File extension is svg → SvgSanitizer::isSafe() called (line 114)
  3. isSafe() calls decodeAllEntities() — 5 iterations resolve &amp; nesting but leave &#106;&#97;... (numeric entities for javascript)
  4. Pattern matching at line 47 (/href\s*=\s*["\'][\s]*javascript\s*:/i) does not match &#106;&#97;...
  5. isSafe() returns true — file saved without any sanitization
  6. SVG served directly by web server from content/user/images/ with image/svg+xml MIME type
  7. Browser's XML parser decodes &#106; → j, &#97; → a, etc., reconstructing javascript:alert(document.domain)
  8. User clicks the SVG link → JavaScript executes in the phpMyFAQ origin

The bypass is even simpler than initially described — no <script> decoy tag is needed. Since isSafe() itself is bypassed, the file is stored without sanitization and the sanitize() code path is never reached.

Relevant code in decodeAllEntities():

// phpmyfaq/src/phpMyFAQ/Helper/SvgSanitizer.php:223-249
private function decodeAllEntities(string $content): string
{
    $previous = '';
    $decoded = $content;
    $maxIterations = 5;  // <-- insufficient for 5 levels of &amp; + numeric entity

    while ($decoded !== $previous && $maxIterations-- > 0) {
        $previous = $decoded;
        // Step 1: Decode decimal entities (&#106; → j)
        $decoded = preg_replace_callback('/&#(\d+);/', ...);
        // Step 2: Decode hex entities (&#x6A; → j)
        $decoded = preg_replace_callback('/&#x([0-9a-fA-F]+);/', ...);
        // Step 3: Decode named HTML entities (&amp; → &)
        $decoded = html_entity_decode($decoded, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    }
    // After 5 iterations with 5 &amp; levels: &#106; remains undecoded
    return preg_replace('/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/', '', $decoded);
}

PoC

Upload an SVG file containing a javascript: href where each character of javascript is entity-encoded with 5 levels of &amp; nesting around numeric entities. No <script> decoy is required — isSafe() itself is bypassed.

Step 1: Create malicious SVG file (xss.svg):

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
  <a href="&amp;amp;amp;amp;amp;#106;&amp;amp;amp;amp;amp;#97;&amp;amp;amp;amp;amp;#118;&amp;amp;amp;amp;amp;#97;&amp;amp;amp;amp;amp;#115;&amp;amp;amp;amp;amp;#99;&amp;amp;amp;amp;amp;#114;&amp;amp;amp;amp;amp;#105;&amp;amp;amp;amp;amp;#112;&amp;amp;amp;amp;amp;#116;:alert(document.domain)">
    <circle cx="100" cy="100" r="80" fill="red"/>
    <text x="100" y="110" text-anchor="middle" fill="white" font-size="20">Click me</text>
  </a>
</svg>

Step 2: Upload via admin image upload endpoint:

curl -b…
Record
GHSA-whqh-9pq5-c7r3
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
OSVGHSA-wj3q-vw2v-3rj3 5.1

Duplicate Advisory: phpMyFAQ: SVG Sanitizer Entity Decoding Depth Limit Bypass Leading to Stored XSS

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-whqh-9pq5-c7r3. This link is maintained to preserve external references.

Original Description

phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in SvgSanitizer::decodeAllEntities() that limits recursive entity decoding to 5 iterations, allowing attackers to bypass sanitization. Authenticated users with FAQ_EDIT permission can upload malicious SVG files with deeply nested ampersand encoding around numeric HTML entities to reconstruct javascript: URLs, which execute arbitrary JavaScript when clicked by other users viewing the uploaded SVG.

Record
GHSA-wj3q-vw2v-3rj3
CVSS 4.0
5.1 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
phpmyfaq/phpmyfaq
Packagist
through 4.1.1
4.1.2
thorsten/phpmyfaq
Packagist
through 4.1.1
4.1.2
phpmyfaq/phpmyfaq
Packagist
before 4.1.2, plus listed versions
2.10.0-alpha2.8.02.8.0-RC2.8.0-RC22.8.0-RC3
Show 45 moreShow less
2.8.0-RC42.8.0-alpha22.8.0-alpha32.8.0-beta2.8.0-beta22.8.0-beta32.8.12.8.102.8.112.8.122.8.132.8.142.8.152.8.162.8.172.8.182.8.192.8.22.8.202.8.212.8.222.8.232.8.242.8.252.8.262.8.272.8.282.8.292.8.32.8.42.8.52.8.62.8.72.8.82.8.92.9.02.9.0-alpha2.9.0-alpha22.9.0-alpha32.9.0-alpha42.9.0-beta2.9.0-beta22.9.0-rc2.9.0-rc22.9.0-rc3
4.1.2
thorsten/phpmyfaq
Packagist
before 4.1.2, plus listed versions
2.10.0-alpha2.8.02.8.0-RC2.8.0-RC22.8.0-RC3
Show 45 moreShow less
2.8.0-RC42.8.0-alpha22.8.0-alpha32.8.0-beta2.8.0-beta22.8.0-beta32.8.12.8.102.8.112.8.122.8.132.8.142.8.152.8.162.8.172.8.182.8.192.8.22.8.202.8.212.8.222.8.232.8.242.8.252.8.262.8.272.8.282.8.292.8.32.8.42.8.52.8.62.8.72.8.82.8.92.9.02.9.0-alpha2.9.0-alpha22.9.0-alpha32.9.0-alpha42.9.0-beta2.9.0-beta22.9.0-rc2.9.0-rc22.9.0-rc3
4.1.2

History

History
RecordedChangedSources
factsEUVD
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-46360 is a medium vulnerability (CVSS 5.1) in Thorsten Phpmyfaq, published May 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.19%, the 8th percentile. Fixed in phpmyfaq/phpmyfaq 4.1.2.

References

This page doesn't list everything

Left out here: 240 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open