diff options
author | antoine.vinot <antoine.vinot@sonarsource.com> | 2024-09-04 14:31:58 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-09-12 20:02:54 +0000 |
commit | 574637dc96a0655344a0623fa1aae861fe073a86 (patch) | |
tree | d6a3b299b9c243c8a4a1182ecc73cb07650a47fa /server/sonar-webserver-webapi | |
parent | 1d44e3b465b8df3ad3a0c63d17b2b24ef529fd79 (diff) | |
download | sonarqube-574637dc96a0655344a0623fa1aae861fe073a86.tar.gz sonarqube-574637dc96a0655344a0623fa1aae861fe073a86.zip |
SONAR-22914 Create and update APIs for CVEs
Diffstat (limited to 'server/sonar-webserver-webapi')
5 files changed, 251 insertions, 1 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/SearchActionDependenciesIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/SearchActionDependenciesIT.java new file mode 100644 index 00000000000..87b282c9e16 --- /dev/null +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/hotspot/ws/SearchActionDependenciesIT.java @@ -0,0 +1,117 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.hotspot.ws; + +import java.util.Arrays; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.api.impl.utils.TestSystem2; +import org.sonar.api.utils.System2; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ProjectData; +import org.sonar.db.dependency.CveDto; +import org.sonar.db.dependency.IssuesDependencyDto; +import org.sonar.db.issue.IssueDto; +import org.sonar.db.project.ProjectDto; +import org.sonar.db.rule.RuleDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.component.TestComponentFinder; +import org.sonar.server.es.EsTester; +import org.sonar.server.issue.TextRangeResponseFormatter; +import org.sonar.server.issue.index.AsyncIssueIndexing; +import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.issue.index.IssueIndexSyncProgressChecker; +import org.sonar.server.issue.index.IssueIndexer; +import org.sonar.server.issue.index.IssueIteratorFactory; +import org.sonar.server.permission.index.PermissionIndexerTester; +import org.sonar.server.permission.index.WebAuthorizationTypeSupport; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.Hotspots.SearchWsResponse; +import org.sonarqube.ws.Hotspots.SearchWsResponse.Hotspot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.Mockito.mock; +import static org.sonar.db.component.ComponentTesting.newFileDto; + +class SearchActionDependenciesIT { + + @RegisterExtension + private final UserSessionRule userSession = UserSessionRule.standalone(); + @RegisterExtension + private final DbTester db = DbTester.create(); + @RegisterExtension + private final EsTester es = EsTester.create(); + + private final TestSystem2 system2 = new TestSystem2(); + private final DbClient dbClient = db.getDbClient(); + private final IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new WebAuthorizationTypeSupport(userSession)); + private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), mock(AsyncIssueIndexing.class)); + private final PermissionIndexerTester permissionIndexer = new PermissionIndexerTester(es, issueIndexer); + private final HotspotWsResponseFormatter responseFormatter = new HotspotWsResponseFormatter(new TextRangeResponseFormatter()); + private final IssueIndexSyncProgressChecker issueIndexSyncProgressChecker = mock(IssueIndexSyncProgressChecker.class); + private final ComponentFinder componentFinder = TestComponentFinder.from(db); + private final SearchAction underTest = new SearchAction(dbClient, userSession, issueIndex, + issueIndexSyncProgressChecker, responseFormatter, system2, componentFinder); + private final WsActionTester ws = new WsActionTester(underTest); + + private RuleDto rule; + private ProjectData projectData; + private ComponentDto project; + private ComponentDto projectFile; + + @BeforeEach + void setup() { + rule = db.rules().insertHotspotRule(); + projectData = db.components().insertPublicProject(); + project = projectData.getMainBranchComponent(); + projectFile = db.components().insertComponent(newFileDto(project)); + } + + @Test + void search_whenAttachedToCve_shouldReturnsCveId() { + insertHotspotWithCve("1"); + insertHotspotWithCve("2"); + allowAnyoneOnProjects(projectData.getProjectDto()); + issueIndexer.indexAllIssues(); + + SearchWsResponse searchWsResponse = ws.newRequest().setParam("project", project.getKey()).executeProtobuf(SearchWsResponse.class); + + assertThat(searchWsResponse.getHotspotsList()) + .extracting(Hotspot::getKey, Hotspot::getCveId).containsExactlyInAnyOrder(tuple("hotspot_key_1", "CVE-1"), tuple("hotspot_key_2", "CVE-2")); + } + + private void insertHotspotWithCve(String suffix) { + IssueDto issueDto = db.issues().insertHotspot(rule, project, projectFile, issue -> issue.setKee("hotspot_key_"+suffix)); + var cveDto = new CveDto("cve_uuid_"+suffix, "CVE-"+suffix, "Some CVE description "+suffix, 1.0, 2.0, 3.0, 4L, 5L, 6L, 7L); + db.getDbClient().cveDao().insert(db.getSession(), cveDto); + db.issues().insertIssuesDependency(new IssuesDependencyDto(issueDto.getKee(), cveDto.uuid())); + } + + private void allowAnyoneOnProjects(ProjectDto... projects) { + userSession.registerProjects(projects); + Arrays.stream(projects).forEach(permissionIndexer::allowOnlyAnyone); + } + +} diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionDependenciesIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionDependenciesIT.java new file mode 100644 index 00000000000..f7bf2db5d24 --- /dev/null +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionDependenciesIT.java @@ -0,0 +1,129 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.issue.ws; + +import java.time.Clock; +import java.util.Arrays; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.api.resources.Languages; +import org.sonar.api.utils.Durations; +import org.sonar.api.utils.System2; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ProjectData; +import org.sonar.db.dependency.CveDto; +import org.sonar.db.dependency.IssuesDependencyDto; +import org.sonar.db.issue.IssueDto; +import org.sonar.db.project.ProjectDto; +import org.sonar.db.rule.RuleDto; +import org.sonar.server.common.avatar.AvatarResolverImpl; +import org.sonar.server.es.EsTester; +import org.sonar.server.issue.IssueFieldsSetter; +import org.sonar.server.issue.TextRangeResponseFormatter; +import org.sonar.server.issue.TransitionService; +import org.sonar.server.issue.index.AsyncIssueIndexing; +import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.issue.index.IssueIndexSyncProgressChecker; +import org.sonar.server.issue.index.IssueIndexer; +import org.sonar.server.issue.index.IssueIteratorFactory; +import org.sonar.server.issue.index.IssueQueryFactory; +import org.sonar.server.issue.workflow.FunctionExecutor; +import org.sonar.server.issue.workflow.IssueWorkflow; +import org.sonar.server.permission.index.PermissionIndexerTester; +import org.sonar.server.permission.index.WebAuthorizationTypeSupport; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.Issues.Issue; +import org.sonarqube.ws.Issues.SearchWsResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.Mockito.mock; +import static org.sonar.db.component.ComponentTesting.newFileDto; + +class SearchActionDependenciesIT { + + @RegisterExtension + private final UserSessionRule userSession = UserSessionRule.standalone(); + @RegisterExtension + private final DbTester db = DbTester.create(); + @RegisterExtension + private final EsTester es = EsTester.create(); + + private final DbClient dbClient = db.getDbClient(); + private final IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new WebAuthorizationTypeSupport(userSession)); + private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), mock(AsyncIssueIndexing.class)); + private final IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession); + private final IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); + private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); + private final SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow)); + private final Languages languages = new Languages(); + private final UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl()); + private final SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), languages, new TextRangeResponseFormatter(), userFormatter); + private final PermissionIndexerTester permissionIndexer = new PermissionIndexerTester(es, issueIndexer); + + private final IssueIndexSyncProgressChecker issueIndexSyncProgressChecker = new IssueIndexSyncProgressChecker(db.getDbClient()); + + private final WsActionTester ws = new WsActionTester( + new SearchAction(userSession, issueIndex, issueQueryFactory, issueIndexSyncProgressChecker, searchResponseLoader, searchResponseFormat, + System2.INSTANCE, dbClient)); + + private RuleDto rule; + private ProjectData projectData; + private ComponentDto project; + private ComponentDto projectFile; + + @BeforeEach + void setup() { + rule = db.rules().insertIssueRule(); + projectData = db.components().insertPublicProject(); + project = projectData.getMainBranchComponent(); + projectFile = db.components().insertComponent(newFileDto(project)); + } + + @Test + void search_whenAttachedToCve_shouldReturnsCveId() { + insertIssueWithCve("1"); + insertIssueWithCve("2"); + allowAnyoneOnProjects(projectData.getProjectDto()); + issueIndexer.indexAllIssues(); + + SearchWsResponse searchWsResponse = ws.newRequest().executeProtobuf(SearchWsResponse.class); + + assertThat(searchWsResponse.getIssuesList()) + .extracting(Issue::getKey, Issue::getCveId).containsExactlyInAnyOrder(tuple("issue_key_1", "CVE-1"), tuple("issue_key_2", "CVE-2")); + } + + private void insertIssueWithCve(String suffix) { + IssueDto issueDto = db.issues().insertIssue(rule, project, projectFile, issue -> issue.setKee("issue_key_"+suffix)); + var cveDto = new CveDto("cve_uuid_"+suffix, "CVE-"+suffix, "Some CVE description "+suffix, 1.0, 2.0, 3.0, 4L, 5L, 6L, 7L); + db.getDbClient().cveDao().insert(db.getSession(), cveDto); + db.issues().insertIssuesDependency(new IssuesDependencyDto(issueDto.getKee(), cveDto.uuid())); + } + + private void allowAnyoneOnProjects(ProjectDto... projects) { + userSession.registerProjects(projects); + Arrays.stream(projects).forEach(permissionIndexer::allowOnlyAnyone); + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotWsResponseFormatter.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotWsResponseFormatter.java index 90ab338bb21..16afe8303c8 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotWsResponseFormatter.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/HotspotWsResponseFormatter.java @@ -113,6 +113,7 @@ public class HotspotWsResponseFormatter { builder.setCreationDate(formatDateTime(hotspot.getIssueCreationDate())); builder.setUpdateDate(formatDateTime(hotspot.getIssueUpdateDate())); completeHotspotLocations(hotspot, builder, searchResponseData); + ofNullable(hotspot.getCveId()).ifPresent(builder::setCveId); hotspotsList.add(builder.build()); } return hotspotsList; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java index 318ff72b1f6..e309eed6fae 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java @@ -230,6 +230,8 @@ public class SearchResponseFormat { issueBuilder.setScope(UNIT_TEST_FILE.equals(component.qualifier()) ? IssueScope.TEST.name() : IssueScope.MAIN.name()); issueBuilder.setPrioritizedRule(dto.isPrioritizedRule()); + + Optional.ofNullable(dto.getCveId()).ifPresent(issueBuilder::setCveId); } private static void addAdditionalFieldsToIssueBuilder(Collection<SearchAdditionalField> fields, SearchResponseData data, IssueDto dto, Issue.Builder issueBuilder) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java index 94455620be7..47706fd3e39 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java @@ -126,7 +126,8 @@ public class SearchResponseLoader { .toList(); return issueKeys.stream() - .map(new KeyToIssueFunction(unorderedIssues)).filter(Objects::nonNull) + .map(new KeyToIssueFunction(unorderedIssues)) + .filter(Objects::nonNull) .toList(); } |