aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server-common
diff options
context:
space:
mode:
authorBelen Pruvost <belen.pruvost@sonarsource.com>2021-04-14 15:31:51 +0200
committersonartech <sonartech@sonarsource.com>2021-04-14 20:03:29 +0000
commit2ac0f065bea21dda018d33e3b39ea9a4291da0ce (patch)
treef740f44cdc88981c03056faeb7c688af45f81288 /server/sonar-server-common
parent0e3231774e88bf7c76855c13d952deeab7351f1d (diff)
downloadsonarqube-2ac0f065bea21dda018d33e3b39ea9a4291da0ce.tar.gz
sonarqube-2ac0f065bea21dda018d33e3b39ea9a4291da0ce.zip
SONAR-14682 - Filtering local network interfaces on webhooks
Diffstat (limited to 'server/sonar-server-common')
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/webhook/NetworkInterfaceProvider.java41
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookCustomDns.java16
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookModule.java1
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/NetworkInterfaceProviderTest.java37
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java28
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCustomDnsTest.java35
6 files changed, 152 insertions, 6 deletions
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/NetworkInterfaceProvider.java b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/NetworkInterfaceProvider.java
new file mode 100644
index 00000000000..a501809e680
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/NetworkInterfaceProvider.java
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+@ComputeEngineSide
+public class NetworkInterfaceProvider {
+
+ public List<InetAddress> getNetworkInterfaceAddresses() throws SocketException {
+ return Collections.list(NetworkInterface.getNetworkInterfaces())
+ .stream()
+ .flatMap(ni -> Collections.list(ni.getInetAddresses()).stream())
+ .collect(Collectors.toList());
+ }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookCustomDns.java b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookCustomDns.java
index 688cfdb9bd0..71c4448c747 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookCustomDns.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookCustomDns.java
@@ -20,6 +20,7 @@
package org.sonar.server.webhook;
import java.net.InetAddress;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
@@ -36,9 +37,11 @@ import static org.sonar.process.ProcessProperties.Property.SONAR_VALIDATE_WEBHOO
public class WebhookCustomDns implements Dns {
private final Configuration configuration;
+ private final NetworkInterfaceProvider networkInterfaceProvider;
- public WebhookCustomDns(Configuration configuration) {
+ public WebhookCustomDns(Configuration configuration, NetworkInterfaceProvider networkInterfaceProvider) {
this.configuration = configuration;
+ this.networkInterfaceProvider = networkInterfaceProvider;
}
@NotNull
@@ -46,10 +49,19 @@ public class WebhookCustomDns implements Dns {
public List<InetAddress> lookup(@NotNull String host) throws UnknownHostException {
InetAddress address = InetAddress.getByName(host);
if (configuration.getBoolean(SONAR_VALIDATE_WEBHOOKS.getKey()).orElse(true)
- && (address.isLoopbackAddress() || address.isAnyLocalAddress())) {
+ && (address.isLoopbackAddress() || address.isAnyLocalAddress() || isLocalAddress(address))) {
throw new IllegalArgumentException("Invalid URL: loopback and wildcard addresses are not allowed for webhooks.");
}
return Collections.singletonList(address);
}
+ private boolean isLocalAddress(InetAddress address) {
+ try {
+ return networkInterfaceProvider.getNetworkInterfaceAddresses().stream()
+ .anyMatch(a -> a != null && a.equals(address));
+ } catch (SocketException e) {
+ throw new IllegalArgumentException("Network interfaces could not be fetched.");
+ }
+ }
+
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookModule.java b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookModule.java
index 3a515608a06..97f178eaaaf 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookModule.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookModule.java
@@ -25,6 +25,7 @@ public class WebhookModule extends Module {
@Override
protected void configureModule() {
add(
+ NetworkInterfaceProvider.class,
WebhookCustomDns.class,
WebhookCallerImpl.class,
WebhookDeliveryStorage.class,
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/NetworkInterfaceProviderTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/NetworkInterfaceProviderTest.java
new file mode 100644
index 00000000000..be213d0bab5
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/NetworkInterfaceProviderTest.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.webhook;
+
+import java.net.SocketException;
+import java.util.List;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NetworkInterfaceProviderTest {
+ private NetworkInterfaceProvider underTest = new NetworkInterfaceProvider();
+
+ @Test
+ public void itGetsListOfNetworkInterfaceAddresses() throws SocketException {
+ assertThat(underTest.getNetworkInterfaceAddresses())
+ .isInstanceOf(List.class)
+ .hasSizeGreaterThan(0);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
index be4060c8779..885ecae43a6 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
@@ -19,6 +19,8 @@
*/
package org.sonar.server.webhook;
+import com.google.common.collect.ImmutableList;
+import java.net.InetAddress;
import java.util.Optional;
import okhttp3.Credentials;
import okhttp3.HttpUrl;
@@ -63,6 +65,7 @@ public class WebhookCallerImplTest {
public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60));
Configuration configuration = Mockito.mock(Configuration.class);
+ NetworkInterfaceProvider networkInterfaceProvider = Mockito.mock(NetworkInterfaceProvider.class);
private System2 system = new TestSystem2().setNow(NOW);
@@ -248,6 +251,29 @@ public class WebhookCallerImplTest {
assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
}
+ @Test
+ public void silently_catch_error_when_url_is_local_network_interface() throws Exception {
+ String url = "https://192.168.1.21";
+
+ InetAddress inetAddress = InetAddress.getByName(HttpUrl.parse(url).host());
+
+ when(networkInterfaceProvider.getNetworkInterfaceAddresses())
+ .thenReturn(ImmutableList.of(inetAddress));
+
+ Webhook webhook = new Webhook(WEBHOOK_UUID, PROJECT_UUID, CE_TASK_UUID,
+ randomAlphanumeric(40), "my-webhook", url, null);
+
+ WebhookDelivery delivery = newSender(true).call(webhook, PAYLOAD);
+
+ assertThat(delivery.getHttpStatus()).isEmpty();
+ assertThat(delivery.getDurationInMs().get()).isNotNegative();
+ assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class);
+ assertThat(delivery.getErrorMessage()).contains("Invalid URL: loopback and wildcard addresses are not allowed for webhooks.");
+ assertThat(delivery.getAt()).isEqualTo(NOW);
+ assertThat(delivery.getWebhook()).isSameAs(webhook);
+ assertThat(delivery.getPayload()).isSameAs(PAYLOAD);
+ }
+
private RecordedRequest takeAndVerifyPostRequest(String expectedPath) throws Exception {
RecordedRequest request = server.takeRequest();
@@ -260,7 +286,7 @@ public class WebhookCallerImplTest {
private WebhookCaller newSender(boolean validateWebhook) {
SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER, SonarEdition.COMMUNITY);
when(configuration.getBoolean(SONAR_VALIDATE_WEBHOOKS.getKey())).thenReturn(Optional.of(validateWebhook));
- WebhookCustomDns webhookCustomDns = new WebhookCustomDns(configuration);
+ WebhookCustomDns webhookCustomDns = new WebhookCustomDns(configuration, networkInterfaceProvider);
return new WebhookCallerImpl(system, new OkHttpClientProvider().provide(new MapSettings().asConfig(), runtime), webhookCustomDns);
}
}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCustomDnsTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCustomDnsTest.java
index af468b40f36..8c6906b9490 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCustomDnsTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookCustomDnsTest.java
@@ -19,9 +19,12 @@
*/
package org.sonar.server.webhook;
+import com.google.common.collect.ImmutableList;
import java.net.InetAddress;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Optional;
+import okhttp3.HttpUrl;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.mockito.Mockito;
@@ -31,16 +34,19 @@ import static org.mockito.Mockito.when;
import static org.sonar.process.ProcessProperties.Property.SONAR_VALIDATE_WEBHOOKS;
public class WebhookCustomDnsTest {
+ private static final String INVALID_URL = "Invalid URL: loopback and wildcard addresses are not allowed for webhooks.";
private Configuration configuration = Mockito.mock(Configuration.class);
- private WebhookCustomDns underTest = new WebhookCustomDns(configuration);
+ private NetworkInterfaceProvider networkInterfaceProvider = Mockito.mock(NetworkInterfaceProvider.class);
+
+ private WebhookCustomDns underTest = new WebhookCustomDns(configuration, networkInterfaceProvider);
@Test
public void lookup_fail_on_localhost() {
when(configuration.getBoolean(SONAR_VALIDATE_WEBHOOKS.getKey())).thenReturn(Optional.of(true));
Assertions.assertThatThrownBy(() -> underTest.lookup("localhost"))
- .hasMessageContaining("")
+ .hasMessageContaining(INVALID_URL)
.isInstanceOf(IllegalArgumentException.class);
}
@@ -49,7 +55,30 @@ public class WebhookCustomDnsTest {
when(configuration.getBoolean(SONAR_VALIDATE_WEBHOOKS.getKey())).thenReturn(Optional.of(true));
Assertions.assertThatThrownBy(() -> underTest.lookup("127.0.0.1"))
- .hasMessageContaining("")
+ .hasMessageContaining(INVALID_URL)
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ public void lookup_fail_on_192_168_1_21() throws UnknownHostException, SocketException {
+ InetAddress inetAddress = InetAddress.getByName(HttpUrl.parse("https://192.168.1.21/").host());
+
+ when(configuration.getBoolean(SONAR_VALIDATE_WEBHOOKS.getKey())).thenReturn(Optional.of(true));
+ when(networkInterfaceProvider.getNetworkInterfaceAddresses())
+ .thenReturn(ImmutableList.of(inetAddress));
+
+ Assertions.assertThatThrownBy(() -> underTest.lookup("192.168.1.21"))
+ .hasMessageContaining(INVALID_URL)
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ public void lookup_fail_on_network_interface_throwing_socket_exception() throws SocketException {
+ when(networkInterfaceProvider.getNetworkInterfaceAddresses())
+ .thenThrow(new SocketException());
+
+ Assertions.assertThatThrownBy(() -> underTest.lookup("good-url.com"))
+ .hasMessageContaining("Network interfaces could not be fetched.")
.isInstanceOf(IllegalArgumentException.class);
}