CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-5VP3-3CG6-2RQ3

GHSA-5VP3-3CG6-2RQ3: Cross-Site Scripting via Markdown Serialization Breakout in justhtml

Alon Barad
Alon Barad
Software Engineer

Mar 24, 2026·7 min read·94 visits

Executive Summary (TL;DR)

justhtml < 1.13.0 fails to dynamically size backtick fences when serializing <pre> tags to Markdown, enabling XSS through code block breakouts.

The Python library `justhtml` versions prior to 1.13.0 suffer from a Cross-Site Scripting (XSS) vulnerability due to improper handling of HTML `<pre>` elements during Markdown serialization. This flaw permits attackers to break out of generated Markdown code blocks and execute arbitrary JavaScript when the output is processed by downstream Markdown renderers.

Vulnerability Overview

The justhtml package is a Python library designed for handling and serializing HTML content into Markdown formats. It processes raw HTML elements and translates them into corresponding Markdown syntax for downstream rendering. This library is commonly utilized in content management systems, static site generators, and web applications that require automated Markdown conversion.

A security vulnerability exists in versions of justhtml prior to 1.13.0, categorized under CWE-79 (Cross-site Scripting) and CWE-74 (Injection). The vulnerability specifically manifests during the serialization of HTML <pre> elements. The serialization engine fails to properly sanitize or encapsulate nested code block delimiters supplied within the raw input.

This flaw permits an attacker to inject arbitrary HTML payloads that survive the Markdown conversion process. When the resulting Markdown is subsequently parsed by standard engines like CommonMark or GitHub Flavored Markdown (GFM), the injected payload is rendered as raw HTML. This results in Cross-Site Scripting (XSS) if the downstream application serves this content to end users without secondary sanitization.

Root Cause Analysis

The root cause of this vulnerability lies in the to_markdown() method within the justhtml serialization engine, specifically located in the src/justhtml/node.py file. When the engine encounters an HTML <pre> element, it attempts to convert it into a Markdown fenced code block. The implementation utilized a static, hardcoded three-backtick sequence to delineate the start and end of this block.

The serialization logic did not inspect the inner text of the <pre> element for matching backtick sequences. If the user-supplied content contained three or more consecutive backticks, the generated Markdown document would contain nested or premature closing delimiters. The parser systematically prepended and appended the hardcoded fence around the raw input without validating the structural integrity of the output.

Markdown specifications dictate that a fenced code block terminates when it encounters a closing fence of equal or greater length than the opening fence. By supplying a payload containing exactly three backticks, an attacker forcibly terminates the code block prematurely. Any content following this sequence within the original <pre> tag is then treated as standard Markdown or raw HTML by downstream renderers.

Code Analysis

Prior to version 1.13.0, the justhtml codebase handled <pre> tag serialization by blindly wrapping the innerHTML with static backticks. This approach failed to account for adversarial input designed to break the structural boundaries of the generated Markdown document. The lack of dynamic fence sizing created a direct injection vector for subsequent processing stages.

The patch introduced in commit f35f8f723c713bd8f912d86e9ec6881275ff5af9 remediates this issue by implementing a dynamic backtick fence calculation. A new function, _markdown_backtick_fence, scans the input string to determine the longest contiguous run of backtick characters. It then generates a boundary marker that is strictly at least one character longer than the longest identified sequence.

This defensive programming approach ensures that the opening and closing fences will always safely encapsulate the inner content, regardless of how many backticks the attacker provides. The relevant patched logic is demonstrated in the code snippet below, highlighting the calculation methodology. By iterating through the string, the parser guarantees mathematical precedence over the attacker's input.

# Updated logic in src/justhtml/node.py
def _markdown_backtick_fence(s: str | None, *, minimum: int) -> str:
    if s is None: s = ""
    longest = 0
    run = 0
    for ch in s:
        if ch == "`":
            run += 1
            if run > longest:
                longest = run
        else:
            run = 0
    return "`" * max(minimum, longest + 1)

The following architecture diagram illustrates the parser flow and where the dynamic calculation mitigates the breakout path. This visual representation clarifies the state transitions during serialization. The parser strictly enforces the boundary markers before yielding the final document.

Exploitation

Exploitation requires the attacker to submit a crafted HTML string containing a <pre> element to an application utilizing a vulnerable version of justhtml. The payload must contain a sequence of backticks sufficient to close the hardcoded Markdown fence, followed by the actual XSS payload. No authentication is inherently required unless the target application enforces access controls on the input mechanism.

The provided proof-of-concept demonstrates the exact attack mechanics against the vulnerable parser logic. The attacker submits a carefully structured HTML payload containing encoded backticks. The justhtml.to_markdown() function serializes this input, placing the attacker's backticks directly after its own opening sequence.

from justhtml import JustHTML
 
vulnerable_html = "<pre>&#96;&#96;&#96;\n&lt;img src=x onerror=alert(1)&gt;</pre>"
doc = JustHTML(vulnerable_html, fragment=True)
print(doc.to_markdown())
# Output:
# ```
# ```
# <img src=x onerror=alert(1)>
# ```

This execution results in an empty code block, immediately followed by the raw &lt;img src=x onerror=alert(1)&gt; tag on a new line. Downstream Markdown parsers interpret this raw HTML tag verbatim, executing the embedded JavaScript. This completely bypasses intended sanitization filters that operate under the assumption that the justhtml output is purely text-based Markdown.

Impact Assessment

