mirror of
https://github.com/google/nomulus
synced 2026-01-11 00:10:36 +00:00
Add more strict hostname validation on host:check flows (#2915)
We do most of these on host create already so we should also do them on host checks. The only added change is the character validation (our existing hostnames all match these).
This commit is contained in:
@@ -218,7 +218,7 @@ public class DomainFlowUtils {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
private static void validateFirstLabel(String firstLabel) throws EppException {
|
||||
public static void validateFirstLabel(String firstLabel) throws EppException {
|
||||
if (firstLabel.length() > MAX_LABEL_SIZE) {
|
||||
throw new DomainLabelTooLongException();
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ public final class HostCheckFlow implements TransactionalFlow {
|
||||
ForeignKeyUtils.loadKeys(Host.class, hostnames, clock.nowUtc()).keySet();
|
||||
ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>();
|
||||
for (String hostname : hostnames) {
|
||||
HostFlowUtils.validateHostName(hostname);
|
||||
boolean unused = !existingIds.contains(hostname);
|
||||
checks.add(HostCheck.create(unused, hostname, unused ? null : "In use"));
|
||||
}
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
|
||||
package google.registry.flows.host;
|
||||
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateFirstLabel;
|
||||
import static google.registry.model.EppResourceUtils.isActive;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
@@ -38,6 +40,10 @@ import org.joda.time.DateTime;
|
||||
/** Static utility functions for host flows. */
|
||||
public class HostFlowUtils {
|
||||
|
||||
/** Validator for ASCII lowercase letters, digits, and "-_", allowing "." as a separator */
|
||||
private static final CharMatcher HOST_NAME_ALLOWED_CHARS =
|
||||
CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9').or(CharMatcher.anyOf("-._")));
|
||||
|
||||
/** Checks that a host name is valid. */
|
||||
public static InternetDomainName validateHostName(String name) throws EppException {
|
||||
checkArgumentNotNull(name, "Must specify host name to validate");
|
||||
@@ -53,6 +59,9 @@ public class HostFlowUtils {
|
||||
if (!name.equals(hostNamePunyCoded)) {
|
||||
throw new HostNameNotPunyCodedException(hostNamePunyCoded);
|
||||
}
|
||||
if (!HOST_NAME_ALLOWED_CHARS.matchesAllOf(name)) {
|
||||
throw new BadHostNameCharacterException();
|
||||
}
|
||||
InternetDomainName hostName = InternetDomainName.from(name);
|
||||
if (!name.equals(hostName.toString())) {
|
||||
throw new HostNameNotNormalizedException(hostName.toString());
|
||||
@@ -71,6 +80,7 @@ public class HostFlowUtils {
|
||||
if (hostName.parts().size() < effectiveTld.parts().size() + 2) {
|
||||
throw new HostNameTooShallowException();
|
||||
}
|
||||
validateFirstLabel(hostName.parts().getFirst());
|
||||
return hostName;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvalidHostNameException();
|
||||
@@ -180,4 +190,11 @@ public class HostFlowUtils {
|
||||
String.format("Host names must be in normalized format; expected %s", expectedHostName));
|
||||
}
|
||||
}
|
||||
|
||||
/** Host names can only contain a-z, 0-9, '.', '_', and '-'. */
|
||||
static class BadHostNameCharacterException extends ParameterValueSyntaxErrorException {
|
||||
public BadHostNameCharacterException() {
|
||||
super("Host names can only contain a-z, 0-9, '.', '_', and '-'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ import static google.registry.testing.DatabaseHelper.persistDeletedHost;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
import google.registry.flows.ResourceCheckFlowTestCase;
|
||||
import google.registry.flows.exceptions.TooManyResourceChecksException;
|
||||
@@ -95,4 +97,36 @@ class HostCheckFlowTest extends ResourceCheckFlowTestCase<HostCheckFlow, Host> {
|
||||
runFlow();
|
||||
assertIcannReportingActivityFieldLogged("srs-host-check");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_dotHost() throws Exception {
|
||||
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", ".host"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_dashHost() {
|
||||
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", "-host"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_underscoreHost() {
|
||||
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", "_host"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_hostDash() {
|
||||
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", "host-"));
|
||||
assertAboutEppExceptions()
|
||||
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
|
||||
.marshalsToXml();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,12 @@ import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientExcept
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.flows.host.HostCreateFlow.SubordinateHostMustHaveIpException;
|
||||
import google.registry.flows.host.HostCreateFlow.UnexpectedExternalHostIpException;
|
||||
import google.registry.flows.host.HostFlowUtils.BadHostNameCharacterException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameNotLowerCaseException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameNotNormalizedException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
||||
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
|
||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
@@ -286,7 +286,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
|
||||
|
||||
@Test
|
||||
void testFailure_badCharacter() {
|
||||
doFailingHostNameTest("foo bar", InvalidHostNameException.class);
|
||||
doFailingHostNameTest("foo bar", BadHostNameCharacterException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -54,13 +54,13 @@ import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||
import google.registry.flows.ResourceFlowUtils.StatusNotClientSettableException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||
import google.registry.flows.host.HostFlowUtils.BadHostNameCharacterException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostDomainNotOwnedException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameNotLowerCaseException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameNotNormalizedException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
|
||||
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
|
||||
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
|
||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
|
||||
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
|
||||
import google.registry.flows.host.HostUpdateFlow.CannotAddIpToExternalHostException;
|
||||
@@ -1259,7 +1259,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Host> {
|
||||
|
||||
@Test
|
||||
void testFailure_renameToBadCharacter() throws Exception {
|
||||
doFailingHostNameTest("foo bar", InvalidHostNameException.class);
|
||||
doFailingHostNameTest("foo bar", BadHostNameCharacterException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -74,7 +74,9 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
.that(generateActualJson("invalid/host/name"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonError(
|
||||
"invalid/host/name is not a valid nameserver: Invalid host name", 400));
|
||||
"invalid/host/name is not a valid nameserver: Host names can only contain a-z, 0-9,"
|
||||
+ " '.', '_', and '-'",
|
||||
400));
|
||||
assertThat(response.getStatus()).isEqualTo(400);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,56 +3,56 @@
|
||||
<check>
|
||||
<host:check
|
||||
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>www1.tld</host:name>
|
||||
<host:name>www2.tld</host:name>
|
||||
<host:name>www3.tld</host:name>
|
||||
<host:name>www4.tld</host:name>
|
||||
<host:name>www5.tld</host:name>
|
||||
<host:name>www6.tld</host:name>
|
||||
<host:name>www7.tld</host:name>
|
||||
<host:name>www8.tld</host:name>
|
||||
<host:name>www9.tld</host:name>
|
||||
<host:name>www10.tld</host:name>
|
||||
<host:name>www11.tld</host:name>
|
||||
<host:name>www12.tld</host:name>
|
||||
<host:name>www13.tld</host:name>
|
||||
<host:name>www14.tld</host:name>
|
||||
<host:name>www15.tld</host:name>
|
||||
<host:name>www16.tld</host:name>
|
||||
<host:name>www17.tld</host:name>
|
||||
<host:name>www18.tld</host:name>
|
||||
<host:name>www19.tld</host:name>
|
||||
<host:name>www20.tld</host:name>
|
||||
<host:name>www21.tld</host:name>
|
||||
<host:name>www22.tld</host:name>
|
||||
<host:name>www23.tld</host:name>
|
||||
<host:name>www24.tld</host:name>
|
||||
<host:name>www25.tld</host:name>
|
||||
<host:name>www26.tld</host:name>
|
||||
<host:name>www27.tld</host:name>
|
||||
<host:name>www28.tld</host:name>
|
||||
<host:name>www29.tld</host:name>
|
||||
<host:name>www30.tld</host:name>
|
||||
<host:name>www31.tld</host:name>
|
||||
<host:name>www32.tld</host:name>
|
||||
<host:name>www33.tld</host:name>
|
||||
<host:name>www34.tld</host:name>
|
||||
<host:name>www35.tld</host:name>
|
||||
<host:name>www36.tld</host:name>
|
||||
<host:name>www37.tld</host:name>
|
||||
<host:name>www38.tld</host:name>
|
||||
<host:name>www39.tld</host:name>
|
||||
<host:name>www40.tld</host:name>
|
||||
<host:name>www41.tld</host:name>
|
||||
<host:name>www42.tld</host:name>
|
||||
<host:name>www43.tld</host:name>
|
||||
<host:name>www44.tld</host:name>
|
||||
<host:name>www45.tld</host:name>
|
||||
<host:name>www46.tld</host:name>
|
||||
<host:name>www47.tld</host:name>
|
||||
<host:name>www48.tld</host:name>
|
||||
<host:name>www49.tld</host:name>
|
||||
<host:name>www50.tld</host:name>
|
||||
<host:name>ns1.www1.tld</host:name>
|
||||
<host:name>ns1.www2.tld</host:name>
|
||||
<host:name>ns1.www3.tld</host:name>
|
||||
<host:name>ns1.www4.tld</host:name>
|
||||
<host:name>ns1.www5.tld</host:name>
|
||||
<host:name>ns1.www6.tld</host:name>
|
||||
<host:name>ns1.www7.tld</host:name>
|
||||
<host:name>ns1.www8.tld</host:name>
|
||||
<host:name>ns1.www9.tld</host:name>
|
||||
<host:name>ns1.www10.tld</host:name>
|
||||
<host:name>ns1.www11.tld</host:name>
|
||||
<host:name>ns1.www12.tld</host:name>
|
||||
<host:name>ns1.www13.tld</host:name>
|
||||
<host:name>ns1.www14.tld</host:name>
|
||||
<host:name>ns1.www15.tld</host:name>
|
||||
<host:name>ns1.www16.tld</host:name>
|
||||
<host:name>ns1.www17.tld</host:name>
|
||||
<host:name>ns1.www18.tld</host:name>
|
||||
<host:name>ns1.www19.tld</host:name>
|
||||
<host:name>ns1.www20.tld</host:name>
|
||||
<host:name>ns1.www21.tld</host:name>
|
||||
<host:name>ns1.www22.tld</host:name>
|
||||
<host:name>ns1.www23.tld</host:name>
|
||||
<host:name>ns1.www24.tld</host:name>
|
||||
<host:name>ns1.www25.tld</host:name>
|
||||
<host:name>ns1.www26.tld</host:name>
|
||||
<host:name>ns1.www27.tld</host:name>
|
||||
<host:name>ns1.www28.tld</host:name>
|
||||
<host:name>ns1.www29.tld</host:name>
|
||||
<host:name>ns1.www30.tld</host:name>
|
||||
<host:name>ns1.www31.tld</host:name>
|
||||
<host:name>ns1.www32.tld</host:name>
|
||||
<host:name>ns1.www33.tld</host:name>
|
||||
<host:name>ns1.www34.tld</host:name>
|
||||
<host:name>ns1.www35.tld</host:name>
|
||||
<host:name>ns1.www36.tld</host:name>
|
||||
<host:name>ns1.www37.tld</host:name>
|
||||
<host:name>ns1.www38.tld</host:name>
|
||||
<host:name>ns1.www39.tld</host:name>
|
||||
<host:name>ns1.www40.tld</host:name>
|
||||
<host:name>ns1.www41.tld</host:name>
|
||||
<host:name>ns1.www42.tld</host:name>
|
||||
<host:name>ns1.www43.tld</host:name>
|
||||
<host:name>ns1.www44.tld</host:name>
|
||||
<host:name>ns1.www45.tld</host:name>
|
||||
<host:name>ns1.www46.tld</host:name>
|
||||
<host:name>ns1.www47.tld</host:name>
|
||||
<host:name>ns1.www48.tld</host:name>
|
||||
<host:name>ns1.www49.tld</host:name>
|
||||
<host:name>ns1.www50.tld</host:name>
|
||||
</host:check>
|
||||
</check>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<host:check
|
||||
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>%HOSTNAME%</host:name>
|
||||
</host:check>
|
||||
</check>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
Reference in New Issue
Block a user