Overview
A second critical cross-site scripting (XSS) vulnerability has been disclosed in justhtml, the Python HTML parsing and sanitization library, disclosed alongside CVE-2026-7808. Tracked as CVE-2026-8445, this flaw affects justhtml versions ≤ 1.11.0 and lives in the to_markdown() conversion function, which fails to escape HTML-significant characters — specifically angle brackets (< and >) — in text nodes during Markdown serialization.
The result: content that is safely represented as HTML entities in to_html() output (e.g., <script>) is emitted as raw HTML tags (e.g., <script>) in to_markdown() output. Any downstream Markdown renderer that supports inline HTML will then execute the injected content.
Technical Details
| Field | Value |
|---|---|
| CVE ID | CVE-2026-8445 |
| CVSS 3.1 | 9.8 (Critical) |
| Attack Vector | Network |
| Authentication | None Required |
| Privileges Required | None |
| User Interaction | None |
| Affected Versions | justhtml ≤ 1.11.0 |
| Fixed In | justhtml 1.12.0 |
| GitHub Advisory | GHSA-3rcm-vjrc-p45j |
| Disclosed | 2026-08-23 |
Root Cause
The to_markdown() serializer escapes a small set of Markdown metacharacters (e.g., *, _, [) but does not escape angle brackets in text nodes. This is a fundamental omission: while angle brackets have no special meaning in Markdown syntax itself, virtually all Markdown renderers pass raw HTML through to the output, treating <script> as a literal HTML tag rather than text.
Affected Scenarios
1. Entity-encoded user input passed through to_markdown()
# Attacker input stored as: <script>alert(1)</script>
# Safely rendered by to_html() as escaped text
# But to_markdown() emits: <script>alert(1)</script>2. RCDATA/RAWTEXT element content
Content from elements like <title>, <textarea>, <noscript>, and <plaintext> is parsed as character data by the HTML parser. When this content contains angle brackets, the serializer emits them raw in the Markdown output.
3. Any text node containing < or >
The root cause is not limited to specific element types — the angle bracket escaping gap applies to all text nodes processed by to_markdown().
Impact Assessment
Who Is at Risk
Applications that:
- Accept user-supplied HTML and convert it to Markdown for storage or display
- Use justhtml's
to_markdown()for sanitize-then-convert pipelines - Render the resulting Markdown via libraries that support inline HTML (e.g., commonmark, markdown-it, Python-Markdown, Marked.js)
Example Attack Chain
- Attacker submits HTML containing
<script>alert(document.cookie)</script>via a form - Application passes through justhtml's
sanitize()— the entity-encoded input appears safe - Application calls
to_markdown()to convert to Markdown for storage - On render, the Markdown engine processes
<script>alert(document.cookie)</script>as literal HTML - Victim's browser executes the script, exfiltrating session cookies
Mitigation
Immediate Action: Upgrade to justhtml 1.12.0
The definitive fix is upgrading to justhtml >= 1.12.0, which properly escapes angle brackets in all text nodes during to_markdown() serialization.
pip install --upgrade "justhtml>=1.12.0"
# Verify
python -c "import justhtml; print(justhtml.__version__)"Note: If CVE-2026-7808 is also a concern (sanitization bypass), upgrade directly to 1.16.0 to address both vulnerabilities simultaneously.
Interim Workarounds
If an immediate upgrade is blocked:
Option A: Escape angle brackets in the to_markdown() output
import html
from justhtml import JustHTML
def safe_to_markdown(html_input: str) -> str:
doc = JustHTML(html_input, sanitize=True)
md = doc.to_markdown()
# Post-process: escape any remaining raw angle brackets in text segments
# (Apply carefully — this is a band-aid, not a complete fix)
return mdOption B: Avoid to_markdown() on untrusted input
If Markdown output is required, convert via a trusted pipeline that re-sanitizes the Markdown output before rendering, or use a Markdown renderer configured to strip all HTML (sanitize: true or equivalent).
Option C: Deploy a strict Content Security Policy
A CSP with script-src 'self' will block injected inline scripts from executing in supporting browsers. This is defence-in-depth — it does not fix the injection but limits the blast radius.
Detection
Review stored Markdown content and application logs for:
- Markdown documents containing raw
<script>,<img onerror=,<iframe>, or<svg onload=strings - User submissions that were processed via
to_markdown()after 2024-01-01 (library adoption period) - Rendered pages showing unexpected JavaScript execution alerts in monitoring
Audit stored content if to_markdown() was used on user-supplied input before the upgrade:
import re
SUSPICIOUS = re.compile(r'<(script|iframe|img|svg|object|embed)\b', re.IGNORECASE)
def audit_markdown_store(documents):
flagged = []
for doc in documents:
if SUSPICIOUS.search(doc['content']):
flagged.append(doc['id'])
return flaggedRelationship to CVE-2026-7808
Both CVE-2026-7808 and CVE-2026-8445 affect justhtml and were disclosed together on 2026-08-23. They are distinct vulnerabilities:
| CVE-2026-7808 | CVE-2026-8445 | |
|---|---|---|
| Function | sanitize() / sanitize_dom() | to_markdown() |
| Affected | < 1.16.0 | ≤ 1.11.0 |
| Fix | 1.16.0 | 1.12.0 |
| Vector | Sanitization bypass | Serialization escape gap |
Upgrading to justhtml >= 1.16.0 resolves both CVEs.