Skip to content

Commit 0ae7228

Browse files
colinodellclaude
andcommitted
Prepare for 2.10.0 release
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent f27eb72 commit 0ae7228

4 files changed

Lines changed: 73 additions & 2 deletions

File tree

CHANGELOG.md

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

77
## [Unreleased][unreleased]
88

9+
## [2.10.0] - 2026-08-11
10+
11+
This is a **security release** to address a denial of service vulnerability in the `AttributesExtension`.
12+
913
### Added
1014
- Added a new `table_of_contents/max_placeholder_entries` option to limit how many table of contents entries a document may render across all of its placeholders (#1134)
1115
- Added `Cursor::matchInPlace()`, which matches a regular expression at the cursor's position within the line using PCRE's native offset semantics instead of copying the remainder (#1145)
@@ -830,7 +834,8 @@ No changes were introduced since the previous release.
830834
- Alternative 1: Use `CommonMarkConverter` or `GithubFlavoredMarkdownConverter` if you don't need to customize the environment
831835
- Alternative 2: Instantiate a new `Environment` and add the necessary extensions yourself
832836

833-
[unreleased]: https://github.com/thephpleague/commonmark/compare/2.9.2...HEAD
837+
[unreleased]: https://github.com/thephpleague/commonmark/compare/2.10.0...HEAD
838+
[2.10.0]: https://github.com/thephpleague/commonmark/compare/2.9.2...2.10.0
834839
[2.9.2]: https://github.com/thephpleague/commonmark/compare/2.9.1...2.9.2
835840
[2.9.1]: https://github.com/thephpleague/commonmark/compare/2.9.0...2.9.1
836841
[2.9.0]: https://github.com/thephpleague/commonmark/compare/2.8.3...2.9.0

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
},
117117
"extra": {
118118
"branch-alias": {
119-
"dev-main": "2.10-dev"
119+
"dev-main": "2.11-dev"
120120
}
121121
},
122122
"config": {

docs/2.x/security.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ The `xml/max_indentation_level` option (default: `16`) caps how far elements are
128128

129129
Note that `max_nesting_level` is *not* sufficient on its own here, as it only constrains how deeply **blocks** may nest - deeply-nested inlines (like `_______...`) can still produce a deep tree.
130130

131+
## Table of Contents Placeholders
132+
133+
This only applies if you're using the [Table of Contents extension](/2.x/extensions/table-of-contents/) with `'position' => 'placeholder'`.
134+
135+
In placeholder mode every placeholder renders its own copy of the table of contents, so a document containing many headings *and* many placeholders repeats that whole list at each one. A small input can therefore produce output many times its size.
136+
137+
The `table_of_contents/max_placeholder_entries` option bounds the total number of entries rendered across all of a document's placeholders; any placeholder beyond that budget is left as-is. It defaults to `null` (no limit) for backward compatibility, so setting an `int` is recommended when rendering untrusted input.
138+
131139
## Additional Filtering
132140

133141
Although this library does offer these security features out-of-the-box, some users may opt to also run the HTML output through additional filtering layers (like HTMLPurifier). If you do this, make sure you **thoroughly** test your additional post-processing steps and configure them to work properly with the types of HTML elements and attributes that converted Markdown might produce, otherwise, you may end up with weird behavior like missing images, broken links, mismatched HTML tags, etc.

docs/2.x/upgrading.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,64 @@ redirect_from:
1616

1717
# Upgrading to Newer Versions
1818

19+
## Upgrading from 2.9 to 2.10
20+
21+
There are no breaking API changes when upgrading from 2.9 to 2.10, but two constants were deprecated and a few
22+
behaviors changed for integrations which hook into the affected extensions.
23+
24+
### The Table of Contents Is Rendered Once Per Document
25+
26+
In `placeholder` mode the table of contents is now rendered a single time and that one result is shared across every
27+
placeholder, instead of the whole subtree being cloned into each of them. A custom renderer registered for the
28+
`TableOfContents` node is therefore no longer called once per placeholder, and must return the same markup each time it
29+
is called for a given document. The first placeholder still receives the `TableOfContents` node itself, so listeners
30+
which locate and reposition that node continue to work.
31+
32+
### Recommended: Bound Table of Contents Placeholders
33+
34+
Every placeholder legitimately renders its own copy of the table of contents, so a document with many headings and many
35+
placeholders can produce output far larger than its input. If you're parsing untrusted input in `placeholder` mode, we
36+
now recommend setting the new `table_of_contents/max_placeholder_entries` option, which bounds the total number of
37+
entries rendered across all of a document's placeholders; placeholders beyond that budget are left as-is. It defaults
38+
to `null` (unlimited) for backward compatibility.
39+
40+
### `default_attributes` Accepts a New Configuration Shape
41+
42+
`default_attributes` still accepts the node attribute map on its own, and now also accepts that map paired with a new
43+
`strict_callables` option:
44+
45+
```php
46+
$config = [
47+
'default_attributes' => [
48+
'attributes' => [Paragraph::class => ['class' => 'text']],
49+
'strict_callables' => true,
50+
],
51+
];
52+
```
53+
54+
With `strict_callables` enabled only closures and invokable objects are treated as callbacks, so strings and arrays are
55+
always used as literal attribute values. This keeps a value which happens to share its name with a PHP function - such
56+
as `'class' => 'link'`, `'header'`, `'key'`, `'range'`, or `'current'` - from being invoked instead of used. A callback
57+
written as a string or array callable can be wrapped with `Closure::fromCallable()`.
58+
59+
Reading the option back has changed shape to match: `$environment->getConfiguration()->get('default_attributes')` now
60+
returns the normalized structure, so read `default_attributes/attributes` to get the node map. The `strict_callables`
61+
option is itself deprecated, and will be removed in 3.0 when only closures and invokable objects will ever be treated
62+
as callbacks.
63+
64+
### Custom Unique Slug Normalizers Are No Longer Wrapped
65+
66+
A custom `UniqueSlugNormalizerInterface` implementation is now used directly rather than being wrapped by the built-in
67+
`UniqueSlugNormalizer`. Such implementations now receive the `clearHistory()` calls the interface documents - without
68+
which slug history leaked between documents when `slug_normalizer/unique` was set to `'document'` - and are trusted to
69+
enforce uniqueness themselves, so the extra deduplication the wrapper applied on top of them is no longer performed.
70+
71+
### Deprecated: Anchored `RegexHelper` Constants
72+
73+
`RegexHelper::PARTIAL_LINK_TITLE` and `RegexHelper::REGEX_LINK_DESTINATION_BRACES` are deprecated. Use the new
74+
unanchored `RegexHelper::PARTIAL_LINK_TITLE_UNANCHORED` and `RegexHelper::PARTIAL_LINK_DESTINATION_BRACES` fragments
75+
with an anchor of your own choosing instead.
76+
1977
## Upgrading from 2.8 to 2.9
2078

2179
There are no breaking API changes when upgrading from 2.8 to 2.9, but several security fixes change the output of

0 commit comments

Comments
 (0)