import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.dependency.CveDto;
+import org.sonar.db.dependency.IssuesDependencyDto;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.rule.RuleTesting;
tuple(LOW, SECURITY));
}
+ @Test
+ void selectByKeys_shouldFetchCveIds() {
+ prepareTables();
+ var cveDto1 = new CveDto("cve_uuid_1", "CVE-123", "Some CVE description", 1.0, 2.0, 3.0, 4L, 5L, 6L, 7L);
+ db.getDbClient().cveDao().insert(db.getSession(), cveDto1);
+ var cveDto2 = new CveDto("cve_uuid_2", "CVE-456", "Some CVE description", 1.0, 2.0, 3.0, 4L, 5L, 6L, 7L);
+ db.getDbClient().cveDao().insert(db.getSession(), cveDto2);
+ db.issues().insertIssuesDependency(new IssuesDependencyDto(ISSUE_KEY1, cveDto1.uuid()));
+ db.issues().insertIssuesDependency(new IssuesDependencyDto(ISSUE_KEY2, cveDto2.uuid()));
+
+ List<IssueDto> issues = underTest.selectByKeys(db.getSession(), asList("I1", "I2", "I3"));
+
+ assertThat(issues).extracting(IssueDto::getCveId).containsExactlyInAnyOrder(cveDto1.id(), cveDto2.id());
+ }
+
@Test
void scrollIndexationIssues_shouldReturnDto() throws SQLException {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
private CleanCodeAttribute cleanCodeAttribute;
private CleanCodeAttribute ruleCleanCodeAttribute;
+ //issues dependency fields, one-one relationship
+ private String cveId;
+
public IssueDto() {
// nothing to do
}
return this;
}
+ public String getCveId() {
+ return cveId;
+ }
+
+ public void setCveId(String cveId) {
+ this.cveId = cveId;
+ }
+
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
<select id="selectByKeys" parameterType="map" resultMap="issueResultMap">
select
<include refid="issueColumns"/>,
- u.login as assigneeLogin
+ u.login as assigneeLogin,
+ cve.id as cveId
from issues i
inner join rules r on r.uuid=i.rule_uuid
inner join components p on p.uuid=i.component_uuid
left join new_code_reference_issues n on i.kee = n.issue_key
left outer join issues_impacts ii on i.kee = ii.issue_key
left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
+ left join issues_dependency dep on i.kee = dep.issue_uuid
+ left join cves cve on dep.cve_uuid = cve.uuid
where i.kee in
<foreach collection="list" open="(" close=")" item="key" separator=",">
#{key,jdbcType=VARCHAR}
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ProjectData;
+import org.sonar.db.dependency.IssuesDependencyDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.user.UserDto;
db.getDbClient().issueDao().insertAsNewCodeOnReferenceBranch(db.getSession(), IssueTesting.newCodeReferenceIssue(issue));
db.commit();
}
+
+ public void insertIssuesDependency(IssuesDependencyDto issuesDependencyDto) {
+ db.getDbClient().issuesDependencyDao().insert(db.getSession(), issuesDependencyDto);
+ db.commit();
+ }
}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
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;
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) {
.toList();
return issueKeys.stream()
- .map(new KeyToIssueFunction(unorderedIssues)).filter(Objects::nonNull)
+ .map(new KeyToIssueFunction(unorderedIssues))
+ .filter(Objects::nonNull)
.toList();
}
repeated sonarqube.ws.commons.Flow flows = 15;
optional string ruleKey = 16;
repeated sonarqube.ws.commons.MessageFormatting messageFormattings = 17;
+ optional string cveId = 18;
}
}
repeated sonarqube.ws.commons.Impact impacts = 42;
optional string issueStatus = 43;
optional bool prioritizedRule = 44;
+ optional string cveId = 45;
}
message Transitions {