Date: Sat, 20 Jun 2026 16:55:47 +0100
Subject: [PATCH 09/10] Fix tag balance checkers not accounting for void tags
---
lib/markdown2.py | 11 +++++++++++
.../improper_void_tag_hashing_pr705.html | 17 +++++++++++++++++
.../improper_void_tag_hashing_pr705.opts | 1 +
.../improper_void_tag_hashing_pr705.text | 10 ++++++++++
4 files changed, 39 insertions(+)
create mode 100644 test/tm-cases/improper_void_tag_hashing_pr705.html
create mode 100644 test/tm-cases/improper_void_tag_hashing_pr705.opts
create mode 100644 test/tm-cases/improper_void_tag_hashing_pr705.text
diff --git a/lib/markdown2.py b/lib/markdown2.py
index 04e8934d..6518d783 100755
--- a/lib/markdown2.py
+++ b/lib/markdown2.py
@@ -862,6 +862,10 @@ def _detab(self, text: str) -> str:
output.append(self._detab_line(line))
return '\n'.join(output)
+ # https://developer.mozilla.org/en-US/docs/Glossary/Void_element
+ # technically "self closing tags" (eg:
) are not real HTML but noone cares
+ _void_tags = 'area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr'
+
# I broke out the html5 tags here and add them to _block_tags_a and
# _block_tags_b. This way html5 tags are easy to keep track of.
_html5tags = '|address|article|aside|canvas|figcaption|figure|footer|header|main|nav|section|video'
@@ -906,6 +910,7 @@ def _detab(self, text: str) -> str:
_html_markdown_attr_re = re.compile(
# markdown attr, with optional assignment to true, must be followed by whitespace/boundary/closing tag chars
r'''\s+markdown(?:="1"|='1'|=1)?(?![^\s/>\b])''')
+
def _hash_html_block_sub(
self,
match: Union[re.Match[str], str],
@@ -1128,6 +1133,9 @@ def _strict_tag_block_sub(
return result
def _tag_is_closed(self, tag_name: str, text: str) -> bool:
+ if re.match(self._void_tags, tag_name):
+ return True
+
# check if number of open tags == number of close tags
if len(re.findall('<%s(?:.*?)>' % tag_name, text)) != text.count('%s>' % tag_name):
return False
@@ -1149,6 +1157,9 @@ def _tag_imbalance(self, tag_name: str, text: str) -> int:
0 for balanced tags, positive int for more opening tags than closing, negative int for
more closing tags than opening
'''
+ if re.match(self._void_tags, tag_name):
+ return 0
+
count = 0
for tag in re.finditer(r'<(/)?%s\b>?' % tag_name, text):
if tag.group(1):
diff --git a/test/tm-cases/improper_void_tag_hashing_pr705.html b/test/tm-cases/improper_void_tag_hashing_pr705.html
new file mode 100644
index 00000000..2da86384
--- /dev/null
+++ b/test/tm-cases/improper_void_tag_hashing_pr705.html
@@ -0,0 +1,17 @@
+
+
+
+
+) <script>alert(origin)</script>
+
+"
diff --git a/test/tm-cases/improper_void_tag_hashing_pr705.opts b/test/tm-cases/improper_void_tag_hashing_pr705.opts
new file mode 100644
index 00000000..ad487c04
--- /dev/null
+++ b/test/tm-cases/improper_void_tag_hashing_pr705.opts
@@ -0,0 +1 @@
+{"safe_mode": "escape"}
diff --git a/test/tm-cases/improper_void_tag_hashing_pr705.text b/test/tm-cases/improper_void_tag_hashing_pr705.text
new file mode 100644
index 00000000..223862cf
--- /dev/null
+++ b/test/tm-cases/improper_void_tag_hashing_pr705.text
@@ -0,0 +1,10 @@
+---
+* ```
+ * ```
+
+ x
+```
+---
+```) ```
+"
+---
From 2f0cf2cf7becd15572123e85b5c8fa2e3cd3bdf6 Mon Sep 17 00:00:00 2001
From: Crozzers
Date: Fri, 26 Jun 2026 18:09:04 +0100
Subject: [PATCH 10/10] Loosen link-within-autolink check for if link starts OR
ends inside autolink
---
lib/markdown2.py | 3 ++-
test/tm-cases/xss_smuggling_spans_in_image_attrs.html | 2 ++
test/tm-cases/xss_smuggling_spans_in_image_attrs.text | 2 ++
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/lib/markdown2.py b/lib/markdown2.py
index 6518d783..e0f69301 100755
--- a/lib/markdown2.py
+++ b/lib/markdown2.py
@@ -3243,7 +3243,8 @@ def run(self, text: str):
# check that this link is not inside an autolink
if any(
- autolink.start() < start_idx < p < autolink.end()
+ autolink.start() < start_idx < autolink.end()
+ or autolink.start() < p < autolink.end()
for autolink in self.md._auto_link_re.finditer(text)
):
curr_pos = start_idx + 1
diff --git a/test/tm-cases/xss_smuggling_spans_in_image_attrs.html b/test/tm-cases/xss_smuggling_spans_in_image_attrs.html
index ed1ca655..b4061829 100644
--- a/test/tm-cases/xss_smuggling_spans_in_image_attrs.html
+++ b/test/tm-cases/xss_smuggling_spans_in_image_attrs.html
@@ -20,4 +20,6 @@
http://onclick=alert(origin)//
+http://onclick=alert(origin)//[Click me]()>
+
">`)
diff --git a/test/tm-cases/xss_smuggling_spans_in_image_attrs.text b/test/tm-cases/xss_smuggling_spans_in_image_attrs.text
index bee50136..bcca4fc0 100644
--- a/test/tm-cases/xss_smuggling_spans_in_image_attrs.text
+++ b/test/tm-cases/xss_smuggling_spans_in_image_attrs.text
@@ -16,5 +16,7 @@
+
+
![x](<"`"![x][id]
[id]: x "`