mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-13 10:56:38 +00:00
[Profiler] Escape template and profile names in HtmlDumper
The HtmlDumper output is intended to be rendered in a browser, and the template and macro/block names it interpolates are loader-controlled (e.g. the key for ArrayLoader or a database row id), so they can carry arbitrary HTML when an application stores templates under user-supplied identifiers.
This commit is contained in:
committed by
Fabien Potencier
parent
50535718b8
commit
a5f6e8793e
@@ -32,16 +32,21 @@ final class HtmlDumper extends BaseDumper
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
protected function formatTime(Profile $profile, $percent): string
|
||||
{
|
||||
return \sprintf('<span style="color: %s">%.2fms/%.0f%%</span>', $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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
</pre>
|
||||
EOF, $dumper->dump($this->getProfile()));
|
||||
}
|
||||
|
||||
public function testDumpEscapesTemplateAndProfileNames()
|
||||
{
|
||||
$root = new Profile('main');
|
||||
$child = new Profile('<img src=x onerror=alert(1)>', Profile::TEMPLATE);
|
||||
$grandchild = new Profile('<img src=x onerror=alert(2)>', Profile::MACRO, '<img src=x onerror=alert(3)>');
|
||||
|
||||
(new \ReflectionProperty($child, 'profiles'))->setValue($child, [$grandchild]);
|
||||
(new \ReflectionProperty($root, 'profiles'))->setValue($root, [$child]);
|
||||
|
||||
$output = (new HtmlDumper())->dump($root);
|
||||
|
||||
$this->assertStringNotContainsString('<img src=x onerror=alert(1)>', $output);
|
||||
$this->assertStringNotContainsString('<img src=x onerror=alert(2)>', $output);
|
||||
$this->assertStringNotContainsString('<img src=x onerror=alert(3)>', $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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user