From 0b22d5e63a842ad66e10509321104088a3d920c2 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Mon, 30 Oct 2023 17:57:45 -0400 Subject: [PATCH 01/13] added isValidDomain to host validation; added INVALID_HOST to error messages --- src/registrar/forms/domain.py | 19 ++++++++++++++++--- src/registrar/models/domain.py | 27 ++++++++++++++++++++++++++- src/registrar/utility/errors.py | 3 +++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index 3aca7af6d..992d283a4 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -51,9 +51,8 @@ class DomainNameserverForm(forms.Form): ip_list = self.extract_ip_list(ip) - if ip and not server and ip_list: - self.add_error("server", NameserverError(code=nsErrorCodes.MISSING_HOST)) - elif server: + # validate if the form has a server or an ip + if ip and ip_list or server: self.validate_nameserver_ip_combo(domain, server, ip_list) return cleaned_data @@ -86,6 +85,20 @@ class DomainNameserverForm(forms.Form): code=nsErrorCodes.MISSING_IP, nameserver=domain, ip=ip_list ), ) + elif e.code == nsErrorCodes.MISSING_HOST: + self.add_error( + "server", + NameserverError( + code=nsErrorCodes.MISSING_HOST, nameserver=domain, ip=ip_list + ), + ) + elif e.code == nsErrorCodes.INVALID_HOST: + self.add_error( + "server", + NameserverError( + code=nsErrorCodes.INVALID_HOST, nameserver=server, ip=ip_list + ), + ) else: self.add_error("ip", str(e)) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 07e49dfdd..5f201865c 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -304,6 +304,25 @@ class Domain(TimeStampedModel, DomainHelper): regex = re.compile(full_pattern) return bool(regex.match(nameserver)) + @classmethod + def isValidDomain(cls, nameserver: str): + """Checks for validity of nameserver string based on these rules: + - first character is alpha + - last character is not - or . + - all characters alpha, 0-9, -, or . + - 2 character min, 24 character max + """ + # pattern to test for valid domain + # pattern = r'^[a-zA-Z][a-zA-Z0-9-.]{0,22}[a-zA-Z0-9]$' + pattern = r"^[a-zA-Z][a-zA-Z0-9-.]*(\.[a-zA-Z0-9-]+){2}[a-zA-Z0-9]$" + + # attempt to match the pattern + match = re.match(pattern, nameserver) + + # return true if nameserver matches, and length less than 25; + # otherwise false + return bool(match) and len(nameserver) < 25 + @classmethod def checkHostIPCombo(cls, name: str, nameserver: str, ip: list[str]): """Checks the parameters past for a valid combination @@ -311,6 +330,8 @@ class Domain(TimeStampedModel, DomainHelper): - nameserver is a subdomain but is missing ip - nameserver is not a subdomain but has ip - nameserver is a subdomain but an ip passed is invalid + - nameserver is not a valid domain + - ip is provided but is missing domain Args: hostname (str)- nameserver or subdomain @@ -319,7 +340,11 @@ class Domain(TimeStampedModel, DomainHelper): NameserverError (if exception hit) Returns: None""" - if cls.isSubdomain(name, nameserver) and (ip is None or ip == []): + if ip and not nameserver: + raise NameserverError(code=nsErrorCodes.MISSING_HOST) + elif nameserver and not cls.isValidDomain(nameserver): + raise NameserverError(code=nsErrorCodes.INVALID_HOST, nameserver=nameserver) + elif cls.isSubdomain(name, nameserver) and (ip is None or ip == []): raise NameserverError(code=nsErrorCodes.MISSING_IP, nameserver=nameserver) elif not cls.isSubdomain(name, nameserver) and (ip is not None and ip != []): diff --git a/src/registrar/utility/errors.py b/src/registrar/utility/errors.py index c1d3c5849..9b9cfea0e 100644 --- a/src/registrar/utility/errors.py +++ b/src/registrar/utility/errors.py @@ -66,6 +66,7 @@ class NameserverErrorCodes(IntEnum): - 4 TOO_MANY_HOSTS more than the max allowed host values - 5 UNABLE_TO_UPDATE_DOMAIN unable to update the domain - 6 MISSING_HOST host is missing for a nameserver + - 7 INVALID_HOST host is invalid for a nameserver """ MISSING_IP = 1 @@ -74,6 +75,7 @@ class NameserverErrorCodes(IntEnum): TOO_MANY_HOSTS = 4 UNABLE_TO_UPDATE_DOMAIN = 5 MISSING_HOST = 6 + INVALID_HOST = 7 class NameserverError(Exception): @@ -102,6 +104,7 @@ class NameserverError(Exception): NameserverErrorCodes.MISSING_HOST: ( "Name server must be provided to enter IP address." ), + NameserverErrorCodes.INVALID_HOST: ("Name server, {}, is not valid."), } def __init__(self, *args, code=None, nameserver=None, ip=None, **kwargs): From 194a115777f1d84da9166b0e0a71283fed59781c Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Mon, 30 Oct 2023 18:12:44 -0400 Subject: [PATCH 02/13] excluding - and . from string length --- src/registrar/models/domain.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 5f201865c..18d597204 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -310,7 +310,7 @@ class Domain(TimeStampedModel, DomainHelper): - first character is alpha - last character is not - or . - all characters alpha, 0-9, -, or . - - 2 character min, 24 character max + - 2 character min, 24 character max (not including dashes/periods) """ # pattern to test for valid domain # pattern = r'^[a-zA-Z][a-zA-Z0-9-.]{0,22}[a-zA-Z0-9]$' @@ -319,9 +319,16 @@ class Domain(TimeStampedModel, DomainHelper): # attempt to match the pattern match = re.match(pattern, nameserver) + # length of nameserver, not including - or . + characters_to_exclude = "-." + filtered_nameserver = "".join( + char for char in nameserver if char not in characters_to_exclude + ) + nameserverLength = len(filtered_nameserver) + # return true if nameserver matches, and length less than 25; # otherwise false - return bool(match) and len(nameserver) < 25 + return bool(match) and nameserverLength < 25 @classmethod def checkHostIPCombo(cls, name: str, nameserver: str, ip: list[str]): From 918c5ef0b276cf873ce6a08ba926024511420e62 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Mon, 30 Oct 2023 18:25:58 -0400 Subject: [PATCH 03/13] added test case --- src/registrar/tests/test_views.py | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 95af4c542..a5ec33537 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1583,6 +1583,39 @@ class TestDomainNameservers(TestDomainOverview): status_code=200, ) + def test_domain_nameservers_form_submit_invalid_host(self): + """Nameserver form catches invalid host on submission. + + Uses self.app WebTest because we need to interact with forms. + """ + nameserver = "invalid-nameserver.gov" + valid_ip = "123.2.45.111" + # initial nameservers page has one server with two ips + nameservers_page = self.app.get( + reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}) + ) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + # attempt to submit the form without two hosts, both subdomains, + # only one has ips + nameservers_page.form["form-1-server"] = nameserver + nameservers_page.form["form-1-ip"] = valid_ip + with less_console_noise(): # swallow log warning message + result = nameservers_page.form.submit() + # form submission was a post with an error, response should be a 200 + # error text appears twice, once at the top of the page, once around + # the required field. nameserver has invalid host + self.assertContains( + result, + str( + NameserverError( + code=NameserverErrorCodes.INVALID_HOST, nameserver=nameserver + ) + ), + count=2, + status_code=200, + ) + def test_domain_nameservers_form_submits_successfully(self): """Nameserver form submits successfully with valid input. From 662bb1a1eff1f91f092c01d6ad21350c92c45b5e Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 31 Oct 2023 10:42:42 -0400 Subject: [PATCH 04/13] handling of OBJECT_EXISTS in nameserver create_host and update_host methods --- src/registrar/models/domain.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 18d597204..faec1389a 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -277,7 +277,12 @@ class Domain(TimeStampedModel, DomainHelper): return response.code except RegistryError as e: logger.error("Error _create_host, code was %s error was %s" % (e.code, e)) - raise e + # OBJECT_EXISTS is an expected error code that should not raise + # an exception, rather return the code to be handled separately + if response and response.code == ErrorCode.OBJECT_EXISTS: + return response.code + else: + raise e def _convert_list_to_dict(self, listToConvert: list[tuple[str, list]]): """converts a list of hosts into a dictionary @@ -1625,7 +1630,12 @@ class Domain(TimeStampedModel, DomainHelper): return response.code except RegistryError as e: logger.error("Error _update_host, code was %s error was %s" % (e.code, e)) - raise e + # OBJECT_EXISTS is an expected error code that should not raise + # an exception, rather return the code to be handled separately + if response and response.code == ErrorCode.OBJECT_EXISTS: + return response.code + else: + raise e def addAndRemoveHostsFromDomain( self, hostsToAdd: list[str], hostsToDelete: list[str] From 7dd054d1c1333936aaead9d10567eab02310195a Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 31 Oct 2023 16:50:10 -0400 Subject: [PATCH 05/13] updated error handling on create_host and update_host; updated regex for nameservers for new reqs --- src/registrar/models/domain.py | 37 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index faec1389a..ccb96a7d5 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -279,8 +279,8 @@ class Domain(TimeStampedModel, DomainHelper): logger.error("Error _create_host, code was %s error was %s" % (e.code, e)) # OBJECT_EXISTS is an expected error code that should not raise # an exception, rather return the code to be handled separately - if response and response.code == ErrorCode.OBJECT_EXISTS: - return response.code + if e.code == ErrorCode.OBJECT_EXISTS: + return e.code else: raise e @@ -312,28 +312,27 @@ class Domain(TimeStampedModel, DomainHelper): @classmethod def isValidDomain(cls, nameserver: str): """Checks for validity of nameserver string based on these rules: - - first character is alpha - - last character is not - or . - - all characters alpha, 0-9, -, or . - - 2 character min, 24 character max (not including dashes/periods) + - first character is alpha or digit + - first and last character in each label is alpha or digit + - all characters alpha (lowercase), digit, -, or . + - each label has a min length of 1 and a max length of 63 + - total host name has a max length of 253 """ # pattern to test for valid domain - # pattern = r'^[a-zA-Z][a-zA-Z0-9-.]{0,22}[a-zA-Z0-9]$' - pattern = r"^[a-zA-Z][a-zA-Z0-9-.]*(\.[a-zA-Z0-9-]+){2}[a-zA-Z0-9]$" + # label pattern for each section of the host name, separated by . + labelpattern = r"[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?" + # lookahead pattern ensures first character not - and total length < 254 + lookaheadpatterns = r"^((?!-))(?=.{1,253}\.?$)" + # pattern assembles lookaheadpatterns and ensures there are at least + # 3 labels in the host name + pattern = lookaheadpatterns + labelpattern + r"(\." + labelpattern + r"){2,}$" # attempt to match the pattern match = re.match(pattern, nameserver) - # length of nameserver, not including - or . - characters_to_exclude = "-." - filtered_nameserver = "".join( - char for char in nameserver if char not in characters_to_exclude - ) - nameserverLength = len(filtered_nameserver) - - # return true if nameserver matches, and length less than 25; + # return true if nameserver matches # otherwise false - return bool(match) and nameserverLength < 25 + return bool(match) @classmethod def checkHostIPCombo(cls, name: str, nameserver: str, ip: list[str]): @@ -1632,8 +1631,8 @@ class Domain(TimeStampedModel, DomainHelper): logger.error("Error _update_host, code was %s error was %s" % (e.code, e)) # OBJECT_EXISTS is an expected error code that should not raise # an exception, rather return the code to be handled separately - if response and response.code == ErrorCode.OBJECT_EXISTS: - return response.code + if e.code == ErrorCode.OBJECT_EXISTS: + return e.code else: raise e From aca8d9e7fb91912f63daa27b0bf213c245bd0fef Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 31 Oct 2023 17:30:41 -0400 Subject: [PATCH 06/13] updated hatch marks to quotation marks --- src/registrar/templates/domain_nameservers.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_nameservers.html b/src/registrar/templates/domain_nameservers.html index d00126698..28ce26e93 100644 --- a/src/registrar/templates/domain_nameservers.html +++ b/src/registrar/templates/domain_nameservers.html @@ -17,7 +17,7 @@
-

