GHSA-8J5Q-MFJ2-5Q9Q
Vulnerability from github – Published: 2026-07-20 23:21 – Updated: 2026-07-20 23:21Summary
In @astrojs/rss, the source.title and enclosure.type item fields are interpolated directly into XML template strings without XML-character escaping before being parsed by fast-xml-parser. An attacker who controls these field values can inject arbitrary XML elements into the generated RSS feed.
Details
Two fields in packages/astro-rss/src/index.ts are affected:
source.title
item.source = parser.parse(
`<source url="${result.source.url}">${result.source.title}</source>`,
).source;
source.title is validated only as z.string(), with no restriction on XML special characters. A value containing </source> followed by arbitrary XML is parsed as real XML elements, merging injected nodes into the RSS item.
enclosure.type
item.enclosure = parser.parse(
`<enclosure url="${enclosureURL}" length="${result.enclosure.length}" type="${result.enclosure.type}"/>`,
).enclosure;
enclosure.type is also z.string() and is interpolated into an XML attribute without escaping. A value containing " followed by additional XML can break out of the attribute and inject extra elements.
Proof of Concept
source.title injection:
source: {
url: 'https://legit.example.com',
title: '</source><item><title>INJECTED</title><link>https://evil.com</link></item><source>',
}
// Result: RSS feed contains an injected <item> element with an evil.com link
enclosure.type injection:
enclosure: {
url: 'https://example.com/a.mp3',
length: 0,
type: 'audio/mpeg" /><link>https://evil.example.com</link><enclosure fake="',
}
// Result: RSS feed contains an injected <link> element
Both injections were confirmed with fast-xml-parser: the injected "link": "https://evil.com" appears in the parsed output.
Impact
An attacker who can control source.title or enclosure.type values (e.g., via a CMS, database, or user-submitted content that populates RSSFeedItem) can inject arbitrary XML into the generated RSS feed. This corrupts feed structure, injects false metadata (e.g., a fake <link> pointing to a malicious URL), and can cause feed readers to misparse or display attacker-controlled content. In SSR mode (output: 'server'), the poisoned feed is served on every request to all subscribers.
Patches
Fixed in @astrojs/rss@4.0.19.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@astrojs/rss"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "4.0.19"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-59728"
],
"database_specific": {
"cwe_ids": [
"CWE-91"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-20T23:21:47Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\nIn `@astrojs/rss`, the `source.title` and `enclosure.type` item fields are interpolated directly into XML template strings without XML-character escaping before being parsed by `fast-xml-parser`. An attacker who controls these field values can inject arbitrary XML elements into the generated RSS feed.\n\n## Details\n\nTwo fields in `packages/astro-rss/src/index.ts` are affected:\n\n### `source.title`\n\n```typescript\nitem.source = parser.parse(\n `\u003csource url=\"${result.source.url}\"\u003e${result.source.title}\u003c/source\u003e`,\n).source;\n```\n\n`source.title` is validated only as `z.string()`, with no restriction on XML special characters. A value containing `\u003c/source\u003e` followed by arbitrary XML is parsed as real XML elements, merging injected nodes into the RSS item.\n\n### `enclosure.type`\n\n```typescript\nitem.enclosure = parser.parse(\n `\u003cenclosure url=\"${enclosureURL}\" length=\"${result.enclosure.length}\" type=\"${result.enclosure.type}\"/\u003e`,\n).enclosure;\n```\n\n`enclosure.type` is also `z.string()` and is interpolated into an XML attribute without escaping. A value containing `\"` followed by additional XML can break out of the attribute and inject extra elements.\n\n## Proof of Concept\n\n`source.title` injection:\n\n```javascript\nsource: {\n url: \u0027https://legit.example.com\u0027,\n title: \u0027\u003c/source\u003e\u003citem\u003e\u003ctitle\u003eINJECTED\u003c/title\u003e\u003clink\u003ehttps://evil.com\u003c/link\u003e\u003c/item\u003e\u003csource\u003e\u0027,\n}\n// Result: RSS feed contains an injected \u003citem\u003e element with an evil.com link\n```\n\n`enclosure.type` injection:\n\n```javascript\nenclosure: {\n url: \u0027https://example.com/a.mp3\u0027,\n length: 0,\n type: \u0027audio/mpeg\" /\u003e\u003clink\u003ehttps://evil.example.com\u003c/link\u003e\u003cenclosure fake=\"\u0027,\n}\n// Result: RSS feed contains an injected \u003clink\u003e element\n```\n\nBoth injections were confirmed with `fast-xml-parser`: the injected `\"link\": \"https://evil.com\"` appears in the parsed output.\n\n## Impact\n\nAn attacker who can control `source.title` or `enclosure.type` values (e.g., via a CMS, database, or user-submitted content that populates `RSSFeedItem`) can inject arbitrary XML into the generated RSS feed. This corrupts feed structure, injects false metadata (e.g., a fake `\u003clink\u003e` pointing to a malicious URL), and can cause feed readers to misparse or display attacker-controlled content. In SSR mode (`output: \u0027server\u0027`), the poisoned feed is served on every request to all subscribers.\n\n## Patches\n\nFixed in `@astrojs/rss@4.0.19`.",
"id": "GHSA-8j5q-mfj2-5q9q",
"modified": "2026-07-20T23:21:47Z",
"published": "2026-07-20T23:21:47Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/withastro/astro/security/advisories/GHSA-8j5q-mfj2-5q9q"
},
{
"type": "WEB",
"url": "https://github.com/withastro/astro/pull/17209"
},
{
"type": "WEB",
"url": "https://github.com/withastro/astro/commit/fbcfa039dfe3d700b239f595a6c55ee35e45bd06"
},
{
"type": "PACKAGE",
"url": "https://github.com/withastro/astro"
},
{
"type": "WEB",
"url": "https://github.com/withastro/astro/releases/tag/@astrojs/rss@4.0.19"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "@astrojs/rss: XML Injection via Unescaped RSS Feed Fields"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.