Executive brief
Grav is a flat-file content management system that allows administrators to enable Twig template processing in page content. When this feature is enabled, any user with page creation permission can inject stored cross-site scripting (XSS) attacks using a bypass technique. An attacker can use Twig's string concatenation operator to construct malicious JavaScript event handlers and HTML tags that evade the XSS validator. The validator inspects the template code before processing, sees harmless Twig expressions, and approves the content. After rendering, the concatenated strings form complete XSS payloads that execute in every visitor's browser, potentially leading to session hijacking, account takeover, or website defacement.
Technical details
The vulnerability exploits a two-stage processing gap in Grav's content pipeline. The XSS validator (Security::detectXss()) operates on raw page content before Twig template engine processing. An attacker can use Twig's ~ (string concatenation) operator and {% set %} variable assignment (both allowed in the sandbox) to construct XSS payloads that the regex-based validator cannot detect. For example, {{ "on" ~ "error" }} appears benign to the validator's on_events regex (which looks for literal "on" preceded by whitespace), but after Twig rendering produces the literal string "onerror" that becomes a functional event handler in HTML. The validator never re-inspects Twig output before it is rendered with {{ content|raw }}, which bypasses Twig's default HTML auto-escaping. This technique defeats all four XSS validator regexes and the dangerous_tags blocklist, allowing injection of <script>, <iframe>, javascript: protocols, and event handlers. Exploitation requires twig_content.process_enabled to be true (admin configuration) and api.pages.write permission (page creation capability).
Affected products
- Grav Grav 2.0.0
Timeline
- 2026-06-24: disclosed: GitHub advisory published
- 2026-07-15: advisory: Published to National Vulnerability Database
- 2026: patched: Patched in version 2.0.1