// ============================================================ // Lightweight magic-byte sniffer — verifies the claimed MIME // against the file's actual header. Covers the types the app // currently accepts: PDF, PNG, JPEG, GIF, WEBP, DOCX/ODT (zip), // plain text / CSV / MD (heuristic). // ============================================================ function sniff(buffer) { if (!Buffer.isBuffer(buffer) || buffer.length < 4) return 'application/octet-stream'; // PDF if (buffer.slice(0, 4).toString('ascii') === '%PDF') return 'application/pdf'; // PNG if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) return 'image/png'; // JPEG if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return 'image/jpeg'; // GIF if (buffer.slice(0, 3).toString('ascii') === 'GIF') return 'image/gif'; // WEBP: "RIFF" .... "WEBP" if (buffer.length >= 12 && buffer.slice(0, 4).toString('ascii') === 'RIFF' && buffer.slice(8, 12).toString('ascii') === 'WEBP') return 'image/webp'; // ZIP-based (docx, xlsx, pptx, odt) if (buffer[0] === 0x50 && buffer[1] === 0x4b && (buffer[2] === 0x03 || buffer[2] === 0x05 || buffer[2] === 0x07)) return 'application/zip'; // Office legacy (.doc, .xls, .ppt) if (buffer[0] === 0xd0 && buffer[1] === 0xcf && buffer[2] === 0x11 && buffer[3] === 0xe0) return 'application/x-cfb'; // Heuristic: printable ASCII / UTF-8 → text var printable = 0; var sample = buffer.slice(0, Math.min(512, buffer.length)); for (var i = 0; i < sample.length; i++) { var b = sample[i]; if (b === 9 || b === 10 || b === 13 || (b >= 32 && b < 127) || b >= 128) printable++; } if (printable / sample.length > 0.95) return 'text/plain'; return 'application/octet-stream'; } // Check that the claimed MIME type is compatible with the sniffed type. function matches(claimedMime, buffer) { var actual = sniff(buffer); if (!claimedMime) return false; if (claimedMime === actual) return true; // Word docx files claim application/vnd.openxmlformats-... but sniff as zip if (actual === 'application/zip' && ( claimedMime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' || claimedMime === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || claimedMime === 'application/vnd.openxmlformats-officedocument.presentationml.presentation' || claimedMime === 'application/vnd.oasis.opendocument.text' )) return true; // Older .doc/.xls if (actual === 'application/x-cfb' && /msword|excel|ms-office|ms-powerpoint/.test(claimedMime)) return true; // text variants if (actual === 'text/plain' && (claimedMime === 'text/csv' || claimedMime === 'text/markdown' || claimedMime === 'application/json' || claimedMime === 'text/plain')) return true; return false; } module.exports = { sniff: sniff, matches: matches };