Skip to content

Fix escaped document tags in code being taken for the document's own - #288

Merged
colinodell merged 1 commit into
masterfrom
fix/escaped-document-tags-254
Oct 19, 2026
Merged

colinodell merged 1 commit into
masterfrom
fix/escaped-document-tags-254

Conversation

@colinodell

Copy link
Copy Markdown
Member

Fixes #254

HtmlConverter::sanitize() decodes the serialized document before stripping the document's own wrapper tags from it, so text inside of code which looks like one of those tags, once decoded, could be mistaken for it:

Input Before After
<pre><code>&lt;/body&gt;</code></pre> ```\n</body>\n```\n\n</body> ```\n</body>\n```
<p>a <code>&lt;/body&gt;&lt;/html&gt;</code></p> a `</body></html>`</body></html> a `</body></html>`
<p>Use <code>&lt;!DOCTYPE html</code> first.</p><p>a &gt; b</p> Use ` Use `<!DOCTYPE html` first.\n\na &gt; b
  • The closing tags were found with strpos(), which returns the first occurrence, so an escaped one in code was taken for the real one and the real tags at the end were left in. They're now only stripped where they end the output.
  • The doctype regex wasn't anchored, so it removed everything from an escaped <!DOCTYPE in code up to the next >, which could be the rest of the document. saveHTML() always puts the real doctype first (checked on libxml 2.9.4, 2.9.14 and 2.13.9, including documents with no doctype, a lowercase one, or comments and processing instructions before it), so it's now anchored to the start.

Whatever the old checks stripped, the new ones still strip; they only stop stripping the copies which aren't at the start or end. Preserved comments containing </body> are fixed by the same change.

This deliberately keeps the change to sanitize() minimal, as it's a protected method and its input is unchanged. A wider rework which avoids serializing and decoding the document altogether (which would also clean up leftovers like </head><body> from documents with a <head>) is better suited to a minor release.

🤖 Generated with Claude Code

https://claude.ai/code/session_0192z2KwkS1QtyN2hrGVpQ4z

Code containing `&lt;/body&gt;` or `&lt;/html&gt;` is decoded before the
document's own closing tags are stripped, and the first occurrence was
taken for the real one, so the real tags were left at the end of the
output. Only strip closing tags which end the output.

The doctype regex likewise removed any `&lt;!DOCTYPE ...&gt;` from code,
so it now only matches at the start.

Fixes #254

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0192z2KwkS1QtyN2hrGVpQ4z

Copilot AI 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.

🟢 Approval recommended

The narrowly scoped changes address the reported failures with targeted regression coverage and no unresolved findings.

No open issues 🎉

What changed in this PR

Fixes #254 by preventing escaped document tags in code and preserved comments from interfering with HTML-to-Markdown wrapper removal.

Changes:

  • Restricts doctype removal to the start and closing-tag removal to the end.
  • Adds regression tests for document-like text in code and comments.
  • Documents the fix in the changelog.
File Description
tests/​HtmlConverterTest.php Adds regression coverage for escaped document tags.
src/​HtmlConverter.php Anchors document wrapper removal to output boundaries.
CHANGELOG.md Records the fixes for #254.

Review effort: Balanced

@colinodell
colinodell merged commit 750fd90 into master Oct 19, 2026
30 of 31 checks passed
@colinodell
colinodell deleted the fix/escaped-document-tags-254 branch October 19, 2026 21:05
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.

Closing document tags added, when code blocks contain closing body or html tag

2 participants