fix: allow a tab before the closing sequence of an ATX heading - #4084
Merged
UziTech merged 1 commit intoSep 5, 2026
Merged
Conversation
CommonMark 0.31.2 says the optional closing sequence of #s must be preceded by spaces or tabs, but the heading tokenizer tested for a space only, so `# foo<tab>#` kept the trailing hashes as content. The shared endingSpaceChar rule stays space only because code spans need exactly that, so the heading path gets its own rule.
|
@giaBaoJS is attempting to deploy a commit to the MarkedJS Team on Vercel. A member of the Team first needs to authorize it. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
calculuschild
approved these changes
Sep 5, 2026
calculuschild
left a comment
Contributor
There was a problem hiding this comment.
Is endingSpaceChar used for anything now? If no we can remove that.
But otherwise looks good to me!
Member
|
Ya it is still used in codespan |
github-actions Bot
pushed a commit
that referenced
this pull request
Sep 7, 2026
## [18.0.12](v18.0.11...v18.0.12) (2026-09-07) ### Bug Fixes * allow a tab before the closing sequence of an ATX heading ([#4084](#4084)) ([4417582](4417582)) * allow one more level of nested brackets in a link label ([#4064](#4064)) ([37b28d8](37b28d8)) * do not add a newline to an empty code block ([#4073](#4073)) ([23b1706](23b1706)) * escape character references in autolink destinations ([#4053](#4053)) ([8f432f0](8f432f0)) * reject GFM email autolink when the domain ends in _ or - ([#4063](#4063)) ([df57534](df57534)) * reject invalid characters in HTML tag names ([#4083](#4083)) ([300bb1d](300bb1d)) * remove up to the fence indentation from each content line ([#4074](#4074)) ([0244f08](0244f08))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Marked version: 18.0.11 (master, df57534)
Markdown flavor: CommonMark
Description
No issue for this one.
src/Tokenizer.ts:141decides whether a trailing run of#closes an ATX heading:endingSpaceCharis/ $/(src/rules.ts:50), so only U+0020 counts. A tab in that position does not, and the closing sequence stays in the heading text.Expectation
<TAB>below stands for one literal tab character.# foo<TAB>#Result
Minimal repro:
marked.parse('# foo\t#\n')returns'<h1>foo\t#</h1>\n'.What was attempted
CommonMark 0.31.2, "ATX headings":
and again above example 75:
(source: https://github.com/commonmark/commonmark-spec/blob/0.31.2/spec.txt, lines 1102 to 1104 and 1257)
marked already handles the other two halves of that sentence with tabs: the opening sequence uses
(?=\s|$)in theheadingrule, and "followed by spaces or tabs only" falls out ofString.prototype.trim. Only "preceded by" was space-only.endingSpaceCharcannot simply be widened, because its other caller is the code span normalization atsrc/Tokenizer.ts:848, and there the spec really does mean U+0020:So the heading path gets its own
endingSpaceTabCharrule and the code span path keepsendingSpaceChar.Pedantic mode is untouched: it takes the earlier branch and strips trailing hashes regardless of what precedes them.
Blast radius
I generated 33,075 documents (3 opening sequences x 5 separators after it x 7 bodies x 7 separators before the closing sequence x 9 tails, over 5 wrappings: bare, in a blockquote, in a list item, after a paragraph, before a paragraph) and diffed master against this branch, using the
commonmark0.31.2 package already in devDependencies as the reference. Comparison ignores whitespace between tags.6,480 documents change output. All 6,480 go from disagreeing with the reference to matching it, and 0 go the other way.
CommonMark and GFM spec completion tables are identical before and after: no spec example covers a tab in this position, which is why the suite never caught it.
ReDoS
npm run test:redoscovers theotherrules, and the new one checks clean:It is a single anchored character class with no quantifier.
Tests
One spec file in
test/specs/newcovering a tab before a one-hash and a multi-hash closing sequence, a hash that is part of the content followed by a tab and a real closing hash, a mixed space and tab run, and a control line (##### quux#) whose hash is not preceded by whitespace and must stay in the text. Its expected HTML is byte for byte whatcommonmark0.31.2 produces.On master the test fails with:
Local run: 1797 to 1799 spec tests, 191 unit tests, 0 failures.
test:lint,test:types,test:umdandtest:cjspass.Contributor
Committer
In most cases, this should be a different person than the contributor.