le-app/app/lib/upload-security.ts

28 lines
1.2 KiB
TypeScript

import path from "path";
const EXECUTABLE_EXTENSIONS = new Set([
".bat", ".cmd", ".com", ".dll", ".exe", ".hta", ".htm", ".html",
".jar", ".js", ".jsp", ".mjs", ".cjs", ".msi", ".php", ".phtml",
".phar", ".ps1", ".sh", ".vbs", ".war", ".wsf",
]);
export function isExecutableAttachment(filename: string): boolean {
return EXECUTABLE_EXTENSIONS.has(path.extname(filename).toLowerCase());
}
export async function hasValidImageSignature(file: Blob): Promise<boolean> {
const bytes = new Uint8Array(await file.slice(0, 16).arrayBuffer());
if (bytes.length < 3) return false;
const jpeg = bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff;
const png = bytes.length >= 8 &&
bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47 &&
bytes[4] === 0x0d && bytes[5] === 0x0a && bytes[6] === 0x1a && bytes[7] === 0x0a;
const gifHeader = bytes.length >= 6 ? String.fromCharCode(...bytes.slice(0, 6)) : "";
const gif = gifHeader === "GIF87a" || gifHeader === "GIF89a";
const webp = bytes.length >= 12 &&
String.fromCharCode(...bytes.slice(0, 4)) === "RIFF" &&
String.fromCharCode(...bytes.slice(8, 12)) === "WEBP";
return jpeg || png || gif || webp;
}