Skip to content

Commit 1924c43

Browse files
committed
Filter::renderHtmlAttribute & renderXmlAttribute: added name validation
1 parent 6504453 commit 1924c43

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

src/Latte/Runtime/Filters.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,10 @@ public static function safeTag(mixed $name, bool $xml = false): string
279279
*/
280280
public static function renderHtmlAttribute(string $name, mixed $value): ?string
281281
{
282-
if ($value === null || $value === false) {
282+
if (!preg_match('~^[^\p{C} "\'>=/]+$~Du', $name)) {
283+
throw new Latte\RuntimeException("Invalid HTML attribute name '$name'");
284+
285+
} elseif ($value === null || $value === false) {
283286
return null;
284287

285288
} elseif ($value === true) {
@@ -322,7 +325,11 @@ public static function renderHtmlAttribute(string $name, mixed $value): ?string
322325
*/
323326
public static function renderXmlAttribute(string $name, mixed $value): ?string
324327
{
325-
if ($value === null || $value === false) {
328+
// https://www.w3.org/TR/xml/#NT-Name
329+
if (!preg_match('~^[:A-Z_a-z\x{C0}-\x{D6}\x{D8}-\x{F6}\x{F8}-\x{2FF}\x{370}-\x{37D}\x{37F}-\x{1FFF}\x{200C}-\x{200D}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}-\x{EFFFF}][\-\.0-9:A-Z_a-z\x{B7}\x{C0}-\x{D6}\x{D8}-\x{F6}\x{F8}-\x{2FF}\x{300}-\x{36F}\x{370}-\x{37D}\x{37F}-\x{1FFF}\x{200C}-\x{200D}\x{203F}-\x{2040}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}-\x{EFFFF}]*$~Du', $name)) {
330+
throw new Latte\RuntimeException("Invalid XML attribute name '$name'");
331+
332+
} elseif ($value === null || $value === false) {
326333
return null;
327334

328335
} elseif ($value === true) {

tests/filters/renderHtmlAttribute.phpt

+13
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,16 @@ test('special values (numbers, Infinity, NaN)', function () {
6060
Assert::same("a=\"foo \u{D800} bar\"", Filters::renderHtmlAttribute('a', "foo \u{D800} bar")); // invalid codepoint high surrogates
6161
Assert::same("a='foo \xE3\x80\x22 bar'", Filters::renderHtmlAttribute('a', "foo \xE3\x80\x22 bar")); // stripped UTF
6262
});
63+
64+
65+
test('name validity', function () {
66+
Assert::type('string', Filters::renderHtmlAttribute('_name', ''));
67+
Assert::type('string', Filters::renderHtmlAttribute('42name', ''));
68+
Assert::type('string', Filters::renderHtmlAttribute('元素', '')); // Chinese for "element"
69+
Assert::type('string', Filters::renderHtmlAttribute('-my&HTML_element.name:2', ''));
70+
71+
Assert::exception(fn() => Filters::renderHtmlAttribute('', ''), Latte\RuntimeException::class);
72+
Assert::exception(fn() => Filters::renderHtmlAttribute("name\n", ''), Latte\RuntimeException::class);
73+
Assert::exception(fn() => Filters::renderHtmlAttribute('name name', ''), Latte\RuntimeException::class);
74+
Assert::exception(fn() => Filters::renderHtmlAttribute('name"name', ''), Latte\RuntimeException::class);
75+
});

tests/filters/renderXmlAttribute.phpt

+15
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,18 @@ test('special values (numbers, Infinity, NaN)', function () {
4141
Assert::same("a=\"foo \u{FFFD} bar\"", Filters::renderXmlAttribute('a', "foo \u{D800} bar")); // invalid codepoint high surrogates
4242
Assert::same("a=\"foo \u{FFFD}" bar\"", Filters::renderXmlAttribute('a', "foo \xE3\x80\x22 bar")); // stripped UTF
4343
});
44+
45+
46+
test('name validity', function () {
47+
Assert::type('string', Filters::renderXmlAttribute('_name', ''));
48+
Assert::type('string', Filters::renderXmlAttribute('元素', '')); // Chinese for "element"
49+
Assert::type('string', Filters::renderXmlAttribute(':my-XML_element.name:2', ''));
50+
51+
Assert::exception(fn() => Filters::renderXmlAttribute('', ''), Latte\RuntimeException::class);
52+
Assert::exception(fn() => Filters::renderXmlAttribute("name\n", ''), Latte\RuntimeException::class);
53+
Assert::exception(fn() => Filters::renderXmlAttribute('1name', ''), Latte\RuntimeException::class);
54+
Assert::exception(fn() => Filters::renderXmlAttribute('-name', ''), Latte\RuntimeException::class);
55+
Assert::exception(fn() => Filters::renderXmlAttribute('name name', ''), Latte\RuntimeException::class);
56+
Assert::exception(fn() => Filters::renderXmlAttribute('name&name', ''), Latte\RuntimeException::class);
57+
Assert::exception(fn() => Filters::renderXmlAttribute('name"name', ''), Latte\RuntimeException::class);
58+
});

0 commit comments

Comments
 (0)