Skip to content

chore(deps): update dependency astro to v6.1.10 [security]#31

Open
renovate[bot] wants to merge 1 commit into
devfrom
renovate/npm-astro-vulnerability
Open

chore(deps): update dependency astro to v6.1.10 [security]#31
renovate[bot] wants to merge 1 commit into
devfrom
renovate/npm-astro-vulnerability

Conversation

@renovate
Copy link
Copy Markdown
Contributor

@renovate renovate Bot commented May 13, 2026

This PR contains the following updates:

Package Change Age Confidence
astro (source) 6.1.76.1.10 age confidence

Astro: XSS in define:vars via incomplete </script> tag sanitization

CVE-2026-41067 / GHSA-j687-52p2-xcff

More information

Details

Summary

The defineScriptVars function in Astro's server-side rendering pipeline uses a case-sensitive regex /<\/script>/g to sanitize values injected into inline <script> tags via the define:vars directive. HTML parsers close <script> elements case-insensitively and also accept whitespace or / before the closing >, allowing an attacker to bypass the sanitization with payloads like </Script>, </script >, or </script/> and inject arbitrary HTML/JavaScript.

Details

The vulnerable function is defineScriptVars at packages/astro/src/runtime/server/render/util.ts:42-53:

export function defineScriptVars(vars: Record<any, any>) {
	let output = '';
	for (const [key, value] of Object.entries(vars)) {
		output += `const ${toIdent(key)} = ${JSON.stringify(value)?.replace(
			/<\/script>/g,       // ← Case-sensitive, exact match only
			'\\x3C/script>',
		)};\n`;
	}
	return markHTMLString(output);
}

This function is called from renderElement at util.ts:172-174 when a <script> element has define:vars:

if (name === 'script') {
	delete props.hoist;
	children = defineScriptVars(defineVars) + '\n' + children;
}

The regex /<\/script>/g fails to match three classes of closing script tags that HTML parsers accept per the HTML specification §13.2.6.4:

  1. Case variations: </Script>, </SCRIPT>, </sCrIpT> — HTML tag names are case-insensitive but the regex has no i flag.
  2. Whitespace before >: </script >, </script\t>, </script\n> — after the tag name, the HTML tokenizer enters the "before attribute name" state on ASCII whitespace.
  3. Self-closing slash: </script/> — the tokenizer enters "self-closing start tag" state on /.

JSON.stringify() does not escape <, >, or / characters, so all these payloads pass through serialization unchanged.

Execution flow: User-controlled input (e.g., Astro.url.searchParams) → assigned to a variable → passed via define:vars on a <script> tag → renderElementdefineScriptVars → incomplete sanitization → injected into <script> block in HTML response → browser closes the script element early → attacker-controlled HTML parsed and executed.

PoC

Step 1: Create an SSR Astro page (src/pages/index.astro):

---
const name = Astro.url.searchParams.get('name') || 'World';
---
<html>
<body>
  <h1>Hello</h1>
  <script define:vars=>
    console.log(name);
  </script>
</body>
</html>

Step 2: Ensure SSR is enabled in astro.config.mjs:

export default defineConfig({
  output: 'server'
});

Step 3: Start the dev server and visit:

http://localhost:4321/?name=</Script><img/src=x%20onerror=alert(document.cookie)>

Step 4: View the HTML source. The output contains:

<script>const name = "</Script><img/src=x onerror=alert(document.cookie)>";
  console.log(name);
</script>

The browser's HTML parser matches </Script> case-insensitively, closing the script block. The <img onerror=alert(document.cookie)> is then parsed as HTML and the JavaScript in onerror executes.

Alternative bypass payloads:

/?name=</script ><img/src=x onerror=alert(1)>
/?name=</script/><img/src=x onerror=alert(1)>
/?name=</SCRIPT><img/src=x onerror=alert(1)>
Impact

An attacker can execute arbitrary JavaScript in the context of a victim's browser session on any SSR Astro application that passes request-derived data to define:vars on a <script> tag. This is a documented and expected usage pattern in Astro.

Exploitation enables:

  • Session hijacking via cookie theft (document.cookie)
  • Credential theft by injecting fake login forms or keyloggers
  • Defacement of the rendered page
  • Redirection to attacker-controlled domains

The vulnerability affects all Astro versions that support define:vars and is exploitable in any SSR deployment where user input reaches a define:vars script variable.

Recommended Fix

Replace the case-sensitive exact-match regex with a comprehensive escape that covers all HTML parser edge cases. The simplest correct fix is to escape all < characters in the JSON output:

export function defineScriptVars(vars: Record<any, any>) {
	let output = '';
	for (const [key, value] of Object.entries(vars)) {
		output += `const ${toIdent(key)} = ${JSON.stringify(value)?.replace(
			/</g,
			'\\u003c',
		)};\n`;
	}
	return markHTMLString(output);
}

This is the standard approach used by frameworks like Next.js and Rails. Replacing every < with \u003c is safe inside JSON string contexts (JavaScript treats \u003c as < at runtime) and eliminates all possible </script> variants including case variations, whitespace, and self-closing forms.

Severity

  • CVSS Score: 6.1 / 10 (Medium)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Astro: Server island encrypted parameters vulnerable to cross-component replay

CVE-2026-45028 / GHSA-xr5h-phrj-8vxv

More information

Details

Impact

Astro versions prior to 6.1.10 used AES-GCM encryption to protect the confidentiality and integrity of server island props and slots parameters, but did not bind the ciphertext to its intended component or parameter type. An attacker could replay one component's encrypted props (p) value as another component's slots (s) value, or vice versa.

Since slots contain raw unescaped HTML while props may contain user-controlled values, this could lead to XSS in applications that meet all of the following conditions:

  • The application uses server islands
  • Two different server island components share the same key name for a prop and a slot
  • An attacker has full control over the value of the overlapping prop (requires a dynamically rendered page)

These conditions are very unlikely to occur in real-world production applications.

Patches

This has been patched in astro@6.1.10.

The fix binds each encrypted parameter to its target component and purpose using AES-GCM authenticated additional data (AAD). Each ciphertext now includes context like props:IslandName or slots:IslandName, so encrypted data for one component cannot be replayed against a different component, and encrypted props cannot be reused as slots.

References

Severity

  • CVSS Score: 2.9 / 10 (Low)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:P

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

withastro/astro (astro)

v6.1.10

Compare Source

Patch Changes
  • #​16479 1058428 Thanks @​matthewp! - Fixes a spurious [WARN] [content] Content config not loaded warning during astro dev for projects that don't use content collections

  • #​16457 3d82220 Thanks @​matthewp! - Hardens server island encryption to prevent encrypted data from one island component being replayed against a different one

  • #​16481 152700e Thanks @​matthewp! - Fixes a spurious 404 request for a dev toolbar sourcemap during astro dev caused by the browser mis-resolving a relative sourceMappingURL from the /@&#8203;id/ URL prefix

  • #​16480 1bcb43b Thanks @​matthewp! - Fixes an unnecessary full page reload on first navigation during dev

v6.1.9

Compare Source

Patch Changes

v6.1.8

Compare Source

Patch Changes
  • #​16367 a6866a7 Thanks @​ematipico! - Fixes an issue where build output files could contain special characters (!, ~, {, }) in their names, causing deploy failures on platforms like Netlify.

  • #​16381 217c5b3 Thanks @​ematipico! - Slightly improved the performance of the dev server by caching the internal crawling of the dependencies of a project.

  • #​16348 7d26cd7 Thanks @​ocavue! - Fixes a bug where emitted assets during a client build would contain always fresh, new hashes in their name. Now the build should be more stable.

  • #​16317 d012bfe Thanks @​das-peter! - Fixes a bug where allowedDomains weren't correctly propagated when using the development server.

  • #​16379 5a84551 Thanks @​martrapp! - Improves Vue scoped style handling in DEV mode during client router navigation.

  • #​16317 d012bfe Thanks @​das-peter! - Adds tests to verify settings are properly propagated when using the development server.

  • #​16282 5b0fdaa Thanks @​jmurty! - Fixes build errors on platforms with skew protection enabled (e.g. Vercel, Netlify) for inter-chunk Javascript using dynamic imports

  • Updated dependencies [e0b240e]:


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • ""
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants