---
id: "GHSA-mh32-jxcr-mh9f"
page: "https://infrastack.ai/live/GHSA-mh32-jxcr-mh9f"
kind: "vulnerability"
aliases: ["GHSA-mh32-jxcr-mh9f"]
title: "XSS via attribute injection in dynamic tag names"
severity: "low"
cvss: null
epss: null
exploited: false
status: "open"
category: "package"
products: [{"name": "astro", "fixed": ["7.4.5"], "affected": "<= 7.4.4", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["astro", "cms", "cwe-184", "cwe-79", "package", "web-apps"]
not_affected_if: ["fixed-tag-names", "no-untrusted-tag-input"]
published_at: "2026-11-09T12:21:20+00:00"
first_seen_at: "2026-11-09T22:53:16+00:00"
updated_at: "2026-11-09T22:58:32+00:00"
sources: [{"source": "ghsa-repo", "url": "https://github.com/withastro/astro/security/advisories/GHSA-mh32-jxcr-mh9f", "first_seen_at": "2026-11-09T22:53:16+00:00"}]
exploit_sources: []
---

# XSS via attribute injection in dynamic tag names

Link for people: https://infrastack.ai/live/GHSA-mh32-jxcr-mh9f (this .md is for agents)

When a dynamic Astro tag resolves to a string from untrusted input, a crafted name such as img/src/onerror=alert(1) renders as an element with an injected attribute. An attacker gets cross-site scripting in pages that build element names from request data.

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

## Affected

- npm:astro affected <= 7.4.4 fixed 7.4.5

## How to check

- Run `npm ls astro` and look for any `astro` version at or below `7.4.4`.
- Search `.astro`, JSX and MDX files for dynamic tags, such as `<Tag />` where `Tag` comes from a string, and check whether that string can come from a request parameter or CMS field.

## Fix

- `astro` `7.4.5` or later.
- Until upgraded, map untrusted input to a fixed set of known tags instead of using it as an element name.

## Timeline

- 2026-11-09T22:53:16+00:00 ghsa-repo https://github.com/withastro/astro/security/advisories/GHSA-mh32-jxcr-mh9f

## Documents

- [advisory] Maintainer's advisory: github.com https://github.com/withastro/astro/security/advisories/GHSA-mh32-jxcr-mh9f

## From the sources

## Summary

When a dynamic tag (`const Tag = ...; <Tag />`) resolves to a string, Astro renders it as a raw HTML element name. The name was filtered to remove characters that could inject attributes, but the filter did not handle `/`. Because HTML parsers treat `/` inside a tag as an attribute separator, a tag name such as `img/src/onerror=alert(1)` was rendered as an `<img>` element with an `onerror` attribute. The JSX renderer used for MDX did not filter string tag names at all.

For example:

```astro
---
const Tag = Astro.url.searchParams.get('as') ?? 'div';
---
<Tag>hello</Tag>
```

rendered `?as=img/src/onerror=alert(1)` as live HTML.

## Impact

Reflected or stored cross-site scripting, but only in applications that use untrusted input (such as a request parameter or an uncontrolled CMS field) as all or part of an element name. Astro itself never derives element names from request data.

Applications that let content choose an element should map that input to a fixed set of known tags. Letting untrusted input choose an element name is unsafe even with a correct filter, because it can still select elements such as `script` or `iframe`.

## Affected versions

`astro` <= 7.4.4.

## Patches

Fixed in `astro` 7.4.5. String tag names in `.astro` dynamic tags, JSX, and MDX now render only the leading valid element name, so characters such as `/` or `=` can no longer change the generated markup. Strings that do not start with a valid element name now throw an error.

## Workarounds

Upgrade to `astro` 7.4.5 or later. Before upgrading, do not use untrusted input as an element name, or map it to a fixed set of known tags.

## Credits

Reported by @uttej-badwane.

---

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