You can now turn off file attachment filtering, fixes #1162

This commit is contained in:
Timothy Hobbs 2024-02-20 22:57:39 +01:00
parent b1b83cd593
commit 9a353bf4a5
5 changed files with 34 additions and 20 deletions

View File

@ -85,9 +85,11 @@ These changes are visible throughout django-helpdesk
**Default:** ``HELPDESK_MAX_EMAIL_ATTACHMENT_SIZE = 512000`` **Default:** ``HELPDESK_MAX_EMAIL_ATTACHMENT_SIZE = 512000``
- **VALID_EXTENSIONS** Valid extensions for file types that can be attached to tickets - **HELPDESK_VALID_EXTENSIONS** Valid extensions for file types that can be attached to tickets
**Default:** ``VALID_EXTENSIONS = ['.txt', '.asc', '.htm', '.html', '.pdf', '.doc', '.docx', '.odt', '.jpg', '.png', '.eml'] **Default:** ``HELPDESK_VALID_EXTENSIONS = ['.txt', '.asc', '.htm', '.html', '.pdf', '.doc', '.docx', '.odt', '.jpg', '.png', '.eml']
- **HELPDESK_VALIDATE_ATTACHMENT_TYPES** If you'd like to turn of filtering of helpdesk extension types you can set this to False.
- **QUEUE_EMAIL_BOX_UPDATE_ONLY** Only process mail with a valid tracking ID; all other mail will be ignored instead of creating a new ticket. - **QUEUE_EMAIL_BOX_UPDATE_ONLY** Only process mail with a valid tracking ID; all other mail will be ignored instead of creating a new ticket.

View File

@ -337,6 +337,10 @@ HELPDESK_IMAP_DEBUG_LEVEL = getattr(settings, 'HELPDESK_IMAP_DEBUG_LEVEL', 0)
# Override it in your own Django settings.py # Override it in your own Django settings.py
HELPDESK_ATTACHMENT_DIR_PERMS = int(getattr(settings, 'HELPDESK_ATTACHMENT_DIR_PERMS', "755"), 8) HELPDESK_ATTACHMENT_DIR_PERMS = int(getattr(settings, 'HELPDESK_ATTACHMENT_DIR_PERMS', "755"), 8)
HELPDESK_VALID_EXTENSIONS = getattr(settings, 'HELPDESK_VALID_EXTENSIONS', ['.txt', '.asc', '.htm', '.html', '.pdf', '.doc', '.docx', '.odt', '.jpg', '.png', '.eml'])
HELPDESK_VALIDATE_ATTACHMENT_TYPES = getattr(settings, 'HELPDESK_VALIDATE_ATTACHMENT_TYPES', True)
def get_followup_webhook_urls(): def get_followup_webhook_urls():
urls = os.environ.get('HELPDESK_FOLLOWUP_WEBHOOK_URLS', None) urls = os.environ.get('HELPDESK_FOLLOWUP_WEBHOOK_URLS', None)
if urls: if urls:

View File

@ -200,12 +200,11 @@ class GetEmailCommonTests(TestCase):
# The extractor prepends a part identifier so compare the ending # The extractor prepends a part identifier so compare the ending
self.assertTrue(sent_file.name.endswith(filename), f"Filename extracted does not match: {sent_file.name}") self.assertTrue(sent_file.name.endswith(filename), f"Filename extracted does not match: {sent_file.name}")
@override_settings(VALID_EXTENSIONS=['.png'])
def test_wrong_extension_attachment(self): def test_wrong_extension_attachment(self):
""" """
Tests if an attachment with a wrong extension doesn't stop the email process Tests if an attachment with a wrong extension doesn't stop the email process
""" """
message, _, _ = utils.generate_multipart_email(type_list=['plain', 'image']) message, _, _ = utils.generate_multipart_email(type_list=['plain', 'executable'])
self.assertEqual(len(mail.outbox), 0) # @UndefinedVariable self.assertEqual(len(mail.outbox), 0) # @UndefinedVariable
@ -213,7 +212,7 @@ class GetEmailCommonTests(TestCase):
extract_email_metadata(message.as_string(), self.queue_public, self.logger) extract_email_metadata(message.as_string(), self.queue_public, self.logger)
self.assertIn( self.assertIn(
"ERROR:helpdesk:['Unsupported file extension: .jpg']", "ERROR:helpdesk:['Unsupported file extension: .exe']",
cm.output cm.output
) )
@ -237,12 +236,11 @@ class GetEmailCommonTests(TestCase):
followup = ticket.followup_set.get() followup = ticket.followup_set.get()
self.assertEqual(2, followup.followupattachment_set.count()) self.assertEqual(2, followup.followupattachment_set.count())
@override_settings(VALID_EXTENSIONS=['.txt'])
def test_multiple_attachments_with_wrong_extension(self): def test_multiple_attachments_with_wrong_extension(self):
""" """
Tests that a wrong extension won't stop from saving other valid attachment Tests that a wrong extension won't stop from saving other valid attachment
""" """
message, _, _ = utils.generate_multipart_email(type_list=['plain', 'image', 'file', 'image']) message, _, _ = utils.generate_multipart_email(type_list=['plain', 'executable', 'file', 'executable'])
self.assertEqual(len(mail.outbox), 0) # @UndefinedVariable self.assertEqual(len(mail.outbox), 0) # @UndefinedVariable
@ -250,7 +248,7 @@ class GetEmailCommonTests(TestCase):
extract_email_metadata(message.as_string(), self.queue_public, self.logger) extract_email_metadata(message.as_string(), self.queue_public, self.logger)
self.assertIn( self.assertIn(
"ERROR:helpdesk:['Unsupported file extension: .jpg']", "ERROR:helpdesk:['Unsupported file extension: .exe']",
cm.output cm.output
) )

