CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-GW32-9RMW-QWWW
7.5

Svelte SSR Jailbreak: The Tale of the Unescaped Textarea

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 17, 2026·6 min read·6 visits

PoC Available

Executive Summary (TL;DR)

Svelte versions prior to 3.59.2 failed to escape user input bound to `<textarea>` elements during server-side rendering. This allowed attackers to close the textarea tag prematurely and inject malicious scripts that execute immediately upon page load. The fix involves wrapping the output in an escape function.

A high-severity Cross-Site Scripting (XSS) vulnerability was discovered in the Svelte framework's Server-Side Rendering (SSR) engine. By exploiting improper escaping in `bind:value` directives on `<textarea>` elements, attackers can break out of the HTML tag context and inject arbitrary JavaScript. This affects applications rendering Svelte components on the server (e.g., SvelteKit) prior to version 3.59.2.

The Hook: When Compilers Lie

We love Svelte. It's the framework that isn't a framework. It compiles your code away into tiny, efficient, imperative JavaScript. It promises to handle the heavy lifting, the reactivity, and—crucially—the security. We trust the compiler to sanitize our inputs because, frankly, writing document.createElement by hand in 2024 is a form of masochism reserved for people who enjoy writing assembly.

But here's the thing about Server-Side Rendering (SSR): it's not DOM manipulation. It is, at its core, sophisticated string concatenation. The Svelte compiler takes your components and turns them into a function that spits out a long HTML string to send to the browser. And whenever you are concatenating strings to build HTML, you are dancing on the edge of a volcano.

This vulnerability, living in the shadows of the <textarea> tag, is a reminder that even the smartest compilers can slip up on the most basic HTML rules. It's a classic case of "it works on my machine (client-side)" failing catastrophically when moved to the server.

The Flaw: RCDATA and the Closing Tag

To understand this bug, you have to understand how browsers parse HTML. Most input elements use the value attribute (e.g., <input value="...">). Attributes are easy; you quote them, you escape quotes, and you move on.

But <textarea> is a weird beast. It doesn't use a value attribute. Its value is the text content between the opening and closing tags. In HTML parsing terms, this is often treated as RCDATA (Raw Text). The browser generally doesn't parse markup inside a textarea... until it sees a closing tag.

The vulnerability lies in src/compiler/compile/render_ssr/handlers/Element.ts. When Svelte compiled a component with <textarea bind:value={userInput}> for SSR, it essentially generated code that said: "Print <textarea>, then print userInput, then print </textarea>."

Do you see the problem? It didn't sanitize userInput. It assumed that because it was inside a textarea, it was safe. But if userInput contains </textarea>, the browser stops parsing the textarea context immediately and treats everything following it as raw HTML. The compiler was locking the front door but leaving the keys under the mat.

The Code: The Smoking Gun

Let's look at the actual compiler code. This is where the magic (and the mistake) happened. The compiler is written in TypeScript and generates JavaScript string templates.

The Vulnerable Code (Pre-3.59.2):

// src/compiler/compile/render_ssr/handlers/Element.ts
} else if (binding.name === 'value' && node.name === 'textarea') {
    const snippet = expression.node;
    // FATAL FLAW: The snippet is injected directly without escaping
    node_contents = x`${snippet} || ""`;
}

The variable node_contents is what eventually gets shoved between the <textarea> tags. The x literal tag function is just a helper for generating code. The crucial part is ${snippet} || "". It takes whatever variable you bound to the textarea and dumps it into the HTML string.

The Fix (Commit a31dec5):

// src/compiler/compile/render_ssr/handlers/Element.ts
} else if (binding.name === 'value' && node.name === 'textarea') {
    const snippet = expression.node;
    // FIX: Wrap the content in the @escape macro
    node_contents = x`@escape(${snippet} || "")`;
}

The fix is elegantly simple. They wrapped the expression in @escape(...). This internal Svelte function converts characters like < to &lt; and > to &gt;. So, if an attacker tries to inject </textarea>, it becomes &lt;/textarea&gt;, which the browser safely renders as text inside the box rather than parsing it as a tag closer.

The Exploit: Breaking Out

Exploiting this is trivial if you can control data that gets pre-rendered on the server. Imagine a user profile page where you can set a "Bio". This bio is rendered into a generic <textarea> so the user can edit it later.

The Setup:

  1. Target: A SvelteKit app using SSR.
  2. Component: <textarea bind:value={userBio}></textarea>
  3. Attacker Input: sometext</textarea><script>alert('pwned')</script>

The Execution: When the server renders this page, it blindly concatenates the strings. The HTML sent to the victim looks like this:

<textarea>sometext</textarea><script>alert('pwned')</script></textarea>

The Browser's Perspective:

  1. See <textarea>: "Okay, I'm starting a text box."
  2. See sometext: "Putting this in the text box."
  3. See </textarea>: "Okay, closing the text box."
  4. See <script>...: "Oh, a script tag! I should execute this immediately."
  5. See </textarea>: "Weird floating closing tag. I'll ignore it or show it in the DOM."

Boom. XSS. The script executes as soon as the HTML is parsed, allowing cookie theft, session hijacking, or redirection.

The Impact: Why Panic?

Since this is an SSR vulnerability, the XSS payload is delivered in the initial HTML document. This is often more dangerous than client-side reflected XSS because:

  1. Immediate Execution: The script runs before the application even hydrates. It runs before your client-side security routers or hydration logic kicks in.
  2. SEO Poisoning: If the injected content contains spam links or hidden divs instead of scripts, search engines scraping your SSR content will index it.
  3. Widespread Reach: Any page rendering user-controlled data into a textarea on the server is vulnerable. This is common in CMSs, admin panels, and forum software.

It breaks the trust model of the framework. Developers use Svelte specifically because they expect bind:value to be safe by default. This bug silently turned a standard feature into a security hole.

The Mitigation: Patch or perish

The remediation is straightforward, but mandatory.

1. Upgrade Svelte If you are on version 3.x, you must upgrade to 3.59.2 or later. If you are already on Svelte 4 or 5, you are safe (the fix was carried forward). Check your package.json and package-lock.json.

npm update svelte
# Check version
npm list svelte

2. Defensive Coding (The Paranoia approach) Even with the patch, it is good hygiene to treat all user input as radioactive. If you are rendering user content on the server, ensure you have input validation layers (like Zod or Joi) active before the data even reaches the component tree. Never rely solely on the view layer to sanitize your database's dirty secrets.

Official Patches

SvelteCommit fixing the SSR escaping logic

Fix Analysis (1)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

Affected Systems

Svelte Framework (npm package `svelte`)SvelteKit (rendering via Svelte SSR)

Affected Versions Detail

Product
Affected Versions
Fixed Version
svelte
Svelte
>= 3.0.0, < 3.59.23.59.2
AttributeDetail
CWE IDCWE-79 (Cross-Site Scripting)
Attack VectorNetwork
CVSS7.5 (High)
Affected ComponentSvelte SSR Compiler (Textarea Handler)
ImpactCode Execution (Browser Context)
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Known Exploits & Detection

GitHub AdvisoryProof of Concept demonstrating the breakout from textarea context.

Vulnerability Timeline

Fix commit merged (a31dec5)
2023-06-20
Svelte v3.59.2 released
2023-06-20
GHSA-GW32-9RMW-QWWW Published
2024-08-30

References & Sources

  • [1]GHSA-GW32-9RMW-QWWW
  • [2]Svelte Documentation

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.