From b6338611aafba3eefb645faa487d7393a65e1b97 Mon Sep 17 00:00:00 2001
From: Crozzers
Date: Sun, 22 Sep 2024 19:21:19 +0100
Subject: [PATCH 1/2] Fix XSS injection in image URLs (#603)
---
CHANGES.md | 1 +
lib/markdown2.py | 25 ++++++++++++++++++++-----
test/tm-cases/issue603_xss.html | 4 ++++
test/tm-cases/issue603_xss.opts | 1 +
test/tm-cases/issue603_xss.text | 8 ++++++++
5 files changed, 34 insertions(+), 5 deletions(-)
create mode 100644 test/tm-cases/issue603_xss.html
create mode 100644 test/tm-cases/issue603_xss.opts
create mode 100644 test/tm-cases/issue603_xss.text
diff --git a/CHANGES.md b/CHANGES.md
index 4283b966..f07fb095 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,6 +5,7 @@
- [pull #590] Fix underscores within bold text getting emphasized (#589)
- [pull #591] Add Alerts extra
- [pull #595] Fix img alt text being processed as markdown (#594)
+- [pull #604] Fix XSS injection in image URLs (#603)
## python-markdown2 2.5.0
diff --git a/lib/markdown2.py b/lib/markdown2.py
index d0f10eea..26e5b075 100755
--- a/lib/markdown2.py
+++ b/lib/markdown2.py
@@ -1354,9 +1354,23 @@ def _is_comment(token):
is_html_markup = not is_html_markup
return ''.join(tokens)
- def _unhash_html_spans(self, text: str) -> str:
- for key, sanitized in list(self.html_spans.items()):
- text = text.replace(key, sanitized)
+ def _unhash_html_spans(self, text: str, spans=True, code=False) -> str:
+ '''
+ Recursively unhash a block of text
+
+ Args:
+ spans: unhash anything from `self.html_spans`
+ code: unhash code blocks
+ '''
+ orig = ''
+ while text != orig:
+ if spans:
+ for key, sanitized in list(self.html_spans.items()):
+ text = text.replace(key, sanitized)
+ if code:
+ for code, key in list(self._code_table.items()):
+ text = text.replace(key, code)
+ orig = text
return text
def _sanitize_html(self, s: str) -> str:
@@ -1582,8 +1596,9 @@ def _do_links(self, text: str) -> str:
# We've got to encode these to avoid conflicting
# with italics/bold.
- url = url.replace('*', self._escape_table['*']) \
- .replace('_', self._escape_table['_'])
+ url = self._unhash_html_spans(url, code=True) \
+ .replace('*', self._escape_table['*']) \
+ .replace('_', self._escape_table['_'])
if title:
title_str = ' title="%s"' % (
_xml_escape_attr(title)
diff --git a/test/tm-cases/issue603_xss.html b/test/tm-cases/issue603_xss.html
new file mode 100644
index 00000000..12d22fdb
--- /dev/null
+++ b/test/tm-cases/issue603_xss.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/test/tm-cases/issue603_xss.opts b/test/tm-cases/issue603_xss.opts
new file mode 100644
index 00000000..ad487c04
--- /dev/null
+++ b/test/tm-cases/issue603_xss.opts
@@ -0,0 +1 @@
+{"safe_mode": "escape"}
diff --git a/test/tm-cases/issue603_xss.text b/test/tm-cases/issue603_xss.text
new file mode 100644
index 00000000..cbb0fdf8
--- /dev/null
+++ b/test/tm-cases/issue603_xss.text
@@ -0,0 +1,8 @@
+![](`" onerror=alert()//`)
+
+
+![][XSS]
+[][XSS]
+
+
+[XSS]: " onerror=alert()//
From 1b3d7057492adee781ad1a54fef176efd7d46868 Mon Sep 17 00:00:00 2001
From: Crozzers
Date: Sun, 22 Sep 2024 19:28:15 +0100
Subject: [PATCH 2/2] Expand link shortcut tests in XSS issue 604 case
---
test/tm-cases/issue603_xss.html | 4 +++-
test/tm-cases/issue603_xss.text | 4 ++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/test/tm-cases/issue603_xss.html b/test/tm-cases/issue603_xss.html
index 12d22fdb..2293d586 100644
--- a/test/tm-cases/issue603_xss.html
+++ b/test/tm-cases/issue603_xss.html
@@ -1,4 +1,6 @@
-
+
+
+
diff --git a/test/tm-cases/issue603_xss.text b/test/tm-cases/issue603_xss.text
index cbb0fdf8..85835be0 100644
--- a/test/tm-cases/issue603_xss.text
+++ b/test/tm-cases/issue603_xss.text
@@ -3,6 +3,10 @@
![][XSS]
[][XSS]
+![][XSS2]
+![][XSS3]
[XSS]: " onerror=alert()//
+[XSS2]: `" onerror=alert()//`
+[XSS3]: " onerror=alert()//