Skip to content

Limit the nesting depth of blockquotes - #408

Open
codeling wants to merge 3 commits into
michelf:libfrom
codeling:claude/limit-blockquote-depth
Open

codeling wants to merge 3 commits into
michelf:libfrom
codeling:claude/limit-blockquote-depth

Conversation

@codeling

@codeling codeling commented Oct 25, 2026 •

Copy link
Copy Markdown

Each level of blockquote nesting re-processes and re-indents everything inside it, so memory use grows with the square of the depth: a text of 1000 ">" characters (1 KB) needs more than 512 MB. Any application which renders Markdown from untrusted users can be made to run out of memory.

The new option $max_blockquote_depth (default 32, 0 for no limit) stops looking for nested blockquotes at that depth; deeper ">" markers are output as text.

Disclaimer

Report and fix generated by Claude Code (Opus 5.5 High)

Each level of blockquote nesting re-processes and re-indents everything
inside it, so memory use grows with the square of the depth: a text of
1000 ">" characters (1 KB) needs more than 512 MB. Any application which
renders Markdown from untrusted users can be made to run out of memory.

The new option $max_blockquote_depth (default 32, 0 for no limit) stops
looking for nested blockquotes at that depth; deeper ">" markers are
output as text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqKnhw6Y9HUoKubsfRNp1C
The CI job runs `phpstan analyse Michelf/ --level=5` with an unpinned
`phpstan/phpstan` (>=1.0), so it now picks up 2.x, which reports seven
errors in code that was already on `lib`:

- `@copyright (Original Markdown) ...` in the three file headers: a tag
  value starting with "(" is parsed as annotation arguments and fails.
  Drop the parentheses.
- Markdown::_doLists_callback(): `$list_type == 'ol'` is always true
  where it is tested, because `$ol_start > 1` is only reachable for
  ordered lists. Remove the redundant comparison.
- Markdown::handleSpanToken(): pass the regex delimiter to preg_quote().
  The token is only backticks, so the pattern is unchanged.
- MarkdownExtra::doExtraAttributes(): document `$attr` as `string|null`;
  callers pass possibly unset matches and the body already handles null.
- MarkdownExtra::parseFootnotePlaceholders(): cast the numbers to string
  for str_replace().

No change in output: the 60 bundled fixtures and a ~4,000 case corpus
render identically with both parsers before and after.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TVvenPEq6D9BgMuwN7Cam
These are pre-existing errors on `lib` unrelated to this pull request.
They are fixed separately in their own pull request, so this one stays
focused on its own change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TVvenPEq6D9BgMuwN7Cam
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants