From f5deb24dbb9276f06854f64fcb87be4cb845e51b Mon Sep 17 00:00:00 2001 From: Nicola Soranzo Date: Tue, 25 Oct 2022 18:48:15 +0100 Subject: [PATCH 1/3] Use dnspython to check specifically for an "MX" DNS record when validating the domain name of the email address for a new user. Fix https://github.com/galaxyproject/galaxy/issues/14829 . --- lib/galaxy/security/validate_user_input.py | 12 +++++++----- packages/data/setup.cfg | 1 + pyproject.toml | 1 + test/unit/data/security/test_validate_user_input.py | 8 ++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/galaxy/security/validate_user_input.py b/lib/galaxy/security/validate_user_input.py index 3a5b0666a72..0f00b5207e8 100644 --- a/lib/galaxy/security/validate_user_input.py +++ b/lib/galaxy/security/validate_user_input.py @@ -6,9 +6,11 @@ user inputs - so these methods do not need to be escaped. """ import logging import re -import socket +import dns.resolver +from dns.exception import DNSException from sqlalchemy import func +from typing_extensions import LiteralString log = logging.getLogger(__name__) @@ -71,7 +73,7 @@ def validate_email(trans, email, user=None, check_dup=True, allow_empty=False, v message = validate_email_str(email) if not message and validate_domain: domain = extract_domain(email) - message = validate_domain_resolves(domain) + message = validate_email_domain_name(domain) if ( not message @@ -97,11 +99,11 @@ def validate_email(trans, email, user=None, check_dup=True, allow_empty=False, v return message -def validate_domain_resolves(domain): +def validate_email_domain_name(domain: str) -> LiteralString: message = "" try: - socket.gethostbyname(domain) - except socket.gaierror: + dns.resolver.resolve(domain, "MX") + except DNSException: message = "The email domain cannot be resolved." return message diff --git a/packages/data/setup.cfg b/packages/data/setup.cfg index b3dff768814..c1b722c96c9 100644 --- a/packages/data/setup.cfg +++ b/packages/data/setup.cfg @@ -38,6 +38,7 @@ install_requires = bdbag bx-python contextvars; python_version >= "3.6" and python_version < "3.7" + dnspython galaxy-sequence-utils h5py isa-rwval diff --git a/pyproject.toml b/pyproject.toml index 08c7d9102e0..1f305af56a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ cloudbridge = "*" circus = "*" cwltool = "==3.1.20211107152837" dictobj = "*" +dnspython = "*" docutils = "!=0.17, !=0.17.1" edam-ontology = "*" fastapi = ">=0.68.2, !=0.69.0, !=0.70.0, !=0.70.1" # https://github.com/tiangolo/fastapi/issues/4041 diff --git a/test/unit/data/security/test_validate_user_input.py b/test/unit/data/security/test_validate_user_input.py index 89862ed2b21..e22104e334e 100644 --- a/test/unit/data/security/test_validate_user_input.py +++ b/test/unit/data/security/test_validate_user_input.py @@ -1,6 +1,6 @@ from galaxy.security.validate_user_input import ( extract_domain, - validate_domain_resolves, + validate_email_domain_name, validate_email_str, validate_publicname_str, ) @@ -19,9 +19,9 @@ def test_extract_base_domain(): assert extract_domain("jack@foo.bar.com", base_only=True) == "bar.com" -def test_validate_domain(): - assert validate_domain_resolves("example.org") == "" - assert validate_domain_resolves("this is an invalid domain!") != "" +def test_validate_email_domain_name(): + assert validate_email_domain_name("example.org") == "" + assert validate_email_domain_name("this is an invalid domain!") != "" def test_validate_username(): From fb1b802b48c71a8ddd3e22fc05b24a8b95f623e0 Mon Sep 17 00:00:00 2001 From: Nicola Soranzo Date: Wed, 26 Oct 2022 02:25:10 +0100 Subject: [PATCH 2/3] Try to fall back to the A record if MX fails --- lib/galaxy/security/validate_user_input.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/security/validate_user_input.py b/lib/galaxy/security/validate_user_input.py index 0f00b5207e8..f3bd0a680a8 100644 --- a/lib/galaxy/security/validate_user_input.py +++ b/lib/galaxy/security/validate_user_input.py @@ -104,7 +104,12 @@ def validate_email_domain_name(domain: str) -> LiteralString: try: dns.resolver.resolve(domain, "MX") except DNSException: - message = "The email domain cannot be resolved." + try: + # Per RFC 5321, try to fall back to the A record (implicit MX) for + # the domain, see https://www.rfc-editor.org/rfc/rfc5321#section-5.1 + dns.resolver.resolve(domain, "A") + except DNSException: + message = "The email domain cannot be resolved." return message From dc387613ddc65cae191a4322661bec6a20f4008b Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:33:52 +0100 Subject: [PATCH 3/3] Add missing `dnspython` dependency in 22.05 --- lib/galaxy/dependencies/dev-requirements.txt | 1 + lib/galaxy/dependencies/pinned-requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/galaxy/dependencies/dev-requirements.txt b/lib/galaxy/dependencies/dev-requirements.txt index d34806c71f3..4c088c7063c 100644 --- a/lib/galaxy/dependencies/dev-requirements.txt +++ b/lib/galaxy/dependencies/dev-requirements.txt @@ -57,6 +57,7 @@ defusedxml==0.7.1; python_version >= "3.6" and python_full_version < "3.0.0" and deprecated==1.2.13; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" deprecation==2.1.0 dictobj==0.4 +dnspython==2.2.1 ; python_version >= "3.7" and python_version < "3.11" docopt==0.6.2 docutils==0.16; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") ecdsa==0.17.0; python_version >= "2.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" diff --git a/lib/galaxy/dependencies/pinned-requirements.txt b/lib/galaxy/dependencies/pinned-requirements.txt index e8c2435c4f4..915b2a24016 100644 --- a/lib/galaxy/dependencies/pinned-requirements.txt +++ b/lib/galaxy/dependencies/pinned-requirements.txt @@ -47,6 +47,7 @@ decorator==5.1.1; python_version >= "3.6" and python_version < "4" defusedxml==0.7.1; python_version >= "3.0" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.0" deprecation==2.1.0 dictobj==0.4 +dnspython==2.2.1 ; python_version >= "3.7" and python_version < "3.11" docopt==0.6.2 docutils==0.16; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") ecdsa==0.17.0; python_version >= "2.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0"