fix: preview audio, video and icon attachments sent with a catch-all type

This commit is contained in:
Linus Rath
2026-09-30 15:08:31 +02:00
parent d0a1d0183b
commit 1d90fe17f4
2 changed files with 28 additions and 3 deletions
+15
View File
@@ -106,6 +106,21 @@ describe('previewBlobType', () => {
expect(previewBlobType('photo.jpg', 'application/x-as400attachment', '')).toBe('image/jpeg');
});
it('derives a viewable type from every image, audio and video extension but SVG', () => {
const kinds: Record<string, string[]> = {
image: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'bmp', 'ico'],
audio: ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac', 'opus'],
video: ['mp4', 'webm', 'ogv', 'mov', 'm4v', 'avi', 'mkv'],
};
for (const [kind, extensions] of Object.entries(kinds)) {
for (const ext of extensions) {
const name = `file.${ext}`;
expect(getFilePreviewKind(name)).toBe(kind);
expect(previewBlobType(name, 'application/x-as400attachment', '')).toMatch(new RegExp(`^${kind}/`));
}
}
});
it('hands the unsandboxed PDF viewer nothing but application/pdf', () => {
// The PDF preview iframe cannot be sandboxed (the browser's PDF viewer
// refuses to run there), so XML or XHTML declared on a .pdf must not
+13 -3
View File
@@ -71,9 +71,10 @@ export function isFilePreviewable(name?: string, type?: string): boolean {
return getFilePreviewKind(name, type) !== 'unsupported';
}
// Canonical types for the extensions the preview renders from a blob. No
// script-capable type (SVG, HTML) may appear here: the preview trusts these
// over whatever the sender declared.
// Canonical types for the extensions the preview renders from a blob: PDF and
// every image, audio and video extension above except SVG. No script-capable
// type (SVG, HTML) may appear here: the preview trusts these over whatever
// the sender declared.
const EXT_TO_MIME: Record<string, string> = {
pdf: 'application/pdf',
png: 'image/png',
@@ -83,13 +84,22 @@ const EXT_TO_MIME: Record<string, string> = {
webp: 'image/webp',
avif: 'image/avif',
bmp: 'image/bmp',
ico: 'image/x-icon',
mp3: 'audio/mpeg',
wav: 'audio/wav',
ogg: 'audio/ogg',
m4a: 'audio/mp4',
flac: 'audio/flac',
aac: 'audio/aac',
// Ogg Opus (RFC 7845); audio/opus names the bare RTP payload.
opus: 'audio/ogg',
mp4: 'video/mp4',
webm: 'video/webm',
ogv: 'video/ogg',
mov: 'video/quicktime',
m4v: 'video/mp4',
avi: 'video/x-msvideo',
mkv: 'video/x-matroska',
};
const GENERIC_MIME_TYPES = new Set(['', 'application/octet-stream', 'binary/octet-stream']);