Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a5f6e87

Browse files
nicolas-grekasfabpot
authored andcommitted
[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.
1 parent 5053571 commit a5f6e87

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/Profiler/Dumper/HtmlDumper.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,21 @@ public function dump(Profile $profile): string
3232

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

3838
protected function formatNonTemplate(Profile $profile, $prefix): string
3939
{
40-
return \sprintf('%s└ %s::%s(<span style="background-color: %s">%s</span>)', $prefix, $profile->getTemplate(), $profile->getType(), self::$colors[$profile->getType()] ?? 'auto', $profile->getName());
40+
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()));
4141
}
4242

4343
protected function formatTime(Profile $profile, $percent): string
4444
{
4545
return \sprintf('<span style="color: %s">%.2fms/%.0f%%</span>', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent);
4646
}
47+
48+
private static function escape(string $value): string
49+
{
50+
return htmlspecialchars($value, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
51+
}
4752
}

tests/Profiler/Dumper/HtmlTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222

2323
use Twig\Profiler\Dumper\HtmlDumper;
24+
use Twig\Profiler\Profile;
2425

2526
class HtmlTest extends ProfilerTestCase
2627
{
@@ -39,4 +40,23 @@ public function testDump()
3940
</pre>
4041
EOF, $dumper->dump($this->getProfile()));
4142
}
43+
44+
public function testDumpEscapesTemplateAndProfileNames()
45+
{
46+
$root = new Profile('main');
47+
$child = new Profile('<img src=x onerror=alert(1)>', Profile::TEMPLATE);
48+
$grandchild = new Profile('<img src=x onerror=alert(2)>', Profile::MACRO, '<img src=x onerror=alert(3)>');
49+
50+
(new \ReflectionProperty($child, 'profiles'))->setValue($child, [$grandchild]);
51+
(new \ReflectionProperty($root, 'profiles'))->setValue($root, [$child]);
52+
53+
$output = (new HtmlDumper())->dump($root);
54+
55+
$this->assertStringNotContainsString('<img src=x onerror=alert(1)>', $output);
56+
$this->assertStringNotContainsString('<img src=x onerror=alert(2)>', $output);
57+
$this->assertStringNotContainsString('<img src=x onerror=alert(3)>', $output);
58+
$this->assertStringContainsString('&lt;img src=x onerror=alert(1)&gt;', $output);
59+
$this->assertStringContainsString('&lt;img src=x onerror=alert(2)&gt;', $output);
60+
$this->assertStringContainsString('&lt;img src=x onerror=alert(3)&gt;', $output);
61+
}
4262
}

0 commit comments

Comments
 (0)