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-QVC2-MG72-JJHX

GHSA-qvc2-mg72-jjhx: Mutation XSS (mXSS) in justhtml HTML Serializer

Amit Schendel
Amit Schendel
Senior Security Researcher

Mar 19, 2026·7 min read·32 visits

Executive Summary (TL;DR)

justhtml versions prior to 1.12.0 are vulnerable to Mutation XSS when configured with custom sanitization policies allowing <script> or <style> tags, enabling arbitrary JavaScript execution via unescaped closing tags.

The justhtml Python library prior to version 1.12.0 contains a Cross-Site Scripting (XSS) vulnerability due to flawed HTML serialization logic. The serializer preserves the literal text content of raw-text elements like script and style to maintain round-trip fidelity. If an application uses a custom sanitization policy that permits these elements, an attacker can supply closing tag sequences to break out of the context and execute arbitrary JavaScript in the victim's browser.

Vulnerability Overview

The justhtml Python package is a library utilized for building, sanitizing, and serializing HTML documents. Prior to version 1.12.0, the library contains a Mutation Cross-Site Scripting (mXSS) vulnerability within its HTML serialization component. The flaw is tracked under GitHub Advisory GHSA-qvc2-mg72-jjhx and carries a moderate severity rating with a CVSS 4.0 score of 5.3.

The vulnerability is not exposed under the library's default configuration. The default SanitizationPolicy automatically drops the contents of script and style elements, thereby mitigating the attack vector entirely. The issue strictly manifests when developers implement a custom sanitization policy that explicitly permits these raw-text elements.

When a custom policy allows script or style tags, the serializer fails to properly neutralize closing-tag sequences embedded within text nodes. An attacker can leverage this oversight to supply input that the server-side parser treats as a single text node, but the client-side browser interprets as a context breakout. This mismatch enables the execution of arbitrary JavaScript in the victim's browser context.

Root Cause Analysis

The root cause of this vulnerability lies in a feature introduced in justhtml version 1.9.1, designed to improve round-trip parsing fidelity. The maintainers updated the HTML serialization logic to preserve the literal text content of raw-text elements, specifically script and style. This change prevented the serializer from converting necessary syntax characters, such as >, into HTML entities like &gt;, which would otherwise break the intended script or stylesheet logic.

The implementation of this feature relied on a hardcoded set of elements (_LITERAL_TEXT_SERIALIZATION_ELEMENTS) and a function named _serialize_text_for_parent. When the serializer processes a text node, it checks the identity of the parent element. If the parent is a script or style tag, the function returns the text unmodified, bypassing the standard _escape_text routine applied to all other elements.

This exact preservation of text creates a critical desynchronization between the server-side DOM representation and the client-side browser parsing behavior. In the HTML specification, a browser's HTML parser terminates a script context immediately upon encountering the first </script> sequence, regardless of the document structure established by the server. The justhtml serializer failed to account for this client-side mutation behavior.

Because the serializer outputs the text node literally, an attacker can embed a premature closing tag followed by a new, executable HTML element. The server correctly models this as a single string within a script tag, but the browser parses it as the end of the script block followed by a new, active DOM element.

Code Analysis

The vulnerability is isolated to the text serialization routines within serialize.py. Prior to version 1.12.0, the code utilized a static set to identify elements that required literal text preservation. The standard text escaping mechanism was deliberately bypassed for these specific tags.

_LITERAL_TEXT_SERIALIZATION_ELEMENTS = frozenset({"script", "style"})
 
def _serialize_text_for_parent(text: str | None, parent_name: str | None) -> str:
    if not text:
        return ""
    # If the parent is a script or style tag, the text is emitted literally
    if parent_name in _LITERAL_TEXT_SERIALIZATION_ELEMENTS:
        return text
    # Other elements have their text escaped (e.g., <div>, <span>)
    return _escape_text(text)

The fix implemented in version 1.12.0 introduces two primary hardening measures to address the breakout vector. First, the serialization logic was updated to actively identify and neutralize closing-tag sequences (such as </script> or </style>) whenever they appear within the text nodes of raw-text elements. This neutralization ensures that the browser parser cannot prematurely terminate the context.

