Check the host is under registry suffix instead of public suffix

Guava now has support to distinguish a registry suffix from a public suffix. Since we are only interested in registrable domains, registry suffix is the proper thing to check.

See:

692446a303/guava/src/com/google/common/net/InternetDomainName.java

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=176126916
This commit is contained in:
jianglai 2017-11-17 10:28:41 -08:00
parent 9ab68613a0
commit 0796a0ff1c
4 changed files with 50 additions and 59 deletions

View file

@ -1032,7 +1032,7 @@ allows creating a host name, and if necessary enqueues tasks to update DNS.
* 2304 * 2304
* Superordinate domain for this hostname is in pending delete. * Superordinate domain for this hostname is in pending delete.
* 2306 * 2306
* Host names must be at least two levels below the public suffix. * Host names must be at least two levels below the registry suffix.
## HostDeleteFlow ## HostDeleteFlow
@ -1131,7 +1131,7 @@ are enqueued to update DNS accordingly.
* Cannot rename an external host. * Cannot rename an external host.
* 2306 * 2306
* Cannot add and remove the same value. * Cannot add and remove the same value.
* Host names must be at least two levels below the public suffix. * Host names must be at least two levels below the registry suffix.
## LoginFlow ## LoginFlow

View file

@ -57,32 +57,21 @@ public class HostFlowUtils {
if (!name.equals(hostName.toString())) { if (!name.equals(hostName.toString())) {
throw new HostNameNotNormalizedException(hostName.toString()); throw new HostNameNotNormalizedException(hostName.toString());
} }
// Checks whether a hostname is deep enough. Technically a host can be just one under a // The effective TLD is, in order of preference, the registry suffix, if the TLD is a real TLD
// public suffix (e.g. example.com) but we require by policy that it has to be at least one // published in the public suffix list (https://publicsuffix.org/, note that a registry suffix
// part beyond that (e.g. ns1.example.com). The public suffix list includes all current // is in the "ICANN DOMAINS" in that list); or a TLD managed by Nomulus (in-bailiwick), found
// ccTlds, so this check requires 4+ parts if it's a ccTld that doesn't delegate second // by #findTldForName; or just the last part of a domain name.
// level domains, such as .co.uk. But the list does not include new tlds, so in that case InternetDomainName effectiveTld =
// we just ensure 3+ parts. In the particular case where our own tld has a '.' in it, we know hostName.isUnderRegistrySuffix()
// that there need to be 4 parts as well. ? hostName.registrySuffix()
// TODO(b/63128999): Use better method (once implemented) that determines if it's a public : findTldForName(hostName).orElse(InternetDomainName.from("invalid"));
// suffix that domain names can be registered under. // Checks whether a hostname is deep enough. Technically a host can be just one level beneath
if (hostName.isUnderPublicSuffix()) { // the effective TLD (e.g. example.com) but we require by policy that it has to be at least
if (hostName.parent().isUnderPublicSuffix()) { // one part beyond that (e.g. ns1.example.com).
return hostName; if (hostName.parts().size() < effectiveTld.parts().size() + 2) {
} throw new HostNameTooShallowException();
} else {
// We need to know how many parts the hostname has beyond the public suffix, but we don't
// know what the public suffix is. If the host is in bailiwick and we are hosting a
// multipart "tld" like .co.uk the public suffix might be 2 parts. Otherwise it's an
// unrecognized tld that's not on the public suffix list, so assume the tld alone is the
// public suffix.
Optional<InternetDomainName> tldParsed = findTldForName(hostName);
int suffixSize = tldParsed.isPresent() ? tldParsed.get().parts().size() : 1;
if (hostName.parts().size() >= suffixSize + 2) {
return hostName;
}
} }
throw new HostNameTooShallowException(); return hostName;
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new InvalidHostNameException(); throw new InvalidHostNameException();
} }
@ -119,8 +108,7 @@ public class HostFlowUtils {
/** Ensure that the superordinate domain is sponsored by the provided clientId. */ /** Ensure that the superordinate domain is sponsored by the provided clientId. */
static void verifySuperordinateDomainOwnership( static void verifySuperordinateDomainOwnership(
String clientId, String clientId, DomainResource superordinateDomain) throws EppException {
DomainResource superordinateDomain) throws EppException {
if (superordinateDomain != null if (superordinateDomain != null
&& !clientId.equals(superordinateDomain.getCurrentSponsorClientId())) { && !clientId.equals(superordinateDomain.getCurrentSponsorClientId())) {
throw new HostDomainNotOwnedException(); throw new HostDomainNotOwnedException();
@ -135,8 +123,8 @@ public class HostFlowUtils {
} }
/** Ensure that the superordinate domain is not in pending delete. */ /** Ensure that the superordinate domain is not in pending delete. */
static void verifySuperordinateDomainNotInPendingDelete( static void verifySuperordinateDomainNotInPendingDelete(DomainResource superordinateDomain)
DomainResource superordinateDomain) throws EppException { throws EppException {
if ((superordinateDomain != null) if ((superordinateDomain != null)
&& superordinateDomain.getStatusValues().contains(StatusValue.PENDING_DELETE)) { && superordinateDomain.getStatusValues().contains(StatusValue.PENDING_DELETE)) {
throw new SuperordinateDomainInPendingDeleteException(); throw new SuperordinateDomainInPendingDeleteException();
@ -158,10 +146,10 @@ public class HostFlowUtils {
} }
} }
/** Host names must be at least two levels below the public suffix. */ /** Host names must be at least two levels below the registry suffix. */
static class HostNameTooShallowException extends ParameterValuePolicyErrorException { static class HostNameTooShallowException extends ParameterValuePolicyErrorException {
public HostNameTooShallowException() { public HostNameTooShallowException() {
super("Host names must be at least two levels below the public suffix"); super("Host names must be at least two levels below the registry suffix");
} }
} }

View file

@ -1006,10 +1006,10 @@ def com_google_gdata_core():
def com_google_guava(): def com_google_guava():
java_import_external( java_import_external(
name = "com_google_guava", name = "com_google_guava",
jar_sha256 = "7baa80df284117e5b945b19b98d367a85ea7b7801bd358ff657946c3bd1b6596", jar_sha256 = "4a87d5b1ca996e5e46a99594cf3566ae16885d0cf00b381b5e9f816a0e0125b3",
jar_urls = [ jar_urls = [
"http://repo1.maven.org/maven2/com/google/guava/guava/23.0/guava-23.0.jar", "http://repo1.maven.org/maven2/com/google/guava/guava/23.3-jre/guava-23.3-jre.jar",
"http://domain-registry-maven.storage.googleapis.com/repo1.maven.org/maven2/com/google/guava/guava/23.0/guava-23.0.jar", "http://maven.ibiblio.org/maven2/com/google/guava/guava/23.3-jre/guava-23.3-jre.jar",
], ],
licenses = ["notice"], # The Apache Software License, Version 2.0 licenses = ["notice"], # The Apache Software License, Version 2.0
exports = [ exports = [
@ -1021,10 +1021,10 @@ def com_google_guava():
def com_google_guava_testlib(): def com_google_guava_testlib():
java_import_external( java_import_external(
name = "com_google_guava_testlib", name = "com_google_guava_testlib",
jar_sha256 = "7e328d0f89a5ea103de4f9b689130eb555ff277e83bf86294bc14c2c40a59a80", jar_sha256 = "377c60720828a655ffd0f64d8b64643962b2957635323ddc9c5223827f6e5482",
jar_urls = [ jar_urls = [
"http://repo1.maven.org/maven2/com/google/guava/guava-testlib/23.0/guava-testlib-23.0.jar", "http://maven.ibiblio.org/maven2/com/google/guava/guava-testlib/23.3-jre/guava-testlib-23.3-jre.jar",
"http://domain-registry-maven.storage.googleapis.com/repo1.maven.org/maven2/com/google/guava/guava-testlib/23.0/guava-testlib-23.0.jar", "http://repo1.maven.org/maven2/com/google/guava/guava-testlib/23.3-jre/guava-testlib-23.3-jre.jar",
], ],
licenses = ["notice"], # The Apache Software License, Version 2.0 licenses = ["notice"], # The Apache Software License, Version 2.0
testonly_ = True, testonly_ = True,

View file

@ -16,6 +16,7 @@ package google.registry.flows.host;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.flows.host.HostFlowUtils.validateHostName; import static google.registry.flows.host.HostFlowUtils.validateHostName;
import static google.registry.testing.JUnitBackports.assertThrows;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import google.registry.flows.host.HostFlowUtils.HostNameNotLowerCaseException; import google.registry.flows.host.HostFlowUtils.HostNameNotLowerCaseException;
@ -25,8 +26,6 @@ import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException; import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException; import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import google.registry.testing.ExceptionRule;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -36,8 +35,6 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
public class HostFlowUtilsTest { public class HostFlowUtilsTest {
@Rule public final ExceptionRule thrown = new ExceptionRule();
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
@Test @Test
@ -46,47 +43,53 @@ public class HostFlowUtilsTest {
} }
@Test @Test
@Ignore public void test_validExternalHostNameOnRegistrySuffixList_validates() throws Exception {
// TODO(b/63128999): Fix handling of public suffix lists so that hostnames that aren't on
// effective TLDs validate.
public void test_validExternalHostNameOnPublicSuffixList_validates() throws Exception {
assertThat(validateHostName("host.blogspot.com").toString()).isEqualTo("host.blogspot.com"); assertThat(validateHostName("host.blogspot.com").toString()).isEqualTo("host.blogspot.com");
} }
@Test
public void test_validExternalHostNameOnRegistrySuffixList_multipartTLD_validates()
throws Exception {
assertThat(validateHostName("ns1.host.co.uk").toString()).isEqualTo("ns1.host.co.uk");
}
@Test
public void test_validExternalHostNameOnRegistrySuffixList_multipartTLD_tooShallow()
throws Exception {
assertThrows(
HostNameTooShallowException.class, () -> validateHostName("host.co.uk").toString());
}
@Test @Test
public void test_validateHostName_hostNameTooLong() throws Exception { public void test_validateHostName_hostNameTooLong() throws Exception {
thrown.expect(HostNameTooLongException.class); assertThrows(
validateHostName(Strings.repeat("na", 200) + ".wat.man"); HostNameTooLongException.class,
() -> validateHostName(Strings.repeat("na", 200) + ".wat.man"));
} }
@Test @Test
public void test_validateHostName_hostNameNotLowerCase() throws Exception { public void test_validateHostName_hostNameNotLowerCase() throws Exception {
thrown.expect(HostNameNotLowerCaseException.class); assertThrows(HostNameNotLowerCaseException.class, () -> validateHostName("NA.CAPS.TLD"));
validateHostName("NA.CAPS.TLD");
} }
@Test @Test
public void test_validateHostName_hostNameNotPunyCoded() throws Exception { public void test_validateHostName_hostNameNotPunyCoded() throws Exception {
thrown.expect(HostNameNotPunyCodedException.class); assertThrows(
validateHostName("motörhead.death.metal"); HostNameNotPunyCodedException.class, () -> validateHostName("motörhead.death.metal"));
} }
@Test @Test
public void test_validateHostName_hostNameNotNormalized() throws Exception { public void test_validateHostName_hostNameNotNormalized() throws Exception {
thrown.expect(HostNameNotNormalizedException.class); assertThrows(HostNameNotNormalizedException.class, () -> validateHostName("root.node.yeah."));
validateHostName("root.node.yeah.");
} }
@Test @Test
public void test_validateHostName_hostNameHasLeadingHyphen() throws Exception { public void test_validateHostName_hostNameHasLeadingHyphen() throws Exception {
thrown.expect(InvalidHostNameException.class); assertThrows(InvalidHostNameException.class, () -> validateHostName("-giga.mega.tld"));
validateHostName("-giga.mega.tld");
} }
@Test @Test
public void test_validateHostName_hostNameTooShallow() throws Exception { public void test_validateHostName_hostNameTooShallow() throws Exception {
thrown.expect(HostNameTooShallowException.class); assertThrows(HostNameTooShallowException.class, () -> validateHostName("domain.tld"));
validateHostName("domain.tld");
} }
} }