Oct 28, 2026·10 min read·5 visits
TinyMCE's media plugin instantiates HTML elements from data-mce-object placeholders inside the active, scripting-enabled document before sanitization occurs. Attackers can inject stored XSS payloads using data-mce-p-onerror attributes on video, audio, or img placeholders. Users must upgrade to TinyMCE 7.9.4 or 8.9.3, or disable the media plugin.
CVE-2026-116675 is a stored Cross-Site Scripting (XSS) vulnerability in the TinyMCE rich text editor affecting versions 7.9.3 and 8.5.1 through 8.9.2. The flaw resides in the media plugin's serialization logic, where placeholder elements configured with the data-mce-object attribute are converted into real DOM elements using the active window's document context prior to sanitization. When an attacker supplies a media element with an invalid source attribute alongside an inline event handler attribute such as data-mce-p-onerror, the browser resolves the resource and executes the malicious JavaScript before the sanitizer can strip the dangerous attributes.
TinyMCE is an open-source, web-based rich text editor widely integrated into content management systems, enterprise web applications, and messaging platforms. To allow users to embed rich media without executing active content during editing sessions, TinyMCE utilizes an internal placeholder abstraction mechanism within its media plugin. When content is loaded into or serialized out of the editor, the plugin converts between safe placeholder elements (such as <span> or <img> tags) and standard HTML5 media elements (<video>, <audio>, <iframe>, <object>).
CVE-2026-116675 (tracked internally as TINYMCE-14932 and published under GHSA-mf2p-h6hf-fcwm) is a stored Cross-Site Scripting (XSS) vulnerability classified under CWE-79. The vulnerability specifically impacts TinyMCE deployments where the media plugin is enabled. The affected release ranges are version 7.9.3 in the 7.x branch and versions 8.5.1 through 8.9.2 in the 8.x branch.
The core issue stems from an insecure order of operations combined with the use of an active DOM document context during element reconstruction. When the media plugin processes elements containing the data-mce-object attribute, it dynamically creates the corresponding HTML element in the active browser document before passing the node tree to the HTML sanitizer. This premature instantiation in a live document allows attacker-controlled event handlers (e.g., onerror, onload) to fire immediately upon attribute assignment.
Because rich text editors are frequently used to author content viewed or edited by other users, an attacker with low privileges (such as a standard user submitting a comment, profile bio, or article draft) can store a crafted payload in the application backend. When another user or administrator loads the content into a TinyMCE instance or triggers serialization, the malicious script executes within the victim's authenticated browser session.
To represent complex media objects within the editable canvas without allowing native playback or layout disruption, TinyMCE converts media elements into placeholder nodes. During this process, the original tag name is stored in the data-mce-object attribute (for example, data-mce-object="video"), while the original attributes of the media element are prefixed with data-mce-p- (for example, src="/file.mp4" becomes data-mce-p-src="/file.mp4").
When the editor serializes the content or filters nodes via modules/tinymce/src/plugins/media/main/ts/core/FilterContent.ts, the helper function buildMediaElement(editor, node) is invoked to transform these abstract AST (Abstract Syntax Tree) placeholder nodes back into their target HTML representations. During this reconstruction phase, the function extracts the target tag name from node.attr('data-mce-object') and iterates through the data-mce-p-* attributes to copy them onto the newly created element before running sanitization checks.
In vulnerable versions of TinyMCE, buildMediaElement instantiated the target HTML element by calling document.createElement(realElmName). In a browser environment, window.document represents the active, scripting-enabled document of the host page. According to the HTML specification, certain elements—specifically <img>, <video>, and <audio>—begin resource fetching and lifecycle processing immediately when their src attributes are assigned via DOM APIs, even if the element has not yet been appended to the main document tree (document.body).
Consequently, when buildMediaElement copied both a non-existent source (data-mce-p-src="/missing.mp4") and an error handler (data-mce-p-onerror="...") onto the live DOM element, the browser queued a network request for the missing resource and triggered the onerror JavaScript callback. This execution occurred before the reconstructed element was serialized and passed to the Sanitize module, completely bypassing TinyMCE's XSS filtering mechanisms.
The vulnerability was addressed in commit bd1ab5ef024c648c3f9c0b409e54a6c227c7d8a2 by modifying how FilterContent.ts instantiates temporary DOM elements during media node reconstruction. Prior to the fix, the code directly referenced the global document object.
// Vulnerable implementation in modules/tinymce/src/plugins/media/main/ts/core/FilterContent.ts
const buildMediaElement = (editor: Editor, node: AstNode) => {
const realElmName = node.attr('data-mce-object') as string;
// FLAW: Creates element in the active, scripting-enabled document context
const element = document.createElement(realElmName);
// Add width/height to everything but audio
if (realElmName !== 'audio') {
// ... subsequent code copies data-mce-p-* attributes to `element`To eliminate the active execution context during element assembly, the patch introduces a singleton helper function, createNonScriptingDocument(), which uses the DOMParser API to generate an inert HTML document. Elements are then constructed using doc.createElement(realElmName) rather than document.createElement(realElmName).
// Patched implementation in modules/tinymce/src/plugins/media/main/ts/core/FilterContent.ts
let nonNonScriptingDocument: Document;
const createNonScriptingDocument = () => {
if (Type.isNonNullable(nonNonScriptingDocument)) {
return nonNonScriptingDocument;
}
// FIX: DOMParser creates an inert document where scripting and resource fetching are disabled
nonNonScriptingDocument = new DOMParser().parseFromString('', 'text/html');
return nonNonScriptingDocument;
};
const buildMediaElement = (editor: Editor, node: AstNode) => {
const doc = createNonScriptingDocument();
const realElmName = node.attr('data-mce-object') as string;
// FIX: Element is instantiated inside the inert document
const element = doc.createElement(realElmName);
// Add width/height to everything but audio
if (realElmName !== 'audio') {According to the WHATWG HTML Living Standard, any Document object created via DOMParser.prototype.parseFromString() has its scripting flag disabled and has no active browsing context (defaultView is null). When elements such as <img>, <video>, or <audio> are created within an inert document, the browser does not initiate network requests for src attributes and refuses to compile or dispatch inline event handlers like onerror or onload.
This fix is complete and directly addresses the root cause of the vulnerability. By isolating the temporary DOM manipulation within a cached, non-scripting Document instance, buildMediaElement can safely manipulate attributes and construct the DOM subtree before handing the output off to the downstream sanitization routines. Variant attacks relying on other immediate lifecycle events (such as <svg onload> or <iframe onload>) within buildMediaElement are similarly neutralized by the inert document context.
Exploiting CVE-2026-116675 requires two conditions: the target application must have the TinyMCE media plugin enabled in its configuration, and the attacker must be able to supply HTML markup that is loaded into or processed by the editor. No special timing conditions or complex user interactions are necessary; the payload executes passively whenever the editor processes the malicious markup.
An attacker crafts an HTML element—typically a <span> or <p> tag—that mimics TinyMCE's internal media placeholder structure. The element must include the data-mce-object attribute set to an HTML tag that supports resource loading events (video, audio, or img). The attacker then adds data-mce-p-src pointing to an invalid or non-existent URI (e.g., /missing.mp4) to force a load failure, alongside data-mce-p-onerror containing the arbitrary JavaScript payload.
The official regression test suite in modules/tinymce/src/plugins/media/test/ts/browser/SanitizationTest.ts provides three canonical Proof-of-Concept (PoC) vectors demonstrating the vulnerability across different media types:
<!-- Vector 1: Video Placeholder -->
<span data-mce-object="video" data-mce-p-src="/missing.mp4" data-mce-p-onerror="alert(document.domain)">x</span>
<!-- Vector 2: Audio Placeholder -->
<span data-mce-object="audio" data-mce-p-src="/missing.mp3" data-mce-p-onerror="alert(document.domain)">x</span>
<!-- Vector 3: Image Placeholder -->
<span data-mce-object="img" data-mce-p-src="/missing.jpg" data-mce-p-onerror="alert(document.domain)">x</span>In a typical attack scenario, an authenticated user with content creation privileges intercepts an HTTP request saving rich text content (or uses the editor's source code view, if enabled) to submit one of the PoC strings above. Because the payload consists solely of <span> tags and data-* attributes, it frequently bypasses naive server-side HTML filters or Web Application Firewalls (WAFs) that only look for explicit <script> tags or standard onerror= attributes.
When a victim user or administrator subsequently opens the saved record in the TinyMCE editor, the media plugin's content filter runs buildMediaElement. On unpatched versions, the data-mce-p-onerror attribute is converted into a live onerror attribute on an active DOM node, executing the JavaScript payload in the context of the victim's session before the editor finishes initializing.
CVE-2026-116675 carries a CVSS v4.0 base score of 8.5 (High), with the vector string CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N. The Attack Vector is Network (AV:N) and Attack Complexity is Low (AC:L), reflecting that exploitation occurs over standard web traffic without requiring specific deployment conditions beyond the presence of the media plugin (AT:N).
Privileges Required is rated Low (PR:L) because stored XSS in rich text editors generally requires an account capable of authoring or submitting content, though in unauthenticated comment forms or support ticket portals, the effective privilege requirement may be None. User Interaction is Passive (UI:P), as the victim merely needs to view or load the affected content within the editor interface for the script to execute.
The impact on the vulnerable system's Confidentiality (VC:H) and Integrity (VI:H) is High. Because the injected JavaScript executes within the origin of the hosting web application, the attacker gains full access to the Document Object Model (DOM), browser storage (localStorage, sessionStorage), and non-HttpOnly session cookies. Furthermore, the script can read anti-CSRF tokens and issue arbitrary authenticated HTTP requests (via fetch or XMLHttpRequest) on behalf of the victim.
In multi-user platforms such as Content Management Systems (CMS), learning management platforms, or customer support portals, this vulnerability provides a direct path for privilege escalation. A low-privileged user can inject the data-mce-object payload into a draft, ticket, or profile field; when an administrator opens the item in the backend editor to review or moderate it, the payload executes with administrative privileges, enabling full account takeover or unauthorized system configuration changes.
Tiny Technologies has resolved this vulnerability in TinyMCE versions 7.9.4 (for the 7.x release train) and 8.9.3 (for the 8.x release train). All application developers and system administrators utilizing affected versions (7.9.3, and 8.5.1 through 8.9.2) should upgrade their dependencies to the patched versions immediately.
To update TinyMCE across common package ecosystems, run the appropriate package manager command for your release branch:
# For npm users (8.x branch)
npm install tinymce@^8.9.3
# For npm users (7.x branch)
npm install tinymce@^7.9.4
# For Yarn users
yarn add tinymce@^8.9.3
# For PHP Composer / Packagist users
composer require tinymce/tinymce:^8.9.3> [!NOTE]
> There is no configuration option available to mitigate this vulnerability while keeping the media plugin active in affected versions.
If an immediate version upgrade is not feasible due to build pipeline constraints, the only effective workaround is to disable the media plugin in the tinymce.init() configuration object. Removing media from the plugins array prevents FilterContent.ts from executing the vulnerable buildMediaElement routine:
tinymce.init({
selector: '#editor',
// Ensure 'media' is omitted from the plugins array or string
plugins: [
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
'insertdatetime', 'table', 'help', 'wordcount'
],
toolbar: 'undo redo | blocks | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent'
});In addition to upgrading the client-side library, security teams should implement defense-in-depth measures on the server side. Applications accepting rich text input should sanitize HTML on the backend using a strict allowlist sanitizer (such as DOMPurify running in Node.js, or OWASP Java HTML Sanitizer) before persisting data to the database. Specifically, server-side sanitizers should strip internal editor attributes matching data-mce-* (data-mce-object, data-mce-p-*, data-mce-html), as these attributes are meant strictly for internal editor state management and should never be submitted in raw user payloads.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
TinyMCE Tiny Technologies | >= 7.9.3, < 7.9.4 | 7.9.4 |
TinyMCE Tiny Technologies | >= 8.5.1, < 8.9.3 | 8.9.3 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Stored Cross-Site Scripting (CWE-79) |
| CVSS v4.0 Score | 8.5 HIGH (AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N) |
| Affected Component | TinyMCE Media Plugin (FilterContent.ts) |
| Vulnerable Versions | 7.9.3, and 8.5.1 through 8.9.2 |
| Patched Versions | 7.9.4 and 8.9.3 |
| Attack Vector | Crafted data-mce-object and data-mce-p-onerror attributes |
| Exploit Status | Public PoC Available (Unit Tests) |
| CISA KEV Status | Not Listed |
The product 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.
CVE-2026-116686 is a Server-Side Request Forgery (SSRF) and Denial of Service (DoS) vulnerability in the official Model Context Protocol (MCP) Python SDK (mcp package on PyPI). When an MCP client validates structured tool execution results against a server-provided outputSchema, the SDK invokes the Python jsonschema library with its default URI resolution configuration. An untrusted or compromised MCP server can supply an outputSchema containing external $ref directives (using http://, https://, or file:// schemes), forcing the client process to synchronously fetch internal network resources, read local files, or block the main event loop indefinitely.
CVE-2026-116685 (GHSA-5h93-6whr-6q8j) is a sensitive information disclosure vulnerability in the official Model Context Protocol (MCP) Python SDK (`mcp` on PyPI). In versions 1.8.0 through 1.29.1 and 2.0.0 through 2.1.1, the SDK's HTTP-based client transports—including Streamable HTTP, Server-Sent Events (SSE), and OAuth client flows—unconditionally enable automatic HTTP redirect following (`follow_redirects=True`) across origin boundaries. When a configured endpoint returns a redirect to an external origin, the HTTP client forwards caller-supplied custom authentication headers (such as `X-API-Key`) and, in the case of HTTP 307 or 308 responses, replays the full POST request body containing JSON-RPC payloads or OAuth parameters to the untrusted destination.
CVE-2026-116694 (GHSA-f5m9-jfrj-jjj7) is an unbounded resource allocation vulnerability (CWE-770) in the optional Jaeger propagator component (opentelemetry-propagator-jaeger) of the OpenTelemetry Python SDK prior to version 1.45.0. When extracting distributed tracing context from inbound carriers, JaegerPropagator.extract() processes all headers prefixed with 'uberctx-' without enforcing limits on entry count, per-entry byte size, or aggregate byte size. In environments where transport-level header constraints are absent, relaxed, or bypassed (such as non-HTTP message brokers, RPC frameworks, or web servers with elevated buffer configurations), an unauthenticated remote attacker can supply high-volume or oversized baggage headers to trigger excessive URL unquoting, short-lived immutable dictionary allocations, and CPU starvation.
CVE-2026-116684 is an incorrect authorization vulnerability (CWE-863) in the Model Context Protocol (MCP) Python SDK (`modelcontextprotocol/python-sdk`). The server-side bearer authentication middleware (`BearerAuthBackend`) verifies token cryptographic signatures and expiration timestamps via `TokenVerifier`, but fails to verify that the token's audience (`aud` claim or RFC 8707 resource indicator) matches the receiving MCP server's configured `resource_server_url`. In multi-service environments sharing a single OAuth 2.0 Authorization Server, an attacker possessing a valid token for an unrelated service can replay it against the MCP server to gain unauthorized access to protected tools, resources, and prompts.
CVE-2026-116693 is a high-severity Regular Expression Denial of Service (ReDoS) vulnerability affecting the official Datadog Application Performance Monitoring (APM) client library for Node.js, dd-trace. The defect resides in the default regular expression defined by the DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP configuration parameter, which is used to sanitize incoming and outgoing HTTP URL query strings prior to recording trace span metadata. Due to overlapping sub-patterns and unanchored repetition blocks in the JSON Web Token (JWT) and URL-encoded string matching branches, the V8 JavaScript engine's regular expression subsystem (Irregexp) can be forced into catastrophic or high-degree polynomial backtracking. Because regular expression replacement occurs synchronously on the primary Node.js event loop, an unauthenticated remote attacker can send crafted HTTP requests with malicious query strings to consume 100% of the process CPU core, stalling concurrent request processing and rendering the application unavailable.
CVE-2026-116692 (GHSA-26p8-2jq9-3vq9) is a remote code execution vulnerability in MLflow affecting versions 3.12.0rc0 prior to 3.17.0. The flaw stems from an overly permissive module prefix check during the deserialization of third-party GenAI evaluation scorers in mlflow/genai/scorers/base.py. By setting an experiment's artifact root to a local package directory within the Python module search path and uploading a crafted Python file as a run artifact, an attacker can invoke the /ajax-api/3.0/mlflow/scorer/invoke endpoint to dynamically import and execute arbitrary code on the MLflow tracking server.