diff --git a/requirements/main.in b/requirements/main.in index 521bbf70fb93..6a388042e687 100644 --- a/requirements/main.in +++ b/requirements/main.in @@ -13,6 +13,7 @@ click cryptography>=43.0.1 datadog>=0.19.0 disposable-email-domains +dnspython email-validator first forcediphttpsadapter diff --git a/requirements/main.txt b/requirements/main.txt index cfd7e5d0ec7f..627f3f8bb84f 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -520,7 +520,9 @@ disposable-email-domains==0.0.107 \ dnspython==2.7.0 \ --hash=sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86 \ --hash=sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1 - # via email-validator + # via + # -r requirements/main.in + # email-validator docutils==0.21.2 \ --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \ --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 diff --git a/warehouse/accounts/forms.py b/warehouse/accounts/forms.py index efdcb6f452df..7e9e4489af2c 100644 --- a/warehouse/accounts/forms.py +++ b/warehouse/accounts/forms.py @@ -12,10 +12,12 @@ from __future__ import annotations +import contextlib import json import re import disposable_email_domains +import dns.resolver import email_validator import humanize import markupsafe @@ -301,6 +303,20 @@ def validate_email(self, field): for _prio, mx_host in resp.mx } + # Resolve the returned MX domain's IP address to a PTR record, to a domain + all_mx_domains = set() + for mx_domain in mx_domains: + with contextlib.suppress(dns.resolver.NoAnswer, dns.resolver.NXDOMAIN): + mx_ip = dns.resolver.resolve(mx_domain, "A") + mx_ptr = dns.resolver.resolve_address(mx_ip[0].address) + mx_ptr_domain = extractor( + mx_ptr[0].target.to_text().lower() + ).registered_domain + all_mx_domains.add(mx_ptr_domain) + + # combine both sets + all_mx_domains.update(mx_domains) + if ( domain in disposable_email_domains.blocklist or self.request.db.query( @@ -309,7 +325,7 @@ def validate_email(self, field): & (ProhibitedEmailDomain.is_mx_record == False) # noqa: E712 ) | exists().where( - (ProhibitedEmailDomain.domain.in_(mx_domains)) + (ProhibitedEmailDomain.domain.in_(all_mx_domains)) & (ProhibitedEmailDomain.is_mx_record == True) # noqa: E712 ) ).scalar() diff --git a/warehouse/locale/messages.pot b/warehouse/locale/messages.pot index 76f6785fa667..55b7b4de5aaa 100644 --- a/warehouse/locale/messages.pot +++ b/warehouse/locale/messages.pot @@ -14,108 +14,108 @@ msgstr "" msgid "Locale updated" msgstr "" -#: warehouse/accounts/forms.py:52 warehouse/accounts/forms.py:290 +#: warehouse/accounts/forms.py:54 warehouse/accounts/forms.py:292 msgid "The email address isn't valid. Try again." msgstr "" -#: warehouse/accounts/forms.py:53 +#: warehouse/accounts/forms.py:55 msgid "The password is invalid. Try again." msgstr "" -#: warehouse/accounts/forms.py:54 +#: warehouse/accounts/forms.py:56 msgid "" "The username is invalid. Usernames must be composed of letters, numbers, " "dots, hyphens and underscores. And must also start and finish with a " "letter or number. Choose a different username." msgstr "" -#: warehouse/accounts/forms.py:72 +#: warehouse/accounts/forms.py:74 msgid "Null bytes are not allowed." msgstr "" -#: warehouse/accounts/forms.py:86 +#: warehouse/accounts/forms.py:88 msgid "No user found with that username" msgstr "" -#: warehouse/accounts/forms.py:106 +#: warehouse/accounts/forms.py:108 msgid "TOTP code must be ${totp_length} digits." msgstr "" -#: warehouse/accounts/forms.py:126 +#: warehouse/accounts/forms.py:128 msgid "Recovery Codes must be ${recovery_code_length} characters." msgstr "" -#: warehouse/accounts/forms.py:141 +#: warehouse/accounts/forms.py:143 msgid "Choose a username with 50 characters or less." msgstr "" -#: warehouse/accounts/forms.py:158 +#: warehouse/accounts/forms.py:160 msgid "" "This username is already being used by another account. Choose a " "different username." msgstr "" -#: warehouse/accounts/forms.py:172 warehouse/accounts/forms.py:221 -#: warehouse/accounts/forms.py:234 +#: warehouse/accounts/forms.py:174 warehouse/accounts/forms.py:223 +#: warehouse/accounts/forms.py:236 msgid "Password too long." msgstr "" -#: warehouse/accounts/forms.py:208 +#: warehouse/accounts/forms.py:210 msgid "" "There have been too many unsuccessful login attempts. You have been " "locked out for ${time}. Please try again later." msgstr "" -#: warehouse/accounts/forms.py:237 +#: warehouse/accounts/forms.py:239 msgid "Your passwords don't match. Try again." msgstr "" -#: warehouse/accounts/forms.py:271 +#: warehouse/accounts/forms.py:273 msgid "The email address is too long. Try again." msgstr "" -#: warehouse/accounts/forms.py:322 +#: warehouse/accounts/forms.py:338 msgid "You can't use an email address from this domain. Use a different email." msgstr "" -#: warehouse/accounts/forms.py:337 +#: warehouse/accounts/forms.py:353 msgid "" "This email address is already being used by this account. Use a different" " email." msgstr "" -#: warehouse/accounts/forms.py:348 +#: warehouse/accounts/forms.py:364 msgid "" "This email address is already being used by another account. Use a " "different email." msgstr "" -#: warehouse/accounts/forms.py:388 warehouse/manage/forms.py:139 +#: warehouse/accounts/forms.py:404 warehouse/manage/forms.py:139 #: warehouse/manage/forms.py:728 msgid "The name is too long. Choose a name with 100 characters or less." msgstr "" -#: warehouse/accounts/forms.py:395 +#: warehouse/accounts/forms.py:411 msgid "URLs are not allowed in the name field." msgstr "" -#: warehouse/accounts/forms.py:484 +#: warehouse/accounts/forms.py:500 msgid "Invalid TOTP code." msgstr "" -#: warehouse/accounts/forms.py:501 +#: warehouse/accounts/forms.py:517 msgid "Invalid WebAuthn assertion: Bad payload" msgstr "" -#: warehouse/accounts/forms.py:570 +#: warehouse/accounts/forms.py:586 msgid "Invalid recovery code." msgstr "" -#: warehouse/accounts/forms.py:579 +#: warehouse/accounts/forms.py:595 msgid "Recovery code has been previously used." msgstr "" -#: warehouse/accounts/forms.py:609 +#: warehouse/accounts/forms.py:625 msgid "The username isn't valid. Try again." msgstr ""