Skip to content

chore(deps): update dependency astro to v7.1.0 [security] - #186

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

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

Conversation

@renovate

@renovate renovate Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
astro (source) 7.0.67.1.0 age confidence

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


Release Notes

withastro/astro (astro)

v7.1.0

Compare Source

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

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

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

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

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

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

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

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

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

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

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

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

Scoping sources and hashes in your config

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

v7.0.9

Compare Source

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

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

v7.0.8

Compare Source

Patch Changes

v7.0.7

Compare Source

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

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

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

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

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

  • Updated dependencies [4298883, 4298883]:


Configuration

📅 Schedule: (UTC)

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

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

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

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


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

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

@renovate
renovate Bot temporarily deployed to cloudflare-pages July 21, 2026 19:29 Inactive
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 21, 2026

Copy link
Copy Markdown

Deploying auth-wiki with  Cloudflare Pages  Cloudflare Pages

Latest commit: e0c530d
Status: ✅  Deploy successful!
Preview URL: https://2fb5c07e.auth-wiki.pages.dev
Branch Preview URL: https://renovate-npm-astro-vulnerabi.auth-wiki.pages.dev

View logs

@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 6205139 to 533a5ee Compare July 24, 2026 21:48
@renovate
renovate Bot temporarily deployed to cloudflare-pages July 24, 2026 21:48 Inactive
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 533a5ee to a307024 Compare July 30, 2026 20:16
@renovate
renovate Bot temporarily deployed to cloudflare-pages July 30, 2026 20:16 Inactive
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from a307024 to 49635d6 Compare August 12, 2026 01:32
@renovate
renovate Bot temporarily deployed to cloudflare-pages August 12, 2026 01:32 Inactive
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 49635d6 to e0c530d Compare August 14, 2026 17:38
@renovate
renovate Bot temporarily deployed to cloudflare-pages August 14, 2026 17:38 Inactive
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from e0c530d to 6bbe13c Compare August 26, 2026 15:41
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from 6bbe13c to ad6c566 Compare September 2, 2026 21:31
@renovate
renovate Bot had a problem deploying to cloudflare-pages September 2, 2026 21:31 Failure
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from ad6c566 to b5e8aba Compare September 3, 2026 13:27
@renovate
renovate Bot had a problem deploying to cloudflare-pages September 3, 2026 13:27 Failure
@renovate
renovate Bot force-pushed the renovate/npm-astro-vulnerability branch from b5e8aba to e102740 Compare September 3, 2026 23:57
@renovate
renovate Bot had a problem deploying to cloudflare-pages September 3, 2026 23:57 Failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

0 participants