May 15, 2026·6 min read·5 visits
Svelte versions prior to 5.55.7 contain an XSS vulnerability in the `hydratable` SSR feature. Attackers can leverage the '$' replacement token in promise values to execute arbitrary JavaScript in the victim's browser.
A critical Cross-Site Scripting (XSS) vulnerability exists in the Server-Side Rendering (SSR) engine of the Svelte framework. The vulnerability occurs due to insecure promise serialization within the experimental `hydratable` feature. Attackers controlling the output of a resolved promise can inject JavaScript string replacement tokens, causing the SSR engine to duplicate template strings into executable script blocks.
The Svelte framework provides server-side rendering (SSR) capabilities to pre-render applications into HTML strings. A recent experimental feature, hydratable, allows developers to serialize promises during SSR and resolve them on the client side. This vulnerability (GHSA-f3cj-j4f6-wq85) resides specifically within the serialization logic of this feature.
The flaw requires developers to pass attacker-controlled input into a promise evaluated by the hydratable function. If the attacker supplies input containing specific JavaScript string replacement tokens, the Svelte rendering engine misinterprets the data. This behavior allows the attacker to break out of the intended string literal context and inject arbitrary JavaScript payloads into the generated HTML document.
According to CVSS v4.0 metrics, the vulnerability carries a base score of 9.1. The attack vector is network-based, requires low privileges, and mandates specific usage conditions within the target application. Once exploited, the vulnerability results in high impact to the confidentiality and integrity of the subsequent system, defined in this context as the end user's web browser.
Applications not utilizing the experimental hydratable feature remain unaffected. Organizations employing Svelte must assess their codebases for calls to this specific function that process untrusted data.
The core vulnerability stems from the insecure usage of the String.prototype.replace() method within Svelte's hydratable serialization pipeline. The vulnerable module, located at packages/svelte/src/internal/server/hydratable.js, manages the insertion of resolved promise values into a pre-computed HTML template string. The engine utilizes the uneval function from the devalue library to serialize the output safely.
When String.prototype.replace() receives a string as its second argument, the JavaScript runtime evaluates specific character sequences starting with a dollar sign ($) as special replacement patterns. For example, the token $' instructs the runtime to insert the portion of the source string that follows the matched substring. Other tokens, such as $& (matched substring) and $n (captured groups), exhibit similar dynamic behavior.
The Svelte SSR engine constructs the final template by calling .replace(placeholder, string) where the replacement string contains the attacker-controlled, serialized promise value. The devalue library correctly escapes standard HTML injection characters such as < and /. However, it does not escape the $ character, as it is a valid literal character within standard JavaScript string literals.
By supplying the $' token, an attacker forces the replace function to copy the trailing portion of the pre-computed template string and insert it directly into the serialized output. This mechanism breaks the syntax of the generated JavaScript execution block. The injected trailing string closes the intended variable assignment and allows subsequent payload bytes to execute as arbitrary code in the browser context.
The original implementation of packages/svelte/src/internal/server/hydratable.js evaluated the serialization using a string literal as the replacement parameter. The code dynamically constructed a function call r() containing the output of uneval().
// Vulnerable implementation
entry.serialized = entry.serialized.replace(placeholder, `r(${uneval(v)})`);If the attacker controls v and sets it to $', uneval(v) returns the literal string "$'". The replacement string becomes r("'"). When evaluated by String.prototype.replace(), the $' sequence is processed as a replacement token rather than a literal string, duplicating the template suffix.
The patch addresses this structural flaw by utilizing the functional form of String.prototype.replace(). When the second argument is a callback function, the JavaScript engine treats the function's return value as a strictly literal replacement string. The engine bypasses the evaluation of $ replacement tokens entirely.
// Patched implementation (commit a16ebc67bbcf8f708360195687e1b2719463e1a4)
entry.serialized = entry.serialized.replace(
placeholder,
() => `r(${uneval(v)})`
);This simple architectural change neutralizes the vulnerability class. The return string is injected exactly as constructed, ensuring that the JavaScript literal context remains bounded. The patch was uniformly applied across the core SSR module and corresponding playground components (ssr-dev.js and ssr-prod.js).
Exploitation of this vulnerability requires the target application to accept user input and pass it directly into the resolution of a hydratable promise. The attacker does not require direct access to the server infrastructure; the injection vector operates entirely through the standard application input channels.
The attacker crafts a specific string payload containing the $' token. In a standard attack, the payload combines the replacement token with standard JavaScript syntax designed to escape the current scope and execute arbitrary commands. A theoretical payload might look like $'};alert(document.domain);//.
The provided test case from the Svelte codebase demonstrates the exact mechanical failure. When the SSR engine evaluates hydratable('key', () => Promise.resolve($')), the generated HTML output is corrupted. The script tag containing the serialized state receives a duplicated section of the page template.
// Exploitation Proof of Concept
test('treats replacement tokens in hydratable promise values as literals', async () => {
const component = (renderer: Renderer) => {
hydratable('key', () => Promise.resolve(`$'`));
// ... render logic ...
};
const { head } = await Renderer.render(component as unknown as Component);
// The resulting head string contains the XSS payload
});When a victim visits the pre-rendered page, the browser parses the HTML document. The malformed <script> block, containing the attacker's payload, is executed synchronously during document parsing. This allows the attacker to execute code within the origin of the vulnerable application.
The primary impact of this vulnerability is arbitrary JavaScript execution within the context of the user's browser session. Cross-Site Scripting allows attackers to bypass the Same-Origin Policy (SOP). The severity is reflected in the CVSS v4.0 vector CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N.
Attackers successfully exploiting this flaw gain full read and write access to the DOM of the affected application. They can exfiltrate sensitive data, including session tokens, CSRF tokens, and local storage contents. The attacker can also perform unauthorized actions on behalf of the victim by issuing authenticated API requests.
While the consequences of the XSS are severe, the attack surface is constrained by the application's architecture. The vulnerable code path strictly requires the usage of the experimental hydratable feature. Applications relying solely on standard Svelte state management or non-hydrated SSR pipelines are fundamentally immune to this vector.
The definitive remediation for this vulnerability is upgrading the svelte package to version 5.55.7 or later. The patch modifies the internal serialization logic to properly handle replacement tokens, ensuring that user input cannot alter the structural integrity of the generated HTML template.
Developers must audit their application codebases for instances of the hydratable function. If immediate patching is not feasible, implement strict input validation on all data passed into hydrated promises. Specifically, block or sanitize the dollar sign character ($) in data intended for SSR serialization.
Organizations should also enforce a strict Content Security Policy (CSP). Implementing CSP directives such as script-src with nonces or strict hash requirements significantly mitigates the impact of XSS vulnerabilities. While a CSP does not patch the underlying engine flaw, it prevents the execution of unauthorized inline scripts and external payloads.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
svelte Svelte | >= 5.46.0, < 5.55.7 | 5.55.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS v4.0 | 9.1 (Critical) |
| Impact | High Confidentiality, High Integrity (Subsequent System) |
| Exploit Status | PoC Available |
| KEV Status | Not Listed |
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.