Verified reproduction
GHSA-gw32-9rmw-qwww: Svelte XSS via textarea bind:value in SSR
GHSA-gw32-9rmw-qwww is verified against svelte · npm vulnerability class: XSS This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00067.
Severity HIGH
Confidence HIGH
Reproduced in 8m 49s
Tool calls 104
Spend $1.07
$
pruva-verify REPRO-2026-00067 or
curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00067/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Run in a VM or disposable container. This exploits a real vulnerability.
A server-side rendered <textarea> with two-way bound value does not have its value correctly escaped in the rendered HTML.
## 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. The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired. Phases: support triages the advisory · repro reproduces it · vuln_variant confirms the fix blocks it · judge verifies.
Loading session...
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available