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·72 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

•34 minutes ago•GHSA-2RP4-X2J7-QMCC
8.2

GHSA-2RP4-X2J7-QMCC: Stored Cross-Site Scripting via Draft Names in Craft CMS Control Panel

An authenticated stored Cross-Site Scripting (XSS) vulnerability exists in the Control Panel helper of Craft CMS before version 5.10.8. Due to lack of HTML entity encoding within the elementLabelHtml method, unescaped draft names are rendered directly into administrative interfaces.

Amit Schendel
Amit Schendel
0 views•6 min read
•about 2 hours ago•GHSA-7HXC-F267-H5Q7
4.9

GHSA-7HXC-F267-H5Q7: Path Traversal via Validation-then-Normalization in Craft CMS

A path traversal vulnerability exists in the local filesystem driver of Craft CMS. Due to validation occurring before path normalization, directory containment checks can be bypassed by utilizing specific protocol schemes like 'file://' along with directory traversal sequences. This allows authenticated users with administrative privileges to access or manipulate files outside the defined storage root directory.

Alon Barad
Alon Barad
0 views•8 min read
•about 3 hours ago•GHSA-RVMM-V933-JGXQ
5.3

GHSA-rvmm-v933-jgxq: Missing Authorization Check in Craft CMS ChartsController

An authorization bypass vulnerability in Craft CMS allows unauthenticated or low-privileged users to query and obtain sensitive time-series user registration counts and demographic metrics. This is due to a missing authorization check inside the actionGetNewUsersData endpoint of the ChartsController class.

Alon Barad
Alon Barad
1 views•6 min read
•about 4 hours ago•GHSA-596P-6JV8-775V
5.1

GHSA-596p-6jv8-775v: Authenticated Leak of Secret Environment Variables in Craft CMS

An authenticated information disclosure vulnerability in Craft CMS allows high-privilege administrators to extract sensitive environment variables, including the CRAFT_SECURITY_KEY and database credentials, using a blind error-based template injection attack within element select condition rules.

Amit Schendel
Amit Schendel
3 views•5 min read
•about 5 hours ago•CVE-2026-71554
5.3

CVE-2026-71554: HTTP Request Smuggling via Duplicate Host Headers in h2 Protocol Stack

A protocol-parsing vulnerability in the pure-Python HTTP/2 library 'h2' (versions <= 4.4.0) allows unauthenticated remote attackers to perform HTTP Request Smuggling (CWE-444). The vulnerability exists because the library does not validate the uniqueness of 'Host' headers in incoming HTTP/2 request streams. When an upstream gateway parses such requests and downgrades them to HTTP/1.1 for internal backend servers, the resulting stream contains duplicate Host headers, which leads to parsing inconsistency and potential bypass of security filters.

Alon Barad
Alon Barad
3 views•5 min read
•about 6 hours ago•GHSA-957R-QF9P-67XW
4.9

GHSA-957R-QF9P-67XW: Arbitrary File Read via SplFileObject in Craft CMS Twig Extension

An information disclosure vulnerability in Craft CMS allows users with administrative or non-sandboxed template-authoring privileges to read arbitrary system and configuration files. The issue stems from an incomplete class instantiation blocklist in the Twig template extension, which omitted PHP's built-in SplFileObject class.

Alon Barad
Alon Barad
4 views•6 min read