Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ const MINIMAL_PNG = Buffer.from(
'base64'
);

// RIFF containers share the "RIFF" prefix; bytes 8-11 carry the actual form tag.
const riffBuffer = (form: string) =>
Buffer.concat([
Buffer.from([0x52, 0x49, 0x46, 0x46]), // "RIFF"
Buffer.from([0x24, 0x00, 0x00, 0x00]), // chunk size (non-zero, like a real file)
Buffer.from(form, 'ascii'), // form tag at bytes 8-11
Buffer.alloc(16),
]);
const MINIMAL_WEBP = riffBuffer('WEBP');
const MINIMAL_WAV = riffBuffer('WAVE');

// Large PNG-like binary: PNG magic + binary junk with null bytes (>= 256 base64 chars, >= 128 decoded bytes)
const LARGE_PNG_BINARY = (() => {
const buf = Buffer.alloc(300);
Expand Down Expand Up @@ -382,6 +393,14 @@ describe('detectExtensionFromMagic', () => {
test('returns empty for tiny buffer', () => {
expect(detectExtensionFromMagic(Buffer.from([0x89]))).toBe('');
});

test('detects WebP from the RIFF form tag, not as WAV', () => {
expect(detectExtensionFromMagic(MINIMAL_WEBP)).toBe('.webp');
});

test('still detects WAV from the RIFF/WAVE form tag', () => {
expect(detectExtensionFromMagic(MINIMAL_WAV)).toBe('.wav');
});
});

// ============================================================
Expand Down
18 changes: 17 additions & 1 deletion packages/desktop/packages/shared/src/utils/binary-detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const MAGIC_SIGNATURES: Array<{ bytes: number[]; ext: string }> = [
{ bytes: [0x42, 0x4D], ext: '.bmp' }, // BM
{ bytes: [0x49, 0x44, 0x33], ext: '.mp3' }, // ID3 (MP3 with ID3 tag)
{ bytes: [0xFF, 0xFB], ext: '.mp3' }, // MP3 frame sync
{ bytes: [0x52, 0x49, 0x46, 0x46], ext: '.wav' }, // RIFF (WAV container)
{ bytes: [0x4F, 0x67, 0x67, 0x53], ext: '.ogg' }, // OggS
{ bytes: [0x66, 0x4C, 0x61, 0x43], ext: '.flac' }, // fLaC
];
Expand Down Expand Up @@ -118,6 +117,23 @@ export function looksLikeBinary(buffer: Buffer): boolean {
export function detectExtensionFromMagic(buffer: Buffer): string {
if (buffer.length < 8) return '';

// RIFF is a shared container (WAV, WebP, AVI); the four-character form tag at
// bytes 8-11 is what actually distinguishes them, so the "RIFF" prefix alone
// is ambiguous and must not be assumed to be WAV.
if (
buffer.length >= 12 &&
buffer[0] === 0x52 &&
buffer[1] === 0x49 &&
buffer[2] === 0x46 &&
buffer[3] === 0x46
) {
const form = buffer.toString('ascii', 8, 12);
if (form === 'WEBP') return '.webp';
if (form === 'AVI ') return '.avi';
if (form === 'WAVE') return '.wav';
return '';
}

for (const sig of MAGIC_SIGNATURES) {
if (sig.bytes.every((byte, i) => buffer[i] === byte)) {
return sig.ext;
Expand Down
Loading