Skip to content

Fix IndexError in html_block/heading terminator rules on table-in-blockquote at EOF - #416

Open
saket3395 wants to merge 1 commit into
executablebooks:masterfrom
saket3395:fix/gh-415-table-blockquote-eof-indexerror
Open

Fix IndexError in html_block/heading terminator rules on table-in-blockquote at EOF#416
saket3395 wants to merge 1 commit into
executablebooks:masterfrom
saket3395:fix/gh-415-table-blockquote-eof-indexerror

Conversation

@saket3395

Copy link
Copy Markdown

Summary

Fixes #415. Input that ends on a blockquote marker while a table is open inside that quote raises IndexError: string index out of range:

from markdown_it import MarkdownIt

# html_block.py — raises IndexError
MarkdownIt().enable("table").parse("> | a | b |\n> |---|---|\n>")

# heading.py — same input, raises once html_block is out of the way
MarkdownIt("commonmark", {"html": False}).enable("table").parse("> | a | b |\n> |---|---|\n>")

Root cause

With table enabled, html_block and heading run as terminator rules on the empty final line that a trailing > produces. There, pos = state.bMarks[startLine] + state.tShift[startLine] equals len(state.src), and both rules index state.src[pos] without guarding that boundary:

  • html_block.py: if state.src[pos] != "<":
  • heading.py: ch = state.src[pos] — its pos >= maximum check is on the next line, after the index

In markdown-it (JS) the equivalent state.src.charCodeAt(pos) returns NaN out of range instead of raising — the port hazard tracked in #190.

Fix

Sibling terminator rules already defend against exactly this — hr.py and blockquote.py wrap the same index access in try: ... except IndexError: return False (added for #185 / #204). html_block.py and heading.py were missed. This applies the same established guard to both.

html_block runs before heading, so it shadows it (disabling html moves the traceback to heading rather than fixing it) — hence both rules need the guard, and there is a regression test for each path.

Tests

Added two regression tests in tests/test_fuzzer.py (the existing home for crash-regression cases), one per crash path — asserting the input renders without raising.

Note on local verification

I verified the guard logic in isolation (the old expression raises IndexError when pos == len(src); the guarded version returns False, i.e. the rule declines, with identical behavior for in-range characters — matching how hr.py/blockquote.py already behave). I was not able to run the full pytest suite locally (my environment's Python predates this package's minimum), so I'd appreciate CI confirming the two new tests pass and that the rendered output for that input is sensible. Happy to adjust the tests to assert exact rendered HTML if you'd prefer that over "does not raise".

…blebooksgh-415)

Input that ends on a blockquote marker while a table is open inside
that quote (e.g. "> | a | b |\n> |---|---|\n>") raised
IndexError: string index out of range.

The table rule runs html_block and heading as terminator rules on the
empty final line a trailing '>' produces, where
pos = bMarks[startLine] + tShift[startLine] equals len(state.src).
Both rules index state.src[pos] without guarding that boundary:
html_block at 'if state.src[pos] != "<"' and heading at
'ch = state.src[pos]' (its pos >= maximum check runs on the next line,
after the index). In markdown-it (JS) the equivalent charCodeAt(pos)
returns NaN out of range instead of raising -- the port hazard from
executablebooksGH-190.

Sibling terminator rules already defend against exactly this: hr.py
and blockquote.py wrap the same index in try/except IndexError ->
return False (added for executablebooksGH-185 / executablebooksGH-204). This applies the same guard
to the two rules that were missed.

Adds regression tests for both crash paths (html_block runs first with
html enabled; heading is reached when html is disabled).
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.

IndexError: table inside a blockquote at end of input (html_block.py, heading.py)

1 participant