Skip to content

Commit

Permalink
Merge branch '4.4' into 5.4
Browse files Browse the repository at this point in the history
* 4.4:
  [TwigBridge] Add integration tests on twig code helpers
  [TwigBridge] Ensure CodeExtension's filters properly escape their input
  [HttpClient] fix missing dep
  Update VERSION for 4.4.50
  Update CHANGELOG for 4.4.50
  • Loading branch information
nicolas-grekas committed Nov 9, 2023
2 parents cbe9acb + 83b021c commit fc6ee0a
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 30 deletions.
22 changes: 13 additions & 9 deletions Extension/CodeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public function __construct($fileLinkFormat, string $projectDir, string $charset
public function getFilters(): array
{
return [
new TwigFilter('abbr_class', [$this, 'abbrClass'], ['is_safe' => ['html']]),
new TwigFilter('abbr_method', [$this, 'abbrMethod'], ['is_safe' => ['html']]),
new TwigFilter('abbr_class', [$this, 'abbrClass'], ['is_safe' => ['html'], 'pre_escape' => 'html']),
new TwigFilter('abbr_method', [$this, 'abbrMethod'], ['is_safe' => ['html'], 'pre_escape' => 'html']),
new TwigFilter('format_args', [$this, 'formatArgs'], ['is_safe' => ['html']]),
new TwigFilter('format_args_as_text', [$this, 'formatArgsAsText']),
new TwigFilter('file_excerpt', [$this, 'fileExcerpt'], ['is_safe' => ['html']]),
Expand Down Expand Up @@ -85,22 +85,23 @@ public function formatArgs(array $args): string
$result = [];
foreach ($args as $key => $item) {
if ('object' === $item[0]) {
$item[1] = htmlspecialchars($item[1], \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
$parts = explode('\\', $item[1]);
$short = array_pop($parts);
$formattedValue = sprintf('<em>object</em>(<abbr title="%s">%s</abbr>)', $item[1], $short);
} elseif ('array' === $item[0]) {
$formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
$formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset));
} elseif ('null' === $item[0]) {
$formattedValue = '<em>null</em>';
} elseif ('boolean' === $item[0]) {
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
$formattedValue = '<em>'.strtolower(htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset)).'</em>';
} elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>';
} else {
$formattedValue = str_replace("\n", '', htmlspecialchars(var_export($item[1], true), \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset));
}

$result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
$result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", htmlspecialchars($key, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $formattedValue);
}

return implode(', ', $result);
Expand Down Expand Up @@ -166,11 +167,14 @@ public function formatFile(string $file, int $line, string $text = null): string
$file = trim($file);

