import { createBlob, createRepBlob, uploadSingleFile } from "../../../actions/azurestorage"; import nextConnect from "next-connect"; import middleware from "../middleware/middleware"; import { consoleLogger } from "../../../actions/core/logger"; import { hashAPIPath } from "../../../actions/core/hash"; import { fileTypeFromBuffer } from "file-type"; import fs from "fs"; import path from "path"; const FILENAME_ALLOWED = /^[A-Za-z0-9 ._\-:()—']+$/; function validateFilenameServer(originalFilename) { const name = path.basename(originalFilename || ""); if (!name) return { ok: false, reason: "Empty filename" }; if (name.includes("#")) return { ok: false, reason: "Filename contains #" }; if (/[<>"/\\|?*]/.test(name)) return { ok: false, reason: "Filename contains reserved characters" }; if (!FILENAME_ALLOWED.test(name)) return { ok: false, reason: "Filename contains invalid characters" }; if (name === "." || name === "..") return { ok: false, reason: "Invalid filename" }; return { ok: true, name }; } const ApiProxy = nextConnect(); ApiProxy.use(middleware); ApiProxy.post(async (req, res) => { var checkHash = req.query.hash; var checkquerypath = "/api/file/uploadsinglefile"; if (hashAPIPath(checkquerypath) != "?hash=" + checkHash) { return res.status(400).json(); } const containerID = req.body?.containerID?.[0]; const casefolderID = req.body?.casefolderID?.[0]; if ( typeof containerID === "undefined" || containerID.length === 0 || typeof casefolderID === "undefined" || casefolderID.length === 0 ) { return res.status(400).json(); } const uploadedFiles = req.files || {}; // Add other mimetypes here const allowedMimeTypes = [ "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "image/tiff", "image/jpeg", "application/zip", "image/png", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ]; var allowedFilesFormData = {}; var invalidFiles = []; // To store the names of invalid files for (const [fileName, fileDetails] of Object.entries(uploadedFiles)) { const file = fileDetails[0]; const filePath = file.path; // validate ORIGINAL filename from upload const fnCheck = validateFilenameServer(file.originalFilename); if (!fnCheck.ok) { invalidFiles.push(`${file.originalFilename} - Invalid filename`); continue; // do not process further } // Check the MIME type from file headers const fileMimeType = file.headers["content-type"]; if (allowedMimeTypes.includes(fileMimeType)) { // Check file signature (magic number) using file-type const buffer = fs.readFileSync(filePath); try { const type = await fileTypeFromBuffer(buffer); // Correct usage of fileTypeFromBuffer if (type && allowedMimeTypes.includes(type.mime)) { // If the MIME type from the file signature matches the allowed list allowedFilesFormData[fileName] = fileDetails.map( (file) => ({ fieldName: file.fieldName, originalFilename: file.originalFilename, path: file.path, size: file.size, headers: file.headers, contentType: fileMimeType }) ); } else { invalidFiles.push(fileName); // Track invalid file } } catch (error) { consoleLogger(error); } } else { invalidFiles.push(fileName); // Track invalid file } } try { const data = await uploadSingleFile( allowedFilesFormData, containerID, casefolderID ); return res.status(200).json({ data, invalidFiles }); } catch (error) { consoleLogger(error); return res.status(500).json({ error: "Failed to upload files" }); } }); export const config = { api: { bodyParser: false } }; export default ApiProxy;