Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit540d850

Browse files
committed
Fix quadratic complexity parsing long backtick code spans with no matching closers
1 parente1cfa8d commit540d850

File tree

2 files changed

+68
-5
lines changed

2 files changed

+68
-5
lines changed

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi
110110
- Fixed quadratic complexity parsing emphasis and strikethrough delimiters
111111
- Fixed issue where having 500,000+ delimiters could trigger a[known segmentation fault issue in PHP's garbage collection](https://bugs.php.net/bug.php?id=68606)
112112
- Fixed quadratic complexity deactivating link openers
113+
- Fixed quadratic complexity parsing long backtick code spans with no matching closers
113114
- Fixed catastrophic backtracking when parsing link labels/titles
114115

115116
##[2.4.1] - 2023-08-30

‎src/Extension/CommonMark/Parser/Inline/BacktickParser.php‎

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,27 @@
1818

1919
useLeague\CommonMark\Extension\CommonMark\Node\Inline\Code;
2020
useLeague\CommonMark\Node\Inline\Text;
21+
useLeague\CommonMark\Parser\Cursor;
2122
useLeague\CommonMark\Parser\Inline\InlineParserInterface;
2223
useLeague\CommonMark\Parser\Inline\InlineParserMatch;
2324
useLeague\CommonMark\Parser\InlineParserContext;
2425

2526
finalclass BacktickParserimplements InlineParserInterface
2627
{
28+
/**
29+
* Max bound for backtick code span delimiters.
30+
*
31+
* @see https://github.com/commonmark/cmark/commit/8ed5c9d
32+
*/
33+
privateconstMAX_BACKTICKS =1000;
34+
35+
/** @var \WeakReference<Cursor>|null */
36+
private ?\WeakReference$lastCursor =null;
37+
privatebool$lastCursorScanned =false;
38+
39+
/** @var array<int, int> backtick count => position of known ender */
40+
privatearray$seenBackticks = [];
41+
2742
publicfunctiongetMatchDefinition():InlineParserMatch
2843
{
2944
return InlineParserMatch::regex('`+');
@@ -38,11 +53,7 @@ public function parse(InlineParserContext $inlineContext): bool
3853
$currentPosition =$cursor->getPosition();
3954
$previousState =$cursor->saveState();
4055

41-
while ($matchingTicks =$cursor->match('/`+/m')) {
42-
if ($matchingTicks !==$ticks) {
43-
continue;
44-
}
45-
56+
if ($this->findMatchingTicks(\strlen($ticks),$cursor)) {
4657
$code =$cursor->getSubstring($currentPosition,$cursor->getPosition() -$currentPosition -\strlen($ticks));
4758

4859
$c =\preg_replace('/\n/m','',$code) ??'';
@@ -67,4 +78,55 @@ public function parse(InlineParserContext $inlineContext): bool
6778

6879
returntrue;
6980
}
81+
82+
/**
83+
* Locates the matching closer for a backtick code span.
84+
*
85+
* Leverages some caching to avoid traversing the same cursor multiple times when
86+
* we've already seen all the potential backtick closers.
87+
*
88+
* @see https://github.com/commonmark/cmark/commit/8ed5c9d
89+
*
90+
* @param int $openTickLength Number of backticks in the opening sequence
91+
* @param Cursor $cursor Cursor to scan
92+
*
93+
* @return bool True if a matching closer was found, false otherwise
94+
*/
95+
privatefunctionfindMatchingTicks(int$openTickLength,Cursor$cursor):bool
96+
{
97+
// Reset the seenBackticks cache if this is a new cursor
98+
if ($this->lastCursor ===null ||$this->lastCursor->get() !==$cursor) {
99+
$this->seenBackticks = [];
100+
$this->lastCursor = \WeakReference::create($cursor);
101+
$this->lastCursorScanned =false;
102+
}
103+
104+
if ($openTickLength >self::MAX_BACKTICKS) {
105+
returnfalse;
106+
}
107+
108+
// Return if we already know there's no closer
109+
if ($this->lastCursorScanned &&isset($this->seenBackticks[$openTickLength]) &&$this->seenBackticks[$openTickLength] <=$cursor->getPosition()) {
110+
returnfalse;
111+
}
112+
113+
while ($ticks =$cursor->match('/`{1,' .self::MAX_BACKTICKS .'}/m')) {
114+
$numTicks =\strlen($ticks);
115+
116+
// Did we find the closer?
117+
if ($numTicks ===$openTickLength) {
118+
returntrue;
119+
}
120+
121+
// Store position of closer
122+
if ($numTicks <=self::MAX_BACKTICKS) {
123+
$this->seenBackticks[$numTicks] =$cursor->getPosition() -$numTicks;
124+
}
125+
}
126+
127+
// Got through whole input without finding closer
128+
$this->lastCursorScanned =true;
129+
130+
returnfalse;
131+
}
70132
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp