Skip to content

Commit d49f798

Browse files
Merge branch 'master' into gfm-link-refs
2 parents de8cea7 + 929d505 commit d49f798

File tree

9 files changed

+56
-9
lines changed

9 files changed

+56
-9
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
- [pull #591] Add Alerts extra
77
- [pull #595] Fix img alt text being processed as markdown (#594)
88
- [pull #598] Add `link-shortrefs` extra (#597)
9+
- [pull #600] Use urandom for SECRET_SALT
10+
- [pull #602] Fix XSS issue in safe mode (#601)
11+
- [pull #604] Fix XSS injection in image URLs (#603)
912

1013

1114
## python-markdown2 2.5.0

CONTRIBUTORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ Kishore (github.com/jk6521)
6060
Ircama (github.com/Ircama)
6161
Ankit Mahato (github.com/animator)
6262
Eric Dufresne (github.com/edufresne)
63+
Lyra Rebane (github.com/rebane2001)

lib/markdown2.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,10 @@
121121
from abc import ABC, abstractmethod
122122
import functools
123123
from hashlib import sha256
124-
from random import randint, random
124+
from random import random
125125
from typing import Any, Callable, Collection, Dict, List, Literal, Optional, Tuple, Type, TypedDict, Union
126126
from enum import IntEnum, auto
127+
from os import urandom
127128

128129
if sys.version_info[1] < 9:
129130
from typing import Iterable
@@ -144,7 +145,7 @@
144145
DEFAULT_TAB_WIDTH = 4
145146

146147

147-
SECRET_SALT = bytes(randint(0, 1000000))
148+
SECRET_SALT = urandom(16)
148149
# MD5 function was previously used for this; the "md5" prefix was kept for
149150
# backwards compatibility.
150151
def _hash_text(s: str) -> str:
@@ -1262,8 +1263,13 @@ def _run_span_gamut(self, text: str) -> str:
12621263
(?:
12631264
# tag
12641265
</?
1265-
(?:\w+) # tag name
1266-
(?:\s+(?:[\w-]+:)?[\w-]+=(?:".*?"|'.*?'))* # attributes
1266+
(?:\w+) # tag name
1267+
(?: # attributes
1268+
\s+ # whitespace after tag
1269+
(?:[^\t<>"'=/]+:)?
1270+
[^<>"'=/]+= # attr name
1271+
(?:".*?"|'.*?'|[^<>"'=/\s]+) # value, quoted or unquoted. If unquoted, no spaces allowed
1272+
)*
12671273
\s*/?>
12681274
|
12691275
# auto-link (e.g., <http://www.activestate.com/>)
@@ -1356,9 +1362,23 @@ def _is_comment(token):
13561362
is_html_markup = not is_html_markup
13571363
return ''.join(tokens)
13581364

1359-
def _unhash_html_spans(self, text: str) -> str:
1360-
for key, sanitized in list(self.html_spans.items()):
1361-
text = text.replace(key, sanitized)
1365+
def _unhash_html_spans(self, text: str, spans=True, code=False) -> str:
1366+
'''
1367+
Recursively unhash a block of text
1368+
1369+
Args:
1370+
spans: unhash anything from `self.html_spans`
1371+
code: unhash code blocks
1372+
'''
1373+
orig = ''
1374+
while text != orig:
1375+
if spans:
1376+
for key, sanitized in list(self.html_spans.items()):
1377+
text = text.replace(key, sanitized)
1378+
if code:
1379+
for code, key in list(self._code_table.items()):
1380+
text = text.replace(key, code)
1381+
orig = text
13621382
return text
13631383

13641384
def _sanitize_html(self, s: str) -> str:
@@ -1584,8 +1604,9 @@ def _do_links(self, text: str) -> str:
15841604

15851605
# We've got to encode these to avoid conflicting
15861606
# with italics/bold.
1587-
url = url.replace('*', self._escape_table['*']) \
1588-
.replace('_', self._escape_table['_'])
1607+
url = self._unhash_html_spans(url, code=True) \
1608+
.replace('*', self._escape_table['*']) \
1609+
.replace('_', self._escape_table['_'])
15891610
if title:
15901611
title_str = ' title="%s"' % (
15911612
_xml_escape_attr(title)

test/tm-cases/issue601_xss.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>&lt;img src=# onerror="alert()"&gt;&lt;/p&gt;</p>

test/tm-cases/issue601_xss.opts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"safe_mode": "escape"}

test/tm-cases/issue601_xss.text

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<img src=# onerror="alert()"></p>

test/tm-cases/issue603_xss.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<p><img src="code&gt;&quot; onerror=alert()//&lt;/code" alt="" /></p>
2+
3+
<p><img src="&quot; onerror=alert()//" alt="" />
4+
<a href="#"></a>
5+
<img src="`&quot; onerror=alert()//`" alt="" />
6+
<img src="&lt;code&gt;&quot; onerror=alert()//&lt;code&gt;" alt="" /></p>

test/tm-cases/issue603_xss.opts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"safe_mode": "escape"}

test/tm-cases/issue603_xss.text

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
![](`" onerror=alert()//`)
2+
3+
4+
![][XSS]
5+
[][XSS]
6+
![][XSS2]
7+
![][XSS3]
8+
9+
10+
[XSS]: " onerror=alert()//
11+
[XSS2]: `" onerror=alert()//`
12+
[XSS3]: <code>" onerror=alert()//<code>

0 commit comments

Comments
 (0)