From ea97f297d7437fa14edd46acf2ba4314aed206dc Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:25:20 +0800 Subject: [PATCH] fix(weixin): confirm the WEBP signature, not just the RIFF prefix detectImageMime treated any data starting with the four RIFF bytes as image/webp, but RIFF is a generic container also used by WAV and AVI. A non-WebP RIFF file (e.g. a WAV renamed to .webp) therefore passed the magic-byte check in validateImagePath, which exists to confirm a file's real type. Verify the 'WEBP' marker at bytes 8-11 as well, matching the stricter check already in core's imageTokenizer and the earlier full-signature PNG fix. --- packages/channels/weixin/src/send.test.ts | 36 +++++++++++++++++++++-- packages/channels/weixin/src/send.ts | 8 ++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/channels/weixin/src/send.test.ts b/packages/channels/weixin/src/send.test.ts index f36ab339486..87d21948669 100644 --- a/packages/channels/weixin/src/send.test.ts +++ b/packages/channels/weixin/src/send.test.ts @@ -164,11 +164,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 9ba7b023137..a511598d1bd 100644 --- a/packages/channels/weixin/src/send.ts +++ b/packages/channels/weixin/src/send.ts @@ -80,11 +80,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'; }