---
id: "GHSA-f4j9-crrj-867q"
page: "https://infrastack.ai/live/GHSA-f4j9-crrj-867q"
kind: "vulnerability"
aliases: ["GHSA-f4j9-crrj-867q"]
title: "Reflected XSS via unescaped slot names in @astrojs/svelte"
severity: "medium"
cvss: null
epss: null
exploited: false
status: "open"
category: "package"
products: [{"name": "@astrojs/svelte", "fixed": ["9.1.2"], "affected": ">= 5.7.3, <= 9.1.1", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["astro", "cwe-79", "npm", "package", "self-hosted", "svelte"]
not_affected_if: ["auth-required", "default-config", "static-export"]
published_at: "2026-10-28T19:27:20+00:00"
first_seen_at: "2026-11-09T22:53:17+00:00"
updated_at: "2026-11-09T22:58:52+00:00"
sources: [{"source": "ghsa-repo", "url": "https://github.com/withastro/astro/security/advisories/GHSA-f4j9-crrj-867q", "first_seen_at": "2026-11-09T22:53:17+00:00"}]
exploit_sources: []
---

# Reflected XSS via unescaped slot names in @astrojs/svelte

Link for people: https://infrastack.ai/live/GHSA-f4j9-crrj-867q (this .md is for agents)

The @astrojs/svelte renderer writes slot names into an HTML attribute without escaping them, so a crafted slot name can break out and inject HTML. An attacker who gets a user to open a crafted link can run JavaScript on the site's origin, but only under specific conditions.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:@astrojs/svelte affected >= 5.7.3, <= 9.1.1 fixed 9.1.2

## How to check

- Check the installed version: `npm ls @astrojs/svelte`
- Look for pages that pass request input (such as a query parameter) as a slot name to a Svelte component with a `client:*` directive
- Look for Svelte 5 components that render snippets by a dynamic name, such as `{@render snippets[active]?.()}`

## Fix

- Upgrade `@astrojs/svelte` to `9.1.2` or later
- Affected range: `>= 5.7.3, <= 9.1.1`
- Until upgraded, do not use request-controlled input as a slot name, or map it to a fixed set of known slot names

## Timeline

- 2026-11-09T22:53:17+00:00 ghsa-repo https://github.com/withastro/astro/security/advisories/GHSA-f4j9-crrj-867q

## Documents

- [advisory] Maintainer's advisory: github.com https://github.com/withastro/astro/security/advisories/GHSA-f4j9-crrj-867q

## From the sources

## 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.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