Second, the sanitization process was strictly hardened to drop any non-text children assigned to script and style tags. By enforcing that these elements can only contain valid text nodes, the maintainers eliminated the possibility of complex, nested DOM structures being mis-serialized into executable contexts. These dual measures effectively close the mXSS vector while maintaining the required round-trip fidelity.

Exploitation

Exploitation of this vulnerability requires a specific prerequisite: the target application must instantiate justhtml using a custom SanitizationPolicy that permits script or style tags. The attacker must then identify an input field or parameter processed by this configuration.

The attack payload is designed to initiate a valid script context, immediately break out of it using a literal closing tag, and append a subsequent payload. For example, an attacker supplies the string alert('injected'); </script><svg onload=alert('broken-out')>. The application processes this input and, due to the vulnerability, emits it exactly as provided without HTML entity encoding.

from justhtml import JustHTML, SanitizationPolicy
 
# 1. Define a custom policy that allows script or style tags
policy = SanitizationPolicy(
    allowed_tags=["script", "body"],
    allowed_attributes={}
)
 
# 2. Payload designed to break out of the script tag
xss_payload = "alert('injected'); </script><svg onload=alert('broken-out')>"
html_input = f"<body><script>{xss_payload}</script></body>"
 
# 3. Sanitize and serialize
sanitized_output = JustHTML(html_input, policy=policy).to_html()
 
# 4. Observe the result (Vulnerable output shown below)
print(sanitized_output)
# <body><script>alert('injected'); </script><svg onload=alert('broken-out')></script></body>

When the resulting HTML is rendered by a user's browser, the parser encounters the </script> tag and terminates the script context. It then processes the newly exposed <svg onload=alert('broken-out')> element, executing the embedded JavaScript. The final dangling </script> tag is typically ignored or discarded by the browser's error-handling routines.

Impact Assessment

The vulnerability carries a CVSS 4.0 vector string of CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N. This scoring reflects a network-based attack vector with low complexity, requiring no specialized access privileges but mandating user interaction. The vulnerable system itself suffers no direct confidentiality, integrity, or availability impact.

The concrete security impact is measured on the subsequent system, which is the web browser of the user viewing the serialized output. Successful exploitation grants the attacker the ability to execute arbitrary JavaScript within the context of the user's active session. This represents a classic Stored or Reflected Cross-Site Scripting (XSS) scenario, depending on how the application handles the input.

With JavaScript execution capabilities, an attacker can access sensitive session tokens, manipulate the Document Object Model (DOM), or perform actions on behalf of the authenticated user. Applications heavily reliant on custom HTML sanitization for user-generated content are at the highest risk if they explicitly allow raw-text elements.

The scope of the vulnerability is strictly confined to the client side. The server infrastructure executing the justhtml library remains entirely unaffected, as the flaw pertains exclusively to the generation of output that is misinterpreted by a downstream consumer.

Remediation

The primary remediation strategy is to upgrade the justhtml dependency to version 1.12.0 or later. This release introduces the necessary neutralization routines for closing-tag sequences and strictly enforces text-only children for raw-text elements. The update resolves the serialization flaw without requiring structural changes to the consuming application.

For environments where immediate patching is unfeasible, administrators must audit and modify their application code. Developers should inspect all instances of SanitizationPolicy and explicitly remove script and style from the allowed_tags parameter. Reverting to the default sanitization behavior entirely eliminates the attack surface.

Developers should review the official JustHTML Sanitization Documentation for comprehensive guidance on secure, context-aware serialization. Maintaining an aggressive deny-by-default posture for raw-text elements is a standard defense-in-depth practice for HTML sanitizers.

Technical Appendix

CVSS Score
5.3/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N

Affected Systems

justhtml HTML serialization libraryPython applications utilizing custom justhtml SanitizationPolicy configurations

Affected Versions Detail

Product
Affected Versions
Fixed Version
justhtml
EmilStenstrom
< 1.12.01.12.0
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork
CVSS Base Score5.3
Exploit StatusPoC Available
ImpactCross-Site Scripting (XSS)
Patch StatusFixed in 1.12.0

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
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.

Vulnerability Timeline