View File

@ -156,6 +156,21 @@ def generate_file_mime_part(locale: str="en_US",filename: str = None, content: s
part.add_header('Content-Disposition', "attachment; filename=%s" % filename) part.add_header('Content-Disposition', "attachment; filename=%s" % filename)
return part return part
def generate_executable_mime_part(locale: str="en_US",filename: str = None, content: str = None) -> Message:
"""
:param locale: change this to generate locale specific file name and attachment content
:param filename: pass a file name if you want to specify a specific name otherwise a random name will be generated
:param content: pass a string value if you want have specific content otherwise a random string will be generated
"""
part = MIMEBase('application', 'vnd.microsoft.portable-executable')
part.set_payload(get_fake("text", locale=locale, min_length=1024) if content is None else content)
encoders.encode_base64(part)
if not filename:
filename = get_fake("word", locale=locale, min_length=8) + ".exe"
part.add_header('Content-Disposition', "attachment; filename=%s" % filename)
return part
def generate_image_mime_part(locale: str="en_US",imagename: str = None, disposition_primary_type: str = "attachment") -> Message: def generate_image_mime_part(locale: str="en_US",imagename: str = None, disposition_primary_type: str = "attachment") -> Message:
""" """
@ -221,6 +236,8 @@ def generate_mime_part(locale: str="en_US",
msg = MIMEText(body, part_type) msg = MIMEText(body, part_type)
elif "file" == part_type: elif "file" == part_type:
msg = generate_file_mime_part(locale=locale) msg = generate_file_mime_part(locale=locale)
elif "executable" == part_type:
msg = generate_executable_mime_part(locale=locale)
elif "image" == part_type: elif "image" == part_type:
msg = generate_image_mime_part(locale=locale) msg = generate_image_mime_part(locale=locale)
else: else:
@ -236,7 +253,7 @@ def generate_multipart_email(locale: str="en_US",
Generates an email including headers with the defined multiparts Generates an email including headers with the defined multiparts
:param locale: :param locale:
:param type_list: options are plain, html, image (attachment), file (attachment) :param type_list: options are plain, html, image (attachment), file (attachment), and executable (.exe attachment)
:param sub_type: multipart sub type that defaults to "mixed" if not specified :param sub_type: multipart sub type that defaults to "mixed" if not specified
:param use_short_email: produces a "To" or "From" that is only the email address if True :param use_short_email: produces a "To" or "From" that is only the email address if True
""" """

View File

@ -2,9 +2,8 @@
# #
# validators for file uploads, etc. # validators for file uploads, etc.
from django.conf import settings
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from helpdesk import settings as helpdesk_settings
# TODO: can we use the builtin Django validator instead? # TODO: can we use the builtin Django validator instead?
@ -19,16 +18,10 @@ def validate_file_extension(value):
# TODO: we might improve this with more thorough checks of file types # TODO: we might improve this with more thorough checks of file types
# rather than just the extensions. # rather than just the extensions.
# check if VALID_EXTENSIONS is defined in settings.py if not helpdesk_settings.HELPDESK_VALIDATE_ATTACHMENT_TYPES:
# if not use defaults return
if hasattr(settings, 'VALID_EXTENSIONS'): if ext.lower() not in helpdesk_settings.HELPDESK_VALID_EXTENSIONS:
valid_extensions = settings.VALID_EXTENSIONS
else:
valid_extensions = ['.txt', '.asc', '.htm', '.html',
'.pdf', '.doc', '.docx', '.odt', '.jpg', '.png', '.eml']
if ext.lower() not in valid_extensions:
# TODO: one more check in case it is a file with no extension; we # TODO: one more check in case it is a file with no extension; we
# should always allow that? # should always allow that?
if not (ext.lower() == '' or ext.lower() == '.'): if not (ext.lower() == '' or ext.lower() == '.'):