diff --git a/src/Configurables/RecursionLimiter.php b/src/Configurables/RecursionLimiter.php new file mode 100644 index 0000000..56f40a9 --- /dev/null +++ b/src/Configurables/RecursionLimiter.php @@ -0,0 +1,51 @@ +maxDepth = $maxDepth; + $this->currentDepth = $currentDepth; + } + + /** @return self */ + public static function initial() + { + return self::maxDepth(256); + } + + /** + * @param int $maxDepth + * @return self + */ + public static function maxDepth($maxDepth) + { + return new self($maxDepth, 0); + } + + /** @return self */ + public function increment() + { + return new self($this->maxDepth, $this->currentDepth + 1); + } + + /** @return bool */ + public function isDepthExceeded() + { + return ($this->maxDepth < $this->currentDepth); + } +} diff --git a/src/Parsedown.php b/src/Parsedown.php index 8454edc..36effc9 100644 --- a/src/Parsedown.php +++ b/src/Parsedown.php @@ -13,6 +13,7 @@ use Erusev\Parsedown\Components\Inlines\PlainText; use Erusev\Parsedown\Components\StateUpdatingBlock; use Erusev\Parsedown\Configurables\BlockTypes; use Erusev\Parsedown\Configurables\InlineTypes; +use Erusev\Parsedown\Configurables\RecursionLimiter; use Erusev\Parsedown\Html\Renderable; use Erusev\Parsedown\Html\Renderables\Text; use Erusev\Parsedown\Parsing\Excerpt; @@ -82,6 +83,14 @@ final class Parsedown */ public static function blocks(Lines $Lines, State $State) { + $RecursionLimiter = $State->get(RecursionLimiter::class)->increment(); + + if ($RecursionLimiter->isDepthExceeded()) { + $State = $State->setting(new BlockTypes([], [])); + } + + $State = $State->setting($RecursionLimiter); + /** @var Block[] */ $Blocks = []; /** @var Block|null */ @@ -180,6 +189,14 @@ final class Parsedown # standardize line breaks $text = \str_replace(["\r\n", "\r"], "\n", $text); + $RecursionLimiter = $State->get(RecursionLimiter::class)->increment(); + + if ($RecursionLimiter->isDepthExceeded()) { + return [Plaintext::build(new Excerpt($text, 0), $State)]; + } + + $State = $State->setting($RecursionLimiter); + /** @var Inline[] */ $Inlines = []; diff --git a/tests/src/Configurables/RecursionLimiterTest.php b/tests/src/Configurables/RecursionLimiterTest.php new file mode 100644 index 0000000..33e8554 --- /dev/null +++ b/tests/src/Configurables/RecursionLimiterTest.php @@ -0,0 +1,54 @@ +>> foo'; + $exceeded = '>>>> foo'; + $exceededByInline = '>>> fo*o*'; + + $this->assertSame( + ( + \str_repeat("
\n", 3) + . '

foo

' + . \str_repeat("\n
", 3) + ), + $Parsedown->text($borderline) + ); + + $this->assertSame( + ( + \str_repeat("
\n", 3) + . '

> foo

' + . \str_repeat("\n
", 3) + ), + $Parsedown->text($exceeded) + ); + + $this->assertSame( + ( + \str_repeat("
\n", 3) + . '

fo*o*

' + . \str_repeat("\n
", 3) + ), + $Parsedown->text($exceededByInline) + ); + } +}