VYPR
Low severity2.2NVD Advisory· Published Apr 24, 2026· Updated Apr 27, 2026

CVE-2026-41321

CVE-2026-41321

Description

@astrojs/cloudflare is an SSR adapter for use with Cloudflare Workers targets. Prior to 13.1.10, the fetch() call for remote images in packages/integrations/cloudflare/src/utils/image-binding-transform.ts uses the default redirect: 'follow' behavior. This allows the Cloudflare Worker to follow HTTP redirects to arbitrary URLs, bypassing the isRemoteAllowed() domain allowlist check which only validates the initial URL. This vulnerabiity is caused by an incomplete fix for CVE-2025-58179. This vulnerability is fixed in 13.1.10.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@astrojs/cloudflarenpm
< 13.1.1013.1.10

Affected products

1

Patches

1
a43eb4b40b4f

Use redirect: manual in Cloudflare image binding transform (#16320)

https://github.com/withastro/astroMatthew PhillipsApr 15, 2026via ghsa
4 files changed · +39 1
  • .changeset/cloudflare-image-binding-no-redirect-follow.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +'@astrojs/cloudflare': patch
    +---
    +
    +Uses `redirect: 'manual'` for remote image fetches in the Cloudflare binding image transform, consistent with all other image fetch paths
    
  • packages/integrations/cloudflare/src/utils/image-binding-transform.ts+8 1 modified
    @@ -25,7 +25,14 @@ export async function transform(
     	}
     
     	const imageSrc = new URL(href, url.origin);
    -	const content = await (isRemotePath(href) ? fetch(imageSrc) : assets.fetch(imageSrc));
    +	const content = await (isRemotePath(href)
    +		? fetch(imageSrc, { redirect: 'manual' })
    +		: assets.fetch(imageSrc));
    +
    +	if (content.status >= 300 && content.status < 400) {
    +		return new Response('Not Found', { status: 404 });
    +	}
    +
     	if (!content.body) {
     		return new Response(null, { status: 404 });
     	}
    
  • packages/integrations/cloudflare/test/binding-image-service.test.js+23 0 modified
    @@ -1,12 +1,28 @@
     import * as assert from 'node:assert/strict';
    +import { createServer } from 'node:http';
     import { after, before, describe, it } from 'node:test';
     import { loadFixture } from './_test-utils.js';
     
     describe('BindingImageService', () => {
     	let fixture;
     	let previewServer;
    +	let redirectServer;
    +	let redirectServerPort;
     
     	before(async () => {
    +		// Start a local HTTP server that always responds with a 302 redirect.
    +		// Used to test that the image transform endpoint does not follow redirects.
    +		redirectServer = createServer((_req, res) => {
    +			res.writeHead(302, { Location: 'http://example.com/secret' });
    +			res.end();
    +		});
    +		await new Promise((resolve) => {
    +			redirectServer.listen(0, () => {
    +				redirectServerPort = redirectServer.address().port;
    +				resolve();
    +			});
    +		});
    +
     		fixture = await loadFixture({
     			root: './fixtures/binding-image-service/',
     		});
    @@ -16,6 +32,7 @@ describe('BindingImageService', () => {
     
     	after(async () => {
     		await previewServer.stop();
    +		await new Promise((resolve) => redirectServer.close(resolve));
     	});
     
     	it('returns 403 for missing href parameter', async () => {
    @@ -52,4 +69,10 @@ describe('BindingImageService', () => {
     		assert.equal(res.status, 200);
     		assert.equal(res.headers.get('content-type'), 'image/avif');
     	});
    +
    +	it('does not follow redirects for remote images', async () => {
    +		const href = `http://localhost:${redirectServerPort}/image.jpg`;
    +		const res = await fixture.fetch(`/_image?href=${encodeURIComponent(href)}&f=webp`);
    +		assert.equal(res.status, 404);
    +	});
     });
    
  • packages/integrations/cloudflare/test/fixtures/binding-image-service/astro.config.mjs+3 0 modified
    @@ -5,5 +5,8 @@ export default defineConfig({
     	adapter: cloudflare({
     		imageService: 'cloudflare-binding',
     	}),
    +	image: {
    +		domains: ['localhost'],
    +	},
     	output: 'server',
     });
    

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

6

News mentions

0

No linked articles in our index yet.