league/commonmark
Extensible PHP Markdown parser supporting the full CommonMark spec and GitHub-Flavored Markdown. Works with PHP 7.4+ (mbstring) and provides simple converters to turn Markdown into HTML with configurable safety options.
This is a security release to address a denial of service vulnerability in the AttributesExtension.
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)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)
\G anchors at the cursor, ^ anchors at the start of the line, and lookbehinds and \b see the characters actually preceding the cursor; this keeps scanning loops linear and enables left-context assertions that match() cannot expressRegexHelper::PARTIAL_LINK_TITLE_UNANCHORED and RegexHelper::PARTIAL_LINK_DESTINATION_BRACES, unanchored fragments so each call site can supply its own anchordefault_attributes configuration format which pairs the node attribute map with a new strict_callables option: ['default_attributes' => ['attributes' => [...], 'strict_callables' => true]]. With strict_callables enabled, only closures and invokable objects are treated as callbacks, so strings and arrays are always used as literal attribute values. Callbacks written as string or array callables can be wrapped with Closure::fromCallable(). The original format - passing the node map directly - is still accepted, and defaults strict_callables to false.slug_normalizer/reserved option which treats the given slugs as already-used, so colliding headings receive an incremental numeric suffix just like duplicate headings do (#1080)TableOfContents extension to render the table of contents once and share it across all placeholders instead of cloning it into each one (#1134)
TableOfContents node is no longer called once per placeholder, so it must return the same markup each time it is called for a given document (#1134)TableOfContents node for listeners which locate and reposition it (#1143)$environment->getConfiguration()->get('default_attributes') now returns the normalized structure with attributes and strict_callables keys instead of the node map; read default_attributes/attributes to get the map. Configuration written in either format continues to work unchanged.RegexHelper::PARTIAL_LINK_TITLE and RegexHelper::REGEX_LINK_DESTINATION_BRACES; use the unanchored variants with an explicit anchor insteaddefault_attributes strict_callables option, which will be removed in 3.0 when only closures and invokable objects will ever be treated as callbacks.default_attributes values which happen to match the name of a PHP function - such as 'class' => 'link', 'header', 'key', 'range', or 'current' - being invoked as callbacks, producing errors like link() expects exactly 2 arguments, 1 given. Enable strict_callables to treat strings and arrays as literal attribute values (#1123)DefaultAttributesExtension re-testing every configured value with is_callable() once per matching node, which asked the autoloader whether the first element of each array value named a real class every single timedefault_attributes value which PHP treats as callable reporting its failure from inside whichever function it collided with; the error now names the attribute and node class responsible, and keeps the original error as its previous exceptionUniqueSlugNormalizerInterface implementations being wrapped by the built-in UniqueSlugNormalizer and never receiving the documented clearHistory() calls, which caused slug history to leak across documents when slug_normalizer/unique was set to 'document' (#1080)
AttributesExtension re-merging and re-filtering everything a node had already collected each time another attribute node was applied to it, causing long runs of distinctly-named attributes to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-jjv6-8j6v-6j52, which covered only the class attribute (GHSA-8rr7-cvq3-gmfh)AttributesExtension re-merging everything an attribute block had already collected on each of its continuation lines, causing long runs of distinctly-named attributes on consecutive lines to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-8rr7-cvq3-gmfh)This release fixes a regression introduced in 2.9.0 which changed the behavior of Cursor::match() for certain regular expression patterns.
Cursor::advanceToNextNonSpaceOrNewline() to scan the line in place instead of copying everything left in the block on every callCursor::match() treated text before the cursor as part of the match subject (#1145). Patterns were matched against the whole line at an offset, which silently changed the meaning of \b, \B, \A, lookbehinds, a ^ anywhere other than the very start of the pattern, and a leading ^ combined with the m modifier. match() once again matches against the remainder, exactly as it did in 2.8; the core parsers keep the optimized in-place matching via a new internal method with PCRE's native offset semantics, anchoring their patterns at the cursor with \Garia-hidden="true" remaining in the keyboard tab order; they are now also given tabindex="-1", as a focusable element removed from the accessibility tree has no accessible name to announce when focused (WCAG 4.1.2)data with the node they were cloned from, so that setting an attribute on either one also set it on the otherThis is a security release to address multiple denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
[label] and [label][]) now apply the spec's 999-character link label limit when resolving the label, matching the limit already enforced when parsing reference definitions and when resolving the [text][label] form. A label longer than 999 characters which collapsed to a shorter, defined label once whitespace was normalized will no longer resolve; this matches cmark's behavior.{<FF>onclick="..."}) bypassing both the on* event handler filter and the allow_unsafe_links protection, as browsers treat that byte as whitespace and parse the name as a genuine onclick or href (GHSA-f8fg-pg57-v4j8)SmartPunctExtension recopying the whole preceding text node when replacing each unpaired quote, causing documents with many apostrophes to be processed in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52)AttributesExtension scanning the remaining siblings of every block-level attribute node, causing long runs of adjacent attribute blocks to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-g2gp-3wwq-f4ph, which covered only inline attributes (GHSA-jjv6-8j6v-6j52)AttributesExtension rebuilding the accumulated class list on every merge, causing long runs of .class attributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52)This is a security release to address five denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
NormalizeHeadingsExtension to constrain headings to a configured level range (#989)
normalize_headings/rebase_to_min_level - rebases each document so its headings begin at min_levelfootnote/enable_inline_footnotes config option to disable the inline ^[Footnote text] syntax (#1112)Cursor::getBytePosition() for obtaining the cursor's current byte offset within the linexml/max_indentation_level config option to control how far XmlRenderer indents nested elements (default: 16; set to 0 for unindented output)FootnoteExtension now uses only the first definition of a footnote label, removing any duplicate definitions instead of rendering them in placeNumberFootnotesListener now stores footnote backrefs under a single footnote/backrefs key in the document data instead of one key per footnote destinationCursor to translate character positions to byte offsets in constant time instead of re-decoding the line with mb_substr()Cursor::match() to match against the line at the cursor's byte offset instead of copying the remaining line on every callInlineParserEngine and UrlAutolinkParser to work with byte offsets directlyjava<TAB>script:), which allowed the allow_unsafe_links protection to be bypassed via href and src attributes (GHSA-29pj-957v-52mc).//-delimited key paths when storing backrefs, which allowed distinct labels such as [^a.b] and [^a/b] to share a single backref list (GHSA-jfm3-95jq-q3rf)[^a] and [^a.b]1 on every collision, causing headings or inline footnotes which normalize to the same slug to be de-duplicated in quadratic time, which could be abused to cause a denial of service (GHSA-mh25-x5hq-wrqp)AttributesExtension scanning the remaining siblings of an inline attribute which can only apply to its parent block, causing long runs of adjacent inline attributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-g2gp-3wwq-f4ph)XmlRenderer indenting every element by its full nesting depth without any upper bound, causing deeply-nested documents to render as quadratically-sized XML, which could be abused to cause a denial of service (GHSA-mj63-m3rc-8ppr)MarkDelimiterProcessor not being declared as a CacheableDelimiterProcessorInterface, preventing the delimiter stack from caching the opener search for == runs (#1133)Special thanks to @GrahamCampbell and @TungNGo02 for responsibly disclosing the security vulnerabilities and contributing to the fixes in this release.
Full Changelog: https://github.com/thephpleague/commonmark/compare/2.8.3...2.9.0
vbscript:, file:, or data: anywhere after the start (#1131)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.8.2...2.8.3
This is a security release to address an issue where the allowed_domains setting for the Embed extension can be bypassed, resulting in a possible SSRF and XSS vulnerabilities.
DomainFilteringAdapter hostname boundary bypass where domains like youtube.com.evil could match an allowlist entry for youtube.com (GHSA-hh8v-hgvp-g3f5)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.8.1...2.8.2
This is a security release to address an issue where DisallowedRawHtml can be bypassed, resulting in a possible cross-site scripting (XSS) vulnerability.
DisallowedRawHtmlRenderer not blocking raw HTML tags with trailing ASCII whitespace (GHSA-4v6x-c7xx-hw9f)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.8.0...2.8.1
HighlightExtension for marking important text using == syntax (#1100)AutolinkExtension incorrectly matching URLs after invalid www. prefix (#1095, #1103)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.7.1...2.8.0
RegexHelper to improve performance (#674, #1086)EmbedProcessor no longer calls updateEmbeds() when there are no embeds to update (#1081)benchmark.php CSV path validation for non-existent files (#1068, #1085)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.7.0...2.7.1
This is a security release to address a potential cross-site scripting (XSS) vulnerability when using the AttributesExtension with untrusted user input.
attributes/allow config option to specify which attributes users are allowed to set on elements (default allows virtually all attributes)AttributesExtension blocks all attributes starting with on unless explicitly allowed via the attributes/allow config optionallow_unsafe_links option is now respected by the AttributesExtension when users specify href and src attributesFull Changelog: https://github.com/thephpleague/commonmark/compare/2.6.1...2.6.2
Full Changelog: https://github.com/thephpleague/commonmark/compare/2.6.0...2.6.1
This is a security release to address potential denial of service attacks when parsing specially crafted, malicious input from untrusted sources (like user input). See https://github.com/thephpleague/commonmark/security/advisories/GHSA-c2pc-g5qf-rfrf for more details.
max_delimiters_per_line config option to prevent denial of service attacks when parsing malicious inputtable/max_autocompleted_cells config option to prevent denial of service attacks when parsing large tablesAttributesExtension now supports attributes without values (#985, #986)AutolinkExtension exposes two new configuration options to override the default behavior (#969, #987):
autolink/allowed_protocols - an array of protocols to allow autolinking forautolink/default_protocol - the default protocol to use when none is specifiedRegexHelper::isWhitespace() method to check if a given character is an ASCII whitespace characterCacheableDelimiterProcessorInterface to ensure linear complexity for dynamic delimiter processingBracket delimiter type to optimize bracket parsing[ and ] are no longer added as Delimiter objects on the stack; a new Bracket type with its own stack is used insteadUrlAutolinkParser no longer parses URLs with more than 127 subdomainsDelimiterInterface::getIndex()
DelimiterStack now accepts integer positions for any $stackBottom argumentsource, add search to list of recognized block tagsFull Changelog: https://github.com/thephpleague/commonmark/compare/2.5.2...2.5.3
true value (#1040)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.5.1...2.5.2
Table start line numbers (#1037)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.5.0...2.5.1
AttributesExtension now supports attributes without values (#985, #986)AutolinkExtension exposes two new configuration options to override the default behavior (#969, #987):
autolink/allowed_protocols - an array of protocols to allow autolinking forautolink/default_protocol - the default protocol to use when none is specifiedParagraphs only containing link reference definitions will be kept in the AST until the Document is finalized
Paragraph)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.4.4...2.5.0
Full Changelog: https://github.com/thephpleague/commonmark/compare/2.4.3...2.4.4
UrlAutolinkParser incorrectly parsing text containing www anywhere before an autolink (#1025)Full Changelog: https://github.com/thephpleague/commonmark/compare/2.4.2...2.4.3
FencedCodeRenderer: don't add language- to class if already prefixedFull Changelog: https://github.com/thephpleague/commonmark/compare/2.4.1...2.4.2
ExternalLinkProcessor not fully disabling the rel attribute when configured to do so (#992)See the upgrading guide for more information about the exception-related changes
CommonMarkException marker interface for all exceptions thrown by the libraryAlreadyInitializedExceptionInvalidArgumentExceptionIOExceptionLogicExceptionMissingDependencyExceptionNoMatchingRendererExceptionParserLogicExceptionheading_permalink/apply_id_to_heading - When true, the id attribute will be applied to the heading element itself instead of the <a> tagheading_permalink/heading_class - class to apply to the heading elementheading_permalink/insert - now accepts none to prevent the creation of the <a> linktable/alignment_attributes configuration option to control how table cell alignment is rendered (#959)RuntimeException to LogicException (or something extending it), including:
CallbackGenerators that fail to set a URL or return an expected valueMarkdownParser when deactivating the last block parser or attempting to get an active block parser when they've all been closedEnvironmentNode when no renderer has been registered for itHeadingPermalinkProcessor now throws InvalidConfigurationException instead of RuntimeException when invalid config values are given.HtmlElement::setAttribute() no longer requires the second parameter for boolean attributes[@throws](https://github.com/throws) docblocks throughout the codebase, including ConverterInterface, MarkdownConverter, and MarkdownConverterInterface.
\RuntimeExceptions were thrown, which was inaccurate as \LogicExceptions were also possible.mb_internal_encoding() is set to something other than UTF-8 (#951)TaskListItemMarkerRenderer not including HTML attributes set on the node by other extensions (#947).) (#943)InlineParserEngine when no inline parsers are registered in the Environment (#908)EmbedStartParser to always capture embed-like lines in container blocks, regardless of parent block typeEmbedProcessor to also remove Embed blocks that aren't direct children of the DocumentEmbedProcessor to 1010EmbedExtension not parsing embeds following a list block (#898)DomainFilteringAdapter not reindexing the embed list (#884, #885)EmbedExtension (#805)DocumentRendererInterface as a replacement for the now-deprecated MarkdownRendererInterfaceMarkdownRendererInterface; use DocumentRendererInterface insteadHow can I help you explore Laravel packages today?