Repository: zulip/zulip

Issue:
Validate form of emails to /register/
Most paths in `zerver/views/registration.py` validate the email by calling `validators.validate_email(email)` where necessary.  However, the registration from `accounts_home` does not, which can [lead to invalid SQL queries when trying to check if the email is already in use](https://sentry.io/share/issue/6b34ad6039df4d029c85230d6b715df7/), and thus a 500.

We should validate the email using `validate_email` before checking if it is in use.  This is probably just a case of:
```
diff --git zerver/views/registration.py zerver/views/registration.py
index f55d73026e..6d9a072b17 100644
--- zerver/views/registration.py
+++ zerver/views/registration.py
@@ -566,6 +566,10 @@ def accounts_home(request: HttpRequest, multiuse_object_key: str="",
             return HttpResponseRedirect(reverse('signup_send_confirm', kwargs={'email': email}))

         email = request.POST['email']
+        try:
+            validators.validate_email(email)
+        except ValidationError:
+            return render(request, "zerver/invalid_email.html", context={"invalid_email": True})
         try:
             validate_email_not_already_in_realm(realm, email)
         except ValidationError:
```

But that is totally untested, and I'd love more coverage on this endpoint.


Discussion:
@zulipbot claim
Hello @tushar912, it looks like you've currently claimed 1 issue in this repository. We encourage new contributors to focus their efforts on at most 1 issue at a time, so please complete your work on your other claimed issues before trying to claim this issue again.

We look forward to your valuable contributions!
I am working on this
@zulipbot claim
Welcome to Zulip, @prakhar-ai! We just sent you an invite to collaborate on this repository at https://github.com/zulip/zulip/invitations. Please accept this invite in order to claim this issue and begin a fun, rewarding experience contributing to Zulip!

Here's some tips to get you off to a good start:
* Join me on the [Zulip developers' server](https://chat.zulip.org), to get help, chat about this issue, and meet the other developers.
* [Unwatch this repository](https://help.github.com/articles/unwatching-repositories/), so that you don't get 100 emails a day.

As you work on this issue, you'll also want to refer to the [Zulip code contribution guide](https://zulip.readthedocs.io/en/latest/contributing/index.html), as well as the rest of the developer documentation on that site.

See you on the other side (that is, the pull request side)!
@prakhar-ai I have almost done this.
@tushar912 I don't think zulipbot allows you to work on two issues simultaneously, which is why you weren't assigned. I'm sorry if this caused any misunderstanding.
@prakhar-ai No problem ....If you need help do comment here.
I'm unable to run tests due to [OSError: [Errno 26] Text file busy](https://zulip.readthedocs.io/en/latest/development/setup-vagrant.html#oserror-errno-26-text-file-busy). I have unassigned myself. @tushar912 feel free to work on this issue.

Resolving patch:
diff --git a/zerver/forms.py b/zerver/forms.py
--- a/zerver/forms.py
+++ b/zerver/forms.py
@@ -19,7 +19,7 @@
 from two_factor.utils import totp_digits
 
 from zerver.lib.actions import do_change_password, email_not_system_bot
-from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm
+from zerver.lib.email_validation import email_allowed_for_realm
 from zerver.lib.name_restrictions import is_disposable_domain, is_reserved_subdomain
 from zerver.lib.rate_limiter import RateLimited, RateLimitedObject
 from zerver.lib.request import JsonableError
@@ -178,8 +178,6 @@ def clean_email(self) -> str:
         except EmailContainsPlusError:
             raise ValidationError(_("Email addresses containing + are not allowed in this organization."))
 
-        validate_email_not_already_in_realm(realm, email)
-
         if realm.is_zephyr_mirror_realm:
             email_is_not_mit_mailing_list(email)
 
diff --git a/zerver/views/registration.py b/zerver/views/registration.py
--- a/zerver/views/registration.py
+++ b/zerver/views/registration.py
@@ -163,9 +163,7 @@ def accounts_register(request: HttpRequest) -> HttpResponse:
         try:
             validate_email_not_already_in_realm(realm, email)
         except ValidationError:
-            view_url = reverse('login')
-            redirect_url = add_query_to_redirect_url(view_url, urlencode({"email": email}))
-            return HttpResponseRedirect(redirect_url)
+            return redirect_to_email_login_url(email)
 
     name_validated = False
     full_name = None
@@ -486,7 +484,7 @@ def send_confirm_registration_email(email: str, activation_url: str, language: s
 
 def redirect_to_email_login_url(email: str) -> HttpResponseRedirect:
     login_url = reverse('login')
-    redirect_url = add_query_to_redirect_url(login_url, urlencode({"already_registered": email}))
+    redirect_url = add_query_to_redirect_url(login_url, urlencode({"email": email, "already_registered": 1}))
     return HttpResponseRedirect(redirect_url)
 
 def create_realm(request: HttpRequest, creation_key: Optional[str]=None) -> HttpResponse:
@@ -555,6 +553,12 @@ def accounts_home(request: HttpRequest, multiuse_object_key: str="",
         form = HomepageForm(request.POST, realm=realm, from_multiuse_invite=from_multiuse_invite)
         if form.is_valid():
             email = form.cleaned_data['email']
+
+            try:
+                validate_email_not_already_in_realm(realm, email)
+            except ValidationError:
+                return redirect_to_email_login_url(email)
+
             activation_url = prepare_activation_url(email, request, streams=streams_to_subscribe,
                                                     invited_as=invited_as)
             try:
@@ -565,11 +569,6 @@ def accounts_home(request: HttpRequest, multiuse_object_key: str="",
 
             return HttpResponseRedirect(reverse('signup_send_confirm', kwargs={'email': email}))
 
-        email = request.POST['email']
-        try:
-            validate_email_not_already_in_realm(realm, email)
-        except ValidationError:
-            return redirect_to_email_login_url(email)
     else:
         form = HomepageForm(realm=realm)
     context = login_context(request)


Test patch:
diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py
--- a/zerver/tests/test_signup.py
+++ b/zerver/tests/test_signup.py
@@ -690,6 +690,18 @@ def test_register_deactivated(self) -> None:
         with self.assertRaises(UserProfile.DoesNotExist):
             self.nonreg_user('test')
 
+    def test_register_with_invalid_email(self) -> None:
+        """
+        If you try to register with invalid email, you get an invalid email
+        page
+        """
+        invalid_email = "foo\x00bar"
+        result = self.client_post('/accounts/home/', {'email': invalid_email},
+                                  subdomain="zulip")
+
+        self.assertEqual(result.status_code, 200)
+        self.assertContains(result, "Enter a valid email address")
+
     def test_register_deactivated_partway_through(self) -> None:
         """
         If you try to register for a deactivated realm, you get a clear error
@@ -1672,7 +1684,7 @@ def test_validate_email_not_already_in_realm(self) -> None:
         response = self.client_post(url, {"key": registration_key, "from_confirmation": 1, "full_name": "alice"})
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.url, reverse('login') + '?' +
-                         urlencode({"email": email}))
+                         urlencode({"email": email, "already_registered": 1}))
 
 class InvitationsTestCase(InviteUserBase):
     def test_do_get_user_invites(self) -> None: