Skip to content

Commit f8593e1

Browse files
committed
implemented mandatory escaping (cannot be disabled using |noescape)
1 parent 28708ef commit f8593e1

12 files changed

Lines changed: 156 additions & 3 deletions

src/Latte/Compiler/Escaper.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,27 @@ public function escape(string $str): string
230230
}
231231

232232

233+
public function escapeMandatory(string $str): string
234+
{
235+
$quote = var_export($this->quote, true);
236+
return match ($this->contentType) {
237+
ContentType::Html => match ($this->state) {
238+
self::HtmlAttributeQuoted => "LR\\Filters::escapeHtmlChar($str, $quote)",
239+
self::HtmlRawText => match ($this->subState) {
240+
self::HtmlText => 'LR\Filters::convertHtmlToHtmlRawText(' . $str . ')',
241+
default => "LR\\Filters::convertJSToHtmlRawText($str)",
242+
},
243+
default => $str,
244+
},
245+
ContentType::Xml => match ($this->state) {
246+
self::HtmlAttributeQuoted => "LR\\Filters::escapeHtmlChar($str, $quote)",
247+
default => $str,
248+
},
249+
default => $str,
250+
};
251+
}
252+
253+
233254
public function check(string $str): string
234255
{
235256
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 and tags).
105118
*/
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
// in bogus tag
30+
Assert::match(
31+
'<!doctype foo a=\'a\' b="b">></p>',
32+
$latte->renderToString('<!doctype {="foo a=\'a\' b=\"b\">"|noescape}></p>'),
33+
);
34+
35+
// attribute unquoted values
36+
Assert::match(
37+
'<p title=foo a=\'a\' b="b">></p>',
38+
$latte->renderToString('<p title={="foo a=\'a\' b=\"b\">"|noescape}></p>'),
39+
);
40+
41+
// attribute quoted values
42+
Assert::match(
43+
'<p title="foo a=\'a\' b=&quot;b&quot;>"></p>',
44+
$latte->renderToString('<p title="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
45+
);
46+
47+
Assert::match(
48+
'<p title=\'foo a=&apos;a&apos; b="b">\'></p>',
49+
$latte->renderToString('<p title=\'{="foo a=\'a\' b=\"b\">"|noescape}\'></p>'),
50+
);
51+
52+
Assert::match(
53+
'<p style="foo a=\'a\' b=&quot;b&quot;>"></p>',
54+
$latte->renderToString('<p style="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
55+
);
56+
57+
Assert::match(
58+
'<p onclick="foo a=\'a\' b=&quot;b&quot;>"></p>',
59+
$latte->renderToString('<p onclick="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
60+
);

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
@@ -36,6 +36,12 @@ Assert::match(
3636
$latte->renderToString('{define a}<script></script>{/define} <script type="text/html">{include a}</script>'),
3737
);
3838

39+
// no escape
40+
Assert::match(
41+
'<script type="text/html">&lt;/script></script>',
42+
$latte->renderToString('<script type="text/html">{="</script>"|noescape}</script>'),
43+
);
44+
3945
// content of <script> is RAWTEXT
4046
Assert::match(
4147
<<<'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
@@ -37,6 +37,12 @@ Assert::exception(
3737
'Including block a with content type HTML into incompatible type HTML/RAW+TEXT.',
3838
);
3939

40+
// no escape
41+
Assert::match(
42+
'<script type="foo"><\/script></script>',
43+
$latte->renderToString('<script type="foo">{="</script>"|noescape}</script>'),
44+
);
45+
4046
// content of <script> is RAWTEXT
4147
Assert::match(
4248
<<<'XX'

tests/common/expected/contentType.xml.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,6 @@
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>
66+
<p foo a='a' b="b">></p>

tests/common/expected/contentType.xml.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ 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>
135+
<p ';
136+
echo 'foo a=\'a\' b="b">' /* line %d% */;
137+
echo '></p>
131138
';
132139
}
133140

0 commit comments

Comments
 (0)