From 3da07373ca8ffe2b51efc5c74f6f2fd1fe63721f Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Mon, 5 Jan 2026 18:52:45 +0800 Subject: [PATCH 1/3] fix: Make github alert parsing case-insensitive Signed-off-by: Frost Ming --- CHANGELOG.md | 6 ++++++ marko/ext/gfm/elements.py | 6 ++++-- tests/test_ext.py | 22 +++++++++++++++------- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0346323..a60220d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +### Fixed + +- Fix a bug in GFM alert block parsing where alert types were case-sensitive. + ## v2.2.1(2025-10-13) ### Changed diff --git a/marko/ext/gfm/elements.py b/marko/ext/gfm/elements.py index fa98289..d57960c 100644 --- a/marko/ext/gfm/elements.py +++ b/marko/ext/gfm/elements.py @@ -225,11 +225,13 @@ class Alert(block.Quote): @classmethod def match(cls, source): - return source.expect_re(r" {,3}>\s*\[\!(WARNING|NOTE|TIP|IMPORTANT|CAUTION)\]") + return source.expect_re( + r"(?i) {,3}>\s*\[\!(WARNING|NOTE|TIP|IMPORTANT|CAUTION)\]" + ) @classmethod def parse(cls, source): - alert_type = source.match.group(1) + alert_type = source.match.group(1).upper() source.next_line(require_prefix=False) source.consume() state = cls(alert_type) diff --git a/tests/test_ext.py b/tests/test_ext.py index d20e2db..7e268c7 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -1,3 +1,5 @@ +import pytest + from marko import Markdown @@ -106,20 +108,26 @@ def setup_method(self): self.md_ast = Markdown(renderer=ASTRenderer, extensions=[GFM]) self.md_html = Markdown(extensions=[GFM]) - def test_alert_ast(self): - text = "> [!WARNING]\n> Foo bar\n> Bar\n" + @pytest.mark.parametrize( + "alert_type", ["WARNING", "CAUTION", "TIP", "warning", "Caution"] + ) + def test_alert_ast(self, alert_type): + text = f"> [!{alert_type}]\n> Foo bar\n> Bar\n" ast = self.md_ast(text) admon = ast["children"][0] assert admon["element"] == "alert" - assert admon["alert_type"] == "WARNING" + assert admon["alert_type"] == alert_type.upper() inner = admon["children"][0]["children"] assert inner[0]["children"] == "Foo bar" assert inner[1]["element"] == "line_break" assert inner[2]["children"] == "Bar" - def test_alert_html(self): - text = "> [!WARNING]\n> Foo bar\n> Bar\n" + @pytest.mark.parametrize( + "alert_type", ["WARNING", "CAUTION", "TIP", "warning", "Caution"] + ) + def test_alert_html(self, alert_type): + text = f"> [!{alert_type}]\n> Foo bar\n> Bar\n" html = self.md_html(text) - assert '
' in html - assert "

Warning

" in html + assert f'
' in html + assert f"

{alert_type.title()}

" in html assert "

Foo bar\nBar

" in html From a8ecd62a6e82685814e7f31728b9a88d0a219121 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Mon, 5 Jan 2026 18:54:54 +0800 Subject: [PATCH 2/3] fix: Update GFM alert regex to disallow content on header line Signed-off-by: Frost Ming --- CHANGELOG.md | 3 ++- marko/ext/gfm/elements.py | 2 +- tests/test_ext.py | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a60220d..9aa152c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -## Unreleased +## v2.2.2(2026-01-05) ### Fixed - Fix a bug in GFM alert block parsing where alert types were case-sensitive. +- Fix GFM alert regex to disallow content on header line. ## v2.2.1(2025-10-13) diff --git a/marko/ext/gfm/elements.py b/marko/ext/gfm/elements.py index d57960c..2edcaeb 100644 --- a/marko/ext/gfm/elements.py +++ b/marko/ext/gfm/elements.py @@ -226,7 +226,7 @@ class Alert(block.Quote): @classmethod def match(cls, source): return source.expect_re( - r"(?i) {,3}>\s*\[\!(WARNING|NOTE|TIP|IMPORTANT|CAUTION)\]" + r"(?im) {,3}>\s*\[\!(WARNING|NOTE|TIP|IMPORTANT|CAUTION)\]\s*$" ) @classmethod diff --git a/tests/test_ext.py b/tests/test_ext.py index 7e268c7..0b3f04e 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -131,3 +131,9 @@ def test_alert_html(self, alert_type): assert f'
' in html assert f"

{alert_type.title()}

" in html assert "

Foo bar\nBar

" in html + + def test_alert_disallow_content_on_header_line(self): + text = "> [!NOTE] This is not allowed.\n> Foo bar\n" + html = self.md_html(text) + # Should be treated as a normal blockquote. + assert "
\n

[!NOTE] This is not allowed." in html From 870dccfc90a881b6c18bb5a42de153aa81781744 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Mon, 5 Jan 2026 18:59:50 +0800 Subject: [PATCH 3/3] fix: Bump version to 2.2.2 Signed-off-by: Frost Ming --- marko/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marko/__init__.py b/marko/__init__.py index 4aae580..a21e6dd 100644 --- a/marko/__init__.py +++ b/marko/__init__.py @@ -23,7 +23,7 @@ from .block import Document from .parser import ElementType -__version__ = "2.2.1" +__version__ = "2.2.2" class SetupDone(Exception):