What's the vulnerability?

A server-side rendered <textarea> with two-way bound value does not have its value correctly escaped in the rendered HTML.

Root Cause Analysis

## Summary
Server-side rendering of `<textarea bind:value>` in vulnerable versions of Svelte emits the bound value without HTML-escaping, so attacker-controlled content can prematurely close the `<textarea>` element and inject arbitrary markup or JavaScript into the SSR response, leading to reflected or stored XSS when delivered to a browser.

## Impact
- **Component:** `svelte` SSR renderer for `<textarea bind:value>`
- **Affected versions:** `>= 3.0.0` and `< 3.59.2` (verified on `3.59.1`)
- **Risk level:** High – injected scripts execute in the victim’s browser, enabling credential/session theft, CSRF, or page defacement whenever SSR output includes untrusted textarea content.

## Root Cause
During SSR the Svelte compiler concatenates the textarea’s bound value directly inside `<textarea>...</textarea>` without escaping special characters. Because `<textarea>` stores its value as innerHTML rather than an attribute, sequences such as `</textarea><script>` terminate the element and introduce attacker-supplied markup. The issue was fixed by commit [`a31dec5`](https://github.com/sveltejs/svelte/commit/a31dec5eb30978cff7ff4d77f4bf316841f711bc), which HTML-escapes textarea content before emission.

## Reproduction Steps
1. Execute `repro/reproduction_steps.sh`.
2. The script bootstraps a clean project pinned to vulnerable `svelte@3.59.1`, writes a malicious textarea component, compiles it for SSR, and captures the output HTML.
3. Reproduction succeeds when the script exits `0` after detecting the injected `</textarea><script>alert('BIM');</script>` sequence in `logs/ssr-output.html`; exit `1` means the payload was not observed.

## Evidence
- **Run log:** `logs/reproduction.log` – shows Node (`v22.21.1`) and npm (`10.9.4`) versions, dependency installation, SSR execution, and the final "VULNERABILITY REPRODUCED" message.
- **SSR output:** `logs/ssr-output.html` contains `<textarea>test'"></textarea><script>alert('BIM');</script></textarea>`, proving that hostile script tags are emitted unescaped.
- **Install log:** `logs/npm-install.log` documents dependency installation for auditability.

## Recommendations / Next Steps
- Upgrade Svelte to version `3.59.2` or later (or the latest 4.x release) where textarea values are escaped during SSR.
- If upgrading is not immediately possible, manually escape user-controlled textarea values before SSR or avoid binding raw user data to `<textarea>` on the server.
- Add regression tests that verify SSR output escapes textarea contents, including cases with `</textarea>` substrings and quotes.
- Re-scan dependent applications for additional SSR contexts (e.g., `<style>`/`<script>` bindings) that may require escaping.

## Additional Notes
- **Idempotency:** `repro/reproduction_steps.sh` was executed multiple times back-to-back, each time yielding the same successful reproduction and clean logs, confirming idempotent behavior.
- **Limitations:** The PoC targets SSR output only; client-side rendering escapes values correctly, so the vulnerability manifests when unescaped SSR HTML is sent to browsers before hydration.
One Command

Verify with pruva-verify

Run the Pruva CLI to automatically fetch and execute the reproduction script.

pruva-verify REPRO-2026-00067
or pruva-verify GHSA-gw32-9rmw-qwww
Install: curl -fsSL https://pruva.dev/install.sh | sh

Or Run Manually

1

Download the script

curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00067/artifacts/reproduction_steps.sh
2

Make executable

chmod +x reproduction_steps.sh
3

Run the script

./reproduction_steps.sh
Run in a VM, container, or disposable environment. This exploits a real vulnerability.

How Pruva Reproduced This

Watch the AI agent's step-by-step process.

Loading session...

Artifacts

No artifacts available