Skip to content

Commit 13436ee

Browse files
committed
Filter |noescape preserves some escaping in some contexts
1 parent c36a143 commit 13436ee

File tree

5 files changed

+126
-3
lines changed

5 files changed

+126
-3
lines changed

src/Latte/Compiler/Escaper.php

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

182182

183+
public function escapeRequisite(string $str): string
184+
{
185+
return match ($this->contentType) {
186+
ContentType::Html => match ($this->state) {
187+
self::HtmlAttribute => $this->quote ? 'LR\Filters::escapeHtmlChar(' . $str . ', ' . var_export($this->quote, true) . ')' : $str,
188+
self::HtmlRawText => 'LR\Filters::escapeHtmlRawText(' . $str . ')',
189+
default => $str,
190+
},
191+
ContentType::Xml => match ($this->state) {
192+
self::HtmlAttribute => $this->quote ? 'LR\Filters::escapeHtmlChar(' . $str . ', ' . var_export($this->quote, true) . ')' : $str,
193+
default => $str,
194+
},
195+
default => $str,
196+
};
197+
}
198+
199+
183200
public function check(string $str): string
184201
{
185202
if ($this->state === self::HtmlAttribute && $this->subType === 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->escapeRequisite($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: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/**
4+
* Test: |noescape
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Tester\Assert;
10+
11+
12+
require __DIR__ . '/../bootstrap.php';
13+
14+
15+
$latte = new Latte\Engine;
16+
$latte->setLoader(new Latte\Loaders\StringLoader);
17+
18+
// html text
19+
Assert::match(
20+
'<p></script></p>',
21+
$latte->renderToString('<p>{="</script>"|noescape}</p>'),
22+
);
23+
24+
// raw text
25+
Assert::match(
26+
'<script><\/script></script>',
27+
$latte->renderToString('<script>{="</script>"|noescape}</script>'),
28+
);
29+
30+
Assert::match(
31+
'<style><\/script></style>',
32+
$latte->renderToString('<style>{="</script>"|noescape}</style>'),
33+
);
34+
35+
Assert::match(
36+
'<script type="text/html"><\/script></script>',
37+
$latte->renderToString('<script type="text/html">{="</script>"|noescape}</script>'),
38+
);
39+
40+
// in tag
41+
Assert::match(
42+
'<p foo a=\'a\' b="b">></p>',
43+
$latte->renderToString('<p {="foo a=\'a\' b=\"b\">"|noescape}></p>'),
44+
);
45+
46+
// attribute unquoted values
47+
Assert::match(
48+
'<p title=foo a=\'a\' b="b">></p>',
49+
$latte->renderToString('<p title={="foo a=\'a\' b=\"b\">"|noescape}></p>'),
50+
);
51+
52+
// attribute quoted values
53+
Assert::match(
54+
'<p title="foo a=\'a\' b=&quot;b&quot;>"></p>',
55+
$latte->renderToString('<p title="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
56+
);
57+
58+
Assert::match(
59+
'<p title=\'foo a=&apos;a&apos; b="b">\'></p>',
60+
$latte->renderToString('<p title=\'{="foo a=\'a\' b=\"b\">"|noescape}\'></p>'),
61+
);
62+
63+
Assert::match(
64+
'<p style="foo a=\'a\' b=&quot;b&quot;>"></p>',
65+
$latte->renderToString('<p style="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
66+
);
67+
68+
Assert::match(
69+
'<p onclick="foo a=\'a\' b=&quot;b&quot;>"></p>',
70+
$latte->renderToString('<p onclick="{="foo a=\'a\' b=\"b\">"|noescape}"></p>'),
71+
);

tests/filters/escapeHtmlChar.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/**
4+
* Test: Latte\Runtime\Filters::escapeHtmlChar
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Latte\Runtime\Filters;
10+
use Tester\Assert;
11+
12+
require __DIR__ . '/../bootstrap.php';
13+
14+
15+
Assert::same('', Filters::escapeHtmlChar(null, '"'));
16+
Assert::same('', Filters::escapeHtmlChar('', '"'));
17+
Assert::same('1', Filters::escapeHtmlChar(1, '"'));
18+
Assert::same('string', Filters::escapeHtmlChar('string', '"'));
19+
Assert::same('< & \' &quot; >', Filters::escapeHtmlChar('< & \' " >', '"'));
20+
Assert::same('< & &apos; " >', Filters::escapeHtmlChar('< & \' " >', "'"));
21+
Assert::same('< & \' " &gt;', Filters::escapeHtmlChar('< & \' " >', '>'));
22+
Assert::same('&quot;', Filters::escapeHtmlChar('&quot;', '"'));

0 commit comments

Comments
 (0)