Skip to content

Commit d2e25c6

Browse files
authored
fix: sanitize generated image filenames (#14160)
* fix: sanitize generated image filenames * Updtae test domain * Infer size in test
1 parent d699956 commit d2e25c6

File tree

4 files changed

+35
-3
lines changed

4 files changed

+35
-3
lines changed

.changeset/purple-peaches-allow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes a bug that meant some remote image URLs could cause invalid filenames to be used for processed images

packages/astro/src/assets/utils/transformToPath.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { shorthash } from '../../runtime/server/shorthash.js';
55
import type { ImageTransform } from '../types.js';
66
import { isESMImportedImage } from './imageKind.js';
77

8+
// Taken from https://github.com/rollup/rollup/blob/a8647dac0fe46c86183be8596ef7de25bc5b4e4b/src/utils/sanitizeFileName.ts
9+
// eslint-disable-next-line no-control-regex
10+
const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$%&*+,:;<=>?[\]^`{|}\u007F]/g;
11+
812
/**
913
* Converts a file path and transformation properties of the transformation image service, into a formatted filename.
1014
*
@@ -33,12 +37,12 @@ export function propsToFilename(filePath: string, transform: ImageTransform, has
3337
if (filePath.startsWith('data:')) {
3438
filename = shorthash(filePath);
3539
} else {
36-
filename = basename(filename, ext);
40+
filename = basename(filename, ext).replace(INVALID_CHAR_REGEX, '_');
3741
}
3842
const prefixDirname = isESMImportedImage(transform.src) ? dirname(filePath) : '';
3943

4044
let outputExt = transform.format ? `.${transform.format}` : ext;
41-
return decodeURIComponent(`${prefixDirname}/${filename}_${hash}${outputExt}`);
45+
return `${prefixDirname}/${filename}_${hash}${outputExt}`;
4246
}
4347

4448
/**

packages/astro/test/core-image.test.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ describe('astro:image', () => {
904904
root: './fixtures/core-image-ssg/',
905905
image: {
906906
service: testImageService(),
907-
domains: ['astro.build', 'avatars.githubusercontent.com'],
907+
domains: ['astro.build', 'avatars.githubusercontent.com', 'kaleidoscopic-biscotti-6fe98c.netlify.app'],
908908
},
909909
});
910910
// Remove cache directory
@@ -953,6 +953,18 @@ describe('astro:image', () => {
953953
assert.equal(data instanceof Buffer, true);
954954
});
955955

956+
it('handles remote images with special characters', async () => {
957+
const html = await fixture.readFile('/special-chars/index.html');
958+
const $ = cheerio.load(html);
959+
const $img = $('img');
960+
assert.equal($img.length, 1);
961+
const src = $img.attr('src');
962+
// The filename should be encoded and sanitized
963+
assert.ok(src.startsWith('/_astro/c_23'));
964+
const data = await fixture.readFile(src, null);
965+
assert.ok(data instanceof Buffer);
966+
});
967+
956968
it('Picture component images are written', async () => {
957969
const html = await fixture.readFile('/picturecomponent/index.html');
958970
const $ = cheerio.load(html);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
import { Image } from 'astro:assets';
3+
---
4+
<html>
5+
<head>
6+
<meta charset="UTF-8" />
7+
<title>Special Characters in Image Paths</title>
8+
</head>
9+
<body>
10+
<Image src="https://kaleidoscopic-biscotti-6fe98c.netlify.app/c%2523.png" alt="C#" inferSize />
11+
</html>

0 commit comments

Comments
 (0)