CTFd icon indicating copy to clipboard operation
CTFd copied to clipboard

Error not catched when email not sent

Open Cryptanalyse opened this issue 2 years ago • 1 comments

I have set up an external SMTP email provider for sending confirmation emails for users upon registration (verify_emails set to True) and for some reasons, the provider detected the emails as spam. The emails were not sent, but in the current code, CTFd cannot detect anything and assumes the email has been sent. The main reason is that the return value for the function responsible for sending the email is not checked.

The return value happens in my case in the sendmail email function here: https://github.com/CTFd/CTFd/blob/master/CTFd/utils/email/providers/smtp.py#L65

class SMTPEmailProvider(EmailProvider):
    @staticmethod
    def sendmail(addr, text, subject):
        try:
            (...)
            return True, "Email sent"
        except smtplib.SMTPException as e:
            return False, str(e)
        except timeout:
            return False, "SMTP server connection timed out"
        except Exception as e:
            return False, str(e) # <=== return here

and should be checked in auth.py here in the /register endpoint: https://github.com/CTFd/CTFd/blob/master/CTFd/auth.py#L338

@auth.route("/register", methods=["POST", "GET"])
def register():
    (...)
    if request.method == "POST":
        (...)
        if len(errors) > 0:
            (...)
        else:
            with app.app_context():
                (...)
                if config.can_send_mail() and get_config("verify_emails"):  
                    (...)
                    email.verify_email_address(user.email) # <== Return value not checked here

Environment:

  • CTFd Version/Commit: https://github.com/CTFd/CTFd/releases/tag/3.5.1
  • Operating System: any
  • Web Browser and Version: any

What happened? Nothing, email not sent with no error.

What did you expect to happen? An error stating that the email has not been sent, and a message saying why.

How to reproduce your issue Hard to reproduce as it depends from an error of an external provider.

Any associated stack traces or error logs To produce the stack trace, I removed the try/except located here https://github.com/CTFd/CTFd/blob/master/CTFd/utils/email/providers/smtp.py#L38 to make the error apparent.

ERROR [CTFd] Exception on /register [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.9/site-packages/flask_restx/api.py", line 672, in error_router
    return original_handler(e)
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.9/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/opt/CTFd/CTFd/utils/decorators/visibility.py", line 119, in _check_registration_visibility
    return f(*args, **kwargs)
  File "/opt/CTFd/CTFd/utils/decorators/__init__.py", line 187, in ratelimit_function
    return f(*args, **kwargs)
  File "/opt/CTFd/CTFd/auth.py", line 358, in register
    ret = email.verify_email_address(user.email)
  File "/opt/CTFd/CTFd/utils/email/__init__.py", line 99, in verify_email_address
    return sendmail(addr=addr, text=text, subject=subject)
  File "/opt/CTFd/CTFd/utils/email/__init__.py", line 51, in sendmail
    return EmailProvider.sendmail(addr, text, subject)
  File "/opt/CTFd/CTFd/utils/email/providers/smtp.py", line 54, in sendmail
    smtp.send_message(msg)
  File "/usr/local/lib/python3.9/smtplib.py", line 986, in send_message
    return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
  File "/usr/local/lib/python3.9/smtplib.py", line 908, in sendmail
    raise SMTPDataError(code, resp)
smtplib.SMTPDataError: (550, b'5.7.1  Reject for policy reason RULE3_2: Spam detected')

Cryptanalyse avatar Apr 04 '23 14:04 Cryptanalyse

I think the code here is designed in a way so that we can capture the error and decide what to do with it. Some places in CTFd will surface the error and some places won't. I would accept a PR that made the error more noticeable for users if you can create one.

ColdHeat avatar Apr 04 '23 15:04 ColdHeat