Skip to content

Commit

Permalink
implemented mandatory escaping (cannot be disabled using |noescape)
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Dec 27, 2022
1 parent 1dfbbbe commit e4e401f
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 3 deletions.
17 changes: 17 additions & 0 deletions src/Latte/Compiler/Escaper.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,23 @@ public function escape(string $str): string
}


public function escapeMandatory(string $str): string
{
return match ($this->contentType) {
ContentType::Html => match ($this->state) {
self::HtmlAttributeQuoted => 'LR\Filters::escapeHtmlChar(' . $str . ', ' . var_export($this->quote, true) . ')',
self::HtmlRawText => 'LR\Filters::escapeHtmlRawText(' . $str . ')',
default => $str,
},
ContentType::Xml => match ($this->state) {
self::HtmlAttributeQuoted => 'LR\Filters::escapeHtmlChar(' . $str . ', ' . var_export($this->quote, true) . ')',
default => $str,
},
default => $str,
};
}


public function check(string $str): string
{
if ($this->isHtmlAttribute() && $this->subState === self::Url) {
Expand Down
6 changes: 3 additions & 3 deletions src/Latte/Compiler/Nodes/Php/ModifierNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ public function printSimple(PrintContext $context, string $expr): string
$expr = $escaper->check($expr);
}

if ($escape) {
$expr = $escaper->escape($expr);
}
$expr = $escape
? $escaper->escape($expr)
: $escaper->escapeMandatory($expr);

return $expr;
}
Expand Down
13 changes: 13 additions & 0 deletions src/Latte/Runtime/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ public static function escapeHtmlComment($s): string
}


/**
* Escapes a certain special character.
*/
public static function escapeHtmlChar($s, string $char): string
{
return str_replace(
$char,
htmlspecialchars($char, ENT_QUOTES | ENT_HTML5),
(string) $s,
);
}


/**
* Escapes string for use everywhere inside XML (except for comments).
*/
Expand Down
54 changes: 54 additions & 0 deletions tests/common/Compiler.noescape.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

/**
* Test: |noescape
*/

declare(strict_types=1);

use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


$latte = new Latte\Engine;
$latte->setLoader(new Latte\Loaders\StringLoader);

// html text
Assert::match(
'<p></script></p>',
$latte->renderToString('<p>{="</script>"|noescape}</p>'),
);

// in tag
Assert::match(
'<p foo a=\'a\' b="b">></p>',
$latte->renderToString('<p {="foo a=\'a\' b=\"b\">"|noescape}></p>'),
);

// attribute unquoted values
Assert::match(
'<p title=foo a=\'a\' b="b">></p>',
$latte->renderToString('<p title={="foo a=\'a\' b=\"b\">"|noescape}></p>'),
);

// attribute quoted values
Assert::match(
'<p title="foo a=\'a\' b=&quot;b&quot;>"></p>',
$latte->renderToString('<p title="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
);

Assert::match(
'<p title=\'foo a=&apos;a&apos; b="b">\'></p>',
$latte->renderToString('<p title=\'{="foo a=\'a\' b=\"b\">"|noescape}\'></p>'),
);

Assert::match(
'<p style="foo a=\'a\' b=&quot;b&quot;>"></p>',
$latte->renderToString('<p style="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
);

Assert::match(
'<p onclick="foo a=\'a\' b=&quot;b&quot;>"></p>',
$latte->renderToString('<p onclick="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
);
6 changes: 6 additions & 0 deletions tests/common/contentType.html.css.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,9 @@ Assert::match(
'<style> a { background: url("\"") } </style>',
$latte->renderToString('<style> a { background: url("{=\'"\'}") } </style>'),
);

// no escape
Assert::match(
'<style><\/style></style>',
$latte->renderToString('<style>{="</style>"|noescape}</style>'),
);
6 changes: 6 additions & 0 deletions tests/common/contentType.html.html.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ Assert::match(
),
);

// no escape
Assert::match(
'<script type="text/html"><\/script></script>',
$latte->renderToString('<script type="text/html">{="</script>"|noescape}</script>'),
);

// content of <script> is RAWTEXT
Assert::match(
<<<'XX'
Expand Down
6 changes: 6 additions & 0 deletions tests/common/contentType.html.javascript.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,9 @@ Assert::match(
['foo' => new Html("<div title='</script>'></div>")],
),
);

// no escape
Assert::match(
'<script><\/script></script>',
$latte->renderToString('<script>{="</script>"|noescape}</script>'),
);
6 changes: 6 additions & 0 deletions tests/common/contentType.html.unknown.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ Assert::match(
),
);

// no escape
Assert::match(
'<script type="foo"><\/script></script>',
$latte->renderToString('<script type="foo">{="</script>"|noescape}</script>'),
);

// content of <script> is RAWTEXT
Assert::match(
<<<'XX'
Expand Down
2 changes: 2 additions & 0 deletions tests/common/expected/contentType.xml.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@
<p val="some&amp;&lt;&gt;&quot;&apos;/chars" val2="`mxss"> </p>

<p onclick="some&amp;&lt;&gt;&quot;&apos;/chars"> </p>

<p title="foo a=\'a\' b=&quot;b&quot;>"></p>
4 changes: 4 additions & 0 deletions tests/common/expected/contentType.xml.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ public function main(array $ʟ_args): void
<p onclick=';
echo '"' . LR\Filters::escapeXml($xss) . '"' /* line %d% */;
echo '> </p>
<p title="';
echo LR\Filters::escapeHtmlChar('foo a=\\\'a\\\' b="b">', '"') /* line %d% */;
echo '"></p>
';
}

Expand Down
2 changes: 2 additions & 0 deletions tests/common/templates/contentType.xml.latte
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ var html = {$el};
<p val = {$xss} val2={$mxss}> </p>
<p onclick = {$xss}> </p>
<p title="{="foo a=\'a\' b=\"b\">"|noescape}"></p>
22 changes: 22 additions & 0 deletions tests/filters/escapeHtmlChar.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/**
* Test: Latte\Runtime\Filters::escapeHtmlChar
*/

declare(strict_types=1);

use Latte\Runtime\Filters;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


Assert::same('', Filters::escapeHtmlChar(null, '"'));
Assert::same('', Filters::escapeHtmlChar('', '"'));
Assert::same('1', Filters::escapeHtmlChar(1, '"'));
Assert::same('string', Filters::escapeHtmlChar('string', '"'));
Assert::same('< & \' &quot; >', Filters::escapeHtmlChar('< & \' " >', '"'));
Assert::same('< & &apos; " >', Filters::escapeHtmlChar('< & \' " >', "'"));
Assert::same('< & \' " &gt;', Filters::escapeHtmlChar('< & \' " >', '>'));
Assert::same('&quot;', Filters::escapeHtmlChar('&quot;', '"'));

0 comments on commit e4e401f

Please sign in to comment.