CVE-2023-26303: Other (NVD-CWE-Other)
value: Denial of service could be caused to markdown-it-py, before v2.2.0, if an attacker was allowed to force null assertions with specially crafted input.

CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 5.5
Published: 2023-02-23T00:15Z
Repository: https://github.com/executablebooks/markdown-it-py
Language: Python
Files changed: markdown_it/renderer.py, markdown_it/rules_core/replacements.py, markdown_it/rules_core/smartquotes.py, tests/test_port/fixtures/issue-fixes.md, tests/test_port/test_fixtures.py
Commit date: 2023-02-22 05:56:39 +0100
Commit message: 🐛 FIX: CVE-2023-26303 (#246)

Fix unnecessary asserts, leading to crashes
Diff: diff --git a/markdown_it/renderer.py b/markdown_it/renderer.py
index aa6272a..81d0bc3 100644
--- a/markdown_it/renderer.py
+++ b/markdown_it/renderer.py
@@ -81,12 +81,12 @@ class RendererHTML(RendererProtocol):
         """
         result = ""
 
         for i, token in enumerate(tokens):
             if token.type == "inline":
-                assert token.children is not None
-                result += self.renderInline(token.children, options, env)
+                if token.children:
+                    result += self.renderInline(token.children, options, env)
             elif token.type in self.rules:
                 result += self.rules[token.type](tokens, i, options, env)
             else:
                 result += self.renderToken(tokens, i, options, env)
 
@@ -204,12 +204,12 @@ class RendererHTML(RendererProtocol):
 
         for token in tokens or []:
             if token.type == "text":
                 result += token.content
             elif token.type == "image":
-                assert token.children is not None
-                result += self.renderInlineAsText(token.children, options, env)
+                if token.children:
+                    result += self.renderInlineAsText(token.children, options, env)
             elif token.type == "softbreak":
                 result += "\n"
 
         return result
 
@@ -303,18 +303,14 @@ class RendererHTML(RendererProtocol):
     ) -> str:
         token = tokens[idx]
 
         # "alt" attr MUST be set, even if empty. Because it's mandatory and
         # should be placed on proper position for tests.
-
-        assert (
-            token.attrs and "alt" in token.attrs
-        ), '"image" token\'s attrs must contain `alt`'
-
-        # Replace content with actual value
-
-        token.attrSet("alt", self.renderInlineAsText(token.children, options, env))
+        if token.children:
+            token.attrSet("alt", self.renderInlineAsText(token.children, options, env))
+        else:
+            token.attrSet("alt", "")
 
         return self.renderToken(tokens, idx, options, env)
 
     def hardbreak(
         self, tokens: Sequence[Token], idx: int, options: OptionsDict, *args
diff --git a/markdown_it/rules_core/replacements.py b/markdown_it/rules_core/replacements.py
index 45377d3..5e9b7ae 100644
--- a/markdown_it/rules_core/replacements.py
+++ b/markdown_it/rules_core/replacements.py
@@ -114,11 +114,12 @@ def replace(state: StateCore) -> None:
         return
 
     for token in state.tokens:
         if token.type != "inline":
             continue
-        assert token.children is not None
+        if token.children is None:
+            continue
 
         if SCOPED_ABBR_RE.search(token.content):
             replace_scoped(token.children)
 
         if RARE_RE.search(token.content):
diff --git a/markdown_it/rules_core/smartquotes.py b/markdown_it/rules_core/smartquotes.py
index 7a39fad..b11a573 100644
--- a/markdown_it/rules_core/smartquotes.py
+++ b/markdown_it/rules_core/smartquotes.py
@@ -195,7 +195,7 @@ def smartquotes(state: StateCore) -> None:
         return
 
     for token in state.tokens:
         if token.type != "inline" or not QUOTE_RE.search(token.content):
             continue
-        assert token.children is not None
-        process_inlines(token.children, state)
+        if token.children is not None:
+            process_inlines(token.children, state)
diff --git a/tests/test_port/fixtures/issue-fixes.md b/tests/test_port/fixtures/issue-fixes.md
index 0c693b0..319945a 100644
--- a/tests/test_port/fixtures/issue-fixes.md
+++ b/tests/test_port/fixtures/issue-fixes.md
@@ -34,5 +34,14 @@
 .
 &#x1F4AC;
 .
 <p>💬</p>
 .
+
+Fix CVE-2023-26303
+.
+![![]()
+]([)
+.
+<p><img src="%5B" alt="
+" /></p>
+.
diff --git a/tests/test_port/test_fixtures.py b/tests/test_port/test_fixtures.py
index 5117c5e..d2199ca 100644
--- a/tests/test_port/test_fixtures.py
+++ b/tests/test_port/test_fixtures.py
@@ -109,6 +109,7 @@ def test_strikethrough(line, title, input, expected):
     read_fixture_file(FIXTURE_PATH.joinpath("issue-fixes.md")),
 )
 def test_issue_fixes(line, title, input, expected):
     md = MarkdownIt()
     text = md.render(input)
+    print(text)
     assert text.rstrip() == expected.rstrip()

Vulnerable code:                 assert token.children is not None
                result += self.renderInline(token.children, options, env)
                assert token.children is not None
                result += self.renderInlineAsText(token.children, options, env)

        assert (
            token.attrs and "alt" in token.attrs
        ), '"image" token\'s attrs must contain `alt`'

        # Replace content with actual value

        token.attrSet("alt", self.renderInlineAsText(token.children, options, env))
        assert token.children is not None
        assert token.children is not None
        process_inlines(token.children, state)
Fixed code:                 if token.children:
                    result += self.renderInline(token.children, options, env)
                if token.children:
                    result += self.renderInlineAsText(token.children, options, env)
        if token.children:
            token.attrSet("alt", self.renderInlineAsText(token.children, options, env))
        else:
            token.attrSet("alt", "")
        if token.children is None:
            continue
        if token.children is not None:
            process_inlines(token.children, state)

Fix CVE-2023-26303
.
![![]()
]([)
.
<p><img src="%5B" alt="
" /></p>
.
    print(text)