Skip to content

fix(render): keep code blocks safe inside list containers - #64

Merged
chaliy merged 3 commits into
mainfrom
fix/fenced-code-container-safety
Oct 31, 2026
Merged

chaliy merged 3 commits into
mainfrom
fix/fenced-code-container-safety

Conversation

@chaliy

@chaliy chaliy commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Keep code blocks inside their intended containers when rendering task lists and definitions. Task bodies start on the line below their checkbox. Definitions render as a term paragraph followed by ordinary GFM list items, preserving nested blocks without depending on a definition-list extension. The definition IR and input parser remain unchanged. Nested lists with an empty rendered first item get a blank line before them; ordinary tight lists keep their formatting. Hard breaks inside headings, link labels, tables and block nodes flattened into inline content become spaces so they cannot close their enclosing Markdown syntax. Redundant nested emphasis wrappers render once, and bracketed nodes cannot combine with following text into unintended links.

Why

The existing nightly Fuzz run found raw HTML escaping from render_ir and markdown. A checkbox or nested : marker consumed the opening fence as paragraph text. Raw code and language-label content could then become HTML in a downstream GFM renderer. Empty leading paragraphs also bypass a fix based only on the first IR node's type.

Before / After

  • Before: the new task regression emits 10. [x] ```html on one line; the GFM oracle observes raw <script>alert(1)</script> instead of code. The exact Markdown crash emits raw <z> from a nested definition's code label.
  • After: all original crash artifacts replay successfully. 51 output-safety tests and 119 HTML integration tests pass; regressions cover empty leading paragraphs, ordered/unordered and checked/unchecked tasks, nested containers, literal code content, definition grouping with the extension enabled or disabled, hard breaks in single-line containers, repeated emphasis wrappers, and cross-node link adjacency.
  • CLI smoke output from HTML definitions and task code blocks:
Example

- ```html
  <script>alert(1)</script>
  ```

- [x]
  ```html
  <script>alert(1)</script>
  ```
  • just pre-pr: passed (format, Clippy, workspace tests, knowledge validation, script tests, examples, cargo-deny).
  • Local fuzz: render_ir 362,192 inputs / 61s; markdown 411,425 inputs / 61s, no crashes. Exact nightly inputs retained as seeds.
  • The first full 300s/target run passed 10 targets and found another render_ir case: an empty first ordered item cannot interrupt a paragraph. Added a failing regression, minimized seed and output-budget test, then fixed the separator using the rendered first item (rendered once within the remaining budget). Both full and minimized crash inputs now replay successfully; just pre-pr passed again.
  • The second full 300s/target run found a separate render_ir case: a hard break inside a link label nested in a blockquote heading closed both inline containers before later code, exposing raw HTML. Added the minimized seed and regression, then flattened hard breaks in all single-line inline containers. The original and minimized artifacts replay successfully; just pre-pr and a fresh 445,222-input / 61s local render_ir fuzz run pass.
  • The third full 300s/target run found redundant nested bold delimiters closing the outer span before a block node. A fresh local run then found a footnote reference and following ( combining into an inline link that consumed later delimiters. Both minimized seeds and regressions are retained. Redundant wrappers are omitted and cross-node link adjacency is escaped; all artifacts replay successfully, just pre-pr passes, and a fresh 500,715-input / 61s local render_ir fuzz run is clean.
  • The full GitHub Fuzz workflow is rerun on the updated branch before merge and on main afterward.

Risk

Medium: Markdown formatting changes for task and definition lists, hard breaks inside single-line containers now render as spaces, redundant nested emphasis wrappers render once, and a ( after a bracketed node may be escaped. Definitions now use standard list grouping instead of : syntax, so re-parsing the rendered Markdown yields ordinary lists. Text and code content remain intact, and direct parsing of definition-list source still produces the same IR. Knowledge, user documentation and changelog describe the output change. No resource limits or security assertions are relaxed.

Checklist

  • Tests added or updated
  • Backward compatibility considered

@chaliy
chaliy merged commit ce5b095 into main Oct 31, 2026
24 of 25 checks passed
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.

1 participant