Skip to content

Commit e4e401f

Browse files
committed
implemented mandatory escaping (cannot be disabled using |noescape)
1 parent 1dfbbbe commit e4e401f

12 files changed

Lines changed: 141 additions & 3 deletions

src/Latte/Compiler/Escaper.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,23 @@ public function escape(string $str): string
229229
}
230230

231231

232+
public function escapeMandatory(string $str): string
233+
{
234+
return match ($this->contentType) {
235+
ContentType::Html => match ($this->state) {
236+
self::HtmlAttributeQuoted => 'LR\Filters::escapeHtmlChar(' . $str . ', ' . var_export($this->quote, true) . ')',
237+
self::HtmlRawText => 'LR\Filters::escapeHtmlRawText(' . $str . ')',
238+
default => $str,
239+
},
240+
ContentType::Xml => match ($this->state) {
241+
self::HtmlAttributeQuoted => 'LR\Filters::escapeHtmlChar(' . $str . ', ' . var_export($this->quote, true) . ')',
242+
default => $str,
243+
},
244+
default => $str,
245+
};
246+
}
247+
248+
232249
public function check(string $str): string
233250
{
234251
if ($this->isHtmlAttribute() && $this->subState === self::Url) {

src/Latte/Compiler/Nodes/Php/ModifierNode.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ public function printSimple(PrintContext $context, string $expr): string
6868
$expr = $escaper->check($expr);
6969
}
7070

71-
if ($escape) {
72-
$expr = $escaper->escape($expr);
73-
}
71+
$expr = $escape
72+
? $escaper->escape($expr)
73+
: $escaper->escapeMandatory($expr);
7474

7575
return $expr;
7676
}

src/Latte/Runtime/Filters.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ public static function escapeHtmlComment($s): string
100100
}
101101

102102

103+
/**
104+
* Escapes a certain special character.
105+
*/
106+
public static function escapeHtmlChar($s, string $char): string
107+
{
108+
return str_replace(
109+
$char,
110+
htmlspecialchars($char, ENT_QUOTES | ENT_HTML5),
111+
(string) $s,
112+
);
113+
}
114+
115+
103116
/**
104117
* Escapes string for use everywhere inside XML (except for comments).
105118
*/
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/**
4+
* Test: |noescape
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Tester\Assert;
10+
11+
require __DIR__ . '/../bootstrap.php';
12+
13+
14+
$latte = new Latte\Engine;
15+
$latte->setLoader(new Latte\Loaders\StringLoader);
16+
17+
// html text
18+
Assert::match(
19+
'<p></script></p>',
20+
$latte->renderToString('<p>{="</script>"|noescape}</p>'),
21+
);
22+
23+
// in tag
24+
Assert::match(
25+
'<p foo a=\'a\' b="b">></p>',
26+
$latte->renderToString('<p {="foo a=\'a\' b=\"b\">"|noescape}></p>'),
27+
);
28+
29+
// attribute unquoted values
30+
Assert::match(
31+
'<p title=foo a=\'a\' b="b">></p>',
32+
$latte->renderToString('<p title={="foo a=\'a\' b=\"b\">"|noescape}></p>'),
33+
);
34+
35+
// attribute quoted values
36+
Assert::match(
37+
'<p title="foo a=\'a\' b=&quot;b&quot;>"></p>',
38+
$latte->renderToString('<p title="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
39+
);
40+
41+
Assert::match(
42+
'<p title=\'foo a=&apos;a&apos; b="b">\'></p>',
43+
$latte->renderToString('<p title=\'{="foo a=\'a\' b=\"b\">"|noescape}\'></p>'),
44+
);
45+
46+
Assert::match(
47+
'<p style="foo a=\'a\' b=&quot;b&quot;>"></p>',
48+
$latte->renderToString('<p style="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
49+
);
50+
51+
Assert::match(
52+
'<p onclick="foo a=\'a\' b=&quot;b&quot;>"></p>',
53+
$latte->renderToString('<p onclick="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
54+
);

tests/common/contentType.html.css.phpt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,9 @@ Assert::match(
5454
'<style> a { background: url("\"") } </style>',
5555
$latte->renderToString('<style> a { background: url("{=\'"\'}") } </style>'),
5656
);
57+
58+
// no escape
59+
Assert::match(
60+
'<style><\/style></style>',
61+
$latte->renderToString('<style>{="</style>"|noescape}</style>'),
62+
);

tests/common/contentType.html.html.phpt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ Assert::match(
2828
),
2929
);
3030

31+
// no escape
32+
Assert::match(
33+
'<script type="text/html"><\/script></script>',
34+
$latte->renderToString('<script type="text/html">{="</script>"|noescape}</script>'),
35+
);
36+
3137
// content of <script> is RAWTEXT
3238
Assert::match(
3339
<<<'XX'

tests/common/contentType.html.javascript.phpt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,9 @@ Assert::match(
111111
['foo' => new Html("<div title='</script>'></div>")],
112112
),
113113
);
114+
115+
// no escape
116+
Assert::match(
117+
'<script><\/script></script>',
118+
$latte->renderToString('<script>{="</script>"|noescape}</script>'),
119+
);

tests/common/contentType.html.unknown.phpt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ Assert::match(
2828
),
2929
);
3030

31+
// no escape
32+
Assert::match(
33+
'<script type="foo"><\/script></script>',
34+
$latte->renderToString('<script type="foo">{="</script>"|noescape}</script>'),
35+
);
36+
3137
// content of <script> is RAWTEXT
3238
Assert::match(
3339
<<<'XX'

tests/common/expected/contentType.xml.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,5 @@
6161
<p val="some&amp;&lt;&gt;&quot;&apos;/chars" val2="`mxss"> </p>
6262

6363
<p onclick="some&amp;&lt;&gt;&quot;&apos;/chars"> </p>
64+
65+
<p title="foo a=\'a\' b=&quot;b&quot;>"></p>

tests/common/expected/contentType.xml.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ public function main(array $ʟ_args): void
128128
<p onclick=';
129129
echo '"' . LR\Filters::escapeXml($xss) . '"' /* line %d% */;
130130
echo '> </p>
131+
132+
<p title="';
133+
echo LR\Filters::escapeHtmlChar('foo a=\\\'a\\\' b="b">', '"') /* line %d% */;
134+
echo '"></p>
131135
';
132136
}
133137

0 commit comments

Comments
 (0)