Our application has pages where file upload facility is available. During the test it was noted that arbitrary files of different content-type and size could be uploaded.
In one instance, testers could insert benign EICAR Test Virus file and also a binary(notepad.exe) file. It was uploaded and acknowledged as successful.
Multiple attack scenarios could be possible with the above vulnerability as attackers could upload malicious files or files with huge size there by forcing system to malfunction or exhaust system resources.
However we have a file size validation where it allows the file size up to the value entered in site_config.json i.e “max_file_size”.
But we also need to validate the content.
Hearing from anyone who has given a though on this before, would be of a great help.
import mimetypes
import frappe
from frappe import _
from frappe.core.doctype.file.exceptions import MaxFileSizeReachedError
from frappe.core.doctype.file.file import File
class CustomFile(File):
def check_max_file_size(self):
# Predefined file size limits by category (in bytes)
file_type_limits = {
"image": 5 * 1024 * 1024, # 5 MB for image files
"document": 10 * 1024 * 1024, # 10 MB for document files
"video": 30 * 1024 * 1024, # 30 MB for video files
}
# Guess the MIME type of the file
file_type = mimetypes.guess_type(self.file_name)[0]
if not file_type:
# If MIME type is undetectable, raise an error
frappe.throw(_("Could not determine file type from MIME type."))
# Determine the file's category based on MIME type
if file_type.startswith("image"):
file_category = "image"
elif file_type.startswith("application"):
# Check for common document types
if any(file_type.startswith(prefix) for prefix in [
"application/pdf",
"application/msword",
"application/vnd.ms-excel",]):
file_category = "document"
elif file_type.startswith("video"):
file_category = "video"
else:
# If file type is unsupported, raise an error
frappe.throw(_("Unsupported file type detected: {0}").format(file_type))
# Get the file content size in bytes
file_size = len(self.get("content") or b"") # Default to empty byte string if no content
# Check if the file size exceeds the maximum allowed limit for the detected category
if file_category and file_size > file_type_limits.get(file_category, 0):
# Raise error if file exceeds limit
max_size_mb = file_type_limits[file_category] / (1024 * 1024) # Convert bytes to MB
msg = _("File size exceeds the maximum allowed limit of {0} MB").format(max_size_mb)
frappe.throw(msg, exc=MaxFileSizeReachedError)
return file_size # Return the file size if it's within limits