Human
Machine
REPRO-2026-00067 HIGH XSS
Verified
Svelte XSS via textarea bind:value in SSR
svelte (npm) Jan 17, 2026
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