Twig Extras CVE-2026-46637
MEDIUMSeverity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:N/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
Primary rating from Vendor (https://github.com/twigphp/Twig) · only source for this CVE.
CVSS VectorVendor: https://github.com/twigphp/Twig
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:N/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
Lifecycle Timeline
3DescriptionCVE.org
Description
Several filters in the twig/* extras packages are registered with is_safe => ['all'], which tells Twig's autoescaper to treat their output as safe in every context (html, js, css, url, ...). The output of these filters is plain text or HTML markup, neither of which is safe in every escaping context.
Affected filters:
html_to_markdown(twig/markdown-extra) emits plain Markdown text.league/html-to-markdowndecodes HTML entities when producing code spans and fenced blocks, so an attacker-controlled<code><img src=x onerror=alert(1)></code>becomes`<img src=x onerror=alert(1)>`, which renders live when interpolated into an HTML page.markdown_to_html(twig/markdown-extra) emits HTML. Safe in an HTML context but not in JS, CSS or URL contexts (e.g. when interpolated into an inline<script>block).inline_css(twig/cssinliner-extra) emits HTML with inlined styles. Same constraint asmarkdown_to_html.
In all three cases, is_safe => ['all'] causes the autoescaper to emit the output verbatim in any context, even when the developer never wrote |raw. In a context such as a JS string or a URL parameter, this produces unescaped HTML and is exploitable as XSS.
Resolution
html_to_markdownno longer claims to be safe in any escaping context; its plain-text output is now autoescaped for the surrounding context.markdown_to_htmlandinline_cssare now declaredis_safe => ['html'], asserting only what they actually guarantee.
Credits
Twig would like to thank Claude Mythos Preview (via Project Glasswing) for reporting the issue and providing the fix for html_to_markdown and markdown_to_html in twig/markdown-extra, and Christophe Coevoet for extending the audit to inline_css in twig/cssinliner-extra.
AnalysisAI
Cross-site scripting in twig/markdown-extra and twig/cssinliner-extra arises from three filters - html_to_markdown, markdown_to_html, and inline_css - being incorrectly registered with is_safe => ['all'], which instructs Twig's autoescaper to suppress output encoding in every context including JS, CSS, and URL. When attacker-controlled content flows through these filters and the result is interpolated into a non-HTML context such as an inline script block, the autoescaper emits the raw output verbatim, enabling XSS without the developer ever using the explicit |raw bypass. Notably, html_to_markdown has an additional entity-decoding gadget via league/html-to-markdown that converts encoded HTML entities back to live markup during code span generation, creating a secondary injection path even in HTML contexts. No public exploit identified at time of analysis and no CISA KEV listing.
Technical ContextAI
Twig's autoescaper assigns each registered filter a set of safe output contexts via the is_safe metadata flag, allowing it to skip re-encoding filter output in those contexts. CWE-116 (Improper Encoding or Escaping of Output) is the root cause: the three affected filters overclaim their safety guarantee by declaring is_safe => ['all'] rather than the narrower is_safe => ['html'] that their actual output (HTML markup or Markdown text) warrants. The affected Composer packages are pkg:composer/twig/markdown-extra and pkg:composer/twig/cssinliner-extra, both below version 3.26.0. The html_to_markdown filter introduces a compounding issue through its dependency league/html-to-markdown, which decodes HTML entities when generating code spans and fenced code blocks - meaning HTML entity-encoded payloads survive the HTML-to-Markdown conversion as live markup rather than inert text. The markdown_to_html and inline_css filters emit HTML, which is safe within an HTML escaping context but becomes unescaped content when Twig interpolates it into a JavaScript string literal, CSS property value, or URL parameter.
RemediationAI
Vendor-released patch: 3.26.0 for both twig/markdown-extra and twig/cssinliner-extra. Upgrade both packages using Composer: run 'composer update twig/markdown-extra twig/cssinliner-extra' and verify the installed version reaches 3.26.0 or higher. The fix retracts the is_safe => ['all'] claim: html_to_markdown now declares no safe context (its output is re-escaped for the surrounding context), and markdown_to_html and inline_css are narrowed to is_safe => ['html']. If an immediate upgrade is not feasible, the most targeted compensating control is to audit every Twig template that uses these three filters and confirm the filter output is only ever interpolated inside an HTML body context, never inside a script block, style attribute, or URL parameter - manually adding |escape('js'), |escape('css'), or |url_encode as appropriate for non-HTML contexts. Note that this manual audit is error-prone and does not address the html_to_markdown entity-decoding vector; upgrading to 3.26.0 is strongly preferred. The full advisory is at https://github.com/twigphp/Twig/security/advisories/GHSA-jv8m-2544-3pg3.
Same weakness CWE-116 – Improper Encoding or Escaping of Output
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-jv8m-2544-3pg3