Spaces:
Runtime error
Runtime error
File size: 1,619 Bytes
2329815 53fe1f0 2329815 53fe1f0 2329815 53fe1f0 2329815 53fe1f0 2329815 53fe1f0 2329815 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | const ALLOWED_MIME_TYPES = [
'image/jpeg',
'image/png',
];
const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png'];
// Magic bytes (file signatures) for image validation
const MAGIC_BYTES: Record<string, Buffer[]> = {
'image/jpeg': [Buffer.from([0xff, 0xd8, 0xff])],
'image/png': [Buffer.from([0x89, 0x50, 0x4e, 0x47])],
};
const MAX_FILE_SIZE = 2 * 1024 * 1024; // 2MB
export function validateImageFile(file: Express.Multer.File): { valid: boolean; error?: string } {
// 1. Check file size
if (file.size > MAX_FILE_SIZE) {
return { valid: false, error: 'File size must be under 2MB' };
}
// 2. Check MIME type
if (!ALLOWED_MIME_TYPES.includes(file.mimetype)) {
return { valid: false, error: 'Only JPEG and PNG images are allowed' };
}
// 3. Check file extension
const ext = file.originalname.split('.').pop()?.toLowerCase();
if (!ext || !ALLOWED_EXTENSIONS.includes(ext)) {
return { valid: false, error: 'Invalid file extension' };
}
// 4. Validate magic bytes (check first 512 bytes for polyglot detection)
const expectedSignatures = MAGIC_BYTES[file.mimetype];
if (expectedSignatures) {
const fileHeader = file.buffer.subarray(0, 512);
const isValid = expectedSignatures.some(sig => fileHeader.subarray(0, sig.length).equals(sig));
if (!isValid) {
return { valid: false, error: 'File content does not match its type' };
}
}
return { valid: true };
}
export function getSafeExtension(mimeType: string): string {
const map: Record<string, string> = {
'image/jpeg': 'jpg',
'image/png': 'png',
};
return map[mimeType] || 'jpg';
}
|