Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-47730

CVE-2026-47730: Symfony Twig XSS Vulnerability

CVE-2026-47730 is a cross-site scripting flaw in Symfony Twig's HTML profiler dumper that allows injection of malicious HTML through unescaped template names. This article covers the technical details, affected versions, and mitigation.

Published:

CVE-2026-47730 Overview

CVE-2026-47730 is a Cross-Site Scripting (XSS) vulnerability in Twig, a widely used template language for PHP. The flaw resides in Twig\Profiler\Dumper\HtmlDumper, which writes the output of Profile::getTemplate() and Profile::getName() directly into HTML without escaping. Attacker-controlled template or profile names can inject arbitrary HTML that executes when a browser renders the profiler dump. The issue affects Twig versions 3.0.0 through 3.25.x and is fixed in version 3.26.0. The vulnerability is classified under [CWE-79] (Improper Neutralization of Input During Web Page Generation).

Critical Impact

Attackers who can influence template or profile names can inject arbitrary HTML or JavaScript into rendered profiler dumps, enabling session theft, credential harvesting, or malicious redirects in developer and staging environments.

Affected Products

  • Symfony Twig versions 3.0.0 through 3.25.x
  • PHP applications using Twig\Profiler\Dumper\HtmlDumper
  • Development and profiling environments exposing Twig profiler output in browsers

Discovery Timeline

  • 2026-07-14 - CVE-2026-47730 published to NVD
  • 2026-07-16 - Last updated in NVD database

Technical Details for CVE-2026-47730

Vulnerability Analysis

The vulnerability exists in the HtmlDumper class within Twig's profiler component. This class formats profiling data as HTML for developer inspection. Two methods, formatTemplate() and formatNonTemplate(), interpolate the return values of $profile->getTemplate() and $profile->getName() into HTML strings using sprintf() without applying HTML escaping.

When template or profile names originate from untrusted input, such as user-supplied file paths, dynamic includes, or attacker-influenced identifiers, the raw values are inserted directly into the generated markup. A browser rendering the profiler dump then interprets any embedded HTML or <script> tags as executable content. The impact is bounded by the environments where profiler output is exposed, typically development, staging, or debugging pages, but reflected XSS in those contexts still enables session hijacking against developers or administrators.

Root Cause

The root cause is missing output encoding in the profiler dumper. Twig's core rendering pipeline auto-escapes output, but the profiler dumper wrote profile metadata to HTML using string formatting rather than the htmlspecialchars() function. Any string containing <, >, ", or ' characters passes through unchanged.

Attack Vector

Exploitation requires that an attacker control a value ultimately returned by Profile::getTemplate() or Profile::getName(). A victim then views the rendered profiler dump in a browser. User interaction is required, and the attacker needs at least low privileges to influence template naming. The following patch shows the fix applied in version 3.26.0.

php
    protected function formatTemplate(Profile $profile, $prefix): string
    {
-        return \sprintf('%s└ <span style="background-color: %s">%s</span>', $prefix, self::$colors['template'], $profile->getTemplate());
+        return \sprintf('%s└ <span style="background-color: %s">%s</span>', $prefix, self::$colors['template'], self::escape($profile->getTemplate()));
    }

    protected function formatNonTemplate(Profile $profile, $prefix): string
    {
-        return \sprintf('%s└ %s::%s(<span style="background-color: %s">%s</span>)', $prefix, $profile->getTemplate(), $profile->getType(), self::$colors[$profile->getType()] ?? 'auto', $profile->getName());
+        return \sprintf('%s└ %s::%s(<span style="background-color: %s">%s</span>)', $prefix, self::escape($profile->getTemplate()), $profile->getType(), self::$colors[$profile->getType()] ?? 'auto', self::escape($profile->getName()));
    }

+    private static function escape(string $value): string
+    {
+        return htmlspecialchars($value, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
+    }

Source: Twig commit a5f6e879

Detection Methods for CVE-2026-47730

Indicators of Compromise

  • HTML profiler dump pages containing unexpected <script>, <img>, <iframe>, or event handler attributes such as onerror= and onload= within template or profile name fields
  • Outbound browser requests from developer or staging hosts to unknown external domains triggered from profiler pages
  • Unusual template names in application logs containing HTML metacharacters like <, >, or quote characters

Detection Strategies

  • Scan application dependency manifests (composer.lock, composer.json) for twig/twig versions below 3.26.0
  • Review web server access logs for requests to profiler endpoints originating from external or unauthenticated sources
  • Inspect rendered profiler HTML output for unescaped angle brackets or scripting payloads in template and profile name positions

Monitoring Recommendations

  • Enable Content Security Policy (CSP) reporting on any environment that may expose Twig profiler output and monitor script-src violations
  • Monitor for the presence of Twig\Profiler\Dumper\HtmlDumper invocations in production code paths, which should not occur in production deployments
  • Alert on modifications to Twig profiler configuration files or environment flags that enable profiling

How to Mitigate CVE-2026-47730

Immediate Actions Required

  • Upgrade twig/twig to version 3.26.0 or later using composer update twig/twig
  • Disable the Twig profiler in any production environment and restrict access to profiler dump pages
  • Audit codebases for direct use of Twig\Profiler\Dumper\HtmlDumper and confirm template and profile names originate from trusted sources

Patch Information

The fix is available in Twig version 3.26.0. The patch introduces a private escape() helper that applies htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') to both Profile::getTemplate() and Profile::getName() before interpolation. Refer to the GitHub Security Advisory GHSA-2g2g-8p8h-fgwm and the Twig v3.26.0 release notes for full details.

Workarounds

  • Replace HtmlDumper with Twig\Profiler\Dumper\TextDumper or BlackfireDumper, which do not emit HTML
  • Wrap profiler output with an application-level HTML sanitizer before returning it to the browser
  • Enforce a strict Content Security Policy that blocks inline script execution on any page that renders profiler output
bash
# Upgrade Twig to the patched release
composer require twig/twig:^3.26.0

# Verify installed version
composer show twig/twig | grep versions

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.