diff --git a/src/Profiler/Dumper/HtmlDumper.php b/src/Profiler/Dumper/HtmlDumper.php
index cdab2de59..a5e59e498 100644
--- a/src/Profiler/Dumper/HtmlDumper.php
+++ b/src/Profiler/Dumper/HtmlDumper.php
@@ -32,16 +32,21 @@ final class HtmlDumper extends BaseDumper
protected function formatTemplate(Profile $profile, $prefix): string
{
- return \sprintf('%s└ %s', $prefix, self::$colors['template'], $profile->getTemplate());
+ return \sprintf('%s└ %s', $prefix, self::$colors['template'], self::escape($profile->getTemplate()));
}
protected function formatNonTemplate(Profile $profile, $prefix): string
{
- return \sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), self::$colors[$profile->getType()] ?? 'auto', $profile->getName());
+ return \sprintf('%s└ %s::%s(%s)', $prefix, self::escape($profile->getTemplate()), $profile->getType(), self::$colors[$profile->getType()] ?? 'auto', self::escape($profile->getName()));
}
protected function formatTime(Profile $profile, $percent): string
{
return \sprintf('%.2fms/%.0f%%', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent);
}
+
+ private static function escape(string $value): string
+ {
+ return htmlspecialchars($value, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
+ }
}
diff --git a/tests/Profiler/Dumper/HtmlTest.php b/tests/Profiler/Dumper/HtmlTest.php
index a89fcfc46..b1170a83a 100644
--- a/tests/Profiler/Dumper/HtmlTest.php
+++ b/tests/Profiler/Dumper/HtmlTest.php
@@ -21,6 +21,7 @@ namespace Twig\Tests\Profiler\Dumper;
*/
use Twig\Profiler\Dumper\HtmlDumper;
+use Twig\Profiler\Profile;
class HtmlTest extends ProfilerTestCase
{
@@ -39,4 +40,23 @@ class HtmlTest extends ProfilerTestCase
EOF, $dumper->dump($this->getProfile()));
}
+
+ public function testDumpEscapesTemplateAndProfileNames()
+ {
+ $root = new Profile('main');
+ $child = new Profile('
', Profile::TEMPLATE);
+ $grandchild = new Profile('
', Profile::MACRO, '
');
+
+ (new \ReflectionProperty($child, 'profiles'))->setValue($child, [$grandchild]);
+ (new \ReflectionProperty($root, 'profiles'))->setValue($root, [$child]);
+
+ $output = (new HtmlDumper())->dump($root);
+
+ $this->assertStringNotContainsString('
', $output);
+ $this->assertStringNotContainsString('
', $output);
+ $this->assertStringNotContainsString('
', $output);
+ $this->assertStringContainsString('<img src=x onerror=alert(1)>', $output);
+ $this->assertStringContainsString('<img src=x onerror=alert(2)>', $output);
+ $this->assertStringContainsString('<img src=x onerror=alert(3)>', $output);
+ }
}