Version 1.3.0 released, hardening <noscript> handling against differential parsing mXSS.
2026-01-28
Version 1.9.1 released, introducing literal preservation in script/style elements, creating the mXSS vulnerability under custom policies.
2026-03-10
Version 1.12.0 released, fixing the mXSS breakout vulnerability.
2026-03-17
GitHub Advisory GHSA-qvc2-mg72-jjhx published.
2026-03-18

References & Sources

  • [1]GitHub Advisory GHSA-qvc2-mg72-jjhx
  • [2]justhtml Package Repository
  • [3]justhtml Changelog
  • [4]OSV Vulnerability Database Entry
  • [5]JustHTML Sanitization Documentation

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

•40 minutes ago•CVE-2026-59163
9.1

CVE-2026-59163: Critical JWT Authentication Bypass in Mnemosyne Sync Server

CVE-2026-59163 is a critical authentication bypass vulnerability in the Mnemosyne sync server. In versions prior to v3.10.1, the server's authentication logic decoded incoming JSON Web Tokens (JWT) but completely skipped cryptographic signature verification. An unauthenticated remote attacker can exploit this vulnerability to bypass authentication, impersonate arbitrary users, read synchronized AI agent states, or write malicious database updates.

Alon Barad
Alon Barad
2 views•7 min read
•about 2 hours ago•CVE-2026-85058
7.5

CVE-2026-85058: Missing Authorization in Moquette MQTT Broker Last Will and Testament Feature

An authorization bypass vulnerability exists in the Moquette MQTT broker prior to version 0.18.1. When an MQTT client registers a Last Will and Testament (LWT) topic during its connection setup, the broker fails to perform write-access checks on that topic. Upon an abrupt client disconnection, the broker publishes the registered Will message to subscribers of the unauthorized topic, bypassing configured Access Control Lists (ACLs).

Amit Schendel
Amit Schendel
5 views•8 min read
•about 3 hours ago•CVE-2026-71537
6.5

CVE-2026-71537: Credit-Refund Double-Spend Race Condition in Paymenter Service Downgrade

A concurrent execution vulnerability (CWE-362) exists in the Paymenter webshop solution within the service downgrade execution path (doUpgrade). Authenticated customers can exploit this concurrency issue by sending concurrent HTTP requests to trigger multiple parallel executions of the refund process. Because the application checks for pending upgrades without database transactional isolation or exclusive row locks, attackers can generate multiple duplicate refunds to their account balance for a single downgrade action. This leads to arbitrary credit inflation on the platform.

Amit Schendel
Amit Schendel
5 views•9 min read
•about 4 hours ago•GHSA-XWMW-PRC4-V3CR
8.8

GHSA-XWMW-PRC4-V3CR: OAuth Dynamic Client Registration Enables API Token Theft via Audience Confusion in Obot Platform

A critical security vulnerability exists in the Obot Platform (versions < 0.23.0) where unauthenticated OAuth dynamic client registration, a consentless authorization flow, and a lack of JWT audience validation enable remote attackers to steal API tokens via audience confusion.

Alon Barad
Alon Barad
6 views•6 min read
•about 5 hours ago•GHSA-PR6H-VR44-XQ8J
5.3

GHSA-PR6H-VR44-XQ8J: Authentication Bypass in Obot Model Context Protocol (MCP) Registry API

An authentication bypass vulnerability in Obot versions <= v0.22.1 allows unauthenticated remote attackers to access Model Context Protocol (MCP) registry metadata and retrieve server lists when OBOT_SERVER_ENABLE_REGISTRY_AUTH is configured. This is due to a routing logic flaw where `/v0.1` paths are incorrectly categorized as public frontend user interface assets.

Amit Schendel
Amit Schendel
5 views•8 min read
•about 10 hours ago•GHSA-JGH3-FGGC-MCPM
7.6

GHSA-jgh3-fggc-mcpm: Non-Blind Server-Side Request Forgery (SSRF) in Obot Platform

An authenticated Server-Side Request Forgery (SSRF) vulnerability in the Obot Platform allows administrative or power users to bypass IP verification and scan or query internal resources, private networks, and cloud instance metadata services (IMDS). Because response bodies and error details are reflected back to the client interface, this constitutes a non-blind SSRF.

Alon Barad
Alon Barad
9 views•8 min read