import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
import org.sonar.api.utils.System2;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
return mapper(dbSession).selectForProjectUuidOrderedByName(componentDto.uuid());
}
+ public void scrollAll(DbSession dbSession, Consumer<WebhookDto> consumer) {
+ mapper(dbSession).scrollAll(context -> {
+ WebhookDto webhook = context.getResultObject();
+ consumer.accept(webhook);
+ });
+ }
+
public void insert(DbSession dbSession, WebhookDto dto) {
checkState(dto.getOrganizationUuid() != null || dto.getProjectUuid() != null,
"A webhook can not be created if not linked to an organization or a project.");
import java.util.List;
import javax.annotation.CheckForNull;
import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.session.ResultHandler;
public interface WebhookMapper {
List<WebhookDto> selectForOrganizationUuidOrderedByName(@Param("organizationUuid") String organizationUuid);
+ void scrollAll(ResultHandler<WebhookDto> handler);
+
void insert(WebhookDto dto);
void update(WebhookDto dto);
</sql>
<select id="selectByUuid" parameterType="String" resultType="org.sonar.db.webhook.WebhookDto">
- select <include refid="sqlColumns" />
+ select <include refid="sqlColumns"/>
from webhooks
where uuid = #{webhookUuid,jdbcType=VARCHAR}
</select>
<select id="selectForOrganizationUuidOrderedByName" parameterType="String" resultType="org.sonar.db.webhook.WebhookDto">
- select <include refid="sqlColumns" />
+ select <include refid="sqlColumns"/>
from webhooks
where organization_uuid = #{organizationUuid,jdbcType=VARCHAR}
order by name asc
</select>
<select id="selectForProjectUuidOrderedByName" parameterType="String" resultType="org.sonar.db.webhook.WebhookDto">
- select <include refid="sqlColumns" />
+ select <include refid="sqlColumns"/>
from webhooks
where project_uuid = #{projectUuid,jdbcType=VARCHAR}
order by name asc
project_uuid = #{projectUuid,jdbcType=VARCHAR}
</delete>
+ <select id="scrollAll" resultType="org.sonar.db.webhook.WebhookDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+ select
+ <include refid="sqlColumns"/>
+ from webhooks
+ </select>
+
</mapper>
*/
package org.sonar.db.webhook;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.organization.OrganizationDto;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
public class WebhookDaoTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private System2 system2 = System2.INSTANCE;
+ private final System2 system2 = System2.INSTANCE;
private final DbClient dbClient = dbTester.getDbClient();
private final DbSession dbSession = dbTester.getSession();
underTest.insert(dbSession, dto);
}
+ @Test
+ public void scrollAll() {
+ WebhookDto w1 = new WebhookDto().setProjectUuid("p1").setName("W1").setUuid("W1").setUrl("http://abc.com/w1");
+ WebhookDto w2 = new WebhookDto().setProjectUuid("p2").setName("W2").setUuid("W2").setUrl("http://abc.com/w2");
+ WebhookDto w3 = new WebhookDto().setOrganizationUuid("432").setName("W3").setUuid("W3").setUrl("http://abc.com/w3");
+ webhookDbTester.insert(w1);
+ webhookDbTester.insert(w2);
+ webhookDbTester.insert(w3);
+ List<WebhookDto> result = new ArrayList<>();
+
+ underTest.scrollAll(dbSession, result::add);
+
+ assertThat(result).extracting(WebhookDto::getUuid, WebhookDto::getName, WebhookDto::getUrl, WebhookDto::getOrganizationUuid, WebhookDto::getProjectUuid)
+ .containsExactlyInAnyOrder(
+ tuple(w1.getUuid(), w1.getName(), w1.getUrl(), null, w1.getProjectUuid()),
+ tuple(w2.getUuid(), w2.getName(), w2.getUrl(), null, w2.getProjectUuid()),
+ tuple(w3.getUuid(), w3.getName(), w3.getUrl(), w3.getOrganizationUuid(), null));
+
+ }
+
private WebhookDto selectByUuid(String uuid) {
Optional<WebhookDto> dto = underTest.selectByUuid(dbSession, uuid);
assertThat(dto).isPresent();
import org.sonar.core.extension.CoreExtension;
import org.sonar.core.extension.ServiceLoaderWrapper;
-import static java.lang.String.format;
-
import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
/**
* Constants shared by search, web server and app processes.
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()),
SONAR_TELEMETRY_ENABLE("sonar.telemetry.enable", "true"),
SONAR_TELEMETRY_URL("sonar.telemetry.url", "https://telemetry.sonarsource.com/sonarqube"),
import org.sonar.server.rule.RegisterRules;
import org.sonar.server.rule.WebServerRuleFinder;
import org.sonar.server.startup.GeneratePluginIndex;
+import org.sonar.server.startup.DeprecatedWebhookUsageCheck;
import org.sonar.server.startup.RegisterMetrics;
import org.sonar.server.startup.RegisterPermissionTemplates;
import org.sonar.server.startup.RegisterPlugins;
BuiltInQualityProfilesUpdateListener.class,
BuiltInQProfileInsertImpl.class,
BuiltInQProfileUpdateImpl.class,
+ DeprecatedWebhookUsageCheck.class,
RegisterQualityProfiles.class,
RegisterPermissionTemplates.class,
RenameDeprecatedPropertyKeys.class);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.startup;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.function.Consumer;
+import okhttp3.HttpUrl;
+import org.picocontainer.Startable;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.webhook.WebhookDto;
+
+public class DeprecatedWebhookUsageCheck implements Startable {
+ private static final Logger LOG = Loggers.get(DeprecatedWebhookUsageCheck.class);
+ private final DbClient dbClient;
+
+ public DeprecatedWebhookUsageCheck(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public void start() {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ dbClient.webhookDao().scrollAll(dbSession, new WebhookConsumer(dbClient, dbSession).consumer);
+ }
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do here
+ }
+
+ static class WebhookConsumer {
+ private DbClient dbClient;
+ private DbSession session;
+
+ final Consumer<WebhookDto> consumer = webhookDto -> {
+ HttpUrl url = HttpUrl.parse(webhookDto.getUrl());
+ if (url != null) {
+ try {
+ InetAddress address = InetAddress.getByName(url.host());
+ if (address.isLoopbackAddress() || address.isAnyLocalAddress()) {
+ if (webhookDto.getProjectUuid() != null) {
+ ComponentDto project = dbClient.componentDao().selectOrFailByUuid(session, webhookDto.getProjectUuid());
+ LOG.warn("Webhook '{}' for project '{}' uses an invalid, unsafe URL and will be automatically removed in a " +
+ "future version of SonarQube. You should update the URL of that webhook or ask a project administrator to do it.",
+ webhookDto.getName(), project.name());
+ } else {
+ LOG.warn("Global webhook '{}' uses an invalid, unsafe URL and will be automatically removed in a future version of SonarQube. " +
+ "You should update the URL of that webhook.", webhookDto.getName());
+ }
+ }
+ } catch (UnknownHostException e) {
+ // do nothing
+ }
+ }
+ };
+
+ WebhookConsumer(DbClient dbClient, DbSession session) {
+ this.dbClient = dbClient;
+ this.session = session;
+ }
+ }
+}
*/
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.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.exceptions.NotFoundException;
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(ComponentDto componentDto) {
}
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
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.startup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDao;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.webhook.WebhookDao;
+import org.sonar.db.webhook.WebhookDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DeprecatedWebhookUsageCheckTest {
+
+ @Rule
+ public LogTester logTester = new LogTester();
+
+ private final WebhookDao webhookDao = mock(WebhookDao.class);
+ private final ComponentDao componentDao = mock(ComponentDao.class);
+ private final DbClient dbClient = mock(DbClient.class);
+ private final DeprecatedWebhookUsageCheck underTest = new DeprecatedWebhookUsageCheck(dbClient);
+
+ @Before
+ public void setup() {
+ when(dbClient.webhookDao()).thenReturn(webhookDao);
+ when(dbClient.componentDao()).thenReturn(componentDao);
+ when(componentDao.selectOrFailByUuid(any(), any())).thenReturn(new ComponentDto().setName("ProjectA"));
+ }
+
+ @Test
+ public void start() {
+ underTest.start();
+
+ verify(webhookDao, times(1)).scrollAll(any(), any());
+ }
+
+ @Test
+ public void stop() {
+ underTest.start();
+ underTest.stop();
+
+ verify(webhookDao, times(1)).scrollAll(any(), any());
+ }
+
+ @Test
+ public void logsWarningWhenDeprecatedAddressIsUsedInProjectLevelWebhook() {
+ DeprecatedWebhookUsageCheck.WebhookConsumer handler = new DeprecatedWebhookUsageCheck.WebhookConsumer(dbClient, mock(DbSession.class));
+
+ handler.consumer.accept(new WebhookDto().setName("W1").setProjectUuid("u1").setUrl("http://127.0.0.1/webhook"));
+
+ assertThat(logTester.logs()).hasSize(1);
+ assertThat(logTester.logs(LoggerLevel.WARN)).hasSize(1);
+ assertThat(logTester.logs(LoggerLevel.WARN))
+ .containsExactlyInAnyOrder("Webhook 'W1' for project 'ProjectA' uses an invalid, unsafe URL and will be automatically removed in a " +
+ "future version of SonarQube. You should update the URL of that webhook or ask a project administrator to do it.");
+ }
+
+ @Test
+ public void logsWarningWhenDeprecatedAddressIsUsedInGlobalLevelWebhook() {
+ DeprecatedWebhookUsageCheck.WebhookConsumer handler = new DeprecatedWebhookUsageCheck.WebhookConsumer(dbClient, mock(DbSession.class));
+
+ handler.consumer.accept(new WebhookDto().setName("W1").setOrganizationUuid("org1").setUrl("http://127.0.0.1/webhook"));
+
+ assertThat(logTester.logs()).hasSize(1);
+ assertThat(logTester.logs(LoggerLevel.WARN)).hasSize(1);
+ assertThat(logTester.logs(LoggerLevel.WARN))
+ .containsExactlyInAnyOrder("Global webhook 'W1' uses an invalid, unsafe URL and will be automatically removed in a future version of SonarQube. " +
+ "You should update the URL of that webhook.");
+ }
+
+ @Test
+ public void doesNotLogAnythingWhenWebhookHasNonDeprecatedAddress() {
+ DeprecatedWebhookUsageCheck.WebhookConsumer handler = new DeprecatedWebhookUsageCheck.WebhookConsumer(dbClient, mock(DbSession.class));
+
+ handler.consumer.accept(new WebhookDto().setName("W1").setProjectUuid("u1").setUrl("http://abc.com/webhook"));
+
+ assertThat(logTester.logs()).isEmpty();
+ }
+
+ @Test
+ public void doesNotLogAnythingWhenUnknownHost() {
+ DeprecatedWebhookUsageCheck.WebhookConsumer handler = new DeprecatedWebhookUsageCheck.WebhookConsumer(dbClient, mock(DbSession.class));
+
+ handler.consumer.accept(new WebhookDto().setName("W1").setProjectUuid("u1").setUrl("http://nice/webhook"));
+
+ assertThat(logTester.logs()).isEmpty();
+ }
+}
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;
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;
@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 org.sonar.server.webhook.ws.CreateAction underTest = new CreateAction(dbClient, userSession, defaultOrganizationProvider, uuidFactory, webhookSupport);
- 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 CreateAction underTest = new CreateAction(dbClient, userSession, defaultOrganizationProvider, uuidFactory, webhookSupport);
+ private final WsActionTester wsActionTester = new WsActionTester(underTest);
@Test
public void test_ws_definition() {
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 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;
@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() {
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;
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;
@Rule
public DbTester db = create();
- private DbClient dbClient = db.getDbClient();
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
- private WebhookSupport webhookSupport = new WebhookSupport(userSession);
- private ListAction underTest = new ListAction(dbClient, userSession, defaultOrganizationProvider, webhookSupport);
-
- 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 ListAction underTest = new ListAction(dbClient, userSession, defaultOrganizationProvider, webhookSupport);
+ 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() {
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;
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;
@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 UpdateAction underTest = new UpdateAction(dbClient, userSession, webhookSupport);
- 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 UpdateAction underTest = new UpdateAction(dbClient, userSession, webhookSupport);
+ private final WsActionTester wsActionTester = new WsActionTester(underTest);
@Test
public void test_ws_definition() {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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();
+ }
+}