You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SearchActionTest.java 89KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.hotspot.ws;
  21. import com.google.common.collect.Ordering;
  22. import com.google.common.collect.Sets;
  23. import com.tngtech.java.junit.dataprovider.DataProvider;
  24. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  25. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  26. import java.util.Arrays;
  27. import java.util.Collection;
  28. import java.util.Collections;
  29. import java.util.Comparator;
  30. import java.util.List;
  31. import java.util.Map;
  32. import java.util.Random;
  33. import java.util.Set;
  34. import java.util.function.Consumer;
  35. import java.util.stream.IntStream;
  36. import java.util.stream.Stream;
  37. import javax.annotation.Nullable;
  38. import org.junit.Rule;
  39. import org.junit.Test;
  40. import org.junit.runner.RunWith;
  41. import org.sonar.api.impl.utils.TestSystem2;
  42. import org.sonar.api.issue.Issue;
  43. import org.sonar.api.rule.RuleKey;
  44. import org.sonar.api.rules.RuleType;
  45. import org.sonar.api.server.ws.WebService;
  46. import org.sonar.api.utils.System2;
  47. import org.sonar.db.DbClient;
  48. import org.sonar.db.DbTester;
  49. import org.sonar.db.component.BranchDto;
  50. import org.sonar.db.component.BranchType;
  51. import org.sonar.db.component.ComponentDto;
  52. import org.sonar.db.component.ComponentTesting;
  53. import org.sonar.db.issue.IssueDto;
  54. import org.sonar.db.project.ProjectDto;
  55. import org.sonar.db.protobuf.DbCommons;
  56. import org.sonar.db.protobuf.DbIssues;
  57. import org.sonar.db.rule.RuleDto;
  58. import org.sonar.db.rule.RuleTesting;
  59. import org.sonar.server.es.EsTester;
  60. import org.sonar.server.exceptions.ForbiddenException;
  61. import org.sonar.server.exceptions.NotFoundException;
  62. import org.sonar.server.issue.TextRangeResponseFormatter;
  63. import org.sonar.server.issue.index.AsyncIssueIndexing;
  64. import org.sonar.server.issue.index.IssueIndex;
  65. import org.sonar.server.issue.index.IssueIndexSyncProgressChecker;
  66. import org.sonar.server.issue.index.IssueIndexer;
  67. import org.sonar.server.issue.index.IssueIteratorFactory;
  68. import org.sonar.server.permission.index.PermissionIndexer;
  69. import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
  70. import org.sonar.server.security.SecurityStandards;
  71. import org.sonar.server.security.SecurityStandards.SQCategory;
  72. import org.sonar.server.tester.UserSessionRule;
  73. import org.sonar.server.view.index.ViewIndexer;
  74. import org.sonar.server.ws.TestRequest;
  75. import org.sonar.server.ws.WsActionTester;
  76. import org.sonarqube.ws.Common;
  77. import org.sonarqube.ws.Hotspots;
  78. import org.sonarqube.ws.Hotspots.Component;
  79. import org.sonarqube.ws.Hotspots.SearchWsResponse;
  80. import static com.google.common.collect.ImmutableSet.of;
  81. import static java.util.Collections.singleton;
  82. import static java.util.stream.Collectors.joining;
  83. import static java.util.stream.Collectors.toList;
  84. import static java.util.stream.Collectors.toSet;
  85. import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
  86. import static org.assertj.core.api.Assertions.assertThat;
  87. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  88. import static org.assertj.core.groups.Tuple.tuple;
  89. import static org.mockito.ArgumentMatchers.any;
  90. import static org.mockito.ArgumentMatchers.eq;
  91. import static org.mockito.Mockito.mock;
  92. import static org.mockito.Mockito.times;
  93. import static org.mockito.Mockito.verify;
  94. import static org.sonar.api.issue.Issue.RESOLUTION_ACKNOWLEDGED;
  95. import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
  96. import static org.sonar.api.issue.Issue.RESOLUTION_SAFE;
  97. import static org.sonar.api.issue.Issue.STATUSES;
  98. import static org.sonar.api.issue.Issue.STATUS_CLOSED;
  99. import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
  100. import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
  101. import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
  102. import static org.sonar.api.utils.DateUtils.formatDateTime;
  103. import static org.sonar.api.web.UserRole.USER;
  104. import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
  105. import static org.sonar.db.component.ComponentTesting.newDirectory;
  106. import static org.sonar.db.component.ComponentTesting.newFileDto;
  107. import static org.sonar.db.issue.IssueTesting.newCodeReferenceIssue;
  108. import static org.sonar.db.issue.IssueTesting.newIssue;
  109. import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
  110. @SuppressWarnings("ALL")
  111. @RunWith(DataProviderRunner.class)
  112. public class SearchActionTest {
  113. private static final String PARAM_PROJECT_KEY = "projectKey";
  114. private static final String PARAM_STATUS = "status";
  115. private static final String PARAM_RESOLUTION = "resolution";
  116. private static final String PARAM_HOTSPOTS = "hotspots";
  117. private static final String PARAM_BRANCH = "branch";
  118. private static final String PARAM_PULL_REQUEST = "pullRequest";
  119. private static final String PARAM_IN_NEW_CODE_PERIOD = "inNewCodePeriod";
  120. private static final String PARAM_ONLY_MINE = "onlyMine";
  121. private static final String PARAM_PCI_DSS_32 = "pciDss-3.2";
  122. private static final String PARAM_PCI_DSS_40 = "pciDss-4.0";
  123. private static final String PARAM_OWASP_TOP_10_2017 = "owaspTop10";
  124. private static final String PARAM_OWASP_TOP_10_2021 = "owaspTop10-2021";
  125. private static final String PARAM_SANS_TOP_25 = "sansTop25";
  126. private static final String PARAM_SONARSOURCE_SECURITY = "sonarsourceSecurity";
  127. private static final String PARAM_CWE = "cwe";
  128. private static final String PARAM_FILES = "files";
  129. private static final Random RANDOM = new Random();
  130. private static final int ONE_MINUTE = 60_000;
  131. private static final List<String> RESOLUTION_TYPES = List.of(RESOLUTION_FIXED, RESOLUTION_SAFE, RESOLUTION_ACKNOWLEDGED);
  132. @Rule
  133. public DbTester dbTester = DbTester.create(System2.INSTANCE);
  134. @Rule
  135. public EsTester es = EsTester.create();
  136. @Rule
  137. public UserSessionRule userSessionRule = UserSessionRule.standalone();
  138. private final TestSystem2 system2 = new TestSystem2();
  139. private final DbClient dbClient = dbTester.getDbClient();
  140. private final IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule));
  141. private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), mock(AsyncIssueIndexing.class));
  142. private final ViewIndexer viewIndexer = new ViewIndexer(dbClient, es.client());
  143. private final PermissionIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);
  144. private final HotspotWsResponseFormatter responseFormatter = new HotspotWsResponseFormatter();
  145. private final IssueIndexSyncProgressChecker issueIndexSyncProgressChecker = mock(IssueIndexSyncProgressChecker.class);
  146. private final SearchAction underTest = new SearchAction(dbClient, userSessionRule, issueIndex,
  147. issueIndexSyncProgressChecker, responseFormatter, new TextRangeResponseFormatter(), system2);
  148. private final WsActionTester actionTester = new WsActionTester(underTest);
  149. @Test
  150. public void verify_ws_def() {
  151. WebService.Param onlyMineParam = actionTester.getDef().param(PARAM_ONLY_MINE);
  152. WebService.Param pciDss32Param = actionTester.getDef().param(PARAM_PCI_DSS_32);
  153. WebService.Param pciDss40Param = actionTester.getDef().param(PARAM_PCI_DSS_40);
  154. WebService.Param owaspTop10Param = actionTester.getDef().param(PARAM_OWASP_TOP_10_2017);
  155. WebService.Param sansTop25Param = actionTester.getDef().param(PARAM_SANS_TOP_25);
  156. WebService.Param sonarsourceSecurityParam = actionTester.getDef().param(PARAM_SONARSOURCE_SECURITY);
  157. WebService.Param filesParam = actionTester.getDef().param(PARAM_FILES);
  158. assertThat(actionTester.getDef().isInternal()).isTrue();
  159. assertThat(onlyMineParam).isNotNull();
  160. assertThat(onlyMineParam.isRequired()).isFalse();
  161. assertThat(actionTester.getDef().param(PARAM_ONLY_MINE).possibleValues())
  162. .containsExactlyInAnyOrder("yes", "no", "true", "false");
  163. assertThat(pciDss32Param).isNotNull();
  164. assertThat(pciDss32Param.isRequired()).isFalse();
  165. assertThat(pciDss40Param).isNotNull();
  166. assertThat(pciDss40Param.isRequired()).isFalse();
  167. assertThat(owaspTop10Param).isNotNull();
  168. assertThat(owaspTop10Param.isRequired()).isFalse();
  169. assertThat(sansTop25Param).isNotNull();
  170. assertThat(sansTop25Param.isRequired()).isFalse();
  171. assertThat(sonarsourceSecurityParam).isNotNull();
  172. assertThat(sonarsourceSecurityParam.isRequired()).isFalse();
  173. assertThat(filesParam).isNotNull();
  174. }
  175. @Test
  176. public void fails_with_IAE_if_parameters_projectKey_and_hotspots_are_missing() {
  177. TestRequest request = actionTester.newRequest();
  178. assertThatThrownBy(request::execute)
  179. .isInstanceOf(IllegalArgumentException.class)
  180. .hasMessage("A value must be provided for either parameter 'projectKey' or parameter 'hotspots'");
  181. }
  182. @Test
  183. public void fail_with_IAE_if_parameter_branch_is_used_without_parameter_projectKey() {
  184. TestRequest request = actionTester.newRequest()
  185. .setParam(PARAM_HOTSPOTS, randomAlphabetic(2))
  186. .setParam(PARAM_BRANCH, randomAlphabetic(1));
  187. assertThatThrownBy(request::execute)
  188. .isInstanceOf(IllegalArgumentException.class)
  189. .hasMessage("Parameter 'branch' must be used with parameter 'projectKey'");
  190. }
  191. @Test
  192. public void fail_with_IAE_if_parameter_pullRequest_is_used_without_parameter_projectKey() {
  193. TestRequest request = actionTester.newRequest()
  194. .setParam(PARAM_HOTSPOTS, randomAlphabetic(2))
  195. .setParam(PARAM_PULL_REQUEST, randomAlphabetic(1));
  196. assertThatThrownBy(request::execute)
  197. .isInstanceOf(IllegalArgumentException.class)
  198. .hasMessage("Parameter 'pullRequest' must be used with parameter 'projectKey'");
  199. }
  200. @Test
  201. public void fail_with_IAE_if_both_parameters_pullRequest_and_branch_are_provided() {
  202. TestRequest request = actionTester.newRequest()
  203. .setParam(PARAM_PROJECT_KEY, randomAlphabetic(2))
  204. .setParam(PARAM_BRANCH, randomAlphabetic(1))
  205. .setParam(PARAM_PULL_REQUEST, randomAlphabetic(1));
  206. assertThatThrownBy(request::execute)
  207. .isInstanceOf(IllegalArgumentException.class)
  208. .hasMessage("Only one of parameters 'branch' and 'pullRequest' can be provided");
  209. }
  210. @Test
  211. @UseDataProvider("badStatuses")
  212. public void fails_with_IAE_if_status_parameter_is_neither_TO_REVIEW_or_REVIEWED(String badStatus) {
  213. TestRequest request = actionTester.newRequest()
  214. .setParam(PARAM_PROJECT_KEY, randomAlphabetic(13))
  215. .setParam(PARAM_STATUS, badStatus);
  216. assertThatThrownBy(request::execute)
  217. .isInstanceOf(IllegalArgumentException.class)
  218. .hasMessage("Value of parameter 'status' (" + badStatus + ") must be one of: [TO_REVIEW, REVIEWED]");
  219. }
  220. @DataProvider
  221. public static Object[][] badStatuses() {
  222. return Stream.concat(
  223. Issue.STATUSES.stream(),
  224. Stream.of(randomAlphabetic(3)))
  225. .filter(t -> !STATUS_REVIEWED.equals(t))
  226. .filter(t -> !STATUS_TO_REVIEW.equals(t))
  227. .map(t -> new Object[]{t})
  228. .toArray(Object[][]::new);
  229. }
  230. @Test
  231. @UseDataProvider("validStatusesAndResolutions")
  232. public void fail_with_IAE_if_parameter_status_is_specified_with_hotspots_parameter(String status, @Nullable String notUsed) {
  233. TestRequest request = actionTester.newRequest()
  234. .setParam(PARAM_HOTSPOTS, randomAlphabetic(12))
  235. .setParam(PARAM_STATUS, status);
  236. assertThatThrownBy(request::execute)
  237. .isInstanceOf(IllegalArgumentException.class)
  238. .hasMessage("Parameter 'status' can't be used with parameter 'hotspots'");
  239. }
  240. @Test
  241. @UseDataProvider("badResolutions")
  242. public void fails_with_IAE_if_resolution_parameter_is_neither_FIXED_nor_SAFE(String badResolution) {
  243. TestRequest request = actionTester.newRequest()
  244. .setParam(PARAM_PROJECT_KEY, randomAlphabetic(13))
  245. .setParam(PARAM_STATUS, STATUS_TO_REVIEW)
  246. .setParam(PARAM_RESOLUTION, badResolution);
  247. assertThatThrownBy(request::execute)
  248. .isInstanceOf(IllegalArgumentException.class)
  249. .hasMessage("Value of parameter 'resolution' (" + badResolution + ") must be one of: [FIXED, SAFE, ACKNOWLEDGED]");
  250. }
  251. @DataProvider
  252. public static Object[][] badResolutions() {
  253. return Stream.of(
  254. Issue.RESOLUTIONS.stream(),
  255. Issue.SECURITY_HOTSPOT_RESOLUTIONS.stream(),
  256. Stream.of(randomAlphabetic(4)))
  257. .flatMap(t -> t)
  258. .filter(t -> !RESOLUTION_TYPES.contains(t))
  259. .map(t -> new Object[]{t})
  260. .toArray(Object[][]::new);
  261. }
  262. @Test
  263. @UseDataProvider("fixedOrSafeResolution")
  264. public void fails_with_IAE_if_resolution_is_provided_with_status_TO_REVIEW(String resolution) {
  265. TestRequest request = actionTester.newRequest()
  266. .setParam(PARAM_PROJECT_KEY, randomAlphabetic(13))
  267. .setParam(PARAM_STATUS, STATUS_TO_REVIEW)
  268. .setParam(PARAM_RESOLUTION, resolution);
  269. assertThatThrownBy(request::execute)
  270. .isInstanceOf(IllegalArgumentException.class)
  271. .hasMessage("Value '" + resolution + "' of parameter 'resolution' can only be provided if value of parameter 'status' is 'REVIEWED'");
  272. }
  273. @Test
  274. @UseDataProvider("fixedOrSafeResolution")
  275. public void fails_with_IAE_if_resolution_is_provided_with_hotspots_parameter(String resolution) {
  276. TestRequest request = actionTester.newRequest()
  277. .setParam(PARAM_HOTSPOTS, randomAlphabetic(13))
  278. .setParam(PARAM_RESOLUTION, resolution);
  279. assertThatThrownBy(request::execute)
  280. .isInstanceOf(IllegalArgumentException.class)
  281. .hasMessage("Parameter 'resolution' can't be used with parameter 'hotspots'");
  282. }
  283. @DataProvider
  284. public static Object[][] fixedOrSafeResolution() {
  285. return new Object[][]{
  286. {RESOLUTION_SAFE},
  287. {RESOLUTION_FIXED}
  288. };
  289. }
  290. @Test
  291. public void fails_with_NotFoundException_if_project_does_not_exist() {
  292. String key = randomAlphabetic(12);
  293. TestRequest request = actionTester.newRequest()
  294. .setParam(PARAM_PROJECT_KEY, key);
  295. assertThatThrownBy(request::execute)
  296. .isInstanceOf(NotFoundException.class)
  297. .hasMessage("Project '%s' not found", key);
  298. }
  299. @Test
  300. public void fails_with_NotFoundException_if_project_is_neither_a_project_nor_an_application() {
  301. ComponentDto project = dbTester.components().insertPrivateProject();
  302. ComponentDto directory = dbTester.components().insertComponent(ComponentTesting.newDirectory(project, "foo"));
  303. ComponentDto file = dbTester.components().insertComponent(ComponentTesting.newFileDto(project));
  304. ComponentDto portfolio = dbTester.components().insertPrivatePortfolio();
  305. TestRequest request = actionTester.newRequest();
  306. for (ComponentDto component : Arrays.asList(directory, file, portfolio)) {
  307. request.setParam(PARAM_PROJECT_KEY, component.getKey());
  308. assertThatThrownBy(request::execute)
  309. .isInstanceOf(NotFoundException.class)
  310. .hasMessage("Project '%s' not found", component.getKey());
  311. }
  312. }
  313. @Test
  314. public void fails_with_ForbiddenException_if_project_is_private_and_not_allowed() {
  315. ComponentDto project = dbTester.components().insertPrivateProject();
  316. userSessionRule.registerComponents(project);
  317. TestRequest request = newRequest(project);
  318. assertThatThrownBy(request::execute)
  319. .isInstanceOf(ForbiddenException.class)
  320. .hasMessage("Insufficient privileges");
  321. }
  322. @Test
  323. public void fails_with_ForbiddenException_if_application_is_private_and_not_allowed() {
  324. ComponentDto application = dbTester.components().insertPrivateApplication();
  325. userSessionRule.registerComponents(application);
  326. TestRequest request = newRequest(application);
  327. assertThatThrownBy(request::execute)
  328. .isInstanceOf(ForbiddenException.class)
  329. .hasMessage("Insufficient privileges");
  330. }
  331. @Test
  332. public void succeeds_on_public_project() {
  333. ComponentDto project = dbTester.components().insertPublicProject();
  334. userSessionRule.registerComponents(project);
  335. SearchWsResponse response = newRequest(project)
  336. .executeProtobuf(SearchWsResponse.class);
  337. assertThat(response.getHotspotsList()).isEmpty();
  338. assertThat(response.getComponentsList()).isEmpty();
  339. }
  340. @Test
  341. public void succeeds_on_public_application() {
  342. ComponentDto application = dbTester.components().insertPublicApplication();
  343. userSessionRule.registerApplication(application);
  344. SearchWsResponse response = newRequest(application)
  345. .executeProtobuf(SearchWsResponse.class);
  346. assertThat(response.getHotspotsList()).isEmpty();
  347. assertThat(response.getComponentsList()).isEmpty();
  348. }
  349. @Test
  350. public void succeeds_on_private_project_with_permission() {
  351. ComponentDto project = dbTester.components().insertPrivateProject();
  352. userSessionRule.registerComponents(project);
  353. userSessionRule.logIn().addProjectPermission(USER, project);
  354. SearchWsResponse response = newRequest(project)
  355. .executeProtobuf(SearchWsResponse.class);
  356. assertThat(response.getHotspotsList()).isEmpty();
  357. assertThat(response.getComponentsList()).isEmpty();
  358. }
  359. @Test
  360. public void succeeds_on_private_application_with_permission() {
  361. ComponentDto application = dbTester.components().insertPrivateApplication();
  362. userSessionRule.logIn().registerApplication(application).addProjectPermission(USER, application);
  363. SearchWsResponse response = newRequest(application)
  364. .executeProtobuf(SearchWsResponse.class);
  365. assertThat(response.getHotspotsList()).isEmpty();
  366. assertThat(response.getComponentsList()).isEmpty();
  367. }
  368. @Test
  369. public void does_not_fail_if_rule_of_hotspot_does_not_exist_in_DB() {
  370. ComponentDto project = dbTester.components().insertPublicProject();
  371. userSessionRule.registerComponents(project);
  372. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  373. indexPermissions();
  374. IssueDto[] hotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  375. .mapToObj(i -> {
  376. RuleDto rule = newRule(SECURITY_HOTSPOT);
  377. return insertHotspot(project, file, rule);
  378. })
  379. .toArray(IssueDto[]::new);
  380. indexIssues();
  381. IssueDto hotspotWithoutRule = hotspots[RANDOM.nextInt(hotspots.length)];
  382. dbTester.executeUpdateSql("delete from rules where uuid=?", hotspotWithoutRule.getRuleUuid());
  383. SearchWsResponse response = newRequest(project)
  384. .executeProtobuf(SearchWsResponse.class);
  385. assertThat(response.getHotspotsList())
  386. .extracting(Hotspots.SearchWsResponse.Hotspot::getKey)
  387. .containsOnly(Arrays.stream(hotspots)
  388. .map(IssueDto::getKey)
  389. .filter(key -> !key.equals(hotspotWithoutRule.getKey()))
  390. .toArray(String[]::new));
  391. }
  392. @Test
  393. public void returns_no_hotspot_component_nor_rule_when_project_has_no_hotspot() {
  394. ComponentDto project = dbTester.components().insertPublicProject();
  395. userSessionRule.registerComponents(project);
  396. indexPermissions();
  397. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  398. Arrays.stream(RuleType.values())
  399. .filter(t -> t != SECURITY_HOTSPOT)
  400. .forEach(ruleType -> {
  401. RuleDto rule = newRule(ruleType);
  402. dbTester.issues().insert(rule, project, file, t -> t.setType(ruleType));
  403. });
  404. indexIssues();
  405. SearchWsResponse response = newRequest(project)
  406. .executeProtobuf(SearchWsResponse.class);
  407. assertThat(response.getHotspotsList()).isEmpty();
  408. }
  409. @Test
  410. public void returns_hotspot_components_when_project_has_hotspots() {
  411. ComponentDto project = dbTester.components().insertPublicProject();
  412. userSessionRule.registerComponents(project);
  413. indexPermissions();
  414. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  415. ComponentDto fileWithHotspot = dbTester.components().insertComponent(newFileDto(project));
  416. Arrays.stream(RuleType.values())
  417. .filter(t -> t != SECURITY_HOTSPOT)
  418. .forEach(ruleType -> {
  419. RuleDto rule = newRule(ruleType);
  420. dbTester.issues().insert(rule, project, file, t -> t.setType(ruleType));
  421. });
  422. IssueDto[] hotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  423. .mapToObj(i -> {
  424. RuleDto rule = newRule(SECURITY_HOTSPOT);
  425. return insertHotspot(project, fileWithHotspot, rule);
  426. })
  427. .toArray(IssueDto[]::new);
  428. indexIssues();
  429. SearchWsResponse response = newRequest(project)
  430. .executeProtobuf(SearchWsResponse.class);
  431. assertThat(response.getHotspotsList())
  432. .extracting(SearchWsResponse.Hotspot::getKey)
  433. .containsOnly(Arrays.stream(hotspots).map(IssueDto::getKey).toArray(String[]::new));
  434. assertThat(response.getComponentsList())
  435. .extracting(Component::getKey)
  436. .containsOnly(project.getKey(), fileWithHotspot.getKey());
  437. }
  438. @Test
  439. public void returns_single_component_when_all_hotspots_are_on_project() {
  440. ComponentDto project = dbTester.components().insertPublicProject();
  441. userSessionRule.registerComponents(project);
  442. indexPermissions();
  443. IssueDto[] hotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  444. .mapToObj(i -> {
  445. RuleDto rule = newRule(SECURITY_HOTSPOT);
  446. return insertHotspot(project, project, rule);
  447. })
  448. .toArray(IssueDto[]::new);
  449. indexIssues();
  450. SearchWsResponse response = newRequest(project)
  451. .executeProtobuf(SearchWsResponse.class);
  452. assertThat(response.getHotspotsList())
  453. .extracting(Hotspots.SearchWsResponse.Hotspot::getKey)
  454. .containsOnly(Arrays.stream(hotspots).map(IssueDto::getKey).toArray(String[]::new));
  455. assertThat(response.getComponentsList())
  456. .extracting(Component::getKey)
  457. .containsOnly(project.getKey());
  458. }
  459. @Test
  460. public void returns_hotspots_of_specified_project() {
  461. ComponentDto project1 = dbTester.components().insertPublicProject();
  462. ComponentDto project2 = dbTester.components().insertPublicProject();
  463. userSessionRule.registerComponents(project1, project2);
  464. indexPermissions();
  465. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  466. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
  467. IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
  468. .mapToObj(i -> {
  469. RuleDto rule = newRule(SECURITY_HOTSPOT);
  470. insertHotspot(project1, file1, rule);
  471. return insertHotspot(project2, file2, rule);
  472. })
  473. .toArray(IssueDto[]::new);
  474. indexIssues();
  475. SearchWsResponse responseProject1 = newRequest(project1)
  476. .executeProtobuf(SearchWsResponse.class);
  477. assertThat(responseProject1.getHotspotsList())
  478. .extracting(SearchWsResponse.Hotspot::getKey)
  479. .doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
  480. assertThat(responseProject1.getComponentsList())
  481. .extracting(Component::getKey)
  482. .containsOnly(project1.getKey(), file1.getKey());
  483. SearchWsResponse responseProject2 = newRequest(project2)
  484. .executeProtobuf(SearchWsResponse.class);
  485. assertThat(responseProject2.getHotspotsList())
  486. .extracting(SearchWsResponse.Hotspot::getKey)
  487. .containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
  488. assertThat(responseProject2.getComponentsList())
  489. .extracting(Component::getKey)
  490. .containsOnly(project2.getKey(), file2.getKey());
  491. }
  492. @Test
  493. public void returns_only_hotspots_to_review_or_reviewed_of_project() {
  494. ComponentDto project = dbTester.components().insertPublicProject();
  495. userSessionRule.registerComponents(project);
  496. indexPermissions();
  497. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  498. IssueDto[] hotspots = STATUSES.stream()
  499. .map(status -> {
  500. RuleDto rule = newRule(SECURITY_HOTSPOT);
  501. return insertHotspot(rule, project, file, t -> t.setStatus(status));
  502. })
  503. .toArray(IssueDto[]::new);
  504. indexIssues();
  505. String[] expectedKeys = Arrays.stream(hotspots)
  506. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()) || STATUS_TO_REVIEW.equals(t.getStatus()))
  507. .map(IssueDto::getKey)
  508. .toArray(String[]::new);
  509. SearchWsResponse response = newRequest(project)
  510. .executeProtobuf(SearchWsResponse.class);
  511. assertThat(response.getHotspotsList())
  512. .extracting(SearchWsResponse.Hotspot::getKey)
  513. .containsOnly(expectedKeys);
  514. }
  515. @Test
  516. public void returns_hotspots_of_specified_application() {
  517. ComponentDto application1 = dbTester.components().insertPublicApplication();
  518. ComponentDto application2 = dbTester.components().insertPublicApplication();
  519. ComponentDto project1 = dbTester.components().insertPublicProject();
  520. ComponentDto project2 = dbTester.components().insertPublicProject();
  521. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project1, application1));
  522. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2, application2));
  523. indexViews();
  524. userSessionRule.registerApplication(application1, project1)
  525. .registerApplication(application2, project2);
  526. indexPermissions();
  527. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  528. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
  529. IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
  530. .mapToObj(i -> {
  531. RuleDto rule = newRule(SECURITY_HOTSPOT);
  532. insertHotspot(project1, file1, rule);
  533. return insertHotspot(project2, file2, rule);
  534. })
  535. .toArray(IssueDto[]::new);
  536. indexIssues();
  537. SearchWsResponse responseApplication1 = newRequest(application1)
  538. .executeProtobuf(SearchWsResponse.class);
  539. assertThat(responseApplication1.getHotspotsList())
  540. .extracting(SearchWsResponse.Hotspot::getKey)
  541. .doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
  542. assertThat(responseApplication1.getComponentsList())
  543. .extracting(Component::getKey)
  544. .containsOnly(project1.getKey(), file1.getKey());
  545. SearchWsResponse responseApplication2 = newRequest(application2)
  546. .executeProtobuf(SearchWsResponse.class);
  547. assertThat(responseApplication2.getHotspotsList())
  548. .extracting(SearchWsResponse.Hotspot::getKey)
  549. .containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
  550. assertThat(responseApplication2.getComponentsList())
  551. .extracting(Component::getKey)
  552. .containsOnly(project2.getKey(), file2.getKey());
  553. }
  554. @Test
  555. public void returns_hotspots_of_specified_application_branch() {
  556. ComponentDto application = dbTester.components().insertPublicApplication();
  557. ComponentDto applicationBranch = dbTester.components().insertProjectBranch(application);
  558. ComponentDto project1 = dbTester.components().insertPublicProject();
  559. ComponentDto project2 = dbTester.components().insertPublicProject();
  560. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project1, application));
  561. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2, applicationBranch));
  562. indexViews();
  563. userSessionRule.registerApplication(application, applicationBranch, project1, project2);
  564. indexPermissions();
  565. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  566. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
  567. IssueDto[] hotspots2 = IntStream.range(0, 1 + RANDOM.nextInt(10))
  568. .mapToObj(i -> {
  569. RuleDto rule = newRule(SECURITY_HOTSPOT);
  570. insertHotspot(project1, file1, rule);
  571. return insertHotspot(project2, file2, rule);
  572. })
  573. .toArray(IssueDto[]::new);
  574. indexIssues();
  575. SearchWsResponse responseApplication = newRequest(application)
  576. .executeProtobuf(SearchWsResponse.class);
  577. assertThat(responseApplication.getHotspotsList())
  578. .extracting(SearchWsResponse.Hotspot::getKey)
  579. .doesNotContainAnyElementsOf(Arrays.stream(hotspots2).map(IssueDto::getKey).collect(toList()));
  580. assertThat(responseApplication.getComponentsList())
  581. .extracting(Component::getKey)
  582. .containsOnly(project1.getKey(), file1.getKey());
  583. SearchWsResponse responseApplicationBranch = newRequest(applicationBranch)
  584. .executeProtobuf(SearchWsResponse.class);
  585. assertThat(responseApplicationBranch.getHotspotsList())
  586. .extracting(SearchWsResponse.Hotspot::getKey)
  587. .containsOnly(Arrays.stream(hotspots2).map(IssueDto::getKey).toArray(String[]::new));
  588. assertThat(responseApplicationBranch.getComponentsList())
  589. .extracting(Component::getKey)
  590. .containsOnly(project2.getKey(), file2.getKey());
  591. }
  592. @Test
  593. public void returns_hotspot_of_branch_or_pullRequest() {
  594. ComponentDto project = dbTester.components().insertPublicProject();
  595. userSessionRule.registerComponents(project);
  596. indexPermissions();
  597. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  598. ComponentDto pullRequest = dbTester.components().insertProjectBranch(project, t -> t.setBranchType(BranchType.PULL_REQUEST));
  599. ComponentDto fileProject = dbTester.components().insertComponent(newFileDto(project));
  600. ComponentDto fileBranch = dbTester.components().insertComponent(newFileDto(branch));
  601. ComponentDto filePR = dbTester.components().insertComponent(newFileDto(pullRequest));
  602. IssueDto[] hotspotProject = IntStream.range(0, 1 + RANDOM.nextInt(10))
  603. .mapToObj(i -> {
  604. RuleDto rule = newRule(SECURITY_HOTSPOT);
  605. return insertHotspot(project, fileProject, rule);
  606. })
  607. .toArray(IssueDto[]::new);
  608. IssueDto[] hotspotBranch = IntStream.range(0, 1 + RANDOM.nextInt(10))
  609. .mapToObj(i -> {
  610. RuleDto rule = newRule(SECURITY_HOTSPOT);
  611. return insertHotspot(branch, fileBranch, rule);
  612. })
  613. .toArray(IssueDto[]::new);
  614. IssueDto[] hotspotPR = IntStream.range(0, 1 + RANDOM.nextInt(10))
  615. .mapToObj(i -> {
  616. RuleDto rule = newRule(SECURITY_HOTSPOT);
  617. return insertHotspot(pullRequest, filePR, rule);
  618. })
  619. .toArray(IssueDto[]::new);
  620. indexIssues();
  621. SearchWsResponse responseProject = newRequest(project)
  622. .executeProtobuf(SearchWsResponse.class);
  623. SearchWsResponse responseBranch = newRequest(branch)
  624. .executeProtobuf(SearchWsResponse.class);
  625. SearchWsResponse responsePR = newRequest(pullRequest)
  626. .executeProtobuf(SearchWsResponse.class);
  627. assertThat(responseProject.getHotspotsList())
  628. .extracting(SearchWsResponse.Hotspot::getKey)
  629. .containsExactlyInAnyOrder(Arrays.stream(hotspotProject).map(IssueDto::getKey).toArray(String[]::new));
  630. assertThat(responseBranch.getHotspotsList())
  631. .extracting(SearchWsResponse.Hotspot::getKey)
  632. .containsExactlyInAnyOrder(Arrays.stream(hotspotBranch).map(IssueDto::getKey).toArray(String[]::new));
  633. assertThat(responsePR.getHotspotsList())
  634. .extracting(SearchWsResponse.Hotspot::getKey)
  635. .containsExactlyInAnyOrder(Arrays.stream(hotspotPR).map(IssueDto::getKey).toArray(String[]::new));
  636. verify(issueIndexSyncProgressChecker, times(3)).checkIfComponentNeedIssueSync(any(), eq(project.getDbKey()));
  637. }
  638. @Test
  639. @UseDataProvider("onlyMineParamValues")
  640. public void returns_hotspots_of_specified_project_assigned_to_current_user_if_only_mine_is_set(String onlyMineParameter, boolean shouldFilter) {
  641. ComponentDto project1 = dbTester.components().insertPublicProject();
  642. String assigneeUuid = this.userSessionRule.logIn().registerComponents(project1).getUuid();
  643. indexPermissions();
  644. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project1));
  645. IssueDto[] assigneeHotspots = IntStream.range(0, 1 + RANDOM.nextInt(10))
  646. .mapToObj(i -> {
  647. RuleDto rule = newRule(SECURITY_HOTSPOT);
  648. insertHotspot(rule, project1, file1, randomAlphabetic(5));
  649. return insertHotspot(rule, project1, file1, assigneeUuid);
  650. })
  651. .toArray(IssueDto[]::new);
  652. indexIssues();
  653. SearchWsResponse allHotspots = newRequest(project1)
  654. .executeProtobuf(SearchWsResponse.class);
  655. SearchWsResponse userHotspots = newRequest(project1, r -> r.setParam(PARAM_ONLY_MINE, onlyMineParameter))
  656. .executeProtobuf(SearchWsResponse.class);
  657. assertThat(allHotspots.getHotspotsList())
  658. .extracting(SearchWsResponse.Hotspot::getKey)
  659. .contains(Arrays.stream(assigneeHotspots).map(IssueDto::getKey).toArray(String[]::new))
  660. .hasSizeGreaterThan(assigneeHotspots.length);
  661. if (shouldFilter) {
  662. assertThat(userHotspots.getHotspotsList())
  663. .extracting(SearchWsResponse.Hotspot::getKey)
  664. .containsOnly(Arrays.stream(assigneeHotspots).map(IssueDto::getKey).toArray(String[]::new));
  665. } else {
  666. assertThat(userHotspots.getHotspotsList())
  667. .extracting(SearchWsResponse.Hotspot::getKey)
  668. .containsOnly(allHotspots.getHotspotsList().stream().map(SearchWsResponse.Hotspot::getKey).toArray(String[]::new));
  669. }
  670. }
  671. @DataProvider
  672. public static Object[][] onlyMineParamValues() {
  673. return new Object[][]{
  674. {"yes", true},
  675. {"true", true},
  676. {"no", false},
  677. {"false", false}
  678. };
  679. }
  680. @Test
  681. public void fail_if_hotspots_provided_with_onlyMine_param() {
  682. ComponentDto project = dbTester.components().insertPrivateProject();
  683. userSessionRule.registerComponents(project);
  684. userSessionRule.logIn().addProjectPermission(USER, project);
  685. TestRequest request = actionTester.newRequest()
  686. .setParam(PARAM_HOTSPOTS, IntStream.range(2, 10).mapToObj(String::valueOf).collect(joining(",")))
  687. .setParam(PARAM_ONLY_MINE, "true");
  688. assertThatThrownBy(request::execute)
  689. .isInstanceOf(IllegalArgumentException.class)
  690. .hasMessage("Parameter 'onlyMine' can be used with parameter 'projectKey' only");
  691. }
  692. @Test
  693. public void fail_if_user_not_authenticated_with_onlyMine_param() {
  694. ComponentDto project = dbTester.components().insertPublicProject();
  695. userSessionRule.anonymous();
  696. TestRequest request = actionTester.newRequest()
  697. .setParam(PARAM_PROJECT_KEY, project.getKey())
  698. .setParam(PARAM_ONLY_MINE, "true");
  699. assertThatThrownBy(request::execute)
  700. .isInstanceOf(IllegalArgumentException.class)
  701. .hasMessage("Parameter 'onlyMine' requires user to be logged in");
  702. }
  703. @Test
  704. public void returns_hotpots_with_any_status_if_no_status_nor_resolution_parameter() {
  705. ComponentDto project = dbTester.components().insertPublicProject();
  706. userSessionRule.registerComponents(project);
  707. indexPermissions();
  708. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  709. List<IssueDto> hotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  710. .collect(toList());
  711. indexIssues();
  712. SearchWsResponse response = newRequest(project, null, null)
  713. .executeProtobuf(SearchWsResponse.class);
  714. assertThat(response.getHotspotsList())
  715. .extracting(SearchWsResponse.Hotspot::getKey)
  716. .containsExactlyInAnyOrder(hotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  717. }
  718. @Test
  719. public void returns_hotpots_reviewed_as_safe_and_fixed_if_status_is_REVIEWED_and_resolution_is_not_set() {
  720. ComponentDto project = dbTester.components().insertPublicProject();
  721. userSessionRule.registerComponents(project);
  722. indexPermissions();
  723. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  724. List<IssueDto> reviewedHotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  725. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()))
  726. .collect(toList());
  727. indexIssues();
  728. SearchWsResponse response = newRequest(project, STATUS_REVIEWED, null)
  729. .executeProtobuf(SearchWsResponse.class);
  730. assertThat(response.getHotspotsList())
  731. .extracting(SearchWsResponse.Hotspot::getKey)
  732. .containsExactlyInAnyOrder(reviewedHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  733. }
  734. @Test
  735. public void returns_hotpots_reviewed_as_safe_if_status_is_REVIEWED_and_resolution_is_SAFE() {
  736. ComponentDto project = dbTester.components().insertPublicProject();
  737. userSessionRule.registerComponents(project);
  738. indexPermissions();
  739. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  740. List<IssueDto> safeHotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  741. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()) && RESOLUTION_SAFE.equals(t.getResolution()))
  742. .collect(toList());
  743. indexIssues();
  744. SearchWsResponse response = newRequest(project, STATUS_REVIEWED, RESOLUTION_SAFE)
  745. .executeProtobuf(SearchWsResponse.class);
  746. assertThat(response.getHotspotsList())
  747. .extracting(SearchWsResponse.Hotspot::getKey)
  748. .containsExactlyInAnyOrder(safeHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  749. }
  750. @Test
  751. public void returns_hotpots_reviewed_as_fixed_if_status_is_REVIEWED_and_resolution_is_FIXED() {
  752. ComponentDto project = dbTester.components().insertPublicProject();
  753. userSessionRule.registerComponents(project);
  754. indexPermissions();
  755. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  756. List<IssueDto> fixedHotspots = insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(project, file)
  757. .filter(t -> STATUS_REVIEWED.equals(t.getStatus()) && RESOLUTION_FIXED.equals(t.getResolution()))
  758. .collect(toList());
  759. indexIssues();
  760. SearchWsResponse response = newRequest(project, STATUS_REVIEWED, RESOLUTION_FIXED)
  761. .executeProtobuf(SearchWsResponse.class);
  762. assertThat(response.getHotspotsList())
  763. .extracting(SearchWsResponse.Hotspot::getKey)
  764. .containsExactlyInAnyOrder(fixedHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  765. }
  766. @Test
  767. public void returns_only_unresolved_hotspots_when_status_is_TO_REVIEW() {
  768. ComponentDto project = dbTester.components().insertPublicProject();
  769. userSessionRule.registerComponents(project);
  770. indexPermissions();
  771. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  772. RuleDto rule = newRule(SECURITY_HOTSPOT);
  773. IssueDto unresolvedHotspot = insertHotspot(rule, project, file, t -> t.setResolution(null));
  774. // unrealistic case since a resolution must be set, but shows a limit of current implementation (resolution is enough)
  775. IssueDto badlyResolved = insertHotspot(rule, project, file, t -> t.setStatus(STATUS_TO_REVIEW).setResolution(randomAlphabetic(5)));
  776. IssueDto badlyReviewed = insertHotspot(rule, project, file, t -> t.setStatus(STATUS_REVIEWED).setResolution(null));
  777. IssueDto badlyClosedHotspot = insertHotspot(rule, project, file, t -> t.setStatus(STATUS_CLOSED).setResolution(null));
  778. indexIssues();
  779. SearchWsResponse response = newRequest(project, STATUS_TO_REVIEW, null)
  780. .executeProtobuf(SearchWsResponse.class);
  781. assertThat(response.getHotspotsList())
  782. .extracting(SearchWsResponse.Hotspot::getKey)
  783. .containsOnly(unresolvedHotspot.getKey());
  784. }
  785. private Stream<IssueDto> insertRandomNumberOfHotspotsOfAllSupportedStatusesAndResolutions(ComponentDto project, ComponentDto file) {
  786. RuleDto rule = newRule(SECURITY_HOTSPOT);
  787. List<IssueDto> hotspots = Arrays.stream(validStatusesAndResolutions())
  788. .flatMap(objects -> {
  789. String status = (String) objects[0];
  790. String resolution = (String) objects[1];
  791. return IntStream.range(0, 1 + RANDOM.nextInt(15))
  792. .mapToObj(i -> newIssue(rule, project, file)
  793. .setKee("hotspot_" + status + "_" + resolution + "_" + i)
  794. .setType(SECURITY_HOTSPOT)
  795. .setStatus(status)
  796. .setResolution(resolution));
  797. })
  798. .collect(toList());
  799. Collections.shuffle(hotspots);
  800. hotspots.forEach(t -> dbTester.issues().insertHotspot(t));
  801. return hotspots.stream();
  802. }
  803. @Test
  804. @UseDataProvider("validStatusesAndResolutions")
  805. public void returns_fields_of_hotspot(String status, @Nullable String resolution) {
  806. ComponentDto project = dbTester.components().insertPublicProject();
  807. userSessionRule.registerComponents(project);
  808. indexPermissions();
  809. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  810. RuleDto rule = newRule(SECURITY_HOTSPOT);
  811. IssueDto hotspot = insertHotspot(rule, project, file,
  812. t -> t
  813. .setStatus(randomAlphabetic(11))
  814. .setLine(RANDOM.nextInt(230))
  815. .setMessage(randomAlphabetic(10))
  816. .setAssigneeUuid(randomAlphabetic(9))
  817. .setAuthorLogin(randomAlphabetic(8))
  818. .setStatus(status)
  819. .setResolution(resolution));
  820. indexIssues();
  821. SearchWsResponse response = newRequest(project)
  822. .executeProtobuf(SearchWsResponse.class);
  823. assertThat(response.getHotspotsList()).hasSize(1);
  824. SearchWsResponse.Hotspot actual = response.getHotspots(0);
  825. assertThat(actual.getComponent()).isEqualTo(file.getKey());
  826. assertThat(actual.getProject()).isEqualTo(project.getKey());
  827. assertThat(actual.getStatus()).isEqualTo(status);
  828. if (resolution == null) {
  829. assertThat(actual.hasResolution()).isFalse();
  830. } else {
  831. assertThat(actual.getResolution()).isEqualTo(resolution);
  832. }
  833. assertThat(actual.getLine()).isEqualTo(hotspot.getLine());
  834. assertThat(actual.getMessage()).isEqualTo(hotspot.getMessage());
  835. assertThat(actual.getAssignee()).isEqualTo(hotspot.getAssigneeUuid());
  836. assertThat(actual.getAuthor()).isEqualTo(hotspot.getAuthorLogin());
  837. assertThat(actual.getCreationDate()).isEqualTo(formatDateTime(hotspot.getIssueCreationDate()));
  838. assertThat(actual.getUpdateDate()).isEqualTo(formatDateTime(hotspot.getIssueUpdateDate()));
  839. }
  840. @DataProvider
  841. public static Object[][] validStatusesAndResolutions() {
  842. return new Object[][]{
  843. {STATUS_TO_REVIEW, null},
  844. {STATUS_REVIEWED, RESOLUTION_FIXED},
  845. {STATUS_REVIEWED, RESOLUTION_SAFE},
  846. };
  847. }
  848. @Test
  849. @UseDataProvider("allSQCategories")
  850. public void returns_SQCategory_and_VulnerabilityProbability_of_rule(Set<String> securityStandards, SQCategory expected) {
  851. ComponentDto project = dbTester.components().insertPublicProject();
  852. userSessionRule.registerComponents(project);
  853. indexPermissions();
  854. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  855. RuleDto rule = newRule(SECURITY_HOTSPOT, t -> t.setSecurityStandards(securityStandards));
  856. IssueDto hotspot = insertHotspot(project, file, rule);
  857. indexIssues();
  858. SearchWsResponse response = newRequest(project)
  859. .executeProtobuf(SearchWsResponse.class);
  860. assertThat(response.getHotspotsList()).hasSize(1);
  861. Hotspots.SearchWsResponse.Hotspot actual = response.getHotspots(0);
  862. assertThat(actual.getSecurityCategory()).isEqualTo(expected.getKey());
  863. assertThat(actual.getVulnerabilityProbability()).isEqualTo(expected.getVulnerability().name());
  864. }
  865. @DataProvider
  866. public static Object[][] allSQCategories() {
  867. Stream<Object[]> allCategoriesButOTHERS = SecurityStandards.CWES_BY_SQ_CATEGORY.entrySet()
  868. .stream()
  869. .map(t -> new Object[]{
  870. t.getValue().stream().map(c -> "cwe:" + c).collect(toSet()),
  871. t.getKey()
  872. });
  873. Stream<Object[]> sqCategoryOTHERS = Stream.of(
  874. new Object[]{Collections.emptySet(), SQCategory.OTHERS},
  875. new Object[]{of("foo", "donut", "acme"), SQCategory.OTHERS});
  876. return Stream.concat(allCategoriesButOTHERS, sqCategoryOTHERS).toArray(Object[][]::new);
  877. }
  878. @Test
  879. public void does_not_fail_when_hotspot_has_none_of_the_nullable_fields() {
  880. ComponentDto project = dbTester.components().insertPublicProject();
  881. userSessionRule.registerComponents(project);
  882. indexPermissions();
  883. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  884. RuleDto rule = newRule(SECURITY_HOTSPOT);
  885. insertHotspot(rule, project, file,
  886. t -> t.setResolution(null)
  887. .setLine(null)
  888. .setMessage(null)
  889. .setAssigneeUuid(null)
  890. .setAuthorLogin(null));
  891. indexIssues();
  892. SearchWsResponse response = newRequest(project)
  893. .executeProtobuf(SearchWsResponse.class);
  894. assertThat(response.getHotspotsList())
  895. .hasSize(1);
  896. SearchWsResponse.Hotspot actual = response.getHotspots(0);
  897. assertThat(actual.hasResolution()).isFalse();
  898. assertThat(actual.hasLine()).isFalse();
  899. assertThat(actual.getMessage()).isEmpty();
  900. assertThat(actual.hasAssignee()).isFalse();
  901. assertThat(actual.getAuthor()).isEmpty();
  902. }
  903. @Test
  904. public void returns_details_of_components() {
  905. ComponentDto project = dbTester.components().insertPublicProject();
  906. userSessionRule.registerComponents(project);
  907. indexPermissions();
  908. ComponentDto directory = dbTester.components().insertComponent(newDirectory(project, "donut/acme"));
  909. ComponentDto directory2 = dbTester.components().insertComponent(newDirectory(project, "foo/bar"));
  910. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  911. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project));
  912. RuleDto rule = newRule(SECURITY_HOTSPOT);
  913. IssueDto fileHotspot = insertHotspot(project, file, rule);
  914. IssueDto dirHotspot = insertHotspot(project, directory, rule);
  915. IssueDto projectHotspot = insertHotspot(project, project, rule);
  916. indexIssues();
  917. SearchWsResponse response = newRequest(project)
  918. .executeProtobuf(SearchWsResponse.class);
  919. assertThat(response.getHotspotsList())
  920. .extracting(SearchWsResponse.Hotspot::getKey)
  921. .containsOnly(fileHotspot.getKey(), dirHotspot.getKey(), projectHotspot.getKey());
  922. assertThat(response.getComponentsList()).hasSize(3);
  923. assertThat(response.getComponentsList())
  924. .extracting(Component::getKey)
  925. .containsOnly(project.getKey(), directory.getKey(), file.getKey());
  926. Map<String, Component> componentByKey = response.getComponentsList().stream().collect(uniqueIndex(Component::getKey));
  927. Component actualProject = componentByKey.get(project.getKey());
  928. assertThat(actualProject.getQualifier()).isEqualTo(project.qualifier());
  929. assertThat(actualProject.getName()).isEqualTo(project.name());
  930. assertThat(actualProject.getLongName()).isEqualTo(project.longName());
  931. assertThat(actualProject.hasPath()).isFalse();
  932. assertThat(actualProject.hasBranch()).isFalse();
  933. assertThat(actualProject.hasPullRequest()).isFalse();
  934. Component actualDirectory = componentByKey.get(directory.getKey());
  935. assertThat(actualDirectory.getQualifier()).isEqualTo(directory.qualifier());
  936. assertThat(actualDirectory.getName()).isEqualTo(directory.name());
  937. assertThat(actualDirectory.getLongName()).isEqualTo(directory.longName());
  938. assertThat(actualDirectory.getPath()).isEqualTo(directory.path());
  939. assertThat(actualDirectory.hasBranch()).isFalse();
  940. assertThat(actualDirectory.hasPullRequest()).isFalse();
  941. Component actualFile = componentByKey.get(file.getKey());
  942. assertThat(actualFile.getQualifier()).isEqualTo(file.qualifier());
  943. assertThat(actualFile.getName()).isEqualTo(file.name());
  944. assertThat(actualFile.getLongName()).isEqualTo(file.longName());
  945. assertThat(actualFile.getPath()).isEqualTo(file.path());
  946. assertThat(actualFile.hasBranch()).isFalse();
  947. assertThat(actualFile.hasPullRequest()).isFalse();
  948. }
  949. @Test
  950. public void returns_branch_field_of_components_of_branch() {
  951. ComponentDto project = dbTester.components().insertPublicProject();
  952. ComponentDto branch = dbTester.components().insertProjectBranch(project);
  953. userSessionRule.registerComponents(project, branch);
  954. indexPermissions();
  955. ComponentDto directory = dbTester.components().insertComponent(newDirectory(branch, "donut/acme"));
  956. ComponentDto file = dbTester.components().insertComponent(newFileDto(branch));
  957. RuleDto rule = newRule(SECURITY_HOTSPOT);
  958. IssueDto fileHotspot = insertHotspot(branch, file, rule);
  959. IssueDto dirHotspot = insertHotspot(branch, directory, rule);
  960. IssueDto projectHotspot = insertHotspot(branch, branch, rule);
  961. indexIssues();
  962. SearchWsResponse response = newRequest(branch)
  963. .executeProtobuf(SearchWsResponse.class);
  964. assertThat(response.getHotspotsList())
  965. .extracting(SearchWsResponse.Hotspot::getKey)
  966. .containsOnly(fileHotspot.getKey(), dirHotspot.getKey(), projectHotspot.getKey());
  967. assertThat(response.getComponentsList())
  968. .extracting(Component::getKey)
  969. .containsOnly(project.getKey(), directory.getKey(), file.getKey());
  970. Map<String, Component> componentByKey = response.getComponentsList().stream().collect(uniqueIndex(Component::getKey));
  971. Component actualProject = componentByKey.get(project.getKey());
  972. assertThat(actualProject.getBranch()).isEqualTo(branch.getBranch());
  973. assertThat(actualProject.hasPullRequest()).isFalse();
  974. Component actualDirectory = componentByKey.get(directory.getKey());
  975. assertThat(actualDirectory.getBranch()).isEqualTo(branch.getBranch());
  976. assertThat(actualDirectory.hasPullRequest()).isFalse();
  977. Component actualFile = componentByKey.get(file.getKey());
  978. assertThat(actualFile.getBranch()).isEqualTo(branch.getBranch());
  979. assertThat(actualFile.hasPullRequest()).isFalse();
  980. }
  981. @Test
  982. public void returns_pullRequest_field_of_components_of_pullRequest() {
  983. ComponentDto project = dbTester.components().insertPublicProject();
  984. ComponentDto pullRequest = dbTester.components().insertProjectBranch(project, t -> t.setBranchType(BranchType.PULL_REQUEST));
  985. userSessionRule.registerComponents(project, pullRequest);
  986. indexPermissions();
  987. ComponentDto directory = dbTester.components().insertComponent(newDirectory(pullRequest, "donut/acme"));
  988. ComponentDto file = dbTester.components().insertComponent(newFileDto(pullRequest));
  989. RuleDto rule = newRule(SECURITY_HOTSPOT);
  990. IssueDto fileHotspot = insertHotspot(pullRequest, file, rule);
  991. IssueDto dirHotspot = insertHotspot(pullRequest, directory, rule);
  992. IssueDto projectHotspot = insertHotspot(pullRequest, pullRequest, rule);
  993. indexIssues();
  994. SearchWsResponse response = newRequest(pullRequest)
  995. .executeProtobuf(SearchWsResponse.class);
  996. assertThat(response.getHotspotsList())
  997. .extracting(SearchWsResponse.Hotspot::getKey)
  998. .containsOnly(fileHotspot.getKey(), dirHotspot.getKey(), projectHotspot.getKey());
  999. assertThat(response.getComponentsList())
  1000. .extracting(Component::getKey)
  1001. .containsOnly(project.getKey(), directory.getKey(), file.getKey());
  1002. Map<String, Component> componentByKey = response.getComponentsList().stream().collect(uniqueIndex(Component::getKey));
  1003. Component actualProject = componentByKey.get(project.getKey());
  1004. assertThat(actualProject.hasBranch()).isFalse();
  1005. assertThat(actualProject.getPullRequest()).isEqualTo(pullRequest.getPullRequest());
  1006. Component actualDirectory = componentByKey.get(directory.getKey());
  1007. assertThat(actualDirectory.hasBranch()).isFalse();
  1008. assertThat(actualDirectory.getPullRequest()).isEqualTo(pullRequest.getPullRequest());
  1009. Component actualFile = componentByKey.get(file.getKey());
  1010. assertThat(actualFile.hasBranch()).isFalse();
  1011. assertThat(actualFile.getPullRequest()).isEqualTo(pullRequest.getPullRequest());
  1012. }
  1013. @Test
  1014. public void returns_hotspots_ordered_by_vulnerabilityProbability_score_then_rule_uuid() {
  1015. ComponentDto project = dbTester.components().insertPublicProject();
  1016. userSessionRule.registerComponents(project);
  1017. indexPermissions();
  1018. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1019. List<IssueDto> hotspots = Arrays.stream(SQCategory.values())
  1020. .sorted(Ordering.from(Comparator.<SQCategory>comparingInt(t1 -> t1.getVulnerability().getScore()).reversed())
  1021. .thenComparing(SQCategory::getKey))
  1022. .flatMap(sqCategory -> {
  1023. Set<String> cwes = SecurityStandards.CWES_BY_SQ_CATEGORY.get(sqCategory);
  1024. Set<String> securityStandards = singleton("cwe:" + (cwes == null ? "unknown" : cwes.iterator().next()));
  1025. RuleDto rule1 = newRule(
  1026. SECURITY_HOTSPOT,
  1027. t -> t.setUuid(sqCategory.name() + "_a").setName("rule_" + sqCategory.name() + "_a").setSecurityStandards(securityStandards));
  1028. RuleDto rule2 = newRule(
  1029. SECURITY_HOTSPOT,
  1030. t -> t.setUuid(sqCategory.name() + "_b").setName("rule_" + sqCategory.name() + "_b").setSecurityStandards(securityStandards));
  1031. return Stream.of(
  1032. newHotspot(rule1, project, file).setKee(sqCategory + "_a"),
  1033. newHotspot(rule2, project, file).setKee(sqCategory + "_b"));
  1034. })
  1035. .collect(toList());
  1036. String[] expectedHotspotKeys = hotspots.stream().map(IssueDto::getKey).toArray(String[]::new);
  1037. // insert hotspots in random order
  1038. Collections.shuffle(hotspots);
  1039. hotspots.forEach(dbTester.issues()::insertHotspot);
  1040. indexIssues();
  1041. SearchWsResponse response = newRequest(project)
  1042. .executeProtobuf(SearchWsResponse.class);
  1043. assertThat(response.getHotspotsList())
  1044. .extracting(SearchWsResponse.Hotspot::getKey)
  1045. .containsExactly(expectedHotspotKeys);
  1046. }
  1047. @Test
  1048. public void returns_hotspots_ordered_by_file_path_then_line_then_key() {
  1049. ComponentDto project = dbTester.components().insertPublicProject();
  1050. userSessionRule.registerComponents(project);
  1051. indexPermissions();
  1052. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project).setPath("b/c/a"));
  1053. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project).setPath("b/c/b"));
  1054. ComponentDto file3 = dbTester.components().insertComponent(newFileDto(project).setPath("a/a/d"));
  1055. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1056. List<IssueDto> hotspots = Stream.of(
  1057. newHotspot(rule, project, file3).setLine(8),
  1058. newHotspot(rule, project, file3).setLine(10),
  1059. newHotspot(rule, project, file1).setLine(null),
  1060. newHotspot(rule, project, file1).setLine(9),
  1061. newHotspot(rule, project, file1).setLine(11).setKee("a"),
  1062. newHotspot(rule, project, file1).setLine(11).setKee("b"),
  1063. newHotspot(rule, project, file2).setLine(null),
  1064. newHotspot(rule, project, file2).setLine(2))
  1065. .collect(toList());
  1066. String[] expectedHotspotKeys = hotspots.stream().map(IssueDto::getKey).toArray(String[]::new);
  1067. // insert hotspots in random order
  1068. Collections.shuffle(hotspots);
  1069. hotspots.forEach(dbTester.issues()::insertHotspot);
  1070. indexIssues();
  1071. SearchWsResponse response = newRequest(project)
  1072. .executeProtobuf(SearchWsResponse.class);
  1073. assertThat(response.getHotspotsList())
  1074. .extracting(SearchWsResponse.Hotspot::getKey)
  1075. .containsExactly(expectedHotspotKeys);
  1076. }
  1077. @Test
  1078. public void returns_hotspot_with_secondary_locations() {
  1079. ComponentDto project = dbTester.components().insertPublicProject();
  1080. userSessionRule.registerComponents(project);
  1081. indexPermissions();
  1082. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1083. ComponentDto anotherFile = dbTester.components().insertComponent(newFileDto(project));
  1084. List<DbIssues.Location> hotspotLocations = Stream.of(
  1085. newHotspotLocation(file.uuid(), "security hotspot flow message 0", 1, 1, 0, 12),
  1086. newHotspotLocation(file.uuid(), "security hotspot flow message 1", 3, 3, 0, 10),
  1087. newHotspotLocation(anotherFile.uuid(), "security hotspot flow message 2", 5, 5, 0, 15),
  1088. newHotspotLocation(anotherFile.uuid(), "security hotspot flow message 3", 7, 7, 0, 18),
  1089. newHotspotLocation(null,"security hotspot flow message 4", 12, 12, 2, 8))
  1090. .collect(toList());
  1091. DbIssues.Locations.Builder locations = DbIssues.Locations.newBuilder().addFlow(DbIssues.Flow.newBuilder().addAllLocation(hotspotLocations));
  1092. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1093. dbTester.issues().insertHotspot(rule, project, file, h -> h.setLocations(locations.build()));
  1094. indexIssues();
  1095. SearchWsResponse response = newRequest(project)
  1096. .executeProtobuf(SearchWsResponse.class);
  1097. assertThat(response.getHotspotsCount()).isOne();
  1098. assertThat(response.getHotspotsList().stream().findFirst().get().getFlowsCount()).isEqualTo(1);
  1099. assertThat(response.getHotspotsList().stream().findFirst().get().getFlowsList().stream().findFirst().get().getLocationsCount()).isEqualTo(5);
  1100. assertThat(response.getHotspotsList().stream().findFirst().get().getFlowsList().stream().findFirst().get().getLocationsList())
  1101. .extracting(
  1102. Common.Location::getComponent,
  1103. Common.Location::getMsg,
  1104. l -> l.getTextRange().getStartLine(),
  1105. l -> l.getTextRange().getEndLine(),
  1106. l -> l.getTextRange().getStartOffset(),
  1107. l -> l.getTextRange().getEndOffset())
  1108. .containsExactlyInAnyOrder(
  1109. tuple(file.getKey(), "security hotspot flow message 0", 1, 1, 0, 12),
  1110. tuple(file.getKey(), "security hotspot flow message 1", 3, 3, 0, 10),
  1111. tuple(anotherFile.getKey(), "security hotspot flow message 2", 5, 5, 0, 15),
  1112. tuple(anotherFile.getKey(), "security hotspot flow message 3", 7, 7, 0, 18),
  1113. tuple(file.getKey(),"security hotspot flow message 4", 12, 12, 2, 8));
  1114. }
  1115. @Test
  1116. public void returns_first_page_with_100_results_by_default() {
  1117. ComponentDto project = dbTester.components().insertPublicProject();
  1118. userSessionRule.registerComponents(project);
  1119. indexPermissions();
  1120. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1121. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1122. int total = 436;
  1123. List<IssueDto> hotspots = IntStream.range(0, total)
  1124. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1125. .collect(toList());
  1126. indexIssues();
  1127. TestRequest request = newRequest(project);
  1128. SearchWsResponse response = request.executeProtobuf(SearchWsResponse.class);
  1129. assertThat(response.getHotspotsList())
  1130. .extracting(SearchWsResponse.Hotspot::getKey)
  1131. .containsExactly(hotspots.stream().limit(100).map(IssueDto::getKey).toArray(String[]::new));
  1132. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1133. assertThat(response.getPaging().getPageIndex()).isOne();
  1134. assertThat(response.getPaging().getPageSize()).isEqualTo(100);
  1135. }
  1136. @Test
  1137. public void returns_specified_page_with_100_results_by_default() {
  1138. ComponentDto project = dbTester.components().insertPublicProject();
  1139. userSessionRule.registerComponents(project);
  1140. indexPermissions();
  1141. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1142. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1143. verifyPaging(project, file, rule, 336, 100);
  1144. }
  1145. @Test
  1146. public void returns_specified_page_with_specified_number_of_results() {
  1147. ComponentDto project = dbTester.components().insertPublicProject();
  1148. userSessionRule.registerComponents(project);
  1149. indexPermissions();
  1150. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1151. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1152. int total = 336;
  1153. int pageSize = 1 + new Random().nextInt(100);
  1154. verifyPaging(project, file, rule, total, pageSize);
  1155. }
  1156. private void verifyPaging(ComponentDto project, ComponentDto file, RuleDto rule, int total, int pageSize) {
  1157. List<IssueDto> hotspots = IntStream.range(0, total)
  1158. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i).setKee("issue_" + i)))
  1159. .collect(toList());
  1160. indexIssues();
  1161. SearchWsResponse response = newRequest(project)
  1162. .setParam("p", "3")
  1163. .setParam("ps", String.valueOf(pageSize))
  1164. .executeProtobuf(SearchWsResponse.class);
  1165. assertThat(response.getHotspotsList())
  1166. .extracting(SearchWsResponse.Hotspot::getKey)
  1167. .containsExactly(hotspots.stream().skip(2 * pageSize).limit(pageSize).map(IssueDto::getKey).toArray(String[]::new));
  1168. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1169. assertThat(response.getPaging().getPageIndex()).isEqualTo(3);
  1170. assertThat(response.getPaging().getPageSize()).isEqualTo(pageSize);
  1171. response = newRequest(project)
  1172. .setParam("p", "4")
  1173. .setParam("ps", String.valueOf(pageSize))
  1174. .executeProtobuf(SearchWsResponse.class);
  1175. assertThat(response.getHotspotsList())
  1176. .extracting(SearchWsResponse.Hotspot::getKey)
  1177. .containsExactly(hotspots.stream().skip(3 * pageSize).limit(pageSize).map(IssueDto::getKey).toArray(String[]::new));
  1178. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1179. assertThat(response.getPaging().getPageIndex()).isEqualTo(4);
  1180. assertThat(response.getPaging().getPageSize()).isEqualTo(pageSize);
  1181. int emptyPage = (hotspots.size() / pageSize) + 10;
  1182. response = newRequest(project)
  1183. .setParam("p", String.valueOf(emptyPage))
  1184. .setParam("ps", String.valueOf(pageSize))
  1185. .executeProtobuf(SearchWsResponse.class);
  1186. assertThat(response.getHotspotsList())
  1187. .extracting(SearchWsResponse.Hotspot::getKey)
  1188. .isEmpty();
  1189. assertThat(response.getPaging().getTotal()).isEqualTo(hotspots.size());
  1190. assertThat(response.getPaging().getPageIndex()).isEqualTo(emptyPage);
  1191. assertThat(response.getPaging().getPageSize()).isEqualTo(pageSize);
  1192. }
  1193. @Test
  1194. public void returns_empty_if_none_of_hotspot_keys_exist() {
  1195. ComponentDto project = dbTester.components().insertPublicProject();
  1196. userSessionRule.registerComponents(project);
  1197. indexPermissions();
  1198. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1199. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1200. List<IssueDto> hotspots = IntStream.range(0, 1 + RANDOM.nextInt(15))
  1201. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1202. .collect(toList());
  1203. indexIssues();
  1204. SearchWsResponse response = newRequest(IntStream.range(0, 1 + RANDOM.nextInt(30)).mapToObj(i -> "key_" + i).collect(toList()))
  1205. .executeProtobuf(SearchWsResponse.class);
  1206. verify(issueIndexSyncProgressChecker).checkIfIssueSyncInProgress(any());
  1207. assertThat(response.getHotspotsList()).isEmpty();
  1208. }
  1209. @Test
  1210. public void returns_specified_hotspots() {
  1211. ComponentDto project = dbTester.components().insertPublicProject();
  1212. userSessionRule.registerComponents(project);
  1213. indexPermissions();
  1214. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1215. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1216. int total = 1 + RANDOM.nextInt(20);
  1217. List<IssueDto> hotspots = IntStream.range(0, total)
  1218. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1219. .collect(toList());
  1220. Collections.shuffle(hotspots);
  1221. List<IssueDto> selectedHotspots = hotspots.stream().limit(total == 1 ? 1 : 1 + RANDOM.nextInt(total - 1)).collect(toList());
  1222. indexIssues();
  1223. SearchWsResponse response = newRequest(selectedHotspots.stream().map(IssueDto::getKey).collect(toList()))
  1224. .executeProtobuf(SearchWsResponse.class);
  1225. assertThat(response.getHotspotsList())
  1226. .extracting(SearchWsResponse.Hotspot::getKey)
  1227. .containsExactlyInAnyOrder(selectedHotspots.stream().map(IssueDto::getKey).toArray(String[]::new));
  1228. }
  1229. @Test
  1230. public void returns_hotspots_with_specified_sonarsourceSecurity_category() {
  1231. ComponentDto project = dbTester.components().insertPublicProject();
  1232. userSessionRule.registerComponents(project);
  1233. indexPermissions();
  1234. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1235. RuleDto rule1 = newRule(SECURITY_HOTSPOT);
  1236. RuleDto rule2 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("cwe:117", "cwe:190")));
  1237. RuleDto rule3 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("owaspTop10:a1", "cwe:601")));
  1238. insertHotspot(project, file, rule1);
  1239. IssueDto hotspot2 = insertHotspot(project, file, rule2);
  1240. insertHotspot(project, file, rule3);
  1241. indexIssues();
  1242. SearchWsResponse response = newRequest(project).setParam(PARAM_SONARSOURCE_SECURITY, "log-injection")
  1243. .executeProtobuf(SearchWsResponse.class);
  1244. assertThat(response.getHotspotsList())
  1245. .extracting(SearchWsResponse.Hotspot::getKey)
  1246. .containsExactly(hotspot2.getKey());
  1247. }
  1248. @Test
  1249. public void returns_hotspots_with_specified_cwes() {
  1250. ComponentDto project = dbTester.components().insertPublicProject();
  1251. userSessionRule.registerComponents(project);
  1252. indexPermissions();
  1253. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1254. RuleDto rule1 = newRule(SECURITY_HOTSPOT);
  1255. RuleDto rule2 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("cwe:117", "cwe:190")));
  1256. RuleDto rule3 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("owaspTop10:a1", "cwe:601")));
  1257. insertHotspot(project, file, rule1);
  1258. IssueDto hotspot2 = insertHotspot(project, file, rule2);
  1259. insertHotspot(project, file, rule3);
  1260. indexIssues();
  1261. SearchWsResponse response = newRequest(project).setParam(PARAM_CWE, "117,190")
  1262. .executeProtobuf(SearchWsResponse.class);
  1263. assertThat(response.getHotspotsList())
  1264. .extracting(SearchWsResponse.Hotspot::getKey)
  1265. .containsExactly(hotspot2.getKey());
  1266. }
  1267. @Test
  1268. public void returns_hotspots_with_specified_owaspTop10_category() {
  1269. ComponentDto project = dbTester.components().insertPublicProject();
  1270. userSessionRule.registerComponents(project);
  1271. indexPermissions();
  1272. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1273. RuleDto rule1 = newRule(SECURITY_HOTSPOT);
  1274. RuleDto rule2 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("cwe:117", "cwe:190")));
  1275. RuleDto rule3 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("owaspTop10:a1", "cwe:601")));
  1276. insertHotspot(project, file, rule1);
  1277. insertHotspot(project, file, rule2);
  1278. IssueDto hotspot3 = insertHotspot(project, file, rule3);
  1279. indexIssues();
  1280. SearchWsResponse response = newRequest(project).setParam(PARAM_OWASP_TOP_10_2017, "a1")
  1281. .executeProtobuf(SearchWsResponse.class);
  1282. assertThat(response.getHotspotsList())
  1283. .extracting(SearchWsResponse.Hotspot::getKey)
  1284. .containsExactly(hotspot3.getKey());
  1285. }
  1286. @Test
  1287. public void returns_hotspots_with_specified_pciDss_category() {
  1288. ComponentDto project = dbTester.components().insertPublicProject();
  1289. userSessionRule.registerComponents(project);
  1290. indexPermissions();
  1291. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1292. RuleDto rule1 = newRule(SECURITY_HOTSPOT);
  1293. RuleDto rule2 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("cwe:117", "cwe:190")));
  1294. RuleDto rule3 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("pciDss-3.2:1.2.3")));
  1295. RuleDto rule4 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("pciDss-4.0:2.3a")));
  1296. insertHotspot(project, file, rule1);
  1297. insertHotspot(project, file, rule2);
  1298. IssueDto hotspot3 = insertHotspot(project, file, rule3);
  1299. IssueDto hotspot4 = insertHotspot(project, file, rule4);
  1300. indexIssues();
  1301. SearchWsResponse response32 = newRequest(project).setParam(PARAM_PCI_DSS_32, "1")
  1302. .executeProtobuf(SearchWsResponse.class);
  1303. assertThat(response32.getHotspotsList())
  1304. .extracting(SearchWsResponse.Hotspot::getKey)
  1305. .containsExactly(hotspot3.getKey());
  1306. SearchWsResponse response40 = newRequest(project).setParam(PARAM_PCI_DSS_40, "2")
  1307. .executeProtobuf(SearchWsResponse.class);
  1308. assertThat(response40.getHotspotsList())
  1309. .extracting(SearchWsResponse.Hotspot::getKey)
  1310. .containsExactly(hotspot4.getKey());
  1311. }
  1312. @Test
  1313. public void returns_hotspots_with_specified_owasp2021Top10_category() {
  1314. ComponentDto project = dbTester.components().insertPublicProject();
  1315. userSessionRule.registerComponents(project);
  1316. indexPermissions();
  1317. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1318. RuleDto rule1 = newRule(SECURITY_HOTSPOT);
  1319. RuleDto rule2 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("cwe:117", "cwe:190")));
  1320. RuleDto rule3 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("owaspTop10-2021:a5", "cwe:489")));
  1321. insertHotspot(project, file, rule1);
  1322. insertHotspot(project, file, rule2);
  1323. IssueDto hotspot3 = insertHotspot(project, file, rule3);
  1324. indexIssues();
  1325. SearchWsResponse response = newRequest(project).setParam(PARAM_OWASP_TOP_10_2021, "a5")
  1326. .executeProtobuf(SearchWsResponse.class);
  1327. assertThat(response.getHotspotsList())
  1328. .extracting(SearchWsResponse.Hotspot::getKey)
  1329. .containsExactly(hotspot3.getKey());
  1330. }
  1331. @Test
  1332. public void returns_hotspots_with_specified_sansTop25_category() {
  1333. ComponentDto project = dbTester.components().insertPublicProject();
  1334. userSessionRule.registerComponents(project);
  1335. indexPermissions();
  1336. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1337. RuleDto rule1 = newRule(SECURITY_HOTSPOT);
  1338. RuleDto rule2 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("cwe:117", "cwe:190")));
  1339. RuleDto rule3 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("owaspTop10:a1", "cwe:601")));
  1340. insertHotspot(project, file, rule1);
  1341. insertHotspot(project, file, rule2);
  1342. IssueDto hotspot3 = insertHotspot(project, file, rule3);
  1343. indexIssues();
  1344. SearchWsResponse response = newRequest(project).setParam(PARAM_SANS_TOP_25, "insecure-interaction")
  1345. .executeProtobuf(SearchWsResponse.class);
  1346. assertThat(response.getHotspotsList())
  1347. .extracting(SearchWsResponse.Hotspot::getKey)
  1348. .containsExactly(hotspot3.getKey());
  1349. }
  1350. @Test
  1351. public void returns_hotspots_with_specified_files() {
  1352. ComponentDto project = dbTester.components().insertPublicProject();
  1353. userSessionRule.registerComponents(project);
  1354. indexPermissions();
  1355. ComponentDto file1 = dbTester.components().insertComponent(newFileDto(project));
  1356. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project));
  1357. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1358. final IssueDto hotspot = insertHotspot(project, file1, rule);
  1359. insertHotspot(project, file2, rule);
  1360. indexIssues();
  1361. SearchWsResponse response = newRequest(project).setParam(PARAM_FILES, file1.path())
  1362. .executeProtobuf(SearchWsResponse.class);
  1363. assertThat(response.getHotspotsList())
  1364. .extracting(SearchWsResponse.Hotspot::getKey)
  1365. .containsExactly(hotspot.getKey());
  1366. }
  1367. @Test
  1368. public void returns_hotspots_on_the_leak_period_when_inNewCodePeriod_is_true() {
  1369. ComponentDto project = dbTester.components().insertPublicProject();
  1370. userSessionRule.registerComponents(project);
  1371. indexPermissions();
  1372. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1373. long periodDate = 800_996_999_332L;
  1374. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(periodDate).setLast(false));
  1375. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(periodDate - 1_500).setLast(true));
  1376. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1377. List<IssueDto> hotspotsInLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1378. .mapToObj(i -> {
  1379. long issueCreationDate = periodDate + ONE_MINUTE + (RANDOM.nextInt(300) * ONE_MINUTE);
  1380. return dbTester.issues().insertHotspot(rule, project, file,
  1381. t -> t.setLine(i).setIssueCreationTime(issueCreationDate));
  1382. })
  1383. .collect(toList());
  1384. // included because
  1385. List<IssueDto> atLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1386. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file,
  1387. t -> t.setType(SECURITY_HOTSPOT).setLine(i).setIssueCreationTime(periodDate)))
  1388. .collect(toList());
  1389. List<IssueDto> hotspotsBefore = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1390. .mapToObj(i -> {
  1391. long issueCreationDate = periodDate - ONE_MINUTE - (RANDOM.nextInt(300) * ONE_MINUTE);
  1392. return dbTester.issues().insertHotspot(rule, project, file,
  1393. t -> t.setLine(i).setIssueCreationTime(issueCreationDate));
  1394. })
  1395. .collect(toList());
  1396. indexIssues();
  1397. SearchWsResponse responseAll = newRequest(project)
  1398. .executeProtobuf(SearchWsResponse.class);
  1399. assertThat(responseAll.getHotspotsList())
  1400. .extracting(SearchWsResponse.Hotspot::getKey)
  1401. .containsExactlyInAnyOrder(Stream.of(
  1402. hotspotsInLeakPeriod.stream(),
  1403. atLeakPeriod.stream(),
  1404. hotspotsBefore.stream())
  1405. .flatMap(t -> t)
  1406. .map(IssueDto::getKey)
  1407. .toArray(String[]::new));
  1408. SearchWsResponse responseOnLeak = newRequest(project,
  1409. t -> t.setParam(PARAM_IN_NEW_CODE_PERIOD, "true"))
  1410. .executeProtobuf(SearchWsResponse.class);
  1411. assertThat(responseOnLeak.getHotspotsList())
  1412. .extracting(SearchWsResponse.Hotspot::getKey)
  1413. .containsExactlyInAnyOrder(Stream.concat(
  1414. hotspotsInLeakPeriod.stream(),
  1415. atLeakPeriod.stream())
  1416. .map(IssueDto::getKey)
  1417. .toArray(String[]::new));
  1418. }
  1419. @Test
  1420. public void returns_hotspots_on_the_leak_period_when_inNewCodePeriod_is_true_and_branch_uses_reference_branch() {
  1421. ComponentDto project = dbTester.components().insertPublicProject();
  1422. userSessionRule.registerComponents(project);
  1423. indexPermissions();
  1424. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1425. dbTester.components().insertSnapshot(project, t -> t.setPeriodMode(REFERENCE_BRANCH.name()).setPeriodParam("master"));
  1426. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1427. List<IssueDto> hotspotsInLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1428. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1429. .collect(toList());
  1430. hotspotsInLeakPeriod.stream().forEach(i -> dbTester.issues().insertNewCodeReferenceIssue(newCodeReferenceIssue(i)));
  1431. List<IssueDto> hotspotsNotInLeakPeriod = IntStream.range(0, 1 + RANDOM.nextInt(20))
  1432. .mapToObj(i -> dbTester.issues().insertHotspot(rule, project, file, t -> t.setLine(i)))
  1433. .collect(toList());
  1434. indexIssues();
  1435. SearchWsResponse responseAll = newRequest(project)
  1436. .executeProtobuf(SearchWsResponse.class);
  1437. assertThat(responseAll.getHotspotsList())
  1438. .extracting(SearchWsResponse.Hotspot::getKey)
  1439. .containsExactlyInAnyOrder(Stream.of(
  1440. hotspotsInLeakPeriod.stream(),
  1441. hotspotsNotInLeakPeriod.stream())
  1442. .flatMap(t -> t)
  1443. .map(IssueDto::getKey)
  1444. .toArray(String[]::new));
  1445. SearchWsResponse responseOnLeak = newRequest(project,
  1446. t -> t.setParam(PARAM_IN_NEW_CODE_PERIOD, "true"))
  1447. .executeProtobuf(SearchWsResponse.class);
  1448. assertThat(responseOnLeak.getHotspotsList())
  1449. .extracting(SearchWsResponse.Hotspot::getKey)
  1450. .containsExactlyInAnyOrder(hotspotsInLeakPeriod
  1451. .stream()
  1452. .map(IssueDto::getKey)
  1453. .toArray(String[]::new));
  1454. }
  1455. @Test
  1456. public void returns_nothing_when_inNewCodePeriod_is_true_and_no_period_exists() {
  1457. long referenceDate = 800_996_999_332L;
  1458. system2.setNow(referenceDate + 10_000);
  1459. ComponentDto project = dbTester.components().insertPublicProject();
  1460. userSessionRule.registerComponents(project);
  1461. indexPermissions();
  1462. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1463. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(referenceDate).setLast(false));
  1464. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(null).setLast(true));
  1465. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1466. IssueDto afterRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setIssueCreationTime(referenceDate + 1000));
  1467. IssueDto atRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setType(SECURITY_HOTSPOT).setIssueCreationTime(referenceDate));
  1468. IssueDto beforeRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setIssueCreationTime(referenceDate - 1000));
  1469. indexIssues();
  1470. SearchWsResponse responseAll = newRequest(project)
  1471. .executeProtobuf(SearchWsResponse.class);
  1472. assertThat(responseAll.getHotspotsList())
  1473. .extracting(SearchWsResponse.Hotspot::getKey)
  1474. .containsExactlyInAnyOrder(Stream.of(afterRef, atRef, beforeRef)
  1475. .map(IssueDto::getKey)
  1476. .toArray(String[]::new));
  1477. SearchWsResponse responseOnLeak = newRequest(project,
  1478. t -> t.setParam(PARAM_IN_NEW_CODE_PERIOD, "true"))
  1479. .executeProtobuf(SearchWsResponse.class);
  1480. assertThat(responseOnLeak.getHotspotsList()).isEmpty();
  1481. }
  1482. @Test
  1483. public void returns_all_issues_when_inNewCodePeriod_is_true_and_is_pr() {
  1484. long referenceDate = 800_996_999_332L;
  1485. system2.setNow(referenceDate + 10_000);
  1486. ComponentDto project = dbTester.components().insertPublicProject();
  1487. ComponentDto pr = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST).setKey("pr"));
  1488. userSessionRule.registerComponents(project);
  1489. indexPermissions();
  1490. ComponentDto file = dbTester.components().insertComponent(newFileDto(pr));
  1491. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(referenceDate).setLast(true));
  1492. dbTester.components().insertSnapshot(pr, t -> t.setPeriodDate(null).setLast(true));
  1493. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1494. IssueDto afterRef = dbTester.issues().insertHotspot(rule, pr, file, t -> t.setIssueCreationTime(referenceDate + 1000));
  1495. IssueDto atRef = dbTester.issues().insertHotspot(rule, pr, file, t -> t.setType(SECURITY_HOTSPOT).setIssueCreationTime(referenceDate));
  1496. IssueDto beforeRef = dbTester.issues().insertHotspot(rule, pr, file, t -> t.setIssueCreationTime(referenceDate - 1000));
  1497. indexIssues();
  1498. SearchWsResponse responseAll = newRequest(project).setParam(PARAM_PULL_REQUEST, "pr")
  1499. .executeProtobuf(SearchWsResponse.class);
  1500. assertThat(responseAll.getHotspotsList())
  1501. .extracting(SearchWsResponse.Hotspot::getKey)
  1502. .containsExactlyInAnyOrder(Stream.of(afterRef, atRef, beforeRef)
  1503. .map(IssueDto::getKey)
  1504. .toArray(String[]::new));
  1505. SearchWsResponse responseOnLeak = newRequest(project,
  1506. t -> t.setParam(PARAM_IN_NEW_CODE_PERIOD, "true").setParam(PARAM_PULL_REQUEST, "pr"))
  1507. .executeProtobuf(SearchWsResponse.class);
  1508. assertThat(responseOnLeak.getHotspotsList()).hasSize(3);
  1509. }
  1510. @Test
  1511. public void returns_issues_when_inNewCodePeriod_is_true_and_is_application_for_main_branch() {
  1512. long referenceDate = 800_996_999_332L;
  1513. system2.setNow(referenceDate + 10_000);
  1514. ComponentDto application = dbTester.components().insertPublicApplication();
  1515. ComponentDto project = dbTester.components().insertPublicProject();
  1516. ComponentDto project2 = dbTester.components().insertPublicProject();
  1517. dbTester.components().addApplicationProject(application, project);
  1518. dbTester.components().addApplicationProject(application, project2);
  1519. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project, application));
  1520. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2, application));
  1521. indexViews();
  1522. userSessionRule.registerApplication(application, project, project2);
  1523. indexPermissions();
  1524. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1525. dbTester.components().insertSnapshot(project, t -> t.setPeriodDate(referenceDate).setLast(true));
  1526. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1527. IssueDto afterRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setIssueCreationTime(referenceDate + 1000));
  1528. IssueDto atRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setType(SECURITY_HOTSPOT).setIssueCreationTime(referenceDate));
  1529. IssueDto beforeRef = dbTester.issues().insertHotspot(rule, project, file, t -> t.setIssueCreationTime(referenceDate - 1000));
  1530. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2));
  1531. IssueDto project2Issue = dbTester.issues().insertHotspot(rule, project2, file2, t -> t.setIssueCreationTime(referenceDate - 1000));
  1532. indexIssues();
  1533. SearchWsResponse responseAll = newRequest(application)
  1534. .executeProtobuf(SearchWsResponse.class);
  1535. assertThat(responseAll.getHotspotsList())
  1536. .extracting(SearchWsResponse.Hotspot::getKey)
  1537. .containsExactlyInAnyOrder(afterRef.getKey(), atRef.getKey(), beforeRef.getKey(), project2Issue.getKey());
  1538. SearchWsResponse responseOnLeak = newRequest(application,
  1539. t -> t.setParam(PARAM_IN_NEW_CODE_PERIOD, "true"))
  1540. .executeProtobuf(SearchWsResponse.class);
  1541. assertThat(responseOnLeak.getHotspotsList())
  1542. .extracting(SearchWsResponse.Hotspot::getKey)
  1543. .containsExactlyInAnyOrder(afterRef.getKey());
  1544. }
  1545. @Test
  1546. public void returns_issues_when_inNewCodePeriod_is_true_and_is_application_for_branch_other_than_main() {
  1547. long referenceDate = 800_996_999_332L;
  1548. system2.setNow(referenceDate + 10_000);
  1549. ProjectDto application = dbTester.components().insertPublicApplicationDto();
  1550. BranchDto applicationBranch = dbTester.components().insertProjectBranch(application, branchDto -> branchDto.setKey("application_branch_1"));
  1551. ProjectDto project = dbTester.components().insertPublicProjectDto();
  1552. BranchDto projectBranch = dbTester.components().insertProjectBranch(project, branchDto -> branchDto.setKey("project_1_branch_1"));
  1553. ProjectDto project2 = dbTester.components().insertPublicProjectDto();
  1554. BranchDto project2Branch = dbTester.components().insertProjectBranch(project2, branchDto -> branchDto.setKey("project_2_branch_1"));
  1555. dbTester.components().addApplicationProject(application, project);
  1556. dbTester.components().addApplicationProject(application, project2);
  1557. dbTester.components().addProjectBranchToApplicationBranch(applicationBranch, projectBranch, project2Branch);
  1558. ComponentDto applicationBranchComponentDto = dbClient.componentDao().selectByUuid(dbTester.getSession(), applicationBranch.getUuid()).get();
  1559. ComponentDto projectBranchComponentDto = dbClient.componentDao().selectByUuid(dbTester.getSession(), projectBranch.getUuid()).get();
  1560. ComponentDto project2BranchComponentDto = dbClient.componentDao().selectByUuid(dbTester.getSession(), project2Branch.getUuid()).get();
  1561. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(projectBranchComponentDto, applicationBranchComponentDto));
  1562. dbTester.components().insertComponent(ComponentTesting.newProjectCopy(project2BranchComponentDto, applicationBranchComponentDto));
  1563. indexViews();
  1564. userSessionRule.registerApplication(application, project, project2);
  1565. indexPermissions();
  1566. ComponentDto file = dbTester.components().insertComponent(newFileDto(projectBranchComponentDto));
  1567. dbTester.components().insertSnapshot(projectBranch, t -> t.setPeriodDate(referenceDate).setLast(true));
  1568. RuleDto rule = newRule(SECURITY_HOTSPOT);
  1569. IssueDto afterRef = dbTester.issues().insertHotspot(rule, projectBranchComponentDto, file, t -> t.setIssueCreationTime(referenceDate + 1000));
  1570. IssueDto atRef = dbTester.issues().insertHotspot(rule, projectBranchComponentDto, file, t -> t.setType(SECURITY_HOTSPOT).setIssueCreationTime(referenceDate));
  1571. IssueDto beforeRef = dbTester.issues().insertHotspot(rule, projectBranchComponentDto, file, t -> t.setIssueCreationTime(referenceDate - 1000));
  1572. ComponentDto file2 = dbTester.components().insertComponent(newFileDto(project2BranchComponentDto));
  1573. IssueDto project2Issue = dbTester.issues().insertHotspot(rule, project2BranchComponentDto, file2, t -> t.setIssueCreationTime(referenceDate - 1000));
  1574. indexIssues();
  1575. ComponentDto applicationComponentDto = dbClient.componentDao().selectByUuid(dbTester.getSession(), application.getUuid()).get();
  1576. SearchWsResponse responseAll = newRequest(applicationComponentDto,
  1577. t -> t.setParam(PARAM_BRANCH, applicationBranch.getKey()))
  1578. .executeProtobuf(SearchWsResponse.class);
  1579. assertThat(responseAll.getHotspotsList())
  1580. .extracting(SearchWsResponse.Hotspot::getKey)
  1581. .containsExactlyInAnyOrder(afterRef.getKey(), atRef.getKey(), beforeRef.getKey(), project2Issue.getKey());
  1582. SearchWsResponse responseOnLeak = newRequest(applicationComponentDto,
  1583. t -> t.setParam(PARAM_IN_NEW_CODE_PERIOD, "true").setParam(PARAM_BRANCH, applicationBranch.getKey()))
  1584. .executeProtobuf(SearchWsResponse.class);
  1585. assertThat(responseOnLeak.getHotspotsList())
  1586. .extracting(SearchWsResponse.Hotspot::getKey)
  1587. .containsExactlyInAnyOrder(afterRef.getKey());
  1588. }
  1589. @Test
  1590. public void verify_response_example() {
  1591. ComponentDto project = dbTester.components().insertPublicProject(componentDto -> componentDto
  1592. .setName("test-project")
  1593. .setLongName("test-project")
  1594. .setDbKey("com.sonarsource:test-project"));
  1595. userSessionRule.registerComponents(project);
  1596. indexPermissions();
  1597. ComponentDto fileWithHotspot = dbTester.components().insertComponent(newFileDto(project)
  1598. .setDbKey("com.sonarsource:test-project:src/main/java/com/sonarsource/FourthClass.java")
  1599. .setName("FourthClass.java")
  1600. .setLongName("src/main/java/com/sonarsource/FourthClass.java")
  1601. .setPath("src/main/java/com/sonarsource/FourthClass.java"));
  1602. long time = 1577976190000L;
  1603. IssueDto[] hotspots = IntStream.range(0, 3)
  1604. .mapToObj(i -> {
  1605. RuleKey ruleKey = RuleKey.of("repository-"+i,"rule-"+i);
  1606. RuleDto rule = newRule(SECURITY_HOTSPOT,ruleKey)
  1607. .setSecurityStandards(Sets.newHashSet(SQCategory.WEAK_CRYPTOGRAPHY.getKey()));
  1608. return insertHotspot(rule, project, fileWithHotspot, issueDto -> issueDto.setKee("hotspot-" + i)
  1609. .setAssigneeUuid("assignee-uuid")
  1610. .setAuthorLogin("joe")
  1611. .setMessage("message-" + i)
  1612. .setLine(10 + i)
  1613. .setIssueCreationTime(time)
  1614. .setIssueUpdateTime(time));
  1615. })
  1616. .toArray(IssueDto[]::new);
  1617. indexIssues();
  1618. assertThat(actionTester.getDef().responseExampleAsString()).isNotNull();
  1619. newRequest(project)
  1620. .execute()
  1621. .assertJson(actionTester.getDef().responseExampleAsString());
  1622. }
  1623. @Test
  1624. public void returns_hotspots_with_ruleKey() {
  1625. ComponentDto project = dbTester.components().insertPublicProject();
  1626. userSessionRule.registerComponents(project);
  1627. indexPermissions();
  1628. ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
  1629. RuleDto rule1 = newRule(SECURITY_HOTSPOT);
  1630. insertHotspot(project, file, rule1);
  1631. indexIssues();
  1632. SearchWsResponse response = newRequest(project)
  1633. .executeProtobuf(SearchWsResponse.class);
  1634. assertThat(response.getHotspotsList())
  1635. .extracting(SearchWsResponse.Hotspot::getRuleKey)
  1636. .containsExactly(rule1.getKey().toString());
  1637. }
  1638. private IssueDto insertHotspot(ComponentDto project, ComponentDto file, RuleDto rule) {
  1639. return insertHotspot(rule, project, file, t -> {
  1640. });
  1641. }
  1642. private IssueDto insertHotspot(RuleDto rule, ComponentDto project, ComponentDto file, @Nullable String assigneeUuid) {
  1643. return insertHotspot(rule, project, file, t -> t.setAssigneeUuid(assigneeUuid));
  1644. }
  1645. private IssueDto insertHotspot(RuleDto rule, ComponentDto project, ComponentDto file, Consumer<IssueDto> consumer) {
  1646. return dbTester.issues().insertHotspot(rule, project, file, consumer);
  1647. }
  1648. private static IssueDto newHotspot(RuleDto rule, ComponentDto project, ComponentDto component) {
  1649. return newHotspot(rule, project, component, t -> {
  1650. });
  1651. }
  1652. private static IssueDto newHotspot(RuleDto rule, ComponentDto project, ComponentDto component, Consumer<IssueDto> consumer) {
  1653. IssueDto res = newIssue(rule, project, component)
  1654. .setStatus(STATUS_TO_REVIEW);
  1655. consumer.accept(res);
  1656. return res.setType(SECURITY_HOTSPOT);
  1657. }
  1658. private static DbIssues.Location newHotspotLocation(@Nullable String componentUuid, String message, int startLine, int endLine, int startOffset, int endOffset) {
  1659. DbIssues.Location.Builder builder = DbIssues.Location.newBuilder();
  1660. if (componentUuid != null) {
  1661. builder.setComponentId(componentUuid);
  1662. }
  1663. builder
  1664. .setMsg(message)
  1665. .setTextRange(DbCommons.TextRange.newBuilder()
  1666. .setStartLine(startLine)
  1667. .setEndLine(endLine)
  1668. .setStartOffset(startOffset)
  1669. .setEndOffset(endOffset)
  1670. .build());
  1671. return builder.build();
  1672. }
  1673. private TestRequest newRequest(ComponentDto project) {
  1674. return newRequest(project, null, null);
  1675. }
  1676. private TestRequest newRequest(ComponentDto project, Consumer<TestRequest> consumer) {
  1677. return newRequest(project, null, null, consumer);
  1678. }
  1679. private TestRequest newRequest(ComponentDto project, @Nullable String status, @Nullable String resolution) {
  1680. return newRequest(project, status, resolution, t -> {
  1681. });
  1682. }
  1683. private TestRequest newRequest(ComponentDto project, @Nullable String status, @Nullable String resolution, Consumer<TestRequest> consumer) {
  1684. TestRequest res = actionTester.newRequest()
  1685. .setParam(PARAM_PROJECT_KEY, project.getKey());
  1686. String branch = project.getBranch();
  1687. if (branch != null) {
  1688. res.setParam(PARAM_BRANCH, branch);
  1689. }
  1690. String pullRequest = project.getPullRequest();
  1691. if (pullRequest != null) {
  1692. res.setParam(PARAM_PULL_REQUEST, pullRequest);
  1693. }
  1694. if (status != null) {
  1695. res.setParam(PARAM_STATUS, status);
  1696. }
  1697. if (resolution != null) {
  1698. res.setParam(PARAM_RESOLUTION, resolution);
  1699. }
  1700. consumer.accept(res);
  1701. return res;
  1702. }
  1703. private TestRequest newRequest(Collection<String> hotspotKeys) {
  1704. return actionTester.newRequest()
  1705. .setParam(PARAM_HOTSPOTS, String.join(",", hotspotKeys));
  1706. }
  1707. private void indexPermissions() {
  1708. permissionIndexer.indexAll(permissionIndexer.getIndexTypes());
  1709. }
  1710. private void indexIssues() {
  1711. issueIndexer.indexAllIssues();
  1712. }
  1713. private void indexViews() {
  1714. viewIndexer.indexAll();
  1715. }
  1716. private RuleDto newRule(RuleType ruleType) {
  1717. return newRule(ruleType, t -> {
  1718. });
  1719. }
  1720. private RuleDto newRule(RuleType ruleType, RuleKey ruleKey){
  1721. RuleDto ruleDto = RuleTesting.newRule(ruleKey)
  1722. .setType(ruleType);
  1723. dbTester.rules().insert(ruleDto);
  1724. return ruleDto;
  1725. }
  1726. private RuleDto newRule(RuleType ruleType, Consumer<RuleDto> populate) {
  1727. RuleDto ruleDto = RuleTesting.newRule()
  1728. .setType(ruleType);
  1729. populate.accept(ruleDto);
  1730. dbTester.rules().insert(ruleDto);
  1731. return ruleDto;
  1732. }
  1733. }