{"id":"GHSA-jmv2-qgxf-f995","kind":"vulnerability","aliases":["GHSA-jmv2-qgxf-f995"],"related":[],"title":"Reflected XSS via unescaped slot names in @astrojs/solid-js","description":"## Summary\n\nThe `@astrojs/solid-js` renderer wraps named slot content passed to Solid 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.\n\nFor example, a page that takes a slot name from the request:\n\n```astro\n---\nimport Tabs from '../components/Tabs.jsx';\nconst tab = Astro.url.searchParams.get('tab');\n---\n<Tabs client:load active={tab}><div slot={tab}>…</div></Tabs>\n```\n\nrendered with a Solid component that renders a slot by name:\n\n```jsx\nexport default function Tabs(props) {\n\treturn <div>{props[props.active]}</div>;\n}\n```\n\nwould output a `tab` value such as `x\"><img src=x onerror=alert(1)>` as live HTML.\n\n## Impact\n\nReflected 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:\n\n- The page passes request-controlled input (such as a query parameter) as a slot name.\n- The Solid component is hydrated with a `client:*` directive. For non-hydrated components the slot wrapper is removed from the output, together with its `name` attribute.\n- The Solid component renders the attacker-chosen slot, which in practice means accessing slot props by a dynamic name. Components that never render the slot only output Astro's already escaped fallback template.\n\nThis is the `@astrojs/solid-js` counterpart of GHSA-8hv8-536x-4wqp (CVE-2026-50146), which fixed the same issue in `astro` core.\n\n## Affected versions\n\n`@astrojs/solid-js` >= 0.2.0, <= 7.1.1.\n\n## Patches\n\nFixed in `@astrojs/solid-js` 7.1.2. Slot names are now HTML-escaped in the server renderer.\n\n## Workarounds\n\nUpgrade to `@astrojs/solid-js` 7.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.\n\n## Credits\n\nReported by @manus-pi.","summary":"The @astrojs/solid-js renderer writes slot names into an HTML attribute without escaping, 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 where the page passes request input as a slot name and the Solid component renders that slot by name.","severity":"medium","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@astrojs/solid-js","fixed":["7.1.2"],"affected":">= 0.2.0, <= 7.1.1","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["astro","cwe-79","npm","package","solid-js"],"not_affected_if":["no-client-directive","no-dynamic-slot-names","static-content-only","version-7.1.2-or-later"],"how_to_check":"- Check the installed version with `npm ls @astrojs/solid-js`; versions `>= 0.2.0` and `<= 7.1.1` are affected.\n- Look for pages that use request data (such as query parameters) as a slot name in a `client:*` hydrated Solid component.","fix":"- `@astrojs/solid-js` upgrade to `7.1.2`","status":"open","published_at":"2026-10-28T19:27:28+00:00","first_seen_at":"2026-11-09T22:53:17+00:00","triaged_at":"2026-11-09T22:58:51+00:00","updated_at":"2026-11-09T22:58:51+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-09T22:58:51Z","model":"claude-haiku-5-5","output":{"fix":"- `@astrojs/solid-js` upgrade to `7.1.2`","reach":"internet","title":"Astro Solid integration: unescaped slot names allow reflected cross-site scripting","impact":"medium","summary":"The @astrojs/solid-js renderer writes slot names into an HTML attribute without escaping, 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 where the page passes request input as a slot name and the Solid component renders that slot by name.","audience":"developers","category":"package","nicknames":[],"who_cares":["astro","solid-js","npm"],"confidence":0.85,"fix_status":"available","needs_auth":"no","worst_case":"Attacker runs JavaScript on the site's origin in a victim's browser","analyst_note":"","how_to_check":"- Check the installed version with `npm ls @astrojs/solid-js`; versions `>= 0.2.0` and `<= 7.1.1` are affected.\n- Look for pages that use request data (such as query parameters) as a slot name in a `client:*` hydrated Solid component.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["static-content-only","no-client-directive","no-dynamic-slot-names","version-7.1.2-or-later"],"who_is_affected":"Sites built with Astro that use the @astrojs/solid-js integration and hydrate Solid components with a client directive. The exposure needs a page that takes a slot name from request input and a component that renders the slot by its dynamic name. Sites that do not use request data as slot names, or that map it to a fixed set of known names, are not exposed.","user_interaction":"yes","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.000834,"input_tokens":5271,"output_tokens":614,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-2026':324C '-50146':325C '-79':22B '/components/tabs.jsx':149C '0.2.0':337C '1':195C '4wqp':322C '536x':321C '7.1.1':338C '7.1.2':343C,359C '8hv8':320C 'access':291C 'activ':157C 'affect':334C 'alert':194C 'alreadi':308C 'applic':227C 'arbitrari':128C 'astro':20B,145C,306C,332C 'astro.url.searchparams.get':152C 'astrojs/solid-js':8A,19A,29B,93C,315C,336C,342C,358C 'attack':52B,206C,284C 'attacker-chosen':283C 'attribut':37B,114C,125C,277C 'break':46B 'chosen':285C 'client':155C,258C 'compon':85B,102C,166C,253C,264C,280C,298C,391C 'const':150C 'contain':121C 'content':98C 'control':240C,369C 'core':333C 'could':122C 'counterpart':316C 'craft':42B,60B,214C 'credit':392C 'cross':202C 'cross-sit':201C 'cve':323C 'cwe':21B 'default':175C 'direct':259C 'div':159C 'dynam':296C 'element':105C 'end':123C 'escap':39B,117C,309C,350C 'exampl':134C 'export':174C 'f995':13A,18A 'fallback':310C 'fix':327C,340C,380C 'follow':232C 'function':176C 'get':54B,208C 'ghsa':10A,15A,319C 'ghsa-8hv8-536x-4wqp':318C 'ghsa-jmv2-qgxf-f995':9A,14A 'html':36B,50B,116C,129C,198C,349C 'html-escap':348C 'hydrat':255C,263C 'img':190C 'impact':199C 'import':146C 'inject':49B,127C 'input':77B,241C,370C 'issu':330C 'javascript':64B,218C 'jmv2':11A,16A 'js':27B 'jsx':173C 'known':383C 'later':361C 'link':61B,215C 'live':197C 'load':156C 'manus':396C 'manus-pi':395C 'map':376C 'mean':290C 'name':6A,33B,44B,81B,90B,96C,110C,113C,120C,141C,172C,250C,276C,297C,345C,374C,385C 'never':300C 'non':262C 'non-hydr':261C 'npm':23B 'onerror':193C 'open':58B,212C 'origin':69B,223C 'output':183C,272C,305C 'packag':24B 'page':74B,132C,136C,236C 'paramet':246C 'pass':75B,99C,237C,387C 'patch':339C 'pi':397C 'practic':289C 'prop':178C,180C,293C 'props.active':181C 'qgxf':12A,17A 'queri':245C 'reflect':1A,200C 'remov':269C 'render':30B,86B,94C,162C,168C,281C,301C,354C 'report':393C 'request':76B,144C,239C,368C 'request-control':238C,367C 'return':179C 'run':63B,217C 'script':204C 'server':353C 'set':381C 'site':67B,203C,221C 'slot':5A,32B,43B,80B,88B,97C,109C,119C,140C,160C,170C,249C,266C,286C,292C,303C,344C,373C,384C 'solid':26B,84B,101C,165C,252C,279C 'solid-j':25B 'src':191C 'summari':91C 'tab':147C,151C,153C,154C,158C,161C,177C,185C 'take':138C 'templat':311C 'togeth':273C 'true':234C 'unescap':4A 'upgrad':356C,363C 'use':366C 'user':56B,210C 'valu':186C 'version':335C 'via':3A 'without':38B,115C 'workaround':355C 'would':182C 'wrap':95C 'wrapper':267C 'write':31B,107C 'x':189C,192C 'xss':2A","nicknames":[],"mitigations":[],"who_affected":"Sites built with Astro that use the @astrojs/solid-js integration and hydrate Solid components with a client directive. The exposure needs a page that takes a slot name from request input and a component that renders the slot by its dynamic name. Sites that do not use request data as slot names, or that map it to a fixed set of known names, are not exposed.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["solidjs"],"ptokens":["astrojs","js","solid"],"labels":["astro","cwe79","medium","npm","package","solidjs"],"campaign_type":null,"search_a":"'astrojs/solid-js':8A,19A 'f995':13A,18A 'ghsa':10A,15A 'ghsa-jmv2-qgxf-f995':9A,14A 'jmv2':11A,16A 'name':6A 'qgxf':12A,17A 'reflect':1A 'slot':5A 'unescap':4A 'via':3A 'xss':2A","search_b":"'-79':22B 'astro':20B 'astrojs/solid-js':29B 'attack':52B 'attribut':37B 'break':46B 'compon':85B 'craft':42B,60B 'cwe':21B 'escap':39B 'get':54B 'html':36B,50B 'inject':49B 'input':77B 'javascript':64B 'js':27B 'link':61B 'name':33B,44B,81B,90B 'npm':23B 'open':58B 'origin':69B 'packag':24B 'page':74B 'pass':75B 'render':30B,86B 'request':76B 'run':63B 'site':67B 'slot':32B,43B,80B,88B 'solid':26B,84B 'solid-j':25B 'user':56B 'without':38B 'write':31B","search_c":"'-2026':324C '-50146':325C '/components/tabs.jsx':149C '0.2.0':337C '1':195C '4wqp':322C '536x':321C '7.1.1':338C '7.1.2':343C,359C '8hv8':320C 'access':291C 'activ':157C 'affect':334C 'alert':194C 'alreadi':308C 'applic':227C 'arbitrari':128C 'astro':145C,306C,332C 'astro.url.searchparams.get':152C 'astrojs/solid-js':93C,315C,336C,342C,358C 'attack':206C,284C 'attacker-chosen':283C 'attribut':114C,125C,277C 'chosen':285C 'client':155C,258C 'compon':102C,166C,253C,264C,280C,298C,391C 'const':150C 'contain':121C 'content':98C 'control':240C,369C 'core':333C 'could':122C 'counterpart':316C 'craft':214C 'credit':392C 'cross':202C 'cross-sit':201C 'cve':323C 'default':175C 'direct':259C 'div':159C 'dynam':296C 'element':105C 'end':123C 'escap':117C,309C,350C 'exampl':134C 'export':174C 'fallback':310C 'fix':327C,340C,380C 'follow':232C 'function':176C 'get':208C 'ghsa':319C 'ghsa-8hv8-536x-4wqp':318C 'html':116C,129C,198C,349C 'html-escap':348C 'hydrat':255C,263C 'img':190C 'impact':199C 'import':146C 'inject':127C 'input':241C,370C 'issu':330C 'javascript':218C 'jsx':173C 'known':383C 'later':361C 'link':215C 'live':197C 'load':156C 'manus':396C 'manus-pi':395C 'map':376C 'mean':290C 'name':96C,110C,113C,120C,141C,172C,250C,276C,297C,345C,374C,385C 'never':300C 'non':262C 'non-hydr':261C 'onerror':193C 'open':212C 'origin':223C 'output':183C,272C,305C 'page':132C,136C,236C 'paramet':246C 'pass':99C,237C,387C 'patch':339C 'pi':397C 'practic':289C 'prop':178C,180C,293C 'props.active':181C 'queri':245C 'reflect':200C 'remov':269C 'render':94C,162C,168C,281C,301C,354C 'report':393C 'request':144C,239C,368C 'request-control':238C,367C 'return':179C 'run':217C 'script':204C 'server':353C 'set':381C 'site':203C,221C 'slot':97C,109C,119C,140C,160C,170C,249C,266C,286C,292C,303C,344C,373C,384C 'solid':101C,165C,252C,279C 'src':191C 'summari':91C 'tab':147C,151C,153C,154C,158C,161C,177C,185C 'take':138C 'templat':311C 'togeth':273C 'true':234C 'upgrad':356C,363C 'use':366C 'user':210C 'valu':186C 'version':335C 'without':115C 'workaround':355C 'would':182C 'wrap':95C 'wrapper':267C 'write':107C 'x':189C,192C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-10-28T19:27:28+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-jmv2-qgxf-f995","url":"https://github.com/withastro/astro/security/advisories/GHSA-jmv2-qgxf-f995","severity":"medium","published_at":"2026-10-28T19:27:28+00:00","modified_at":"2026-10-28T19:27:28+00:00","first_seen_at":"2026-11-09T22:53:17+00:00","fetched_at":"2026-11-09T22:53:17+00:00","note":null}],"documents":[{"id":110448,"url":"https://github.com/withastro/astro/security/advisories/GHSA-jmv2-qgxf-f995","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}