diff --git a/lib/content/sniff.ts b/lib/content/sniff.ts index ebd15b9..6e2503f 100644 --- a/lib/content/sniff.ts +++ b/lib/content/sniff.ts @@ -33,6 +33,8 @@ export type ContentType = | 'wav' | 'ogg' | 'bzip2' + /** A Windows executable (MZ with its PE header): a "movie" that is a program is fake (app/services/release_judge.ts). */ + | 'exe' /** Magic bytes at an offset; null matches any byte. */ const SIGNATURES: [ContentType, number, (number | null)[]][] = [ @@ -75,6 +77,11 @@ export function sniffContentType(data: Buffer): ContentType | null { if (form === 'AVI ') return 'avi' if (form === 'WAVE') return 'wav' } + // MZ, and "PE\0\0" where the DOS header points (e_lfanew at 0x3c): two bytes alone match random data too often. + if (data.length >= 0x40 && data[0] === 0x4d && data[1] === 0x5a) { + const pe = data.readUInt32LE(0x3c) + if (pe + 4 <= data.length && data.readUInt32BE(pe) === 0x50450000) return 'exe' + } const head = data.subarray(0, 512).toString('latin1') if (/^\s*(<\?xml[^>]*>\s*)?(])/.test(head)) return 'nzb' return null diff --git a/tests/unit/content/sniff.spec.ts b/tests/unit/content/sniff.spec.ts new file mode 100644 index 0000000..9497860 --- /dev/null +++ b/tests/unit/content/sniff.spec.ts @@ -0,0 +1,20 @@ +import { test } from '@japa/runner' +import { sniffContentType } from '#lib/content/sniff' + +test.group('content / sniff', () => { + test('a Windows executable: MZ with a PE header where the DOS header points', ({ assert }) => { + const exe = Buffer.alloc(0x100) + exe.write('MZ', 0, 'latin1') + exe.writeUInt32LE(0x80, 0x3c) + exe.write('PE\0\0', 0x80, 'latin1') + assert.equal(sniffContentType(exe), 'exe') + // MZ alone (random data, a text) is no executable. + const mz = Buffer.alloc(0x100) + mz.write('MZ', 0, 'latin1') + assert.isNull(sniffContentType(mz)) + // A pointer past the data: none. + mz.writeUInt32LE(0xffff, 0x3c) + assert.isNull(sniffContentType(mz)) + assert.equal(sniffContentType(Buffer.from([0x1a, 0x45, 0xdf, 0xa3, 0, 0, 0, 0])), 'matroska') + }) +})