The successful exploitation of this vulnerability results in arbitrary JavaScript execution within the context of the victim's browser session. This manifests as a classic Stored or Reflected XSS scenario, contingent upon how the application handles the serialized Markdown output. The primary impact is isolated to the client side, but the consequences for affected users remain severe.

An attacker leveraging this flaw can steal active session cookies, capture anti-CSRF tokens, or perform unauthorized administrative actions on behalf of the victim. If the targeted victim holds elevated privileges within the application, the attacker can systematically pivot this client-side execution into broader system compromise or sensitive data exfiltration.

The vulnerability severity is classified as High due to the low attack complexity and the complete lack of required privileges to trigger the payload in typical deployment architectures. Applications that rely exclusively on justhtml for sanitization before passing data to a Markdown renderer are completely exposed to this structural injection vector.

Variant Analysis

The implemented patch in version 1.13.0 effectively neutralizes the primary code block breakout vector by ensuring dynamic fence sizing. Security engineers must analyze the broader serialization engine for similar structural vulnerabilities. Markdown relies heavily on specific delimiter sequences, such as asterisks for emphasis or hashes for headers, which present similar architectural challenges if not handled dynamically.

An examination of the remaining justhtml codebase indicates that other block-level elements do not currently require the same dynamic delimiter calculation. Elements like <blockquote> or <ul> do not use closing fences in the same structural manner as <pre>. The current patch logic is tightly localized to the to_markdown() backtick enumeration phase and completely resolves the identified CWE-79 vector.

Furthermore, the patch assumes the downstream Markdown parser perfectly adheres to the CommonMark specification regarding backtick fence length termination. If a specific downstream implementation contains parsing discrepancies or edge cases regarding fence termination limits, highly specific variant breakouts might occur. Developers must pair the upgraded justhtml library with a strictly compliant, standardized Markdown renderer to prevent cross-parser exploitation scenarios.

Remediation

The primary and most effective remediation strategy is to immediately upgrade the justhtml library to version 1.13.0 or later. This version contains the dynamic fence calculation logic that mathematically guarantees the strict encapsulation of backtick sequences within <pre> elements. Package managers can apply this update seamlessly via pip install justhtml>=1.13.0.

In environments where immediate patching is not technically feasible, developers must implement secondary, post-processing sanitization controls. This involves passing the finalized output of justhtml through a robust, dedicated HTML sanitizer, such as bleach, before rendering the content in the DOM. Alternatively, downstream Markdown renderers can be strictly configured to universally forbid the processing of raw HTML tags.

Security teams should systematically audit their codebases to identify all instances where untrusted user input is passed to JustHTML.to_markdown(). Implementing strict Content Security Policy (CSP) headers provides a critical defense-in-depth layer, inherently restricting the execution of inline scripts and sharply mitigating the impact of any successful XSS injections.

Official Patches

GitHub AdvisoryOfficial GitHub Security Advisory for justhtml XSS

Fix Analysis (1)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Affected Systems

Python applications utilizing the `justhtml` library for HTML to Markdown serializationContent Management Systems (CMS) relying on `justhtml` for user content processingApplications passing `justhtml` output directly into Markdown-to-HTML engines like GFM or CommonMark

Affected Versions Detail

Product
Affected Versions
Fixed Version
justhtml
EmilStenstrom
< 1.13.01.13.0
AttributeDetail
CWE IDCWE-79, CWE-74
Attack VectorNetwork
CVSS v3.1 Score7.5 (High)
ImpactArbitrary JavaScript Execution
Exploit StatusProof of Concept Available
KEV StatusNot Listed
Affected Componentjusthtml.to_markdown()
RemediationUpgrade to >= 1.13.0

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.

Known Exploits & Detection

Security Research PoCProof of concept demonstrating the Markdown code block breakout using backticks inside a pre element.

Vulnerability Timeline

Vulnerability identified during community security review
2025-12-01
Fix commit f35f8f723c713bd8f912d86e9ec6881275ff5af9 authored
2026-03-21
Version 1.13.0 released and GHSA published
2026-03-31

References & Sources

  • [1]GitHub Advisory: GHSA-5VP3-3CG6-2RQ3
  • [2]justhtml Project Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•2 days ago•CVE-2026-58197
8.8

CVE-2026-58197: Host Escape and Lateral Movement via Insecure Container Network Defaults in ToolHive

A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.

Amit Schendel
Amit Schendel
9 views•8 min read
•2 days ago•CVE-2026-63405
5.9

CVE-2026-63405: Insufficient Verification of Data Authenticity in AnyCable Pusher REST API

AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.

Amit Schendel
Amit Schendel
9 views•5 min read
•2 days ago•CVE-2026-64847
6.8

CVE-2026-64847: Indefinite Denial of Service via Undrained Stderr in AnyIO Process Pool Workers

A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.

Amit Schendel
Amit Schendel
12 views•6 min read
•2 days ago•CVE-2026-63349
7.0

CVE-2026-63349: Privilege Dropping Bypass and Denial of Service in AnyIO Subprocess Module

CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.

Alon Barad
Alon Barad
22 views•5 min read
•2 days ago•CVE-2026-63406
5.9

CVE-2026-63406: Information Disclosure via Insecure Telemetry and Hardcoded Credentials in AnyCable-Go

CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.

Alon Barad
Alon Barad
7 views•5 min read
•2 days ago•CVE-2026-84992
6.1

CVE-2026-84992: Cross-Site Scripting (XSS) via Fenced Code Block Parsing in md-editor-v3

CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.

Amit Schendel
Amit Schendel
11 views•6 min read