CVE-2024-40631
Description
CVE-2024-40631 is an XSS vulnerability in Plate's MediaEmbedElement when custom URL parsers allow javascript:, data:, or vbscript: URLs.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2024-40631 is an XSS vulnerability in Plate's MediaEmbedElement when custom URL parsers allow javascript:, data:, or vbscript: URLs.
Vulnerability
Overview
CVE-2024-40631 is a cross-site scripting (XSS) vulnerability in the open-source rich-text editor Plate, specifically within the MediaEmbedElement component. The flaw arises when editors use custom urlParsers passed to the useMediaState hook; if a parser does not adequately sanitize the URL protocol, it can return dangerous schemes like javascript:, data:, or vbscript:. The same risk applies when the url property from useMediaState or the element is consumed directly without sanitisation [1][3]. The default parsers parseTwitterUrl and parseVideoUrl are not affected [3].
Exploitation
Path
An attacker can exploit this by crafting a malicious URL that, when embedded in a MediaEmbedElement, will be passed unsanitised to an ` src attribute. For example, a custom parser that returns { url: "javascript:alert(1)" } would cause script execution in the context of the editor page. This attack requires the editor to be configured with a vulnerable custom parser or code that directly uses the url` property without validation [3]. No special privileges beyond the ability to insert media content are needed.
Impact
Successful exploitation results in XSS, allowing arbitrary JavaScript execution within the affected page. This can lead to data theft, session hijacking, or other malicious actions, depending on the application context [1][3].
Mitigation
The vulnerability is patched in @udecode/plate-media version 36.0.10, which restricts the embed property from useMediaState to only HTTP and HTTPS URLs. The patch also renames url to unsafeUrl to discourage direct use. Users must update to the patched version or, as a workaround, ensure custom parsers explicitly block dangerous protocols and sanitise any direct use of the url property [2][3].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
@udecode/plate-medianpm | < 36.0.10 | 36.0.10 |
Affected products
2Patches
11bc0971774fbHarden media embed element against XSS
3 files changed · +73 −10
.changeset/selfish-dogs-buy.md+6 −0 added@@ -0,0 +1,6 @@ +--- +"@udecode/plate-media": patch +--- + +- Explicitly prohibit `javascript:` protocol when parsing URLs in `useMediaState`. +- In the return value of `useMediaState`, rename `url` to `unsafeUrl` to indicate that it has not been sanitised.
packages/media/src/media/useMediaState.spec.ts+37 −0 added@@ -0,0 +1,37 @@ +import { type EmbedUrlParser, parseMediaUrl } from './useMediaState'; + +describe('parseMediaUrl', () => { + const parsersWithoutFallback: EmbedUrlParser[] = [ + (url) => (url.startsWith('a') ? { id: 'A' } : undefined), + (url) => (url.endsWith('b') ? { id: 'B' } : undefined), + ]; + + const parsersWithFallback: EmbedUrlParser[] = [ + ...parsersWithoutFallback, + () => ({ id: 'C' }), + ]; + + it('returns undefined if no parsers match', () => { + const embed = parseMediaUrl('x', { urlParsers: parsersWithoutFallback }); + expect(embed).toBeUndefined(); + }); + + it('uses the first matching parser', () => { + const embed = parseMediaUrl('ab', { urlParsers: parsersWithoutFallback }); + expect(embed?.id).toBe('A'); + }); + + it('uses fallback parser if present', () => { + const embed = parseMediaUrl('javascript', { + urlParsers: parsersWithFallback, + }); + expect(embed?.id).toBe('C'); + }); + + it('does not allow javascript: URLs', () => { + const embed = parseMediaUrl('javascript:', { + urlParsers: parsersWithFallback, + }); + expect(embed).toBeUndefined(); + }); +});
packages/media/src/media/useMediaState.ts+30 −10 modified@@ -4,8 +4,9 @@ import { useElement } from '@udecode/plate-common'; import { useFocused, useReadOnly, useSelected } from 'slate-react'; import type { TMediaElement } from './types'; + +import { ELEMENT_MEDIA_EMBED, VIDEO_PROVIDERS } from '../media-embed'; import { ELEMENT_VIDEO } from '../video'; -import { VIDEO_PROVIDERS, ELEMENT_MEDIA_EMBED} from '../media-embed'; export type EmbedUrlData = { id?: string; @@ -15,6 +16,30 @@ export type EmbedUrlData = { export type EmbedUrlParser = (url: string) => EmbedUrlData | undefined; +export const parseMediaUrl = ( + url: string, + { + urlParsers, + }: { + urlParsers: EmbedUrlParser[]; + } +): EmbedUrlData | undefined => { + // Harden against XSS + try { + if (new URL(url).protocol === 'javascript:') { + return undefined; + } + } catch {} + + for (const parser of urlParsers) { + const data = parser(url); + + if (data) { + return data; + } + } +}; + export const useMediaState = ({ urlParsers, }: { @@ -28,15 +53,10 @@ export const useMediaState = ({ const { align = 'left', id, isUpload, name, type, url } = element; const embed = React.useMemo(() => { - if (!urlParsers || (type !== ELEMENT_VIDEO && type !== ELEMENT_MEDIA_EMBED)) return; + if (!urlParsers || (type !== ELEMENT_VIDEO && type !== ELEMENT_MEDIA_EMBED)) + return; - for (const parser of urlParsers) { - const data = parser(url); - - if (data) { - return data; - } - } + return parseMediaUrl(url, { urlParsers }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [urlParsers, url]); @@ -56,6 +76,6 @@ export const useMediaState = ({ name, readOnly, selected, - url, + unsafeUrl: url, }; };
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5News mentions
0No linked articles in our index yet.