Add an IP address only when your name server's address includes your domain name (e.g., if your domain name is "example.gov" and your name server is "ns1.example.gov,” then an IP address is required.) To add multiple IP addresses, separate them with commas.

+

Add an IP address only when your name server's address includes your domain name (e.g., if your domain name is "example.gov" and your name server is "ns1.example.gov," then an IP address is required.) To add multiple IP addresses, separate them with commas.

This step is uncommon unless you self-host your DNS or use custom addresses for your nameserver.

From a631d0d080f06d6c7f907c67fb489e9a0501c28f Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 2 Nov 2023 09:57:42 -0400 Subject: [PATCH 07/13] removed redundant and obsolete code; fixed quotation marks; added some parentheses to emphasize order of operations --- src/registrar/forms/domain.py | 2 +- src/registrar/models/domain.py | 28 ------------------- src/registrar/models/utility/domain_helper.py | 4 --- .../templates/domain_nameservers.html | 2 +- 4 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index 992d283a4..6e0c16b58 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -52,7 +52,7 @@ class DomainNameserverForm(forms.Form): ip_list = self.extract_ip_list(ip) # validate if the form has a server or an ip - if ip and ip_list or server: + if (ip and ip_list) or server: self.validate_nameserver_ip_combo(domain, server, ip_list) return cleaned_data diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index ccb96a7d5..5ca1e18bf 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -1232,34 +1232,6 @@ class Domain(TimeStampedModel, DomainHelper): # ForeignKey on DomainInvitation creates an "invitations" member for # all of the invitations that have been sent for this domain - def _validate_host_tuples(self, hosts: list[tuple[str]]): - """ - Helper function. Validate hostnames and IP addresses. - - Raises: - ValueError if hostname or IP address appears invalid or mismatched. - """ - for host in hosts: - hostname = host[0].lower() - addresses: tuple[str] = host[1:] # type: ignore - if not bool(Domain.HOST_REGEX.match(hostname)): - raise ValueError("Invalid hostname: %s." % hostname) - if len(hostname) > Domain.MAX_LENGTH: - raise ValueError("Too long hostname: %s" % hostname) - - is_subordinate = hostname.split(".", 1)[-1] == self.name - if is_subordinate and len(addresses) == 0: - raise ValueError( - "Must supply IP addresses for subordinate host %s" % hostname - ) - if not is_subordinate and len(addresses) > 0: - raise ValueError("Must not supply IP addresses for %s" % hostname) - - for address in addresses: - allow = set(":." + digits) - if any(c not in allow for c in address): - raise ValueError("Invalid IP address: %s." % address) - def _get_or_create_domain(self): """Try to fetch info about this domain. Create it if it does not exist.""" already_tried_to_create = False diff --git a/src/registrar/models/utility/domain_helper.py b/src/registrar/models/utility/domain_helper.py index 1a77e44b1..49badd5d7 100644 --- a/src/registrar/models/utility/domain_helper.py +++ b/src/registrar/models/utility/domain_helper.py @@ -11,10 +11,6 @@ class DomainHelper: # begin or end with a hyphen, followed by a TLD of 2-6 alphabetic characters DOMAIN_REGEX = re.compile(r"^(?!-)[A-Za-z0-9-]{1,63}(?
-

