Feb 18, 2026·5 min read·40 visits
Svelte 5 versions 5.46.0 through 5.46.2 are vulnerable to XSS during Server-Side Rendering. The framework uses unsafe JSON serialization for hydration keys, allowing attackers to break out of `<script>` tags using a `</script>` payload. Update to 5.46.3 immediately.
A classic 'breakout' vulnerability in Svelte 5's server-side rendering logic allows attackers to inject arbitrary JavaScript via the 'hydratable' function. By trusting 'JSON.stringify' to sanitize data embedded in HTML script tags, the framework inadvertently allows malicious payloads to terminate the script block and execute code in the victim's browser context.
Svelte 5 is the new hotness. It brought us 'runes', fine-grained reactivity, and a promise of better performance. But with great power comes great... complexity in the serialization layer. Specifically, we're looking at the Server-Side Rendering (SSR) mechanism. When you render a page on the server, the client needs to 'hydrate' that page—essentially waking up the static HTML and attaching JavaScript event listeners to it.
To do this, Svelte needs to pass data from the server to the client. It does this by embedding serialized data directly into the HTML, usually inside a <script> tag in the document head. This is a critical handover point. If the server messes up this game of 'telephone,' the client browser interprets the data as executable code rather than passive information.
CVE-2025-15265 is exactly that mess-up. It resides in the hydratable function, a feature used to persist state across the server-client boundary. The vulnerability isn't in your application code; it's deep inside how Svelte decides to write that data to the DOM. It's a classic case of trusting a standard library function (JSON.stringify) to do a security job it was never designed for.
Here is the fundamental rule of web security that developers forget once every generation: The HTML parser runs before the JavaScript parser.
When a browser receives an HTML document, it scans for tags. If it sees a <script> tag, it switches to 'script mode' until it sees the sequence </script>. The browser is dumb. It does not care if that closing tag is inside a JavaScript string, a comment, or a JSON object. If it sees </script>, it closes the block. Period.
Svelte's engineers made a fatal assumption. They assumed that if they ran JSON.stringify() on a key, it would be safe to drop into a script block.
// Ideally, this should happen:
var data = "<script>alert(1)</script>";
// Browser sees a string. Life is good.
// But if you are injecting it via server-side string concatenation:
<script>
var data = "</script><script>alert(1)</script>";
</script>In the vulnerable code, Svelte takes a user-controlled key, stringifies it, and dumps it right into the HTML. Because JSON.stringify does not escape the forward slash / or the less-than symbol < by default, a payload containing </script> successfully tells the browser: 'End the script here.' The text immediately following that—controlled by the attacker—is then rendered as raw HTML, allowing for a new malicious <script> tag to open.
Let's look at the crime scene in packages/svelte/src/internal/server/renderer.js. The vulnerable logic was handling the serialization of entries for the hydration map.
// Svelte 5.46.0 - 5.46.2
// k is the key provided to hydratable()
// v is the value associated with it
entries.push(`[${JSON.stringify(k)},${v.serialized}]`);See that JSON.stringify(k)? That's the kill shot. If k is the string "</script>", the output becomes ["</script>", .... The browser reads the HTML, hits that closing tag inside the string, and terminates the script context immediately.
The Svelte team, credited to the discovery by Camilo Vera, replaced the native JSON serializer with devalue.uneval. The devalue library is specifically designed to emit JavaScript that is safe to embed in HTML.
// Fixed in 5.46.3
import * as devalue from 'devalue';
// ...
entries.push(`[${devalue.uneval(k)},${v.serialized}]`);devalue.uneval turns < into \u003C and / into \u002F (or similar escapes) ensuring the browser never sees the literal sequence </script>.
Exploiting this is trivially easy if you can control the key passed to hydratable. Imagine a scenario where a Svelte app uses a URL parameter or a search query as a hydration key.
<!-- main.svelte -->
<script>
import { hydratable } from "svelte";
// 'key' comes from user input (e.g., URL param)
let { key } = $props();
// The vulnerability triggers here during SSR
hydratable(key, () => { ... });
</script>An attacker sends a link to the victim:
https://vulnerable-app.com/?key=</script><script>alert(document.cookie)</script>
key is </script><script>alert(document.cookie)</script>.JSON.stringify(key). Result: "\"</script><script>alert(document.cookie)</script>\"".<script>
const h = (window.__svelte ??= {}).h ??= new Map();
// The injection happens inside this array structure
h.set(["</script><script>alert(document.cookie)</script>", ...]);
</script></script> inside the string.<script>alert(document.cookie)</script> as a new script block.The remediation path is straightforward: Update Svelte immediately.
Fixed Version: 5.46.3
If you are stuck on a vulnerable version for some god-forsaken reason (enterprise change freezes, am I right?), you technically could sanitize the input before passing it to hydratable, but that is a losing game. You will miss edge cases.
> [!NOTE]
> Content Security Policy (CSP)
> A strict CSP is your best safety net here. If your policy forbids inline scripts (script-src 'self'), this attack fails because the injected script block is inline and unsigned. However, Svelte apps often rely on inline scripts for hydration bootstrapping, so ensuring your CSP is configured correctly (using nonces or hashes) is critical.
Takeaway for Developers: Never assume JSON.stringify makes data safe for HTML embedding. It makes data safe for JavaScript contexts, but HTML context is a different beast entirely. Use libraries like devalue or serialize-javascript that explicitly handle HTML entity escaping.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Svelte Svelte | >= 5.46.0, < 5.46.3 | 5.46.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS Score | 6.1 (Medium) |
| Impact | Cross-Site Scripting (XSS) |
| Vulnerable Component | server/renderer.js (hydratable) |
| Exploit Status | PoC Available |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.