if (null === $text) {
$text = $file;
if (null !== $rel = $this->getFileRelative($text)) {
$rel = explode('/', $rel, 2);
$text = sprintf('<abbr title="%s%2$s">%s</abbr>%s', $this->projectDir, $rel[0], '/'.($rel[1] ?? ''));
if (null !== $rel = $this->getFileRelative($file)) {
$rel = explode('/', htmlspecialchars($rel, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), 2);
$text = sprintf('<abbr title="%s%2$s">%s</abbr>%s', htmlspecialchars($this->projectDir, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset), $rel[0], '/'.($rel[1] ?? ''));
} else {
$text = htmlspecialchars($file, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
}
} else {
$text = htmlspecialchars($text, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
}

if (0 < $line) {
Expand Down
138 changes: 117 additions & 21 deletions Tests/Extension/CodeExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Twig\Extension\CodeExtension;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Twig\Environment;
use Twig\Loader\ArrayLoader;

class CodeExtensionTest extends TestCase
{
Expand All @@ -28,42 +30,136 @@ public function testFileRelative()
$this->assertEquals('file.txt', $this->getExtension()->getFileRelative(\DIRECTORY_SEPARATOR.'project'.\DIRECTORY_SEPARATOR.'file.txt'));
}

/**
* @dataProvider getClassNameProvider
*/
public function testGettingClassAbbreviation($class, $abbr)
public function testClassAbbreviationIntegration()
{
$this->assertEquals($this->getExtension()->abbrClass($class), $abbr);
$data = [
'fqcn' => 'F\Q\N\Foo',
'xss' => '<script>',
];

$template = <<<'TWIG'
{{ 'Bare'|abbr_class }}
{{ fqcn|abbr_class }}
{{ xss|abbr_class }}
TWIG;

$expected = <<<'HTML'
<abbr title="Bare">Bare</abbr>
<abbr title="F\Q\N\Foo">Foo</abbr>
<abbr title="&lt;script&gt;">&lt;script&gt;</abbr>
HTML;

$this->assertEquals($expected, $this->render($template, $data));
}

/**
* @dataProvider getMethodNameProvider
*/
public function testGettingMethodAbbreviation($method, $abbr)
public function testMethodAbbreviationIntegration()
{
$this->assertEquals($this->getExtension()->abbrMethod($method), $abbr);
$data = [
'fqcn' => 'F\Q\N\Foo::Method',
'xss' => '<script>',
];

$template = <<<'TWIG'
{{ 'Bare::Method'|abbr_method }}
{{ fqcn|abbr_method }}
{{ 'Closure'|abbr_method }}
{{ 'Method'|abbr_method }}
{{ xss|abbr_method }}
TWIG;

$expected = <<<'HTML'
<abbr title="Bare">Bare</abbr>::Method()
<abbr title="F\Q\N\Foo">Foo</abbr>::Method()
<abbr title="Closure">Closure</abbr>
<abbr title="Method">Method</abbr>()
<abbr title="&lt;script&gt;">&lt;script&gt;</abbr>()
HTML;

$this->assertEquals($expected, $this->render($template, $data));
}

public static function getClassNameProvider(): array
public function testFormatArgsIntegration()
{
return [
['F\Q\N\Foo', '<abbr title="F\Q\N\Foo">Foo</abbr>'],
['Bare', '<abbr title="Bare">Bare</abbr>'],
$data = [
'args' => [
['object', 'Foo'],
['array', [['string', 'foo'], ['null']]],
['resource'],
['string', 'bar'],
['int', 123],
['bool', true],
],
'xss' => [
['object', '<Foo>'],
['array', [['string', '<foo>']]],
['string', '<bar>'],
['int', 123],
['bool', true],
['<xss>', '<script>'],
],
];

$template = <<<'TWIG'
{{ args|format_args }}
{{ xss|format_args }}
{{ args|format_args_as_text }}
{{ xss|format_args_as_text }}
TWIG;

$expected = <<<'HTML'
<em>object</em>(<abbr title="Foo">Foo</abbr>), <em>array</em>('foo', <em>null</em>), <em>resource</em>, 'bar', 123, true
<em>object</em>(<abbr title="&lt;Foo&gt;">&lt;Foo&gt;</abbr>), <em>array</em>('&lt;foo&gt;'), '&lt;bar&gt;', 123, true, '&lt;script&gt;'
object(Foo), array(&#039;foo&#039;, null), resource, &#039;bar&#039;, 123, true
object(&amp;lt;Foo&amp;gt;), array(&#039;&amp;lt;foo&amp;gt;&#039;), &#039;&amp;lt;bar&amp;gt;&#039;, 123, true, &#039;&amp;lt;script&amp;gt;&#039;
HTML;

$this->assertEquals($expected, $this->render($template, $data));
}


public function testFormatFileIntegration()
{
$template = <<<'TWIG'
{{ 'foo/bar/baz.php'|format_file(21) }}
TWIG;

$expected = <<<'HTML'
<a href="proto://foo/bar/baz.php#&amp;line=21" title="Click to open this file" class="file_link">foo/bar/baz.php at line 21</a>
HTML;

$this->assertEquals($expected, $this->render($template));
}

public static function getMethodNameProvider(): array
public function testFormatFileFromTextIntegration()
{
return [
['F\Q\N\Foo::Method', '<abbr title="F\Q\N\Foo">Foo</abbr>::Method()'],
['Bare::Method', '<abbr title="Bare">Bare</abbr>::Method()'],
['Closure', '<abbr title="Closure">Closure</abbr>'],
['Method', '<abbr title="Method">Method</abbr>()'],
];
$template = <<<'TWIG'
{{ 'in "foo/bar/baz.php" at line 21'|format_file_from_text }}
{{ 'in &quot;foo/bar/baz.php&quot; on line 21'|format_file_from_text }}
{{ 'in "<script>" on line 21'|format_file_from_text }}
TWIG;

$expected = <<<'HTML'
in <a href="proto://foo/bar/baz.php#&amp;line=21" title="Click to open this file" class="file_link">foo/bar/baz.php at line 21</a>
in <a href="proto://foo/bar/baz.php#&amp;line=21" title="Click to open this file" class="file_link">foo/bar/baz.php at line 21</a>
in <a href="proto://&lt;script&gt;#&amp;line=21" title="Click to open this file" class="file_link">&lt;script&gt; at line 21</a>
HTML;

$this->assertEquals($expected, $this->render($template));
}

protected function getExtension(): CodeExtension
{
return new CodeExtension(new FileLinkFormatter('proto://%f#&line=%l&'.substr(__FILE__, 0, 5).'>foobar'), \DIRECTORY_SEPARATOR.'project', 'UTF-8');
}

private function render(string $template, array $context = [])
{
$twig = new Environment(
new ArrayLoader(['index' => $template]),
['debug' => true]
);
$twig->addExtension($this->getExtension());

return $twig->render('index', $context);
}
}

0 comments on commit fc6ee0a

Please sign in to comment.