Svelte SSR XSS: The Textarea Trap
Jan 17, 2026·5 min read
Executive Summary (TL;DR)
Svelte's SSR compiler forgot that `<textarea>` contents are children, not attributes. It didn't escape `bind:value` content during server-side rendering. Attackers can inject `</textarea>` to close the tag early and run scripts. Fixed in 3.59.2.
A high-severity Cross-Site Scripting (XSS) vulnerability exists in Svelte's Server-Side Rendering (SSR) compiler. Due to improper escaping of `bind:value` directives on `<textarea>` elements, attackers can break out of the HTML tag context and execute arbitrary JavaScript.
The Hook: When Compilation Goes Wrong
Svelte is the framework that prides itself on being a compiler, not just a runtime. It shifts the heavy lifting to the build step, turning your declarative components into efficient, surgical JavaScript. It's a beautiful philosophy: compile away the framework.
But here's the catch with compilers: if the compiler has a logic bug, the generated code is the bug. This isn't a runtime library failing to sanitize an input; this is the framework literally writing vulnerable code on your behalf.
In this specific case, the issue lies deep within the Server-Side Rendering (SSR) logic. SSR is great for SEO and perceived performance, but it requires the framework to generate raw HTML strings on the server. And as we know, string concatenation is the natural enemy of security. Svelte usually handles this gracefully, but it stumbled over one of the weirdest elements in the HTML spec: the <textarea>.
The Flaw: HTML's Ugly Duckling
To understand this bug, you have to appreciate that <textarea> is the hipster of HTML elements—it refuses to conform. While a standard <input> element stores its data in the value attribute (e.g., <input value="stuff">), the <textarea> stores its data as text content between its tags (e.g., <textarea>stuff</textarea>).
When you use bind:value={variable} in Svelte, the compiler has to decide how to render that. For an <input>, it sets an attribute. For a <textarea>, it must place the variable inside the tags.
Here is where the logic failed. When generating the SSR code for <textarea bind:value>, the compiler treated the bound value as trusted content. It simply dropped the variable into the template string without passing it through the internal escaping mechanism (@escape). It assumed, quite wrongly, that whatever you bound to a textarea was safe to print directly into the HTML stream.
The Code: The Smoking Gun
Let's look at the vulnerable code in src/compiler/compile/render_ssr/handlers/Element.ts. This typescript file dictates how elements are converted into SSR strings.
Before the patch, the handler for textarea looked like this:
} else if (binding.name === 'value' && node.name === 'textarea') {
const snippet = expression.node;
// THE BUG: No escaping wrapper!
node_contents = x`${snippet} || ""`;
}The variable node_contents is essentially the raw string that gets shoved between <textarea> and </textarea>. If snippet contains malicious HTML, it gets written verbatim.
The fix was a one-liner, adding the all-important @escape wrapper:
} else if (binding.name === 'value' && node.name === 'textarea') {
const snippet = expression.node;
// THE FIX: Wrap it in @escape()
node_contents = x`@escape(${snippet} || "")`;
}This is a classic example of implicit trust. The developer likely assumed that since it's a "value" binding, it would be treated like an attribute value (which usually gets quoted), but the structural difference of the textarea element bypassed that assumption.
The Exploit: Breaking the Box
Exploiting this is trivially easy and incredibly reliable. Because we are in a Server-Side Rendering context, the server sends the HTML to the browser. The browser parses it linearly.
If we control the variable bound to the textarea, we just need to tell the browser: "Hey, the textarea is done now." We do this by injecting the closing tag </textarea>.
The Attack Chain:
- Injection: We input the string
</textarea><script>alert('Pwned')</script>into the application (e.g., a profile bio, a comment, or a saved draft). - Rendering: The server renders the component. Because of the missing escape, it generates:
<textarea>Preceding text...</textarea><script>alert('Pwned')</script></textarea> - Execution: The victim's browser sees the first
</textarea>, closes the element, and immediately parses the next tag as a<script>. The code executes before the page even finishes loading.
Here is a visualization of the parse flow:
The Impact: Why This Hurts
This is a High Severity (CVSS 8.4) vulnerability for a reason. SSR XSS is often more dangerous than client-side DOM XSS because it is delivered in the initial HTML document.
- Bypass CSP: Sometimes inline scripts rendered by the server are whitelisted or nonce-based (though a good CSP would still block this if nonces are handled correctly, often they aren't for user content).
- SEO Poisoning: Attackers can inject visible content that search engines index.
- Session Hijacking: The classic XSS impact. Stealing
document.cookie, local storage tokens, or performing actions on behalf of the user.
This affects any Svelte application (v3.0.0 - 3.59.1) that uses SSR and allows user input to populate a textarea. If you are building a CMS, a forum, or a dashboard, you are likely vulnerable.
The Fix: Remediation
The fix is straightforward: Upgrade.
Svelte released version 3.59.2 specifically to address this. If you are on Svelte 4 or 5, you are already safe as the architecture changes included this fix.
If you are stuck on an old version and cannot upgrade (why?), you have to manually sanitize data before passing it to the component, or stop using bind:value on textareas in SSR contexts. But really, just npm update svelte.
[!NOTE] This vulnerability highlights the importance of Context-Aware Output Encoding. Escaping for an attribute is different than escaping for HTML body content. Frameworks usually handle this for you, but when they fail, they fail hard.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:L/VA:N/SC:H/SI:H/SA:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
svelte sveltejs | >= 3.0.0 < 3.59.2 | 3.59.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| CVSS Score | 8.4 (High) |
| Vector | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:L/VA:N/SC:H/SI:H/SA:N |
| Attack Vector | Network |
| Vulnerability Type | XSS (Cross-Site Scripting) |
| Affected Component | SSR Compiler (Textarea Handler) |
MITRE ATT&CK Mapping
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.