Add an IP address only when your name server's address includes your domain name (e.g., if your domain name is "example.gov" and your name server is "ns1.example.gov," then an IP address is required.) To add multiple IP addresses, separate them with commas.

+

Add an IP address only when your name server's address includes your domain name (e.g., if your domain name is “example.gov” and your name server is “ns1.example.gov,” then an IP address is required.) To add multiple IP addresses, separate them with commas.

This step is uncommon unless you self-host your DNS or use custom addresses for your nameserver.

From 91d8ee1afe6307207be225952a67018f337a1043 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 2 Nov 2023 10:42:27 -0400 Subject: [PATCH 08/13] test cases for valid and invalid domains --- src/registrar/models/domain.py | 4 +- src/registrar/tests/test_models_domain.py | 47 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 5ca1e18bf..95da25ccb 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -310,7 +310,7 @@ class Domain(TimeStampedModel, DomainHelper): return bool(regex.match(nameserver)) @classmethod - def isValidDomain(cls, nameserver: str): + def isValidHost(cls, nameserver: str): """Checks for validity of nameserver string based on these rules: - first character is alpha or digit - first and last character in each label is alpha or digit @@ -353,7 +353,7 @@ class Domain(TimeStampedModel, DomainHelper): None""" if ip and not nameserver: raise NameserverError(code=nsErrorCodes.MISSING_HOST) - elif nameserver and not cls.isValidDomain(nameserver): + elif nameserver and not cls.isValidHost(nameserver): raise NameserverError(code=nsErrorCodes.INVALID_HOST, nameserver=nameserver) elif cls.isSubdomain(name, nameserver) and (ip is None or ip == []): raise NameserverError(code=nsErrorCodes.MISSING_IP, nameserver=nameserver) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index bce442c9c..fd026b53f 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -1630,6 +1630,53 @@ class TestRegistrantNameservers(MockEppLib): return super().tearDown() +class TestNameserverValidation(TestCase): + """Test the isValidDomain method which validates nameservers""" + + def test_255_chars_is_too_long(self): + """Test that domain of 255 chars or longer is invalid""" + domain_too_long = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gov" + self.assertFalse(Domain.isValidHost(domain_too_long)) + + def test_64_char_label_too_long(self): + """Test that label of 64 characters or longer is invalid""" + label_too_long = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + domain_label_too_long = "www." + label_too_long + ".gov" + self.assertFalse(Domain.isValidHost(domain_label_too_long)) + + def test_only_tld_and_sld(self): + """Test that host with only a tld and sld is invalid""" + tld = "gov" + sld = "example" + domain_with_sld_and_tld = sld + "." + tld + self.assertFalse(Domain.isValidHost(domain_with_sld_and_tld)) + + def test_improper_chars_in_nameserver(self): + """Test that host with improper chars is invalid""" + invalid_chars = "*&^" + domain_with_invalid_chars = "www.bad--" + invalid_chars + ".gov" + self.assertFalse(Domain.isValidHost(domain_with_invalid_chars)) + + def test_misplaced_dashes(self): + """Test that misplaced dashes are invalid""" + self.assertFalse(Domain.isValidHost("-www.example.gov")) + self.assertFalse(Domain.isValidHost("www.example-.gov")) + self.assertTrue(Domain.isValidHost("www.ex-ample.gov")) + + def test_valid_hostname(self): + """Test that valid hostnames are valid""" + self.assertTrue(Domain.isValidHost("www.tld.sld.gov")) + self.assertTrue(Domain.isValidHost("www.valid.c")) + self.assertTrue(Domain.isValidHost("2ww.valid.gov")) + self.assertTrue(Domain.isValidHost("w.t.g")) + + class TestRegistrantDNSSEC(MockEppLib): """Rule: Registrants may modify their secure DNS data""" From 812331681b91298c0642cda6025681b85782b0f5 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 2 Nov 2023 10:53:46 -0400 Subject: [PATCH 09/13] formatted long lines for readability and removed unused imports --- src/registrar/models/domain.py | 1 - src/registrar/tests/test_models_domain.py | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 95da25ccb..f5143e8bc 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -3,7 +3,6 @@ import logging import ipaddress import re from datetime import date -from string import digits from typing import Optional from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index fd026b53f..746625dbe 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -1635,18 +1635,21 @@ class TestNameserverValidation(TestCase): def test_255_chars_is_too_long(self): """Test that domain of 255 chars or longer is invalid""" - domain_too_long = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ - ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ - ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ - ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ - ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ - ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gov" + domain_too_long = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + ".bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.gov" + ) self.assertFalse(Domain.isValidHost(domain_too_long)) def test_64_char_label_too_long(self): """Test that label of 64 characters or longer is invalid""" - label_too_long = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + label_too_long = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + ) domain_label_too_long = "www." + label_too_long + ".gov" self.assertFalse(Domain.isValidHost(domain_label_too_long)) From 0e8b24da102754e2d045a0dac7a99ea33f6ff7de Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 2 Nov 2023 12:23:24 -0400 Subject: [PATCH 10/13] removing all whitespace from nameservers --- src/registrar/forms/domain.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index 6e0c16b58..01926055b 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -44,6 +44,8 @@ class DomainNameserverForm(forms.Form): cleaned_data = super().clean() self.clean_empty_strings(cleaned_data) server = cleaned_data.get("server", "") + # remove ANY spaces in the server field + server = server.replace(" ", "") ip = cleaned_data.get("ip", None) # remove ANY spaces in the ip field ip = ip.replace(" ", "") From a194a34123b185270d5d6919994ee2e0ce6fbf36 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 2 Nov 2023 12:42:54 -0400 Subject: [PATCH 11/13] allow uppercase on nameservers, convert to lowercase --- src/registrar/forms/domain.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index 01926055b..1a991e464 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -46,6 +46,9 @@ class DomainNameserverForm(forms.Form): server = cleaned_data.get("server", "") # remove ANY spaces in the server field server = server.replace(" ", "") + # lowercase the server + server = server.lower() + cleaned_data["server"] = server ip = cleaned_data.get("ip", None) # remove ANY spaces in the ip field ip = ip.replace(" ", "") From e7a30014099200eca344b4a138f542a9aa1f0a5c Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 2 Nov 2023 15:37:35 -0400 Subject: [PATCH 12/13] truncated long domains in error messages; updated error messages --- src/registrar/models/domain.py | 4 +++- src/registrar/utility/errors.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index f5143e8bc..67693d8d2 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -365,7 +365,9 @@ class Domain(TimeStampedModel, DomainHelper): for addr in ip: if not cls._valid_ip_addr(addr): raise NameserverError( - code=nsErrorCodes.INVALID_IP, nameserver=nameserver, ip=ip + code=nsErrorCodes.INVALID_IP, + nameserver=nameserver[:40], + ip=ip ) return None diff --git a/src/registrar/utility/errors.py b/src/registrar/utility/errors.py index 2c21e5bcc..d135aa1c2 100644 --- a/src/registrar/utility/errors.py +++ b/src/registrar/utility/errors.py @@ -106,7 +106,9 @@ class NameserverError(Exception): NameserverErrorCodes.MISSING_HOST: ( "Name server must be provided to enter IP address." ), - NameserverErrorCodes.INVALID_HOST: ("Name server, {}, is not valid."), + NameserverErrorCodes.INVALID_HOST: ( + "Enter a name server in the required format, like ns1.example.com" + ), } def __init__(self, *args, code=None, nameserver=None, ip=None, **kwargs): From a2e4dd494fc1191c8e0d1f77335960f8103a835c Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 2 Nov 2023 15:41:43 -0400 Subject: [PATCH 13/13] linting --- src/registrar/models/domain.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 67693d8d2..c5c0b63f7 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -365,9 +365,7 @@ class Domain(TimeStampedModel, DomainHelper): for addr in ip: if not cls._valid_ip_addr(addr): raise NameserverError( - code=nsErrorCodes.INVALID_IP, - nameserver=nameserver[:40], - ip=ip + code=nsErrorCodes.INVALID_IP, nameserver=nameserver[:40], ip=ip ) return None