Sep 18, 2026·6 min read·4 visits
A flaw in the HTML deserialization routines of @platejs/core prior to 53.3.11 allowed remote attackers to execute arbitrary client-side scripts via unauthenticated user interaction with crafted HTML payloads.
Plate core HTML deserialization APIs parse supplied HTML strings in the active document. When an application passes untrusted or cross-user HTML to these APIs, certain HTML attributes can trigger browser behavior before the HTML is converted into editor nodes.
The vulnerability identified as CVE-2026-88976 is an improper neutralization of input during web page generation (Cross-Site Scripting, CWE-79) within the @platejs/core library, a package of the udecode/plate rich-text editor framework. The vulnerability specifically resides within the HTML deserialization utility functions, which convert raw HTML strings into structured editor nodes. Applications using this package to parse and render user-submitted HTML strings are susceptible to client-side code execution.
The attack surface is exposed whenever an application accepts HTML from untrusted sources or other users and passes it to the Plate deserialization APIs. This often occurs during common editor interactions such as pasting formatted content from the clipboard, uploading HTML templates, or rendering persistent documents stored in a shared database. Under vulnerable configurations, the parser operates within the active document scope, triggering immediate code execution before sanitization controls can be applied.
This security flaw carries a Common Vulnerability Scoring System (CVSS) base score of 6.1, indicating a moderate risk level. The exploit complexity is classified as low, and no special authentication privileges are required to initiate the attack vector. However, exploitation relies on user interaction, requiring a target user to view the malicious payload within their active browser session.
The core deficiency resides in the mechanism utilized by @platejs/core to parse HTML strings prior to version 53.3.11. The library relied on standard DOM manipulation APIs associated with the active window context to parse the string. Specifically, functions such as htmlStringToDOMNode created a body element using document.createElement('body') and subsequently assigned the untrusted HTML payload directly to its innerHTML property.
When raw HTML is assigned to the innerHTML property of an element linked to the active document, the browser's HTML parser executes side effects immediately. Although standard <script> tags are not executed when inserted via innerHTML, other elements with executable lifecycle event handlers, such as <img src="invalid" onerror="..."> or elements that permit raw JavaScript execution such as <svg><script>...</script></svg>, are executed without restriction. This occurs because the elements are processed within the scripting-enabled context of the active window object.
The browser handles these elements as active components of the current document tree, causing synchronous loading of external resources and immediate execution of associated event handlers. This behavior precedes any secondary processing or structured conversion performed by the Slate editor. The root of the vulnerability is the lack of an inert parsing context, allowing malicious vectors to achieve execution during the initial parsing phase.
In vulnerable versions of @platejs/core, the functions responsible for translating strings into DOM structures processed inputs directly in the active browser document. Below is the vulnerable implementation found in packages/core/src/lib/plugins/html/utils/htmlStringToDOMNode.ts.
// Vulnerable Implementation
export const htmlStringToDOMNode = (rawHtml: string) => {
const node = document.createElement('body');
// Vulnerability: innerHTML assignment in the active document triggers immediate execution
node.innerHTML = rawHtml;
return node;
};To resolve this issue, the package developers shifted parsing operations from the active window context to an inert document context. The patch introduces DOMParser to parse the markup, which natively suppresses script execution and external resource loading. Below is the patched implementation of the utility functions.
// Patched Implementation
import { parseHtmlDocument } from './parseHtmlDocument';
export const htmlStringToDOMNode = (rawHtml: string) =>
parseHtmlDocument(rawHtml).body;The helper function parseHtmlDocument operates as follows:
export const parseHtmlDocument = (html: string): Document => {
// Parsing with DOMParser inside an inert text/html context disables script execution
return new DOMParser().parseFromString(html, 'text/html');
};Similarly, the static deserialization routine in packages/core/src/static/deserialize/htmlStringToEditorDOM.ts was updated to utilize the inert context.
// Patched static deserializer
export const getEditorDOMFromHtmlString = (html: string) => {
const document = new DOMParser().parseFromString(html, 'text/html');
const editorNode = document.querySelector('[data-slate-editor="true"]');
return editorNode as HTMLElement;
};This architectural change guarantees that any executable payload remains inactive during the deserialization phase.
Exploiting this vulnerability requires the attacker to inject a crafted HTML string containing an event-based payload into an application interface that parses user input via the vulnerable Plate core utility functions. For example, the attacker can leverage copy-and-paste mechanisms or input fields that submit content to be stored and subsequently processed.
The attack flow is structured as follows:
During the innerHTML assignment, the browser resolves the malformed DOM nodes and triggers the event handlers immediately. This allows the attacker to execute arbitrary JavaScript in the context of the client session.
For exploitation to succeed, the target user must load a view containing the malicious markup. There are no other prerequisites, such as specialized software configurations or elevated system privileges. The payload operates entirely within the security origin of the consuming application, enabling actions such as session hijacking, unauthorized token retrieval, or DOM tampering.
The impact of a successful exploitation of CVE-2026-88976 corresponds directly to standard Client-Side Cross-Site Scripting. An attacker executing arbitrary script in the context of the application's origin can access sensitive data stored in local storage, session storage, or document cookies. This access enables the compromise of user sessions and authorization tokens.
Additionally, the script can perform actions on behalf of the victim, such as modifying UI elements, keylogging, or sending unauthorized API requests. This threat is particularly significant in applications that manage sensitive financial, personal, or administrative data. Because the scope of the vulnerability changes from the application to the client-side browser environment, the Scope (S) metric in the CVSS vector is rated as Changed.
Currently, the exploit status of this vulnerability is documented as proof-of-concept. There is no public threat intelligence indicating active exploitation in wild ransomware campaigns or inclusion in the CISA Known Exploited Vulnerabilities catalog. Nevertheless, the ease of crafting compatible HTML payloads maintains a continuous risk for unpatched deployments.
The primary remediation strategy is to upgrade @platejs/core to version 53.3.11 or later. For projects currently utilizing beta builds from the 54.0.0 branch, developers must revert to the patched stable version or apply custom overrides to ensure that parsing is routed through an inert DOMParser instance.
It is critical to recognize that inert parsing prevents code execution only during the deserialization process. It does not sanitize the resulting structure. Callers remain fully responsible for the content they subsequently render to the active DOM. Consequently, a secondary sanitization layer such as DOMPurify must be implemented before rendering deserialized editor nodes.
import DOMPurify from 'dompurify';
const sanitizedHtml = DOMPurify.sanitize(untrustedHtml);Furthermore, developers should configure Content Security Policy (CSP) headers to restrict script sources and eliminate inline scripts. Restricting URL schemas within custom Slate configuration blocks is also recommended to prevent the execution of javascript: links during user interactions. These defense-in-depth measures provide defense if a sanitizer bypass occurs.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@platejs/core udecode | < 53.3.11 | 53.3.11 |
@platejs/core udecode | >= 54.0.0-beta.0 <= 54.0.0-beta.1 | 53.3.11 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 6.1 (Medium) |
| EPSS Score | N/A |
| Exploit Status | PoC (Proof-of-Concept) |
| KEV Status | Not Listed |
The product does not sanitize or incorrectly sanitizes user-controlled input before including it in a web page during parsing, which allows dynamic scripting elements to run.
An infinite loop vulnerability in ReactPHP's react/http chunked transfer encoding decoder (v0.6.0 up to 1.11.1) allows unauthenticated remote attackers to trigger a denial of service (DoS) by sending crafted chunked requests or responses, completely freezing the single-threaded event loop and pegging CPU usage to 100%.
A polynomial-time Regular Expression Denial of Service (ReDoS) vulnerability in Soup Sieve versions prior to 2.9 allows remote unauthenticated attackers to cause CPU exhaustion and thread-pool denial of service. The vulnerability resides in the trailing whitespace and comment preprocessing step of the CSS parser. An attacker can trigger quadratic backtracking by submitting a crafted CSS selector string containing a long run of internal spaces or comments terminated by a non-matching token. This blocks the Python Global Interpreter Lock (GIL) and halts worker threads.
A regular expression denial of service (ReDoS) vulnerability in Soup Sieve prior to version 2.9 allows remote attackers to cause CPU exhaustion and service disruption. The issue lies within the definition of the IDENTIFIER and VALUE selector sub-patterns in the CSS parser component, which uses overlapping adjacent quantified groups. When parsing long, crafted, or unclosed CSS selectors, backtracking-based regular expression engines experience quadratic performance degradation. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected.
A protocol-level validation bypass in CoreDNS versions prior to 1.14.7 allows unauthenticated remote attackers to proxy unauthorized DNS UPDATE messages (Opcode 5) using modern alternative transport layers such as DoH, DoH3, DoQ, and gRPC. If upstream authoritative servers trust the CoreDNS server's source IP and do not enforce TSIG authentication, attackers can inject, alter, or delete DNS zone records, leading to potential zone takeover or traffic redirection.
A path traversal vulnerability exists in Grav CMS versions prior to 2.0.16. The flaw occurs within the file validation mechanisms of the MediaUploadTrait, enabling authenticated users with media management privileges to bypass sandbox limitations. This allows the deletion of arbitrary files on the filesystem, which can result in denial of service or remote code execution.
An unauthenticated directory traversal vulnerability exists in Grav CMS prior to version 2.0.15. Due to an insecure string-based containment check (str_starts_with) in the pre-boot static asset server, attackers can read files in sibling directories sharing a prefix with the configured asset path when plugin-asset-map.php is enabled.