diff --git a/packages/channels/weixin/src/send.test.ts b/packages/channels/weixin/src/send.test.ts index 5824fab14e..1a494a531d 100644 --- a/packages/channels/weixin/src/send.test.ts +++ b/packages/channels/weixin/src/send.test.ts @@ -162,11 +162,43 @@ describe('detectImageMime', () => { expect(detectImageMime(buf)).toBe('image/gif'); }); - it('detects WebP magic bytes (RIFF)', () => { - const buf = Buffer.from([0x52, 0x49, 0x46, 0x46]); + it('detects WebP magic bytes (RIFF....WEBP)', () => { + const buf = Buffer.from([ + 0x52, + 0x49, + 0x46, + 0x46, // "RIFF" + 0x1a, + 0x00, + 0x00, + 0x00, // file size (little-endian) + 0x57, + 0x45, + 0x42, + 0x50, // "WEBP" + ]); expect(detectImageMime(buf)).toBe('image/webp'); }); + it('does not misidentify a non-WebP RIFF container (e.g. WAV) as WebP', () => { + // WAV is also a RIFF container; only bytes 8-11 distinguish it from WebP. + const buf = Buffer.from([ + 0x52, + 0x49, + 0x46, + 0x46, // "RIFF" + 0x24, + 0x00, + 0x00, + 0x00, // file size + 0x57, + 0x41, + 0x56, + 0x45, // "WAVE", not "WEBP" + ]); + expect(() => detectImageMime(buf)).toThrow('Unrecognized image format'); + }); + it('detects JPEG magic bytes', () => { const buf = Buffer.from([0xff, 0xd8, 0xff]); expect(detectImageMime(buf)).toBe('image/jpeg'); diff --git a/packages/channels/weixin/src/send.ts b/packages/channels/weixin/src/send.ts index 3c8c77863a..c49359739b 100644 --- a/packages/channels/weixin/src/send.ts +++ b/packages/channels/weixin/src/send.ts @@ -90,11 +90,17 @@ export function detectImageMime(data: Buffer): string { if (data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46) { return 'image/gif'; } + // WebP is a RIFF container, so the "RIFF" prefix alone is not enough — WAV and + // AVI share it. The bytes at offset 8-11 must spell "WEBP" to confirm the type. if ( data[0] === 0x52 && data[1] === 0x49 && data[2] === 0x46 && - data[3] === 0x46 + data[3] === 0x46 && + data[8] === 0x57 && + data[9] === 0x45 && + data[10] === 0x42 && + data[11] === 0x50 ) { return 'image/webp'; }