From 4ee0f5d7f0b9225a58ff9177e4c8845afa21b834 Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Tue, 23 Dec 2025 16:04:19 -0500 Subject: [PATCH] Don't allow underscores in TLD ROID suffixes Per ICANN it's a disallowed character. --- .../src/main/java/google/registry/model/tld/Tld.java | 5 +++-- .../registry/beam/billing/InvoicingPipelineTest.java | 8 ++++---- .../registry/beam/common/RegistryJpaReadTest.java | 2 +- .../test/java/google/registry/model/tld/TldTest.java | 10 +++++++++- .../google/registry/rdap/RdapDomainActionTest.java | 2 +- .../registry/rdap/RdapDomainSearchActionTest.java | 12 ++++++------ .../java/google/registry/testing/DatabaseHelper.java | 3 +-- .../registry/tools/ConfigureTldCommandTest.java | 2 +- 8 files changed, 26 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/google/registry/model/tld/Tld.java b/core/src/main/java/google/registry/model/tld/Tld.java index 75870bdaaed..4f5dd65f7af 100644 --- a/core/src/main/java/google/registry/model/tld/Tld.java +++ b/core/src/main/java/google/registry/model/tld/Tld.java @@ -1034,12 +1034,13 @@ public Builder setEapFeeSchedule(ImmutableSortedMap eapFeeSched return this; } - public static final Pattern ROID_SUFFIX_PATTERN = Pattern.compile("^[A-Z\\d_]{1,8}$"); + public static final Pattern ROID_SUFFIX_PATTERN = Pattern.compile("^[A-Z\\d]{1,8}$"); public Builder setRoidSuffix(String roidSuffix) { checkArgument( ROID_SUFFIX_PATTERN.matcher(roidSuffix).matches(), - "ROID suffix must be in format %s", + "ROID suffix %s must be in format %s", + roidSuffix, ROID_SUFFIX_PATTERN.pattern()); getInstance().roidSuffix = roidSuffix; return this; diff --git a/core/src/test/java/google/registry/beam/billing/InvoicingPipelineTest.java b/core/src/test/java/google/registry/beam/billing/InvoicingPipelineTest.java index 6cad4a92842..07ba124b621 100644 --- a/core/src/test/java/google/registry/beam/billing/InvoicingPipelineTest.java +++ b/core/src/test/java/google/registry/beam/billing/InvoicingPipelineTest.java @@ -335,7 +335,7 @@ void testSuccess_readFromCloudSqlMissingPAK() throws Exception { .build(); persistResource(registrar); Tld test = - newTld("test", "_TEST", ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY)) + newTld("test", "TEST", ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY)) .asBuilder() .setInvoicingEnabled(true) .build(); @@ -391,7 +391,7 @@ void testSuccess_makeCloudSqlQuery() throws Exception { // Test that comments are removed from the .sql file correctly assertThat(InvoicingPipeline.makeCloudSqlQuery("2017-10")) .isEqualTo( - """ +""" SELECT b, r FROM BillingEvent b JOIN Registrar r ON b.clientId = r.registrarId @@ -449,13 +449,13 @@ private static void setupCloudSql() { persistResource(registrar3); Tld test = - newTld("test", "_TEST", ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY)) + newTld("test", "TEST", ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY)) .asBuilder() .setInvoicingEnabled(true) .build(); persistResource(test); Tld hello = - newTld("hello", "_HELLO", ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY)) + newTld("hello", "HELLO", ImmutableSortedMap.of(START_OF_TIME, GENERAL_AVAILABILITY)) .asBuilder() .setInvoicingEnabled(true) .build(); diff --git a/core/src/test/java/google/registry/beam/common/RegistryJpaReadTest.java b/core/src/test/java/google/registry/beam/common/RegistryJpaReadTest.java index d2742f1ed3f..83e65021e99 100644 --- a/core/src/test/java/google/registry/beam/common/RegistryJpaReadTest.java +++ b/core/src/test/java/google/registry/beam/common/RegistryJpaReadTest.java @@ -163,7 +163,7 @@ void readWithStringTypedQuery() { } private void setupForJoinQuery() { - Tld registry = newTld("com", "ABCD_APP"); + Tld registry = newTld("com", "ABCDAPP"); Registrar registrar = makeRegistrar1() .asBuilder() diff --git a/core/src/test/java/google/registry/model/tld/TldTest.java b/core/src/test/java/google/registry/model/tld/TldTest.java index 37a599d0bda..9e3db6e7069 100644 --- a/core/src/test/java/google/registry/model/tld/TldTest.java +++ b/core/src/test/java/google/registry/model/tld/TldTest.java @@ -751,7 +751,9 @@ void testFailure_roidSuffixTooLong() { assertThrows( IllegalArgumentException.class, () -> Tld.get("tld").asBuilder().setRoidSuffix("123456789")); - assertThat(e).hasMessageThat().isEqualTo("ROID suffix must be in format ^[A-Z\\d_]{1,8}$"); + assertThat(e) + .hasMessageThat() + .isEqualTo("ROID suffix 123456789 must be in format ^[A-Z\\d]{1,8}$"); } @Test @@ -766,6 +768,12 @@ void testFailure_roidSuffixContainsInvalidCharacters() { IllegalArgumentException.class, () -> Tld.get("tld").asBuilder().setRoidSuffix("ABC-DEF")); } + @Test + void testFailure_roidSuffixContainsUnderscores() { + assertThrows( + IllegalArgumentException.class, () -> Tld.get("tld").asBuilder().setRoidSuffix("ABC_DEF")); + } + @Test void testSuccess_setDefaultPromoTokens() { Tld registry = Tld.get("tld"); diff --git a/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java b/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java index ce78689667b..1c665bf7f7f 100644 --- a/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java +++ b/core/src/test/java/google/registry/rdap/RdapDomainActionTest.java @@ -246,7 +246,7 @@ void testMultilevelDomain_works() { .isEqualTo( addDomainBoilerplateNotices( jsonFileBuilder() - .addDomain("cat.1.tld", "D-1_TLD") + .addDomain("cat.1.tld", "D-1TLD") .addNameserver("ns1.cat.lol", "2-ROID") .addNameserver("ns2.cat.lol", "4-ROID") .addRegistrar("Multilevel Registrar") diff --git a/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java b/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java index 9b9d3fa6440..36ddf1d1db2 100644 --- a/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java +++ b/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java @@ -732,7 +732,7 @@ void testDomainMatch_cat_1_test_found() { RequestType.NAME, "cat.1.test", jsonFileBuilder() - .addDomain("cat.1.test", "1B-1_TEST") + .addDomain("cat.1.test", "1B-1TEST") .addRegistrar("1.test") .addNameserver("ns1.cat.1.test", "17-ROID") .addNameserver("ns2.cat.2.test", "19-ROID") @@ -746,7 +746,7 @@ void testDomainMatch_castar_1_test_found() { RequestType.NAME, "ca*.1.test", jsonFileBuilder() - .addDomain("cat.1.test", "1B-1_TEST") + .addDomain("cat.1.test", "1B-1TEST") .addRegistrar("1.test") .addNameserver("ns1.cat.1.test", "17-ROID") .addNameserver("ns2.cat.2.test", "19-ROID") @@ -822,7 +822,7 @@ void testDomainMatch_cat_star_found_sql() { .that(generateActualJson(RequestType.NAME, "cat.*")) .isEqualTo( jsonFileBuilder() - .addDomain("cat.1.test", "1B-1_TEST") + .addDomain("cat.1.test", "1B-1TEST") .addDomain("cat.example", "F-EXAMPLE") .addDomain("cat.lol", "6-LOL") .addDomain("cat.みんな", "15-Q9JYB4C") @@ -860,7 +860,7 @@ void testDomainMatch_catstar_found_sql() { .that(generateActualJson(RequestType.NAME, "cat*")) .isEqualTo( jsonFileBuilder() - .addDomain("cat.1.test", "1B-1_TEST") + .addDomain("cat.1.test", "1B-1TEST") .addDomain("cat.example", "F-EXAMPLE") .addDomain("cat.lol", "6-LOL") .addDomain("cat.みんな", "15-Q9JYB4C") @@ -1284,7 +1284,7 @@ void testNameserverMatch_ns1_cat_1_test_found() { RequestType.NS_LDH_NAME, "ns1.cat.1.test", jsonFileBuilder() - .addDomain("cat.1.test", "1B-1_TEST") + .addDomain("cat.1.test", "1B-1TEST") .addRegistrar("1.test") .addNameserver("ns1.cat.1.test", "17-ROID") .addNameserver("ns2.cat.2.test", "19-ROID") @@ -1298,7 +1298,7 @@ void testNameserverMatch_nsstar_cat_1_test_found() { RequestType.NS_LDH_NAME, "ns*.cat.1.test", jsonFileBuilder() - .addDomain("cat.1.test", "1B-1_TEST") + .addDomain("cat.1.test", "1B-1TEST") .addRegistrar("1.test") .addNameserver("ns1.cat.1.test", "17-ROID") .addNameserver("ns2.cat.2.test", "19-ROID") diff --git a/core/src/test/java/google/registry/testing/DatabaseHelper.java b/core/src/test/java/google/registry/testing/DatabaseHelper.java index aff711cbe63..3f8d0b27813 100644 --- a/core/src/test/java/google/registry/testing/DatabaseHelper.java +++ b/core/src/test/java/google/registry/testing/DatabaseHelper.java @@ -420,8 +420,7 @@ public static Tld createTld(String tld, TldState tldState) { public static Tld createTld(String tld, ImmutableSortedMap tldStates) { // Coerce the TLD string into a valid ROID suffix. String roidSuffix = - Ascii.toUpperCase(tld.replaceFirst(ACE_PREFIX_REGEX, "").replace('.', '_')) - .replace('-', '_'); + Ascii.toUpperCase(tld.replaceFirst(ACE_PREFIX_REGEX, "").replace(".", "")).replace("-", ""); return createTld( tld, roidSuffix.length() > 8 ? roidSuffix.substring(0, 8) : roidSuffix, tldStates); } diff --git a/core/src/test/java/google/registry/tools/ConfigureTldCommandTest.java b/core/src/test/java/google/registry/tools/ConfigureTldCommandTest.java index 8aa8b810e75..6416c846bc4 100644 --- a/core/src/test/java/google/registry/tools/ConfigureTldCommandTest.java +++ b/core/src/test/java/google/registry/tools/ConfigureTldCommandTest.java @@ -368,7 +368,7 @@ void testFailure_invalidRoidSuffix() throws Exception { "TLDSTR", name, "TLDUNICODE", name, "ROIDSUFFIX", "TLLLLLLLLLLLLLLLLLLLLLLD"))); IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> runCommandForced("--input=" + tldFile)); - assertThat(thrown.getMessage()).isEqualTo("ROID suffix must be in format ^[A-Z\\d_]{1,8}$"); + assertThat(thrown.getMessage()).isEqualTo("ROID suffix must be in format ^[A-Z\\d]{1,8}$"); } @Test