aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorMichal Duda <michal.duda@sonarsource.com>2020-06-26 13:53:27 +0200
committersonartech <sonartech@sonarsource.com>2020-06-30 20:05:42 +0000
commitcf787e3f6ab12481d3e75a8a8af77d1d7edc8f6f (patch)
treed9cf4eb28fe34cf528f3dc71c3d55f1ae7b4cb2b /server
parentb3840f39050b6a805c321748c84726038ccb3f6a (diff)
downloadsonarqube-cf787e3f6ab12481d3e75a8a8af77d1d7edc8f6f.tar.gz
sonarqube-cf787e3f6ab12481d3e75a8a8af77d1d7edc8f6f.zip
SONAR-13341 fix SSF-110
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DbVersion84.java2
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooks.java72
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest.java132
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest/schema.sql29
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java24
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java25
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java24
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java25
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java23
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/WebhookSupportTest.java91
11 files changed, 399 insertions, 49 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DbVersion84.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DbVersion84.java
index 490e434c402..040d8d14711 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DbVersion84.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DbVersion84.java
@@ -780,6 +780,8 @@ public class DbVersion84 implements DbVersion {
.add(3803, "Add 'need_issue_sync' column to 'project_branches' table", AddProjectBranchesNeedIssueSync.class)
.add(3804, "Populate 'need_issue_sync' of 'project_branches'", PopulateProjectBranchesNeedIssueSync.class)
.add(3805, "Make 'need_issue_sync' of 'project_branches' not null", MakeProjectBranchesNeedIssueSyncNonNull.class)
+
+ .add(3806, "Drop local webhooks", DropLocalWebhooks.class)
;
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooks.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooks.java
new file mode 100644
index 00000000000..6f6f1bc5cd8
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooks.java
@@ -0,0 +1,72 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.platform.db.migration.version.v84;
+
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.sql.SQLException;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class DropLocalWebhooks extends DataChange {
+ private static final Logger LOG = Loggers.get(DropLocalWebhooks.class);
+
+ public DropLocalWebhooks(Database db) {
+ super(db);
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select w.uuid, w.name, w.url, w.project_uuid, p.name from webhooks w left join projects p on p.uuid = w.project_uuid");
+ massUpdate.update("delete from webhooks where uuid = ?");
+ massUpdate.execute((row, update) -> {
+ try {
+ String webhookName = row.getString(2);
+ String webhookUrl = row.getString(3);
+ URL url = new URL(webhookUrl);
+ InetAddress address = InetAddress.getByName(url.getHost());
+ if (address.isLoopbackAddress() || address.isAnyLocalAddress()) {
+ boolean projectLevel = row.getString(4) != null;
+ if (projectLevel) {
+ String projectName = row.getString(5);
+ LOG.warn("Webhook '{}' for project '{}' has been removed because it used an invalid, unsafe URL. Please recreate " +
+ "this webhook with a valid URL or ask a project administrator to do it if it is still needed.", webhookName, projectName);
+ } else {
+ LOG.warn("Global webhook '{}' has been removed because it used an invalid, unsafe URL. Please recreate this webhook with a valid URL" +
+ " if it is still needed.", webhookName);
+ }
+
+ update.setString(1, row.getString(1));
+ return true;
+ }
+ } catch (MalformedURLException | UnknownHostException e) {
+ return false;
+ }
+
+ return false;
+ });
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest.java
new file mode 100644
index 00000000000..930518f22f3
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest.java
@@ -0,0 +1,132 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.platform.db.migration.version.v84;
+
+import java.sql.SQLException;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DropLocalWebhooksTest {
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private static final String TABLE_NAME = "webhooks";
+
+ @Rule
+ public CoreDbTester dbTester = CoreDbTester.createForSchema(DropLocalWebhooksTest.class, "schema.sql");
+
+ private final DataChange underTest = new DropLocalWebhooks(dbTester.database());
+
+ @Test
+ public void execute() throws SQLException {
+ prepareWebhooks();
+
+ underTest.execute();
+
+ verifyMigrationResult();
+ }
+
+ @Test
+ public void migrationIsReEntrant() throws SQLException {
+ prepareWebhooks();
+
+ underTest.execute();
+ underTest.execute();
+
+ verifyMigrationResult();
+ }
+
+ @Test
+ public void migrationIsSuccessfulWhenNoWebhooksDeleted() throws SQLException {
+ insertProject("p1", "pn1");
+ insertWebhook("uuid-1", "https://10.15.15.15:5555/some_webhook", "p1");
+ insertWebhook("uuid-5", "https://some.valid.address.com/random_webhook", null);
+
+ underTest.execute();
+
+ assertThat(dbTester.countRowsOfTable(TABLE_NAME)).isEqualTo(2);
+ assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty();
+ }
+
+ @Test
+ public void migrationIsSuccessfulWhenNoWebhooksInDb() throws SQLException {
+ insertProject("p1", "pn1");
+
+ underTest.execute();
+
+ assertThat(dbTester.countRowsOfTable(TABLE_NAME)).isZero();
+ assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty();
+ }
+
+ private void prepareWebhooks() {
+ insertProject("p1", "pn1");
+ insertProject("p2", "pn2");
+ insertWebhook("uuid-1", "https://10.15.15.15:5555/some_webhook", "p1");
+ insertWebhook("uuid-2", "https://0.0.0.0/some_webhook", "p1");
+ insertWebhook("uuid-3", "https://172.16.16.16:6666/some_webhook", "p2");
+ insertWebhook("uuid-4", "https://127.0.0.1/some_webhook", "p2");
+ insertWebhook("uuid-5", "https://some.valid.address.com/random_webhook", null);
+ insertWebhook("uuid-6", "https://248.235.76.254:7777/some_webhook", null);
+ insertWebhook("uuid-7", "https://localhost/some_webhook", null);
+ }
+
+ private void verifyMigrationResult() {
+ assertThat(dbTester.countRowsOfTable(TABLE_NAME)).isEqualTo(4);
+ assertThat(dbTester.select("select uuid from " + TABLE_NAME).stream().map(columns -> columns.get("UUID")))
+ .containsOnly("uuid-1", "uuid-3", "uuid-5", "uuid-6");
+
+ List<String> logs = logTester.logs(LoggerLevel.WARN);
+ assertThat(logs).hasSize(3);
+ assertThat(logs).containsExactlyInAnyOrder(
+ "Global webhook 'webhook-uuid-7' has been removed because it used an invalid, unsafe URL. Please recreate this webhook with a valid URL if it is still needed.",
+ "Webhook 'webhook-uuid-4' for project 'pn2' has been removed because it used an invalid, unsafe URL. Please recreate this webhook with a valid URL or ask a project administrator to do it if it is still needed.",
+ "Webhook 'webhook-uuid-2' for project 'pn1' has been removed because it used an invalid, unsafe URL. Please recreate this webhook with a valid URL or ask a project administrator to do it if it is still needed.");
+ }
+
+ private void insertProject(String uuid, String name) {
+ dbTester.executeInsert("PROJECTS",
+ "NAME", name,
+ "ORGANIZATION_UUID", "default",
+ "KEE", uuid + "-key",
+ "UUID", uuid,
+ "PRIVATE", Boolean.toString(false),
+ "QUALIFIER", "TRK",
+ "UPDATED_AT", System2.INSTANCE.now());
+ }
+
+ private void insertWebhook(String uuid, String url, @Nullable String projectUuid) {
+ dbTester.executeInsert(TABLE_NAME,
+ "UUID", uuid,
+ "NAME", "webhook-" + uuid,
+ "PROJECT_UUID", projectUuid,
+ "URL", url,
+ "CREATED_AT", System2.INSTANCE.now());
+ }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest/schema.sql
new file mode 100644
index 00000000000..de11e9e16ed
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v84/DropLocalWebhooksTest/schema.sql
@@ -0,0 +1,29 @@
+CREATE TABLE "PROJECTS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400) NOT NULL,
+ "QUALIFIER" VARCHAR(10) NOT NULL,
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "PROJECTS" ADD CONSTRAINT "PK_NEW_PROJECTS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_PROJECTS_KEE" ON "PROJECTS"("KEE");
+CREATE INDEX "IDX_QUALIFIER" ON "PROJECTS"("QUALIFIER");
+
+CREATE TABLE "WEBHOOKS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "ORGANIZATION_UUID" VARCHAR(40),
+ "PROJECT_UUID" VARCHAR(40),
+ "NAME" VARCHAR(100) NOT NULL,
+ "URL" VARCHAR(2000) NOT NULL,
+ "SECRET" VARCHAR(200),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT
+);
+ALTER TABLE "WEBHOOKS" ADD CONSTRAINT "PK_WEBHOOKS" PRIMARY KEY("UUID");
+CREATE INDEX "ORGANIZATION_WEBHOOK" ON "WEBHOOKS"("ORGANIZATION_UUID");
+CREATE INDEX "PROJECT_WEBHOOK" ON "WEBHOOKS"("PROJECT_UUID");
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
index 45d01f448f2..ec4288bc663 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
@@ -129,6 +129,7 @@ public class ProcessProperties {
SONAR_WEB_SSO_REFRESH_INTERVAL_IN_MINUTES("sonar.web.sso.refreshIntervalInMinutes", "5"),
SONAR_SECURITY_REALM("sonar.security.realm"),
SONAR_AUTHENTICATOR_IGNORE_STARTUP_FAILURE("sonar.authenticator.ignoreStartupFailure", "false"),
+ SONAR_VALIDATE_WEBHOOKS("sonar.validateWebhooks", Boolean.TRUE.toString()),
LDAP_SERVERS("ldap.servers"),
LDAP_URL("ldap.url"),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java
index a18987be054..013dbcb58e8 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java
@@ -19,6 +19,10 @@
*/
package org.sonar.server.webhook.ws;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import okhttp3.HttpUrl;
+import org.sonar.api.config.Configuration;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.exceptions.NotFoundException;
@@ -27,13 +31,16 @@ import org.sonar.server.user.UserSession;
import static java.lang.String.format;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
+import static org.sonar.process.ProcessProperties.Property.SONAR_VALIDATE_WEBHOOKS;
public class WebhookSupport {
private final UserSession userSession;
+ private final Configuration configuration;
- public WebhookSupport(UserSession userSession) {
+ public WebhookSupport(UserSession userSession, Configuration configuration) {
this.userSession = userSession;
+ this.configuration = configuration;
}
void checkPermission(ProjectDto projectDto) {
@@ -45,8 +52,19 @@ public class WebhookSupport {
}
void checkUrlPattern(String url, String message, Object... messageArguments) {
- if (okhttp3.HttpUrl.parse(url) == null) {
- throw new IllegalArgumentException(format(message, messageArguments));
+ try {
+ HttpUrl okUrl = HttpUrl.parse(url);
+ if (okUrl == null) {
+ throw new IllegalArgumentException(String.format(message, messageArguments));
+ }
+ InetAddress address = InetAddress.getByName(okUrl.host());
+ if (configuration.getBoolean(SONAR_VALIDATE_WEBHOOKS.getKey()).orElse(true)
+ && (address.isLoopbackAddress() || address.isAnyLocalAddress())) {
+ throw new IllegalArgumentException("Invalid URL");
+ }
+ } catch (UnknownHostException e) {
+ // if a host can not be resolved the deliveries will fail - no need to block it from being set
+ // this will only happen for public URLs
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
index 95004f6d9f7..80a61729171 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
@@ -22,6 +22,7 @@ package org.sonar.server.webhook.ws;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Configuration;
import org.sonar.api.server.ws.WebService;
import org.sonar.core.util.UuidFactory;
import org.sonar.core.util.UuidFactoryFast;
@@ -46,6 +47,7 @@ import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.tuple;
import static org.junit.rules.ExpectedException.none;
+import static org.mockito.Mockito.mock;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.DbTester.create;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
@@ -68,18 +70,17 @@ public class CreateActionTest {
@Rule
public DbTester db = create();
- private DbClient dbClient = db.getDbClient();
- private WebhookDbTester webhookDbTester = db.webhooks();
- private OrganizationDbTester organizationDbTester = db.organizations();
- private ComponentDbTester componentDbTester = db.components();
-
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
- private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
-
- private WebhookSupport webhookSupport = new WebhookSupport(userSession);
- private ComponentFinder componentFinder = new ComponentFinder(dbClient, null);
- private org.sonar.server.webhook.ws.CreateAction underTest = new CreateAction(dbClient, userSession, defaultOrganizationProvider, uuidFactory, webhookSupport, componentFinder);
- private WsActionTester wsActionTester = new WsActionTester(underTest);
+ private final DbClient dbClient = db.getDbClient();
+ private final WebhookDbTester webhookDbTester = db.webhooks();
+ private final OrganizationDbTester organizationDbTester = db.organizations();
+ private final ComponentDbTester componentDbTester = db.components();
+ private final DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+ private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+ private final Configuration configuration = mock(Configuration.class);
+ private final WebhookSupport webhookSupport = new WebhookSupport(userSession, configuration);
+ private final ComponentFinder componentFinder = new ComponentFinder(dbClient, null);
+ private final CreateAction underTest = new CreateAction(dbClient, userSession, defaultOrganizationProvider, uuidFactory, webhookSupport, componentFinder);
+ private final WsActionTester wsActionTester = new WsActionTester(underTest);
@Test
public void test_ws_definition() {
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java
index 435254cbd1d..ced3736ce2a 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java
@@ -23,12 +23,12 @@ import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Configuration;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDbTester;
-import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDbTester;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.project.ProjectDto;
@@ -48,6 +48,7 @@ import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.tuple;
import static org.junit.rules.ExpectedException.none;
+import static org.mockito.Mockito.mock;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.DbTester.create;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
@@ -66,19 +67,18 @@ public class DeleteActionTest {
@Rule
public DbTester db = create();
- private DbClient dbClient = db.getDbClient();
+ private final DbClient dbClient = db.getDbClient();
private final DbSession dbSession = db.getSession();
- private WebhookDbTester webhookDbTester = db.webhooks();
- private WebhookDeliveryDbTester webhookDeliveryDbTester = db.webhookDelivery();
+ private final WebhookDbTester webhookDbTester = db.webhooks();
+ private final WebhookDeliveryDbTester webhookDeliveryDbTester = db.webhookDelivery();
private final WebhookDeliveryDao deliveryDao = dbClient.webhookDeliveryDao();
- private OrganizationDbTester organizationDbTester = db.organizations();
- private ComponentDbTester componentDbTester = db.components();
-
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
-
- private WebhookSupport webhookSupport = new WebhookSupport(userSession);
- private DeleteAction underTest = new DeleteAction(dbClient, userSession, webhookSupport);
- private WsActionTester wsActionTester = new WsActionTester(underTest);
+ private final OrganizationDbTester organizationDbTester = db.organizations();
+ private final ComponentDbTester componentDbTester = db.components();
+ private final DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+ private final Configuration configuration = mock(Configuration.class);
+ private final WebhookSupport webhookSupport = new WebhookSupport(userSession, configuration);
+ private final DeleteAction underTest = new DeleteAction(dbClient, userSession, webhookSupport);
+ private final WsActionTester wsActionTester = new WsActionTester(underTest);
@Test
public void test_ws_definition() {
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
index 9c869211bab..4f05ce0fc23 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Configuration;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.db.DbClient;
@@ -49,6 +50,7 @@ import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.tuple;
import static org.junit.rules.ExpectedException.none;
+import static org.mockito.Mockito.mock;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.DbTester.create;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
@@ -73,17 +75,18 @@ public class ListActionTest {
@Rule
public DbTester db = create();
- private DbClient dbClient = db.getDbClient();
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
- private WebhookSupport webhookSupport = new WebhookSupport(userSession);
- private ComponentFinder componentFinder = new ComponentFinder(dbClient, null);
- private ListAction underTest = new ListAction(dbClient, userSession, defaultOrganizationProvider, webhookSupport, componentFinder);
-
- private ComponentDbTester componentDbTester = db.components();
- private WebhookDbTester webhookDbTester = db.webhooks();
- private WebhookDeliveryDbTester webhookDeliveryDbTester = db.webhookDelivery();
- private OrganizationDbTester organizationDbTester = db.organizations();
- private WsActionTester wsActionTester = new WsActionTester(underTest);
+ private final DbClient dbClient = db.getDbClient();
+ private final DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+ private final Configuration configuration = mock(Configuration.class);
+ private final WebhookSupport webhookSupport = new WebhookSupport(userSession, configuration);
+ private final ComponentFinder componentFinder = new ComponentFinder(dbClient, null);
+ private final ListAction underTest = new ListAction(dbClient, userSession, defaultOrganizationProvider, webhookSupport, componentFinder);
+
+ private final ComponentDbTester componentDbTester = db.components();
+ private final WebhookDbTester webhookDbTester = db.webhooks();
+ private final WebhookDeliveryDbTester webhookDeliveryDbTester = db.webhookDelivery();
+ private final OrganizationDbTester organizationDbTester = db.organizations();
+ private final WsActionTester wsActionTester = new WsActionTester(underTest);
@Test
public void definition() {
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java
index 85180cf8183..55b30756a43 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java
@@ -23,6 +23,7 @@ import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Configuration;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
@@ -45,6 +46,7 @@ import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.tuple;
import static org.junit.rules.ExpectedException.none;
+import static org.mockito.Mockito.mock;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.DbTester.create;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
@@ -63,17 +65,16 @@ public class UpdateActionTest {
@Rule
public DbTester db = create();
- private DbClient dbClient = db.getDbClient();
- private WebhookDbTester webhookDbTester = db.webhooks();
- private OrganizationDbTester organizationDbTester = db.organizations();
- private ComponentDbTester componentDbTester = db.components();
-
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
-
- private WebhookSupport webhookSupport = new WebhookSupport(userSession);
- private ComponentFinder componentFinder = new ComponentFinder(dbClient, null);
- private UpdateAction underTest = new UpdateAction(dbClient, userSession, webhookSupport, componentFinder);
- private WsActionTester wsActionTester = new WsActionTester(underTest);
+ private final DbClient dbClient = db.getDbClient();
+ private final WebhookDbTester webhookDbTester = db.webhooks();
+ private final OrganizationDbTester organizationDbTester = db.organizations();
+ private final ComponentDbTester componentDbTester = db.components();
+ private final DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+ private final Configuration configuration = mock(Configuration.class);
+ private final WebhookSupport webhookSupport = new WebhookSupport(userSession, configuration);
+ private final ComponentFinder componentFinder = new ComponentFinder(dbClient, null);
+ private final UpdateAction underTest = new UpdateAction(dbClient, userSession, webhookSupport, componentFinder);
+ private final WsActionTester wsActionTester = new WsActionTester(underTest);
@Test
public void test_ws_definition() {
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/WebhookSupportTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/WebhookSupportTest.java
new file mode 100644
index 00000000000..eb9980e98a3
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/webhook/ws/WebhookSupportTest.java
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.ws;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.config.Configuration;
+import org.sonar.server.user.UserSession;
+
+import static java.util.Optional.of;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(DataProviderRunner.class)
+public class WebhookSupportTest {
+ private final Configuration configuration = mock(Configuration.class);
+ private final WebhookSupport underTest = new WebhookSupport(mock(UserSession.class), configuration);
+
+ @DataProvider
+ public static Object[][] validUrls() {
+ return new Object[][] {
+ {"https://some.valid.address.com/random_webhook"},
+ {"https://248.235.76.254/some_webhook"},
+ {"https://248.235.76.254:8454/some_webhook"},
+
+ // local addresses are allowed too
+ {"https://192.168.0.1/some_webhook"},
+ {"https://192.168.0.1:8888/some_webhook"},
+ {"https://10.15.15.15/some_webhook"},
+ {"https://10.15.15.15:7777/some_webhook"},
+ {"https://172.16.16.16/some_webhook"},
+ {"https://172.16.16.16:9999/some_webhook"},
+ };
+ }
+
+ @DataProvider
+ public static Object[][] loopbackUrls() {
+ return new Object[][] {
+ {"https://0.0.0.0/some_webhook"},
+ {"https://0.0.0.0:8888/some_webhook"},
+ {"https://127.0.0.1/some_webhook"},
+ {"https://127.0.0.1:7777/some_webhook"},
+ {"https://localhost/some_webhook"},
+ {"https://localhost:9999/some_webhook"},
+ };
+ }
+
+ @Test
+ @UseDataProvider("validUrls")
+ public void checkUrlPatternSuccessfulForValidAddress(String url) {
+ assertThatCode(() -> underTest.checkUrlPattern(url, "msg")).doesNotThrowAnyException();
+ }
+
+ @Test
+ @UseDataProvider("loopbackUrls")
+ public void checkUrlPatternFailsForLoopbackAddress(String url) {
+ assertThatThrownBy(() -> underTest.checkUrlPattern(url, "msg"))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Invalid URL");
+ }
+
+ @Test
+ @UseDataProvider("loopbackUrls")
+ public void checkUrlPatternSuccessfulForLoopbackAddressWhenSonarValidateWebhooksPropertyDisabled(String url) {
+ when(configuration.getBoolean("sonar.validateWebhooks")).thenReturn(of(false));
+
+ assertThatCode(() -> underTest.checkUrlPattern(url, "msg")).doesNotThrowAnyException();
+ }
+}