diff --git a/api/upload_attachment.php b/api/upload_attachment.php index 6fa4e48..481d99f 100644 --- a/api/upload_attachment.php +++ b/api/upload_attachment.php @@ -194,6 +194,23 @@ if ($file['size'] > $maxSize) { ResponseHelper::error('File size exceeds maximum allowed (' . AttachmentModel::formatFileSize($maxSize) . ')'); } +// Check per-ticket attachment count/storage quota — bounds an authenticated +// low-privilege user slowly filling the uploads/ disk across many tickets, +// which was previously bounded only by the request-rate limiter, not volume. +$attachmentModel = new AttachmentModel($conn); +$maxAttachments = $GLOBALS['config']['MAX_ATTACHMENTS_PER_TICKET'] ?? 50; +if ($attachmentModel->getAttachmentCount($ticketId) >= $maxAttachments) { + ResponseHelper::error("This ticket already has the maximum of {$maxAttachments} attachments"); +} + +$maxTotalSize = $GLOBALS['config']['MAX_TOTAL_ATTACHMENT_SIZE_PER_TICKET'] ?? 104857600; +if ($attachmentModel->getTotalSizeForTicket($ticketId) + $file['size'] > $maxTotalSize) { + ResponseHelper::error( + 'This upload would exceed the ticket\'s total attachment size limit of ' + . AttachmentModel::formatFileSize($maxTotalSize) + ); +} + // Get MIME type $finfo = new finfo(FILEINFO_MIME_TYPE); $mimeType = $finfo->file($file['tmp_name']); @@ -265,7 +282,6 @@ if (empty($originalFilename)) { // Save to database try { - $attachmentModel = new AttachmentModel($conn); $attachmentId = $attachmentModel->addAttachment( $ticketId, $uniqueFilename, diff --git a/config/config.php b/config/config.php index 3ffb242..80ab44e 100644 --- a/config/config.php +++ b/config/config.php @@ -115,6 +115,8 @@ $GLOBALS['config'] = [ // File upload settings 'MAX_UPLOAD_SIZE' => 10485760, // 10MB in bytes + 'MAX_ATTACHMENTS_PER_TICKET' => 50, + 'MAX_TOTAL_ATTACHMENT_SIZE_PER_TICKET' => 104857600, // 100MB in bytes 'ALLOWED_FILE_TYPES' => [ 'image/jpeg', 'image/png',