Skip to content

fix(id-web): validate block/inline placement in prosemirror_to_markdown (#1043) - #1089

Merged
dezren39 merged 2 commits into
mainfrom
queue/1232-1043-placement-validation
Oct 27, 2026
Merged

dezren39 merged 2 commits into
mainfrom
queue/1232-1043-placement-validation

Conversation

@dezren39

@dezren39 dezren39 commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Fixes #1043 · Queue task 1232 (decision from triage task 1229)

Problem

json_to_ast placed any known PM node type anywhere. A misplaced node — text/image/hard_break at doc level, a block inside a paragraph — produced an ill-formed comrak AST: the commonmark formatter panics in debug builds (comrak cm.rs InvalidChildType validation) and silently emits invalid markdown in release builds (validation is debug_assertions-gated). All five shapes were reproduced against main in triage.

Change (codec only, pkgs/id/src/web/markdown.rs)

  • New validate_placement walk over the PM document, run before json_to_ast: doc and block containers (blockquote, list_item, task_list_item) take blocks only; paragraph/heading take inline only; lists enforce item types; table → table_row → cells; code_block takes text only.
  • Violations return the new ConversionError::MisplacedNode { parent, child }. The save path already propagates ConversionError as a save failure (convert_doc_to_bytes, routes.rs:1986) — no caller changes.
  • Deliberately permissive where the codec is: table cells and footnote_definition accept block or inline children (cell paragraph-unwrapping; id(web): footnotes are backslash-escaped on save — [^1] syntax mangled, definition orphaned #945 inline re-wrap); unknown node types fall through to the existing UnknownType error, never masked by validation.

Tests

New test_misplaced_nodes_return_error_not_panic: the five verified panic shapes (text/image/hard_break at top level, paragraph-in-paragraph, heading-in-paragraph) plus inline-in-blockquote, paragraph-directly-in-table, and list_item-in-task_list — all must return MisplacedNode; unknown-type precedence and a well-formed control asserted.

Verified with the verbatim-codec probe crate (sandbox in-crate link OOMs, per prior tasks): suite 71/71, all five shapes now Err, controls unchanged. Full CI is the remaining gate.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

prosemirror_to_markdown placed any known PM node type anywhere, so a
misplaced node (text/image/hard_break at doc level, a block inside a
paragraph) built an ill-formed comrak AST: the formatter panics in
debug builds and silently emits invalid markdown in release builds.

Add a PM-level placement walk (validate_placement) ahead of
json_to_ast: doc and block containers take blocks only, paragraph /
heading take inline only, lists/tables enforce their item/row/cell
structure, code_block takes text only. Violations return the new
ConversionError::MisplacedNode { parent, child } — the save path
already propagates ConversionError as a save failure. Unknown types
still fall through to UnknownType; footnote definitions and table
cells accept both shapes they legitimately carry (#945 / unwrapping).

Tests: test_misplaced_nodes_return_error_not_panic covers the five
verified panic shapes plus blockquote, table, and task_list cases,
unknown-type precedence, and a well-formed control. Verified with the
verbatim-codec probe crate: suite 71/71, all five shapes now Err.
@dezren39
dezren39 requested a review from a team as a code owner October 27, 2026 03:15
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 3 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 13cf7d15-814b-41ac-a0d4-0d83d6e8ee2a
Commits

Reviewing files that changed from the base of the PR and between e793b32 and f6594ee.

Files selected for processing: 1
  • pkgs/id/src/web/markdown.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T03:20:20.678285Z f6594ee New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 124700032d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +967 to +968
"bullet_list" | "ordered_list" => child == Placement::ListItem,
"task_list" => child == Placement::TaskListItem,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept both item kinds in mixed Markdown lists

For Markdown mixing checkbox and ordinary items, such as - [ ] todo\n- plain\n, this rejects the document produced by markdown_to_prosemirror itself. convert_node chooses the list type based only on the first child, while collect_list_items preserves both list_item and task_list_item children. Consequently, mixed lists in either order now return MisplacedNode instead of round-tripping successfully as before. Allow both item kinds under these list containers, or normalize the parser output before enforcing this restriction.

Useful? React with 👍 / 👎.

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.

id(web): prosemirror_to_markdown panics on misplaced nodes — json_to_ast never validates block/inline placement

1 participant