import type { NextApiRequest, NextApiResponse } from "next"; import type { File as FormidableFile } from "formidable"; import formidable from "formidable"; import fs from "fs/promises"; import path from "path"; import { createReport } from "@/lib/pdftron/serverSDK/createReport"; export const config = { api: { bodyParser: false, }, }; // 보안 설정 const SECURITY_CONFIG = { ALLOWED_EXTENSIONS: new Set([ 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv', 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp' ]), FORBIDDEN_EXTENSIONS: new Set([ 'exe', 'bat', 'cmd', 'scr', 'vbs', 'js', 'jar', 'com', 'pif', 'msi', 'reg', 'ps1', 'sh', 'php', 'asp', 'jsp', 'py', 'pl', 'html', 'htm', 'xhtml', 'xml', 'svg' ]), MAX_FILE_SIZE: 100 * 1024 * 1024, // 100MB MAX_FILENAME_LENGTH: 255, }; // 간단한 보안 검증 함수들 function validateExtension(fileName: string): { valid: boolean; error?: string } { const extension = path.extname(fileName).toLowerCase().substring(1); if (!extension) { return { valid: false, error: "파일 확장자가 없습니다" }; } if (SECURITY_CONFIG.FORBIDDEN_EXTENSIONS.has(extension)) { return { valid: false, error: `금지된 파일 형식입니다: .${extension}` }; } if (!SECURITY_CONFIG.ALLOWED_EXTENSIONS.has(extension)) { return { valid: false, error: `허용되지 않은 파일 형식입니다: .${extension}` }; } return { valid: true }; } function validateFileName(fileName: string): { valid: boolean; error?: string } { if (fileName.length > SECURITY_CONFIG.MAX_FILENAME_LENGTH) { return { valid: false, error: "파일명이 너무 깁니다" }; } // 위험한 문자 체크 const dangerousPatterns = [ /[<>:"'|?*]/, /[\x00-\x1f]/, /^\./, /\.\./, /\/|\\$/, /javascript:/i, /data:/i, /vbscript:/i, /on\w+=/i, /