workflows: reject mails over 50MB (#50002) #563

Merged
fpeters merged 1 commits from wip/50002-reject-big-mails into main 2023-08-11 00:31:34 +02:00
4 changed files with 62 additions and 19 deletions

View File

@ -287,6 +287,38 @@ def test_email_django_escaping(pub, emails):
assert emails.get('1 < 3')
def test_email_too_big(pub, emails):
pub.loggederror_class.wipe()
formdef = FormDef()
formdef.name = 'baz'
formdef.fields = [
FileField(id='3', label='File', varname='file'),
]
formdef.store()
upload = PicklableUpload('test.txt', 'text/plain')
upload.receive([b'x' * 50_000_000])
formdata = formdef.data_class()()
formdata.data = {'3': upload}
formdata.just_created()
formdata.store()
pub.substitutions.feed(formdata)
sendmail = SendmailWorkflowStatusItem()
sendmail.subject = 'foobar'
sendmail.body = 'test'
sendmail.to = ['to@example.net']
sendmail.attachments = ['form_var_file_raw']
sendmail.perform(formdata)
get_response().process_after_jobs()
assert emails.count() == 0
assert pub.loggederror_class.count() == 1
logged_error = pub.loggederror_class.select()[0]
assert logged_error.summary == 'Email too big to be sent'
os.unlink(formdata.data['3'].get_fs_filename()) # clean big file
def test_email_attachments(pub, emails):
formdef = FormDef()
formdef.name = 'baz'

View File

@ -344,6 +344,9 @@ def email(
if html_body:
email_msg.content_subtype = 'html'
if len(str(email_msg.message())) > 50_000_000:
raise errors.TooBigEmailError()
email_to_send = EmailToSend(email_msg, smtp_timeout)
if not fire_and_forget:
email_to_send()

View File

@ -76,6 +76,10 @@ class EmailError(Exception):
pass
class TooBigEmailError(EmailError):
Outdated
Review

Peut-être qu'on pourrait ici enregistrer la longueur du mail refusé (et la longueur max acceptée) afin que ça puisse être affiché dans l'erreur enregistrée, pour mieux comprendre la cause.

Peut-être qu'on pourrait ici enregistrer la longueur du mail refusé (et la longueur max acceptée) afin que ça puisse être affiché dans l'erreur enregistrée, pour mieux comprendre la cause.

J'avais une idée comme ça au début mais le modlèle LoggedError ne permet pas d'ajouter des informations supplémentaires et je ne voulais pas me lancer dans des développements là-dedans. (et garder le même message de base pour que ça soit mergé en une seule erreur). Je verrai pour faire évoluer ça plus tard, selon les retours.

J'avais une idée comme ça au début mais le modlèle LoggedError ne permet pas d'ajouter des informations supplémentaires et je ne voulais pas me lancer dans des développements là-dedans. (et garder le même message de base pour que ça soit mergé en une seule erreur). Je verrai pour faire évoluer ça plus tard, selon les retours.
pass
class InternalServerError(PublishError):
status_code = 500

View File

@ -22,6 +22,7 @@ from quixote.html import htmltext
from wcs.mail_templates import MailTemplate
from wcs.qommon import _, emails
from wcs.qommon.errors import TooBigEmailError
from wcs.qommon.form import (
ComputedExpressionWidget,
SingleSelectWidget,
@ -395,25 +396,28 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
)
formdata.store()
if len(addresses) > 1:
emails.email(
mail_subject,
mail_body,
email_rcpt=None,
bcc=addresses,
email_from=email_from,
attachments=attachments,
fire_and_forget=True,
)
else:
emails.email(
mail_subject,
mail_body,
email_rcpt=addresses,
email_from=email_from,
attachments=attachments,
fire_and_forget=True,
)
try:
if len(addresses) > 1:
emails.email(
mail_subject,
mail_body,
email_rcpt=None,
bcc=addresses,
email_from=email_from,
attachments=attachments,
fire_and_forget=True,
)
else:
emails.email(
mail_subject,
mail_body,
email_rcpt=addresses,
email_from=email_from,
attachments=attachments,
fire_and_forget=True,
)
except TooBigEmailError:
get_publisher().record_error(_('Email too big to be sent'), formdata=formdata, status_item=self)
def i18n_scan(self, base_location):
location = '%sitems/%s/' % (base_location, self.id)