Skip to content

Commit 238191e

Browse files
committed
Merge branch '2.8'
2 parents c915c1a + 84b1ca4 commit 238191e

6 files changed

Lines changed: 32 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi
66

77
## [Unreleased][unreleased]
88

9+
## [2.8.1] - 2026-03-05
10+
11+
This is a **security release** to address an issue where `DisallowedRawHtml` can be bypassed, resulting in a possible cross-site scripting (XSS) vulnerability.
12+
13+
### Fixed
14+
- Fixed `DisallowedRawHtmlRenderer` not blocking raw HTML tags with trailing ASCII whitespace (GHSA-4v6x-c7xx-hw9f)
15+
- Fixed PHP 8.5 deprecation (#1107)
16+
917
## [2.8.0] - 2025-11-26
1018

1119
### Added
@@ -717,7 +725,8 @@ No changes were introduced since the previous release.
717725
- Alternative 1: Use `CommonMarkConverter` or `GithubFlavoredMarkdownConverter` if you don't need to customize the environment
718726
- Alternative 2: Instantiate a new `Environment` and add the necessary extensions yourself
719727

720-
[unreleased]: https://github.com/thephpleague/commonmark/compare/2.8.0...HEAD
728+
[unreleased]: https://github.com/thephpleague/commonmark/compare/2.8.1...HEAD
729+
[2.8.1]: https://github.com/thephpleague/commonmark/compare/2.8.0...2.8.1
721730
[2.8.0]: https://github.com/thephpleague/commonmark/compare/2.7.1...2.8.0
722731
[2.7.1]: https://github.com/thephpleague/commonmark/compare/2.7.0...2.7.1
723732
[2.7.0]: https://github.com/thephpleague/commonmark/compare/2.6.2...2.7.0

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@
4242
"phpstan/phpstan": "^1.8.2",
4343
"phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0",
4444
"scrutinizer/ocular": "^1.8.1",
45-
"symfony/finder": "^5.3 | ^6.0 | ^7.0",
46-
"symfony/process": "^5.4 | ^6.0 | ^7.0",
47-
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
45+
"symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0",
46+
"symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0",
47+
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0",
4848
"unleashedtech/php-coding-standard": "^3.1.1",
4949
"vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
5050
},

docs/2.x/customization/rendering.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ redirect_from:
1212
- /2.5/customization/rendering/
1313
- /2.6/customization/rendering/
1414
- /2.7/customization/rendering/
15-
- /customization/block-rendering/
16-
- /customization/inline-rendering/
15+
- /customization/block-rendering/
16+
- /customization/inline-rendering/
1717
---
1818

1919
# Custom Rendering

src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): ?
4545
return $rendered;
4646
}
4747

48-
$regex = \sprintf('/<(\/?(?:%s)[ \/>])/i', \implode('|', \array_map('preg_quote', $tags)));
48+
$regex = \sprintf('/<(\/?(?:%s)[\s\/>])/i', \implode('|', \array_map('preg_quote', $tags)));
4949

5050
// Match these types of tags: <title> </title> <title x="sdf"> <title/> <title />
5151
return \preg_replace($regex, '&lt;$1', $rendered);

src/Util/UrlEncoder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static function unescapeAndEncode(string $uri): string
5656
}
5757
}
5858

59-
if (\ord($code) < 128) {
59+
if (\strlen($code) === 1 && \ord($code) < 128) {
6060
$result .= self::ENCODE_CACHE[\ord($code)];
6161
continue;
6262
}

tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ public static function dataProviderForTestWithDefaultSettings(): iterable
7171
yield ['<script>', '&lt;script>'];
7272
yield ['<plaintext>', '&lt;plaintext>'];
7373

74+
// Newline/whitespace bypass attempts (security fix)
75+
yield ['<script >', '&lt;script >'];
76+
yield ["<script\n>", "&lt;script\n>"];
77+
yield ["<script\t>", "&lt;script\t>"];
78+
yield ["<script\r\n>", "&lt;script\r\n>"];
79+
yield ["<iframe\nwidth=\"560\">", "&lt;iframe\nwidth=\"560\">"];
80+
81+
// Ensure non-disallowed tags with similar names are NOT filtered
82+
yield ['<scriptfoo>', '<scriptfoo>'];
83+
7484
// Tags not escaped by default
7585
yield ['<strong>', '<strong>'];
7686
}
@@ -107,6 +117,11 @@ public static function dataProviderForTestWithCustomSettings(): iterable
107117
yield ['<strong/>', '&lt;strong/>'];
108118
yield ['<strong />', '&lt;strong />'];
109119

120+
// Newline bypass with custom config
121+
yield ['<strong >', '&lt;strong >'];
122+
yield ["<strong\n>", "&lt;strong\n>"];
123+
yield ["<strong\t>", "&lt;strong\t>"];
124+
110125
// Defaults that I didn't include in my custom config
111126
yield ['<title>', '<title>'];
112127
yield ['<textarea>', '<textarea>'];

0 commit comments

Comments
 (0)