1
0
mirror of https://github.com/erusev/parsedown.git synced 2023-08-10 21:13:06 +03:00

Limit recursion depth by configurable

Fixes https://github.com/erusev/parsedown/issues/681
This commit is contained in:
Aidan Woods 2019-04-07 17:34:40 +01:00
parent b9b75dbcea
commit 9eb6a02334
No known key found for this signature in database
GPG Key ID: 9A6A8EFAA512BBB9
3 changed files with 122 additions and 0 deletions

View File

@ -0,0 +1,51 @@
<?php
namespace Erusev\Parsedown\Configurables;
use Erusev\Parsedown\Configurable;
final class RecursionLimiter implements Configurable
{
/** @var int */
private $maxDepth;
/** @var int */
private $currentDepth;
/**
* @param int $maxDepth
* @param int $currentDepth
*/
private function __construct($maxDepth, $currentDepth)
{
$this->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);
}
}

View File

@ -13,6 +13,7 @@ use Erusev\Parsedown\Components\Inlines\PlainText;
use Erusev\Parsedown\Components\StateUpdatingBlock; use Erusev\Parsedown\Components\StateUpdatingBlock;
use Erusev\Parsedown\Configurables\BlockTypes; use Erusev\Parsedown\Configurables\BlockTypes;
use Erusev\Parsedown\Configurables\InlineTypes; use Erusev\Parsedown\Configurables\InlineTypes;
use Erusev\Parsedown\Configurables\RecursionLimiter;
use Erusev\Parsedown\Html\Renderable; use Erusev\Parsedown\Html\Renderable;
use Erusev\Parsedown\Html\Renderables\Text; use Erusev\Parsedown\Html\Renderables\Text;
use Erusev\Parsedown\Parsing\Excerpt; use Erusev\Parsedown\Parsing\Excerpt;
@ -82,6 +83,14 @@ final class Parsedown
*/ */
public static function blocks(Lines $Lines, State $State) 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[] */ /** @var Block[] */
$Blocks = []; $Blocks = [];
/** @var Block|null */ /** @var Block|null */
@ -180,6 +189,14 @@ final class Parsedown
# standardize line breaks # standardize line breaks
$text = \str_replace(["\r\n", "\r"], "\n", $text); $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[] */ /** @var Inline[] */
$Inlines = []; $Inlines = [];

View File

@ -0,0 +1,54 @@
<?php
namespace Erusev\Parsedown\Tests\Configurables;
use Erusev\Parsedown\Configurables\RecursionLimiter;
use Erusev\Parsedown\Parsedown;
use Erusev\Parsedown\State;
use PHPUnit\Framework\TestCase;
final class RecursionLimiterTest extends TestCase
{
/**
* @return void
* @throws \PHPUnit\Framework\ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*/
public function testDepthLimit()
{
$State = new State([RecursionLimiter::maxDepth(3)]);
$Parsedown = new Parsedown($State);
$borderline = '>>> foo';
$exceeded = '>>>> foo';
$exceededByInline = '>>> fo*o*';
$this->assertSame(
(
\str_repeat("<blockquote>\n", 3)
. '<p>foo</p>'
. \str_repeat("\n</blockquote>", 3)
),
$Parsedown->text($borderline)
);
$this->assertSame(
(
\str_repeat("<blockquote>\n", 3)
. '<p>&gt; foo</p>'
. \str_repeat("\n</blockquote>", 3)
),
$Parsedown->text($exceeded)
);
$this->assertSame(
(
\str_repeat("<blockquote>\n", 3)
. '<p>fo*o*</p>'
. \str_repeat("\n</blockquote>", 3)
),
$Parsedown->text($exceededByInline)
);
}
}