Skip to content

Commit eae1b4f

Browse files
committed
Add more strict hostname validation on host:check flows
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).
1 parent db9fc32 commit eae1b4f

File tree

9 files changed

+121
-56
lines changed

9 files changed

+121
-56
lines changed

core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public static InternetDomainName validateDomainName(String name) throws EppExcep
218218
return domainName;
219219
}
220220

221-
private static void validateFirstLabel(String firstLabel) throws EppException {
221+
public static void validateFirstLabel(String firstLabel) throws EppException {
222222
if (firstLabel.length() > MAX_LABEL_SIZE) {
223223
throw new DomainLabelTooLongException();
224224
}

core/src/main/java/google/registry/flows/host/HostCheckFlow.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public EppResponse run() throws EppException {
6565
ForeignKeyUtils.loadKeys(Host.class, hostnames, clock.nowUtc()).keySet();
6666
ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>();
6767
for (String hostname : hostnames) {
68+
HostFlowUtils.validateHostName(hostname);
6869
boolean unused = !existingIds.contains(hostname);
6970
checks.add(HostCheck.create(unused, hostname, unused ? null : "In use"));
7071
}

core/src/main/java/google/registry/flows/host/HostFlowUtils.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414

1515
package google.registry.flows.host;
1616

17+
import static google.registry.flows.domain.DomainFlowUtils.validateFirstLabel;
1718
import static google.registry.model.EppResourceUtils.isActive;
1819
import static google.registry.model.tld.Tlds.findTldForName;
1920
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
2021
import static java.util.stream.Collectors.joining;
2122

2223
import com.google.common.base.Ascii;
24+
import com.google.common.base.CharMatcher;
2325
import com.google.common.net.InternetDomainName;
2426
import google.registry.flows.EppException;
2527
import google.registry.flows.EppException.AuthorizationErrorException;
@@ -38,6 +40,10 @@
3840
/** Static utility functions for host flows. */
3941
public class HostFlowUtils {
4042

43+
/** Validator for ASCII lowercase letters, digits, and "-_", allowing "." as a separator */
44+
private static final CharMatcher HOST_NAME_ALLOWED_CHARS =
45+
CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9').or(CharMatcher.anyOf("-._")));
46+
4147
/** Checks that a host name is valid. */
4248
public static InternetDomainName validateHostName(String name) throws EppException {
4349
checkArgumentNotNull(name, "Must specify host name to validate");
@@ -53,6 +59,9 @@ public static InternetDomainName validateHostName(String name) throws EppExcepti
5359
if (!name.equals(hostNamePunyCoded)) {
5460
throw new HostNameNotPunyCodedException(hostNamePunyCoded);
5561
}
62+
if (!HOST_NAME_ALLOWED_CHARS.matchesAllOf(name)) {
63+
throw new BadHostNameCharacterException();
64+
}
5665
InternetDomainName hostName = InternetDomainName.from(name);
5766
if (!name.equals(hostName.toString())) {
5867
throw new HostNameNotNormalizedException(hostName.toString());
@@ -71,6 +80,7 @@ public static InternetDomainName validateHostName(String name) throws EppExcepti
7180
if (hostName.parts().size() < effectiveTld.parts().size() + 2) {
7281
throw new HostNameTooShallowException();
7382
}
83+
validateFirstLabel(hostName.parts().getFirst());
7484
return hostName;
7585
} catch (IllegalArgumentException e) {
7686
throw new InvalidHostNameException();
@@ -180,4 +190,11 @@ public HostNameNotNormalizedException(String expectedHostName) {
180190
String.format("Host names must be in normalized format; expected %s", expectedHostName));
181191
}
182192
}
193+
194+
/** Host names can only contain a-z, 0-9, '.', '_', and '-'. */
195+
static class BadHostNameCharacterException extends ParameterValueSyntaxErrorException {
196+
public BadHostNameCharacterException() {
197+
super("Host names can only contain a-z, 0-9, '.', '_', and '-'");
198+
}
199+
}
183200
}

core/src/test/java/google/registry/flows/host/HostCheckFlowTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
2121
import static org.junit.jupiter.api.Assertions.assertThrows;
2222

23+
import com.google.common.collect.ImmutableMap;
2324
import google.registry.flows.EppException;
25+
import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
2426
import google.registry.flows.FlowUtils.NotLoggedInException;
2527
import google.registry.flows.ResourceCheckFlowTestCase;
2628
import google.registry.flows.exceptions.TooManyResourceChecksException;
@@ -95,4 +97,36 @@ void testIcannActivityReportField_getsLogged() throws Exception {
9597
runFlow();
9698
assertIcannReportingActivityFieldLogged("srs-host-check");
9799
}
100+
101+
@Test
102+
void testFailure_dotHost() throws Exception {
103+
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", ".host"));
104+
assertAboutEppExceptions()
105+
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
106+
.marshalsToXml();
107+
}
108+
109+
@Test
110+
void testFailure_dashHost() {
111+
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", "-host"));
112+
assertAboutEppExceptions()
113+
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
114+
.marshalsToXml();
115+
}
116+
117+
@Test
118+
void testFailure_underscoreHost() {
119+
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", "_host"));
120+
assertAboutEppExceptions()
121+
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
122+
.marshalsToXml();
123+
}
124+
125+
@Test
126+
void testFailure_hostDash() {
127+
setEppInput("host_check_generic.xml", ImmutableMap.of("HOSTNAME", "host-"));
128+
assertAboutEppExceptions()
129+
.that(assertThrows(ParameterValueSyntaxErrorException.class, this::runFlow))
130+
.marshalsToXml();
131+
}
98132
}

core/src/test/java/google/registry/flows/host/HostCreateFlowTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@
3939
import google.registry.flows.exceptions.ResourceCreateContentionException;
4040
import google.registry.flows.host.HostCreateFlow.SubordinateHostMustHaveIpException;
4141
import google.registry.flows.host.HostCreateFlow.UnexpectedExternalHostIpException;
42+
import google.registry.flows.host.HostFlowUtils.BadHostNameCharacterException;
4243
import google.registry.flows.host.HostFlowUtils.HostNameNotLowerCaseException;
4344
import google.registry.flows.host.HostFlowUtils.HostNameNotNormalizedException;
4445
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
4546
import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
4647
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
47-
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
4848
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
4949
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
5050
import google.registry.model.ForeignKeyUtils;
@@ -286,7 +286,7 @@ private void doFailingHostNameTest(String hostName, Class<? extends EppException
286286

287287
@Test
288288
void testFailure_badCharacter() {
289-
doFailingHostNameTest("foo bar", InvalidHostNameException.class);
289+
doFailingHostNameTest("foo bar", BadHostNameCharacterException.class);
290290
}
291291

292292
@Test

core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@
5454
import google.registry.flows.ResourceFlowUtils.StatusNotClientSettableException;
5555
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
5656
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
57+
import google.registry.flows.host.HostFlowUtils.BadHostNameCharacterException;
5758
import google.registry.flows.host.HostFlowUtils.HostDomainNotOwnedException;
5859
import google.registry.flows.host.HostFlowUtils.HostNameNotLowerCaseException;
5960
import google.registry.flows.host.HostFlowUtils.HostNameNotNormalizedException;
6061
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
6162
import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
6263
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
63-
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
6464
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
6565
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
6666
import google.registry.flows.host.HostUpdateFlow.CannotAddIpToExternalHostException;
@@ -1259,7 +1259,7 @@ private void doFailingHostNameTest(String hostName, Class<? extends EppException
12591259

12601260
@Test
12611261
void testFailure_renameToBadCharacter() throws Exception {
1262-
doFailingHostNameTest("foo bar", InvalidHostNameException.class);
1262+
doFailingHostNameTest("foo bar", BadHostNameCharacterException.class);
12631263
}
12641264

12651265
@Test

core/src/test/java/google/registry/rdap/RdapNameserverActionTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ void testInvalidNameserver_returns400() {
7474
.that(generateActualJson("invalid/host/name"))
7575
.isEqualTo(
7676
generateExpectedJsonError(
77-
"invalid/host/name is not a valid nameserver: Invalid host name", 400));
77+
"invalid/host/name is not a valid nameserver: Host names can only contain a-z, 0-9,"
78+
+ " '.', '_', and '-'",
79+
400));
7880
assertThat(response.getStatus()).isEqualTo(400);
7981
}
8082

core/src/test/resources/google/registry/flows/host/host_check_50.xml

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,56 @@
33
<check>
44
<host:check
55
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
6-
<host:name>www1.tld</host:name>
7-
<host:name>www2.tld</host:name>
8-
<host:name>www3.tld</host:name>
9-
<host:name>www4.tld</host:name>
10-
<host:name>www5.tld</host:name>
11-
<host:name>www6.tld</host:name>
12-
<host:name>www7.tld</host:name>
13-
<host:name>www8.tld</host:name>
14-
<host:name>www9.tld</host:name>
15-
<host:name>www10.tld</host:name>
16-
<host:name>www11.tld</host:name>
17-
<host:name>www12.tld</host:name>
18-
<host:name>www13.tld</host:name>
19-
<host:name>www14.tld</host:name>
20-
<host:name>www15.tld</host:name>
21-
<host:name>www16.tld</host:name>
22-
<host:name>www17.tld</host:name>
23-
<host:name>www18.tld</host:name>
24-
<host:name>www19.tld</host:name>
25-
<host:name>www20.tld</host:name>
26-
<host:name>www21.tld</host:name>
27-
<host:name>www22.tld</host:name>
28-
<host:name>www23.tld</host:name>
29-
<host:name>www24.tld</host:name>
30-
<host:name>www25.tld</host:name>
31-
<host:name>www26.tld</host:name>
32-
<host:name>www27.tld</host:name>
33-
<host:name>www28.tld</host:name>
34-
<host:name>www29.tld</host:name>
35-
<host:name>www30.tld</host:name>
36-
<host:name>www31.tld</host:name>
37-
<host:name>www32.tld</host:name>
38-
<host:name>www33.tld</host:name>
39-
<host:name>www34.tld</host:name>
40-
<host:name>www35.tld</host:name>
41-
<host:name>www36.tld</host:name>
42-
<host:name>www37.tld</host:name>
43-
<host:name>www38.tld</host:name>
44-
<host:name>www39.tld</host:name>
45-
<host:name>www40.tld</host:name>
46-
<host:name>www41.tld</host:name>
47-
<host:name>www42.tld</host:name>
48-
<host:name>www43.tld</host:name>
49-
<host:name>www44.tld</host:name>
50-
<host:name>www45.tld</host:name>
51-
<host:name>www46.tld</host:name>
52-
<host:name>www47.tld</host:name>
53-
<host:name>www48.tld</host:name>
54-
<host:name>www49.tld</host:name>
55-
<host:name>www50.tld</host:name>
6+
<host:name>ns1.www1.tld</host:name>
7+
<host:name>ns1.www2.tld</host:name>
8+
<host:name>ns1.www3.tld</host:name>
9+
<host:name>ns1.www4.tld</host:name>
10+
<host:name>ns1.www5.tld</host:name>
11+
<host:name>ns1.www6.tld</host:name>
12+
<host:name>ns1.www7.tld</host:name>
13+
<host:name>ns1.www8.tld</host:name>
14+
<host:name>ns1.www9.tld</host:name>
15+
<host:name>ns1.www10.tld</host:name>
16+
<host:name>ns1.www11.tld</host:name>
17+
<host:name>ns1.www12.tld</host:name>
18+
<host:name>ns1.www13.tld</host:name>
19+
<host:name>ns1.www14.tld</host:name>
20+
<host:name>ns1.www15.tld</host:name>
21+
<host:name>ns1.www16.tld</host:name>
22+
<host:name>ns1.www17.tld</host:name>
23+
<host:name>ns1.www18.tld</host:name>
24+
<host:name>ns1.www19.tld</host:name>
25+
<host:name>ns1.www20.tld</host:name>
26+
<host:name>ns1.www21.tld</host:name>
27+
<host:name>ns1.www22.tld</host:name>
28+
<host:name>ns1.www23.tld</host:name>
29+
<host:name>ns1.www24.tld</host:name>
30+
<host:name>ns1.www25.tld</host:name>
31+
<host:name>ns1.www26.tld</host:name>
32+
<host:name>ns1.www27.tld</host:name>
33+
<host:name>ns1.www28.tld</host:name>
34+
<host:name>ns1.www29.tld</host:name>
35+
<host:name>ns1.www30.tld</host:name>
36+
<host:name>ns1.www31.tld</host:name>
37+
<host:name>ns1.www32.tld</host:name>
38+
<host:name>ns1.www33.tld</host:name>
39+
<host:name>ns1.www34.tld</host:name>
40+
<host:name>ns1.www35.tld</host:name>
41+
<host:name>ns1.www36.tld</host:name>
42+
<host:name>ns1.www37.tld</host:name>
43+
<host:name>ns1.www38.tld</host:name>
44+
<host:name>ns1.www39.tld</host:name>
45+
<host:name>ns1.www40.tld</host:name>
46+
<host:name>ns1.www41.tld</host:name>
47+
<host:name>ns1.www42.tld</host:name>
48+
<host:name>ns1.www43.tld</host:name>
49+
<host:name>ns1.www44.tld</host:name>
50+
<host:name>ns1.www45.tld</host:name>
51+
<host:name>ns1.www46.tld</host:name>
52+
<host:name>ns1.www47.tld</host:name>
53+
<host:name>ns1.www48.tld</host:name>
54+
<host:name>ns1.www49.tld</host:name>
55+
<host:name>ns1.www50.tld</host:name>
5656
</host:check>
5757
</check>
5858
<clTRID>ABC-12345</clTRID>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
2+
<command>
3+
<check>
4+
<host:check
5+
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
6+
<host:name>%HOSTNAME%</host:name>
7+
</host:check>
8+
</check>
9+
<clTRID>ABC-12345</clTRID>
10+
</command>
11+
</epp>

0 commit comments

Comments
 (0)