fix: verify audio/video MIME types with content check (#16907)

This commit is contained in:
maruto
2026-01-28 23:58:39 +09:00
committed by GitHub
parent f54eaa1713
commit 06c7dc561e
2 changed files with 50 additions and 8 deletions

View File

@@ -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 = `<?xml version="1.0" encoding="UTF-8"?>
<AdapterType Name="ATimeOut" Comment="Adapter for timed events">
<InterfaceList>
<EventInputs>
<Event Name="TimeOut"/>
</EventInputs>
</InterfaceList>
</AdapterType>`;
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(

View File

@@ -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';