High severity7.3NVD Advisory· Published Feb 21, 2025· Updated Apr 15, 2026
CVE-2025-27109
CVE-2025-27109
Description
solid-js is a declarative, efficient, and flexible JavaScript library for building user interfaces. In affected versions Inserts/JSX expressions inside illegal inlined JSX fragments lacked escaping, allowing user input to be rendered as HTML when put directly inside JSX fragments. This issue has been addressed in version 1.9.4 and all users are advised to upgrade. There are no known workarounds for this vulnerability.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
solid-jsnpm | < 1.9.4 | 1.9.4 |
Patches
29316baf658c8b93956f28ed7fix escaping in resolution done outside of DOM Expressions
2 files changed · +65 −5
.changeset/clever-months-cover.md+5 −0 added@@ -0,0 +1,5 @@ +--- +"solid-js": patch +--- + +fix escaping in resolution done outside of DOM Expressions
packages/solid/src/server/rendering.ts+60 −5 modified@@ -29,6 +29,61 @@ export type ComponentProps<T extends ValidComponent> = T extends Component<infer ? JSX.IntrinsicElements[T] : Record<string, unknown>; +// these methods are duplicates from solid-js/web +// we need a better solution for this in the future +function escape(s: any, attr?: boolean) { + const t = typeof s; + if (t !== "string") { + if (!attr && t === "function") return escape(s()); + if (!attr && Array.isArray(s)) { + for (let i = 0; i < s.length; i++) s[i] = escape(s[i]); + return s; + } + if (attr && t === "boolean") return String(s); + return s; + } + const delim = attr ? '"' : "<"; + const escDelim = attr ? """ : "<"; + let iDelim = s.indexOf(delim); + let iAmp = s.indexOf("&"); + + if (iDelim < 0 && iAmp < 0) return s; + + let left = 0, + out = ""; + + while (iDelim >= 0 && iAmp >= 0) { + if (iDelim < iAmp) { + if (left < iDelim) out += s.substring(left, iDelim); + out += escDelim; + left = iDelim + 1; + iDelim = s.indexOf(delim, left); + } else { + if (left < iAmp) out += s.substring(left, iAmp); + out += "&"; + left = iAmp + 1; + iAmp = s.indexOf("&", left); + } + } + + if (iDelim >= 0) { + do { + if (left < iDelim) out += s.substring(left, iDelim); + out += escDelim; + left = iDelim + 1; + iDelim = s.indexOf(delim, left); + } while (iDelim >= 0); + } else + while (iAmp >= 0) { + if (left < iAmp) out += s.substring(left, iAmp); + out += "&"; + left = iAmp + 1; + iAmp = s.indexOf("&", left); + } + + return left < s.length ? out + s.substring(left) : out; +} + function resolveSSRNode(node: any): string { const t = typeof node; if (t === "string") return node; @@ -304,7 +359,7 @@ export function ErrorBoundary(props: { }); if (error) return displayFallback(); sync = false; - return { t: `<!--!$e${id}-->${resolveSSRNode(res)}<!--!$/e${id}-->` }; + return { t: `<!--!$e${id}-->${resolveSSRNode(escape(res))}<!--!$/e${id}-->` }; } // Suspense Context @@ -376,9 +431,9 @@ export function createResource<T, S>( ): ResourceReturn<T> | ResourceReturn<T | undefined> { if (typeof fetcher !== "function") { - source = true as ResourceSource<S>; - fetcher = source as ResourceFetcher<S, T>; options = (fetcher || {}) as ResourceOptions<T> | ResourceOptions<undefined>; + fetcher = source as ResourceFetcher<S, T>; + source = true as ResourceSource<S>; } const contexts = new Set<SuspenseContextType>(); @@ -585,7 +640,7 @@ export function Suspense(props: { fallback?: string; children: string }) { completed: () => { const res = runSuspense(); if (suspenseComplete(value)) { - done!(resolveSSRNode(res)); + done!(resolveSSRNode(escape(res))); } } }); @@ -623,7 +678,7 @@ export function Suspense(props: { fallback?: string; children: string }) { if (ctx.async) { setHydrateContext({ ...ctx, count: 0, id: ctx.id + "0F", noHydrate: true }); const res = { - t: `<template id="pl-${id}"></template>${resolveSSRNode(props.fallback)}<!--pl-${id}-->` + t: `<template id="pl-${id}"></template>${resolveSSRNode(escape(props.fallback))}<!--pl-${id}-->` }; setHydrateContext(ctx); return res;
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.