Recognize blob: and data:image URLs as images (#307)

This commit is contained in:
Kamil Jiwa
2026-06-21 08:56:47 -07:00
committed by GitHub
parent e6ba57886c
commit b6e3a95741
2 changed files with 29 additions and 4 deletions

View File

@@ -215,6 +215,17 @@ describe('Media Resolution and Utilities', () => {
expect(isImageFile('test.webp?a=1&b=2#h')).toBe(true); expect(isImageFile('test.webp?a=1&b=2#h')).toBe(true);
expect(isImageFile('test.pdf?img=test.jpg')).toBe(false); expect(isImageFile('test.pdf?img=test.jpg')).toBe(false);
}); });
it('treats blob: and data:image URLs as images', () => {
expect(isImageFile('blob:https://example.org/0-1-2-3')).toBe(true);
expect(isImageFile('data:image/png;base64,iVBORw0KGgo=')).toBe(true);
expect(isImageFile('data:image/avif;base64,AAAA')).toBe(true);
});
it('does not treat non-image extensionless URLs as images', () => {
expect(isImageFile('data:application/pdf;base64,AAAA')).toBe(false);
expect(isImageFile('https://example.org/photo')).toBe(false);
});
}); });
describe('isBrowserLoadable()', () => { describe('isBrowserLoadable()', () => {

View File

@@ -207,11 +207,25 @@ export function normalizeGedcom(gedcom: JsonGedcomData): JsonGedcomData {
const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp']; const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp'];
/** Returns true if the given file name has a known image extension. */ /**
* Returns true if the given file reference points to an image: a name/URL with
* a known image extension, or an extensionless but inherently browser-renderable
* `blob:` object URL or `data:image/...` URI. The latter two carry no path
* extension but are already accepted by `isBrowserLoadable`, so without this
* `filterImage` would drop them. A `blob:` URL's type is not derivable from the
* string, so it is accepted unconditionally; `data:` is required to be an image.
*/
export function isImageFile(fileName: string): boolean { export function isImageFile(fileName: string): boolean {
const cleanName = fileName.split(/[?#]/)[0]; // Anchored tests read only the scheme, so an inlined data: payload (which can
const lowerName = cleanName.toLowerCase(); // be multi-megabyte) is never scanned or copied.
return IMAGE_EXTENSIONS.some((ext) => lowerName.endsWith(ext)); if (/^blob:/i.test(fileName)) {
return true;
}
if (/^data:/i.test(fileName)) {
return /^data:image\//i.test(fileName);
}
const cleanName = fileName.split(/[?#]/)[0].toLowerCase();
return IMAGE_EXTENSIONS.some((ext) => cleanName.endsWith(ext));
} }
/** /**