VYPR
Moderate severityNVD Advisory· Published Nov 19, 2025· Updated Nov 19, 2025

Astro Cloudflare adapter has a Stored Cross Site Scripting vulnerability in /_image endpoint

CVE-2025-65019

Description

Astro is a web framework. Prior to version 5.15.9, when using Astro's Cloudflare adapter (@astrojs/cloudflare) with output: 'server', the image optimization endpoint (/_image) contains a critical vulnerability in the isRemoteAllowed() function that unconditionally allows data: protocol URLs. This enables Cross-Site Scripting (XSS) attacks through malicious SVG payloads, bypassing domain restrictions and Content Security Policy protections. This issue has been patched in version 5.15.9.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
astronpm
< 5.15.95.15.9

Affected products

1

Patches

1
9e9c528191b6

fix: require explicit authorization to use data urls (#14791)

https://github.com/withastro/astroErikaNov 17, 2025via ghsa
4 files changed · +62 6
  • .changeset/bold-monkeys-return.md+23 0 added
    @@ -0,0 +1,23 @@
    +---
    +'@astrojs/internal-helpers': patch
    +'astro': patch
    +---
    +
    +Changes the remote protocol checks for images to require explicit authorization in order to use data URIs.
    +
    +In order to allow data URIs for remote images, you will need to update your `astro.config.mjs` file to include the following configuration:
    +
    +```js
    +// astro.config.mjs
    +import { defineConfig } from 'astro/config';
    +
    +export default defineConfig({
    +  images: {
    +    remotePatterns: [
    +      {
    +        protocol: 'data',
    +      },
    +    ],
    +  },
    +});
    +```
    
  • packages/astro/test/core-image.test.js+5 0 modified
    @@ -26,6 +26,11 @@ describe('astro:image', () => {
     				image: {
     					service: testImageService({ foo: 'bar' }),
     					domains: ['avatars.githubusercontent.com'],
    +					remotePatterns: [
    +						{
    +							protocol: 'data',
    +						},
    +					],
     				},
     			});
     
    
  • packages/astro/test/units/remote-pattern.test.js+33 0 modified
    @@ -1,6 +1,7 @@
     import * as assert from 'node:assert/strict';
     import { describe, it } from 'node:test';
     import {
    +	isRemoteAllowed,
     	matchHostname,
     	matchPathname,
     	matchPattern,
    @@ -13,6 +14,7 @@ describe('remote-pattern', () => {
     	const url2 = new URL('http://preview.docs.astro.build:8080/');
     	const url3 = new URL('https://astro.build/');
     	const url4 = new URL('https://example.co/');
    +	const url5 = new URL('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==');
     
     	describe('remote pattern matchers', () => {
     		it('matches protocol', async () => {
    @@ -22,6 +24,7 @@ describe('remote-pattern', () => {
     			// defined, true/false
     			assert.equal(matchProtocol(url1, 'http'), false);
     			assert.equal(matchProtocol(url1, 'https'), true);
    +			assert.equal(matchProtocol(url5, 'data'), true);
     		});
     
     		it('matches port', async () => {
    @@ -123,6 +126,36 @@ describe('remote-pattern', () => {
     				}),
     				false,
     			);
    +
    +			assert.equal(
    +				matchPattern(url5, {
    +					protocol: 'data',
    +				}),
    +				true,
    +			);
    +		});
    +	});
    +
    +	describe('remote is allowed', () => {
    +		it('allows remote URLs based on patterns', async () => {
    +			const patterns = {domains: [], remotePatterns: [
    +				{
    +					protocol: 'https',
    +					hostname: '**.astro.build',
    +					pathname: '/en/**',
    +				},
    +				{
    +					protocol: 'http',
    +					hostname: 'preview.docs.astro.build',
    +					port: '8080',
    +				},
    +			]};
    +
    +			assert.equal(isRemoteAllowed(url1, patterns), true);
    +			assert.equal(isRemoteAllowed(url2, patterns), true);
    +			assert.equal(isRemoteAllowed(url3, patterns), false);
    +			assert.equal(isRemoteAllowed(url4, patterns), false);
    +			assert.equal(isRemoteAllowed(url5, patterns), false);
     		});
     	});
     });
    
  • packages/internal-helpers/src/remote.ts+1 6 modified
    @@ -128,13 +128,8 @@ export function isRemoteAllowed(
     
     	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)) {
    +	if (!['http:', 'https:', 'data:'].includes(url.protocol)) {
     		return false;
     	}
     
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.