From 06c7dc561e40c86a65114217b2bed2150bce8f63 Mon Sep 17 00:00:00 2001 From: maruto <53184634+maru0804@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:58:39 +0900 Subject: [PATCH] fix: verify audio/video MIME types with content check (#16907) --- packages/core/src/utils/fileUtils.test.ts | 44 +++++++++++++++++++++-- packages/core/src/utils/fileUtils.ts | 14 +++++--- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/packages/core/src/utils/fileUtils.test.ts b/packages/core/src/utils/fileUtils.test.ts index 7d792217b1..eb2ecd0e97 100644 --- a/packages/core/src/utils/fileUtils.test.ts +++ b/packages/core/src/utils/fileUtils.test.ts @@ -632,8 +632,6 @@ describe('fileUtils', () => { { type: 'image', file: 'file.png', mime: 'image/png' }, { type: 'image', file: 'file.jpg', mime: 'image/jpeg' }, { type: 'pdf', file: 'file.pdf', mime: 'application/pdf' }, - { type: 'audio', file: 'song.mp3', mime: 'audio/mpeg' }, - { type: 'video', file: 'movie.mp4', mime: 'video/mp4' }, { type: 'binary', file: 'archive.zip', mime: 'application/zip' }, { type: 'binary', file: 'app.exe', mime: 'application/octet-stream' }, ])( @@ -644,6 +642,25 @@ describe('fileUtils', () => { }, ); + it.each([ + { type: 'audio', ext: '.mp3', mime: 'audio/mpeg' }, + { type: 'video', ext: '.mp4', mime: 'video/mp4' }, + ])( + 'should detect $type type for binary files with $ext extension', + async ({ type, ext, mime }) => { + const filePath = path.join(tempRootDir, `test${ext}`); + const binaryContent = Buffer.from([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]); + actualNodeFs.writeFileSync(filePath, binaryContent); + + mockMimeGetType.mockReturnValueOnce(mime); + expect(await detectFileType(filePath)).toBe(type); + + actualNodeFs.unlinkSync(filePath); + }, + ); + it('should detect svg type by extension', async () => { expect(await detectFileType('image.svg')).toBe('svg'); expect(await detectFileType('image.icon.svg')).toBe('svg'); @@ -664,6 +681,24 @@ describe('fileUtils', () => { // filePathForDetectTest is already a text file by default from beforeEach expect(await detectFileType(filePathForDetectTest)).toBe('text'); }); + + it('should detect .adp files with XML content as text, not audio (#16888)', async () => { + const adpFilePath = path.join(tempRootDir, 'test.adp'); + const xmlContent = ` + + + + + + +`; + actualNodeFs.writeFileSync(adpFilePath, xmlContent); + mockMimeGetType.mockReturnValueOnce('audio/adpcm'); + + expect(await detectFileType(adpFilePath)).toBe('text'); + + actualNodeFs.unlinkSync(adpFilePath); + }); }); describe('processSingleFileContent', () => { @@ -778,7 +813,10 @@ describe('fileUtils', () => { }); it('should process an audio file', async () => { - const fakeMp3Data = Buffer.from('fake mp3 data'); + const fakeMp3Data = Buffer.from([ + 0x49, 0x44, 0x33, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]); actualNodeFs.writeFileSync(testAudioFilePath, fakeMp3Data); mockMimeGetType.mockReturnValue('audio/mpeg'); const result = await processSingleFileContent( diff --git a/packages/core/src/utils/fileUtils.ts b/packages/core/src/utils/fileUtils.ts index 5525f98d06..5155cad51a 100644 --- a/packages/core/src/utils/fileUtils.ts +++ b/packages/core/src/utils/fileUtils.ts @@ -306,11 +306,15 @@ export async function detectFileType( if (lookedUpMimeType.startsWith('image/')) { return 'image'; } - if (lookedUpMimeType.startsWith('audio/')) { - return 'audio'; - } - if (lookedUpMimeType.startsWith('video/')) { - return 'video'; + // Verify audio/video with content check to avoid MIME misidentification (#16888) + if ( + lookedUpMimeType.startsWith('audio/') || + lookedUpMimeType.startsWith('video/') + ) { + if (!(await isBinaryFile(filePath))) { + return 'text'; + } + return lookedUpMimeType.startsWith('audio/') ? 'audio' : 'video'; } if (lookedUpMimeType === 'application/pdf') { return 'pdf';