From a281cd1d03497f96df45b4107f7c7edeb110ff22 Mon Sep 17 00:00:00 2001 From: Gus Brodman Date: Mon, 29 Jun 2026 13:52:42 -0400 Subject: [PATCH] Verify that marksdb verify URL starts with ry.marksdb.org --- .../registry/tmch/NordnVerifyAction.java | 10 +++ .../java/google/registry/tmch/TmchModule.java | 3 +- ...UpdateRegistrarRdapBaseUrlsActionTest.java | 5 +- .../icann/IcannHttpReporterTest.java | 6 +- .../registry/tmch/NordnUploadActionTest.java | 4 +- .../registry/tmch/NordnVerifyActionTest.java | 63 ++++++++++++------- .../registry/tmch/TmchCrlActionTest.java | 6 +- .../webdriver/DockerWebDriverExtension.java | 12 ++-- 8 files changed, 72 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/google/registry/tmch/NordnVerifyAction.java b/core/src/main/java/google/registry/tmch/NordnVerifyAction.java index 2f701d6ad37..c2e22148e18 100644 --- a/core/src/main/java/google/registry/tmch/NordnVerifyAction.java +++ b/core/src/main/java/google/registry/tmch/NordnVerifyAction.java @@ -14,12 +14,14 @@ package google.registry.tmch; +import static com.google.common.base.Preconditions.checkArgument; import static google.registry.request.UrlConnectionUtils.getResponseBytes; import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT; import static jakarta.servlet.http.HttpServletResponse.SC_OK; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Ascii; import com.google.common.flogger.FluentLogger; import com.google.common.io.ByteSource; import google.registry.request.Action; @@ -62,6 +64,8 @@ public final class NordnVerifyAction implements Runnable { static final String NORDN_URL_PARAM = "nordnUrl"; static final String NORDN_LOG_ID_PARAM = "nordnLogId"; + private static final String MARKSDB_URL_BEGINNING = "ry.marksdb.org"; + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); @Inject LordnRequestInitializer lordnRequestInitializer; @@ -104,6 +108,12 @@ public void run() { */ @VisibleForTesting LordnLog verify() throws IOException, GeneralSecurityException { + String host = Ascii.toLowerCase(url.getHost()); + checkArgument( + host.startsWith(MARKSDB_URL_BEGINNING), + "URL %s must start with %s", + url, + MARKSDB_URL_BEGINNING); logger.atInfo().log("LORDN verify task %s: Sending request to URL %s", actionLogId, url); HttpURLConnection connection = urlConnectionService.createConnection(url); lordnRequestInitializer.initialize(connection, tld); diff --git a/core/src/main/java/google/registry/tmch/TmchModule.java b/core/src/main/java/google/registry/tmch/TmchModule.java index 29926ced979..3f18d377bc1 100644 --- a/core/src/main/java/google/registry/tmch/TmchModule.java +++ b/core/src/main/java/google/registry/tmch/TmchModule.java @@ -25,6 +25,7 @@ import google.registry.request.Parameter; import jakarta.servlet.http.HttpServletRequest; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import org.bouncycastle.openpgp.PGPPublicKey; @@ -53,7 +54,7 @@ static String provideLordnPhase(HttpServletRequest req) { @Parameter(NordnVerifyAction.NORDN_URL_PARAM) static URL provideNordnUrl(HttpServletRequest req) { try { - return new URL(extractRequiredParameter(req, NordnVerifyAction.NORDN_URL_PARAM)); + return URI.create(extractRequiredParameter(req, NordnVerifyAction.NORDN_URL_PARAM)).toURL(); } catch (MalformedURLException e) { throw new BadRequestException("Bad URL: " + NordnVerifyAction.NORDN_URL_PARAM); } diff --git a/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java b/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java index feb98553830..eddbd0f2e87 100644 --- a/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java +++ b/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java @@ -37,7 +37,7 @@ import google.registry.util.UrlConnectionException; import java.io.ByteArrayInputStream; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -88,7 +88,8 @@ void beforeEach() throws Exception { private void assertCorrectRequestSent() throws Exception { assertThat(urlConnectionService.getConnectedUrls()) .containsExactly( - new URL("https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv")); + URI.create("https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv") + .toURL()); verify(connection).setRequestProperty("Accept-Encoding", "gzip"); } diff --git a/core/src/test/java/google/registry/reporting/icann/IcannHttpReporterTest.java b/core/src/test/java/google/registry/reporting/icann/IcannHttpReporterTest.java index d7f79c69bb8..042f3069ce7 100644 --- a/core/src/test/java/google/registry/reporting/icann/IcannHttpReporterTest.java +++ b/core/src/test/java/google/registry/reporting/icann/IcannHttpReporterTest.java @@ -34,7 +34,7 @@ import google.registry.testing.FakeUrlConnectionService; import java.io.ByteArrayOutputStream; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -74,7 +74,7 @@ void testSuccess() throws Exception { assertThat(reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv")).isTrue(); assertThat(urlConnectionService.getConnectedUrls()) - .containsExactly(new URL("https://fake-transactions.url/test/2017-06")); + .containsExactly(URI.create("https://fake-transactions.url/test/2017-06").toURL()); String userPass = "test_ry:fakePass"; String expectedAuth = String.format("Basic %s", BaseEncoding.base64().encode(StringUtils.getBytesUtf8(userPass))); @@ -88,7 +88,7 @@ void testSuccess_internationalTld() throws Exception { assertThat(reporter.send(FAKE_PAYLOAD, "xn--abc123-transactions-201706.csv")).isTrue(); assertThat(urlConnectionService.getConnectedUrls()) - .containsExactly(new URL("https://fake-transactions.url/xn--abc123/2017-06")); + .containsExactly(URI.create("https://fake-transactions.url/xn--abc123/2017-06").toURL()); String userPass = "xn--abc123_ry:fakePass"; String expectedAuth = String.format("Basic %s", BaseEncoding.base64().encode(StringUtils.getBytesUtf8(userPass))); diff --git a/core/src/test/java/google/registry/tmch/NordnUploadActionTest.java b/core/src/test/java/google/registry/tmch/NordnUploadActionTest.java index 9f387db3934..f86ec5971ef 100644 --- a/core/src/test/java/google/registry/tmch/NordnUploadActionTest.java +++ b/core/src/test/java/google/registry/tmch/NordnUploadActionTest.java @@ -57,7 +57,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import java.security.SecureRandom; import java.time.Duration; import java.time.Instant; @@ -249,7 +249,7 @@ private void testRun(String phase, String domain1, String domain2, String csv) t .setRequestProperty(eq(CONTENT_TYPE), startsWith("multipart/form-data; boundary=")); verify(httpUrlConnection).setRequestMethod("POST"); assertThat(httpUrlConnection.getURL()) - .isEqualTo(new URL("http://127.0.0.1/LORDN/tld/" + phase)); + .isEqualTo(URI.create("http://127.0.0.1/LORDN/tld/" + phase).toURL()); assertThat(connectionOutputStream.toString(UTF_8)).contains(csv); verifyColumnCleared(domain1); verifyColumnCleared(domain2); diff --git a/core/src/test/java/google/registry/tmch/NordnVerifyActionTest.java b/core/src/test/java/google/registry/tmch/NordnVerifyActionTest.java index 7545fd7a4e5..3dc20efa840 100644 --- a/core/src/test/java/google/registry/tmch/NordnVerifyActionTest.java +++ b/core/src/test/java/google/registry/tmch/NordnVerifyActionTest.java @@ -38,7 +38,7 @@ import google.registry.testing.FakeUrlConnectionService; import java.io.ByteArrayInputStream; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,33 +49,35 @@ class NordnVerifyActionTest { private static final String LOG_ACCEPTED = """ - 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,accepted,no-warnings,1 - roid,result-code - SH8013-REP,2000"""; + 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,accepted,no-warnings,1 + roid,result-code + SH8013-REP,2000\ + """; private static final String LOG_REJECTED = """ - 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,rejected,no-warnings,1 - roid,result-code - SH8013-REP,2001"""; + 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,rejected,no-warnings,1 + roid,result-code + SH8013-REP,2001\ + """; private static final String LOG_WARNINGS = """ - 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,accepted,warnings-present,3 - roid,result-code - SH8013-REP,2001 - lulz-roid,3609 - sabokitty-roid,3610 - """; + 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,accepted,warnings-present,3 + roid,result-code + SH8013-REP,2001 + lulz-roid,3609 + sabokitty-roid,3610 + """; private static final String LOG_ERRORS = """ - 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,accepted,warnings-present,3 - roid,result-code - SH8013-REP,2000 - lulz-roid,4601 - bogpog,4611 - """; + 1,2012-08-16T02:15:00.0Z,2012-08-16T00:00:00.0Z,0000000000000478Nzs+3VMkR8ckuUynOLmyeqTmZQSbzDuf/R50n2n5QX4=,accepted,warnings-present,3 + roid,result-code + SH8013-REP,2000 + lulz-roid,4601 + bogpog,4611 + """; @RegisterExtension final JpaIntegrationTestExtension jpa = @@ -101,14 +103,15 @@ void beforeEach() throws Exception { .thenReturn(new ByteArrayInputStream(LOG_ACCEPTED.getBytes(UTF_8))); action.lordnRequestInitializer = lordnRequestInitializer; action.response = response; - action.url = new URL("http://127.0.0.1/blobio"); + action.url = URI.create("http://ry.marksdb.org/blobio").toURL(); } @Test @SuppressWarnings("DirectInvocationOnMock") void testSuccess_sendHttpRequest_urlIsCorrect() throws Exception { action.run(); - assertThat(httpUrlConnection.getURL()).isEqualTo(new URL("http://127.0.0.1/blobio")); + assertThat(httpUrlConnection.getURL()) + .isEqualTo(URI.create("http://ry.marksdb.org/blobio").toURL()); } @Test @@ -162,4 +165,22 @@ void failureVerifyNotReady() throws Exception { ConflictException thrown = assertThrows(ConflictException.class, action::run); assertThat(thrown).hasMessageThat().contains("Not ready"); } + + @Test + void testFailure_badUrl() throws Exception { + action.url = URI.create("http://example.com/blobio").toURL(); + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, action::run); + assertThat(thrown) + .hasMessageThat() + .isEqualTo("URL http://example.com/blobio must start with ry.marksdb.org"); + } + + @Test + @SuppressWarnings("DirectInvocationOnMock") + void testSuccess_uppercaseUrl() throws Exception { + action.url = URI.create("http://RY.MARKSDB.ORG/blobio").toURL(); + action.run(); + assertThat(httpUrlConnection.getURL()) + .isEqualTo(URI.create("http://RY.MARKSDB.ORG/blobio").toURL()); + } } diff --git a/core/src/test/java/google/registry/tmch/TmchCrlActionTest.java b/core/src/test/java/google/registry/tmch/TmchCrlActionTest.java index 6966f0acd6c..c86ce54c7ce 100644 --- a/core/src/test/java/google/registry/tmch/TmchCrlActionTest.java +++ b/core/src/test/java/google/registry/tmch/TmchCrlActionTest.java @@ -24,7 +24,7 @@ import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode; import java.io.ByteArrayInputStream; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; import java.security.SignatureException; import java.security.cert.CRLException; import java.security.cert.CertificateNotYetValidException; @@ -39,7 +39,7 @@ private TmchCrlAction newTmchCrlAction(TmchCaMode tmchCaMode) throws MalformedUR TmchCrlAction action = new TmchCrlAction(); action.marksdb = marksdb; action.tmchCertificateAuthority = new TmchCertificateAuthority(tmchCaMode, clock); - action.tmchCrlUrl = new URL("https://sloth.lol/tmch.crl"); + action.tmchCrlUrl = URI.create("https://sloth.lol/tmch.crl").toURL(); return action; } @@ -57,7 +57,7 @@ void testSuccess() throws Exception { newTmchCrlAction(TmchCaMode.PILOT).run(); verify(httpUrlConnection).getInputStream(); assertThat(urlConnectionService.getConnectedUrls()) - .containsExactly(new URL("https://sloth.lol/tmch.crl")); + .containsExactly(URI.create("https://sloth.lol/tmch.crl").toURL()); } @Test diff --git a/core/src/test/java/google/registry/webdriver/DockerWebDriverExtension.java b/core/src/test/java/google/registry/webdriver/DockerWebDriverExtension.java index 9e48bb76689..187bc6cb78c 100644 --- a/core/src/test/java/google/registry/webdriver/DockerWebDriverExtension.java +++ b/core/src/test/java/google/registry/webdriver/DockerWebDriverExtension.java @@ -18,6 +18,7 @@ import google.registry.util.UrlChecker; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.time.Duration; import java.time.temporal.ChronoUnit; @@ -55,11 +56,12 @@ private static URL getWebDriverUrl() { URL url; try { url = - new URL( - String.format( - "http://%s:%d", - container.getContainerIpAddress(), - container.getMappedPort(CHROME_DRIVER_SERVICE_PORT))); + URI.create( + String.format( + "http://%s:%d", + container.getContainerIpAddress(), + container.getMappedPort(CHROME_DRIVER_SERVICE_PORT))) + .toURL(); } catch (MalformedURLException e) { throw new IllegalArgumentException(e); }