From 55468da210291a4d29f8d71050c1c9d65d358ec8 Mon Sep 17 00:00:00 2001 From: hinakhadim Date: Tue, 5 Dec 2023 16:50:17 +0500 Subject: [PATCH] fix: file upload security issue --- cms/djangoapps/contentstore/exceptions.py | 6 +++++ cms/djangoapps/contentstore/views/assets.py | 26 ++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/cms/djangoapps/contentstore/exceptions.py b/cms/djangoapps/contentstore/exceptions.py index bf33bad8f94c..d4c627941149 100644 --- a/cms/djangoapps/contentstore/exceptions.py +++ b/cms/djangoapps/contentstore/exceptions.py @@ -13,3 +13,9 @@ class AssetSizeTooLargeException(Exception): """ Raised when the size of an uploaded asset exceeds the maximum size limit. """ + + +class InvalidFileTypeException(Exception): + """ + Raised when the type of file is not included in mimetypes + """ \ No newline at end of file diff --git a/cms/djangoapps/contentstore/views/assets.py b/cms/djangoapps/contentstore/views/assets.py index 38f11db62c53..a06c07b4f2fa 100644 --- a/cms/djangoapps/contentstore/views/assets.py +++ b/cms/djangoapps/contentstore/views/assets.py @@ -4,6 +4,7 @@ import json import logging import math +import mimetypes import re from functools import partial from urllib.parse import urljoin @@ -30,7 +31,7 @@ from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order -from ..exceptions import AssetNotFoundException, AssetSizeTooLargeException +from ..exceptions import AssetNotFoundException, AssetSizeTooLargeException, InvalidFileTypeException from ..utils import reverse_course_url __all__ = ['assets_handler'] @@ -45,6 +46,10 @@ } +mimetypes.init() +all_mimetypes = list(mimetypes.types_map.values()) + ['text/javascript', 'text/php'] + + @login_required @ensure_csrf_cookie def assets_handler(request, course_key_string=None, asset_key_string=None): @@ -445,6 +450,21 @@ def _get_error_if_course_does_not_exist(course_key): # lint-amnesty, pylint: di return HttpResponseBadRequest() +def _get_sanitized_filename(filename): + splitted_filename = filename.split('.') + extension = splitted_filename[-1] + + # for files with multiple extensions + filename = splitted_filename[:len(splitted_filename) - 1] + filename = "".join(filename) + return "".join(x for x in filename if x.isalnum()) + "." + extension + + +def _validate_mimetype(file_content_type): + if file_content_type in all_mimetypes: return file_content_type + raise InvalidFileTypeException('{} of filetype is not supported'.format(file_content_type)) + + def _get_file_metadata_as_dictionary(upload_file): # lint-amnesty, pylint: disable=missing-function-docstring # compute a 'filename' which is similar to the location formatting; we're # using the 'filename' nomenclature since we're using a FileSystem paradigm @@ -452,8 +472,8 @@ def _get_file_metadata_as_dictionary(upload_file): # lint-amnesty, pylint: disa # keep things a bit more consistent return { 'upload_file': upload_file, - 'filename': upload_file.name, - 'mime_type': upload_file.content_type, + 'filename': _get_sanitized_filename(upload_file.name), + 'mime_type': _validate_mimetype(upload_file.content_type), 'upload_file_size': get_file_size(upload_file) }