diff --git a/query/api-src/org/labkey/remoteapi/RemoteConnections.java b/query/api-src/org/labkey/remoteapi/RemoteConnections.java index e47f8f0bb15..8099413f035 100644 --- a/query/api-src/org/labkey/remoteapi/RemoteConnections.java +++ b/query/api-src/org/labkey/remoteapi/RemoteConnections.java @@ -92,9 +92,10 @@ public static boolean createOrEditRemoteConnection(RemoteConnectionForm remoteCo } // validate the url string and connection + URL urlObj; try { - URL urlObj = new URL(url); + urlObj = new URL(url); URLConnection conn = urlObj.openConnection(); conn.connect(); } @@ -155,6 +156,13 @@ public static boolean createOrEditRemoteConnection(RemoteConnectionForm remoteCo if (CONNECTION_KIND_QUERY.equals(connectionKind)) singleConnectionMap.put(RemoteConnections.FIELD_CONTAINER, folderPath); singleConnectionMap.save(); + + if (isCleartextHttpUrl(urlObj)) + { + // Log the host only — interpolating the full URL could leak credentials embedded in userinfo (e.g., http://user:pass@host/). + LOG.warn("Remote connection '{}' is configured with a cleartext http:// URL (host: {}). Credentials will be sent unencrypted. Use https:// instead.", newName, urlObj.getHost()); + } + return true; } @@ -164,6 +172,11 @@ public static String getBriefMessage(Throwable t) return t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage(); } + public static boolean isCleartextHttpUrl(@NotNull URL url) + { + return "http".equalsIgnoreCase(url.getProtocol()); + } + public static boolean deleteRemoteConnection(RemoteConnectionForm remoteConnectionForm, Container container) { String name = remoteConnectionForm.getConnectionName(); @@ -346,5 +359,14 @@ public void testGetBriefMessage() assertEquals("boom", getBriefMessage(new IOException("boom"))); assertEquals("IOException", getBriefMessage(new IOException())); } + + @Test + public void testIsCleartextHttpUrl() throws MalformedURLException + { + assertTrue("http:// must be detected as cleartext", isCleartextHttpUrl(new URL("http://example.com/labkey"))); + assertTrue("scheme match is case-insensitive", isCleartextHttpUrl(new URL("HTTP://example.com/labkey"))); + assertFalse("https:// is encrypted", isCleartextHttpUrl(new URL("https://example.com/labkey"))); + assertFalse("HTTPS:// is encrypted", isCleartextHttpUrl(new URL("HTTPS://example.com/labkey"))); + } } } diff --git a/query/src/org/labkey/query/view/createRemoteConnection.jsp b/query/src/org/labkey/query/view/createRemoteConnection.jsp index b7a1cb289c3..065046f78df 100644 --- a/query/src/org/labkey/query/view/createRemoteConnection.jsp +++ b/query/src/org/labkey/query/view/createRemoteConnection.jsp @@ -22,6 +22,8 @@ <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.query.controllers.QueryController" %> <%@ page import="org.labkey.remoteapi.RemoteConnections" %> +<%@ page import="java.net.MalformedURLException" %> +<%@ page import="java.net.URL" %> <%@ page extends="org.labkey.api.jsp.FormPage" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <% @@ -35,14 +37,32 @@ String connectionKind = remoteConnectionForm.getConnectionKind(); boolean editConnection = StringUtils.isNotEmpty(name); String nameToShow = editConnection ? name : remoteConnectionForm.getNewConnectionName(); + boolean usingCleartextHttp = false; + if (url != null) + { + try + { + usingCleartextHttp = RemoteConnections.isCleartextHttpUrl(new URL(url)); + } + catch (MalformedURLException ignored) + { + // Malformed URLs are surfaced by server-side validation in createOrEditRemoteConnection; suppress here. + } + } %>
<%=h(RemoteConnections.MANAGEMENT_PAGE_INSTRUCTIONS)%>