Skip to content

Commit

Permalink
Fix edge-case crash in InlineProcessor
Browse files Browse the repository at this point in the history
If an inlineprocessor returns an AtomicString (even though that is pointless, a plain string is atomic in that context), there can be an exception in 2 separate places. The added test case was crashing before this change.
  • Loading branch information
oprypin committed Nov 3, 2023
1 parent f5b151a commit a63e6f3
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/changelog.md
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Remove legacy import needed only in Python 2 (#1403)
* Fix typo that left the attribute `AdmonitionProcessor.content_indent` unset
(#1404)
* Fix edge-case crash in `InlineProcessor` with `AtomicString` (#1406).
* Fix edge-case crash in `codehilite` with an empty `code` tag (#1405).
* Improve and expand type annotations in the code base (#1401).

Expand Down
4 changes: 2 additions & 2 deletions markdown/treeprocessors.py
Expand Up @@ -218,7 +218,7 @@ def linkText(text: str | None) -> None:
text = data[strartIndex:index]
linkText(text)

if not isString(node): # it's Element
if not isinstance(node, str): # it's Element
for child in [node] + list(node):
if child.tail:
if child.tail.strip():
Expand Down Expand Up @@ -304,7 +304,7 @@ def __applyPattern(
if node is None:
return data, True, end

if not isString(node):
if not isinstance(node, str):
if not isinstance(node.text, util.AtomicString):
# We need to process current node too
for child in [node] + list(node):
Expand Down
25 changes: 23 additions & 2 deletions tests/test_apis.py
Expand Up @@ -30,6 +30,7 @@
import markdown
import warnings
from markdown.__main__ import parse_options
from markdown import inlinepatterns
from logging import DEBUG, WARNING, CRITICAL
import yaml
import tempfile
Expand Down Expand Up @@ -664,8 +665,8 @@ class testAtomicString(unittest.TestCase):
""" Test that `AtomicStrings` are honored (not parsed). """

def setUp(self):
md = markdown.Markdown()
self.inlineprocessor = md.treeprocessors['inline']
self.md = markdown.Markdown()
self.inlineprocessor = self.md.treeprocessors['inline']

def testString(self):
""" Test that a regular string is parsed. """
Expand Down Expand Up @@ -710,6 +711,26 @@ def testNestedAtomicString(self):
'*to*</span> *test*</span> *with*</p></div>'
)

def testInlineProcessorDoesntCrashWithWrongAtomicString(self):
""" Test that an `AtomicString` returned from a Pattern doesn't cause a crash. """
tree = etree.Element('div')
p = etree.SubElement(tree, 'p')
p.text = 'a marker c'
self.md.inlinePatterns.register(
_InlineProcessorThatReturnsAtomicString(r'marker', self.md), 'test', 100
)
new = self.inlineprocessor.run(tree)
self.assertEqual(
markdown.serializers.to_html_string(new),
'<div><p>a &lt;b&gt;atomic&lt;/b&gt; c</p></div>'
)


class _InlineProcessorThatReturnsAtomicString(inlinepatterns.InlineProcessor):
""" Return a simple text of `group(1)` of a Pattern. """
def handleMatch(self, m, data):
return markdown.util.AtomicString('<b>atomic</b>'), m.start(0), m.end(0)


class TestConfigParsing(unittest.TestCase):
def assertParses(self, value, result):
Expand Down

0 comments on commit a63e6f3

Please sign in to comment.