CVE-2026-126362: Astro: Reflected XSS via unescaped slot names in @astrojs/svelte
Summary
The @astrojs/svelte renderer wraps slotted content passed to Svelte components in an <astro-slot> element, and writes the slot name into its name attribute without HTML escaping. A slot name containing " could end the attribute and inject arbitrary HTML into the page.
For example, a page that takes a slot name from the request:
astro --- import Tabs from '../components/Tabs.svelte'; const tab = Astro.url.searchParams.get('tab'); --- <Tabs client:load active={tab}><div slot={tab}>…</div></Tabs>
rendered with a Svelte 5 component that renders its snippets by name:
svelte <script> let { active, ...snippets } = $props(); </script> {@render snippets[active]?.()}
would output a tab value such as x"><img src=x onerror=alert(1)> as live HTML.
Impact
Reflected cross-site scripting. An attacker who gets a user to open a crafted link can run JavaScript on the site's origin, but only in applications where all of the following are true:
- The page passes request-controlled input (such as a query parameter) as a slot name. - The Svelte component is hydrated with a client: directive. For non-hydrated components the slot wrapper is removed from the output, together with its name attribute. - The Svelte component renders the attacker-chosen slot, which in practice means a Svelte 5 component that renders snippets by a dynamic name. Components that never render the slot only output Astro's already escaped fallback template.
This is the @astrojs/svelte counterpart of GHSA-8hv8-536x-4wqp (CVE-2026-50146), which fixed the same issue in astro core. The same unescaped value was also used when hydrating the component in the browser.
Affected versions
@astrojs/svelte >= 5.7.3, <= 9.1.1.
Patches
Fixed in @astrojs/svelte 9.1.2. Slot names are now HTML-escaped in the server renderer and in the client-side hydration code.
Workarounds
Upgrade to @astrojs/svelte 9.1.2 or later. Before upgrading, do not use request-controlled input as a slot name, or map it to a fixed set of known slot names before passing it to a component.
Credits
Reported by @manus-pi.
Other sources
Astro is a web framework for content-driven websites. From 5.7.3 until 9.1.2, the @astrojs/svelte renderer inserted a named slot into the quoted name attribute of an astro-slot wrapper without HTML escaping. Request-controlled slot names containing a quote could terminate the attribute and inject active HTML. Exploitation required a hydrated Svelte component using a client: directive and dynamic rendering of the attacker-chosen slot; components that did not render that slot emitted only Astro's escaped fallback content. Both server rendering and client-side hydration used the unsafe slot name. This issue is fixed in version 9.1.2.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/@astrojs/svelteto a version that resolves this vulnerability.Fixed in 9.1.2 - Upgrade
Upgrade
@astrojs/svelteto a version that resolves this vulnerability.Fixed in 9.1.2 - Configuration
Do not use request-controlled input directly as a slot name; map it to a fixed set of known slot names before passing it to the component.
@astrojs/svelte request-controlled slot names = disabled or mapped to a fixed set of known slot names
Event History
Frequently Asked Questions
Which applications are actually exposed to this issue?
Exposure requires all of the following: request-controlled input is used as a slot name, that slot is passed to a Svelte component, and the component uses a client:* hydration directive. Non-hydrated components are not affected because the slot wrapper is removed from the output.
What does an attacker need to exploit it?
An attacker needs to control a slot-name value, such as through a query parameter, and persuade a victim to open a crafted link. A value containing a double quote can terminate the generated name attribute and inject HTML that executes JavaScript in the site's origin.
How can I check whether my application is vulnerable?
Review Astro pages that pass request-derived values into slot attributes, such as slot={Astro.url.searchParams.get(...)}. Confirm whether the receiving Svelte component is hydrated with client:* and renders the relevant named snippet.
What version contains the fix?
The referenced release identifies @astrojs/svelte@9.1.2. The advisory and linked commit provide the upstream fix details.