diff --git a/lib/__tests__/file-preview.test.ts b/lib/__tests__/file-preview.test.ts index ba46bfea0..c8bfe001f 100644 --- a/lib/__tests__/file-preview.test.ts +++ b/lib/__tests__/file-preview.test.ts @@ -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 = { + 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 diff --git a/lib/file-preview.ts b/lib/file-preview.ts index d59bd735c..e7d010ae2 100644 --- a/lib/file-preview.ts +++ b/lib/file-preview.ts @@ -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 = { pdf: 'application/pdf', png: 'image/png', @@ -83,13 +84,22 @@ const EXT_TO_MIME: Record = { 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']);