3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.issue.index;
22 import java.util.ArrayList;
23 import java.util.List;
25 import java.util.OptionalInt;
26 import java.util.stream.Collectors;
27 import org.jetbrains.annotations.NotNull;
28 import org.junit.jupiter.api.Test;
29 import org.sonar.api.issue.Issue;
30 import org.sonar.api.rule.Severity;
31 import org.sonar.api.rules.RuleType;
32 import org.sonar.db.component.ComponentDto;
33 import org.sonar.server.view.index.ViewDoc;
35 import static java.lang.Integer.parseInt;
36 import static java.util.Arrays.asList;
37 import static java.util.Collections.singletonList;
38 import static java.util.Comparator.comparing;
39 import static java.util.stream.Collectors.toList;
40 import static org.assertj.core.api.Assertions.assertThat;
41 import static org.assertj.core.api.Assertions.tuple;
42 import static org.sonar.api.server.rule.RulesDefinition.OwaspAsvsVersion;
43 import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017;
44 import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021;
45 import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion;
46 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
47 import static org.sonar.server.issue.IssueDocTesting.newDocForProject;
48 import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD;
50 class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
53 void getOwaspTop10Report_dont_count_vulnerabilities_from_other_projects() {
54 ComponentDto project = newPrivateProjectDto();
55 ComponentDto another = newPrivateProjectDto();
57 IssueDoc openVulDoc = newDocForProject("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
58 openVulDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
60 IssueDoc otherProjectDoc = newDocForProject("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
61 otherProjectDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
63 indexIssues(openVulDoc, otherProjectDoc);
65 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
66 assertThat(owaspTop10Report)
67 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
68 SecurityStandardCategoryStatistics::getVulnerabilityRating)
70 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
72 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
73 assertThat(owaspTop10For2021Report)
74 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
75 SecurityStandardCategoryStatistics::getVulnerabilityRating)
77 tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
82 void getOwaspTop10Report_dont_count_closed_vulnerabilities() {
83 ComponentDto project = newPrivateProjectDto();
85 newDocForProject("openvul1", project).setOwaspTop10(List.of("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
86 newDocForProject("openvul12021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
87 newDocForProject("notopenvul", project).setOwaspTop10(List.of("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
88 .setSeverity(Severity.BLOCKER),
89 newDocForProject("notopenvul2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
90 .setSeverity(Severity.BLOCKER));
92 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
93 assertThat(owaspTop10Report)
94 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
95 SecurityStandardCategoryStatistics::getVulnerabilityRating)
97 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
99 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
100 assertThat(owaspTop10For2021Report)
101 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
102 SecurityStandardCategoryStatistics::getVulnerabilityRating)
104 tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
108 void getOwaspTop10Report_dont_count_old_vulnerabilities() {
109 ComponentDto project = newPrivateProjectDto();
111 // Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
112 newDocForProject("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
114 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
115 assertThat(owaspTop10Report)
116 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
117 SecurityStandardCategoryStatistics::getVulnerabilityRating)
119 tuple(0L, OptionalInt.empty()));
121 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
122 assertThat(owaspTop10For2021Report)
123 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
124 SecurityStandardCategoryStatistics::getVulnerabilityRating)
126 tuple(0L, OptionalInt.empty()));
130 void getOwaspTop10Report_dont_count_hotspots_from_other_projects() {
131 ComponentDto project = newPrivateProjectDto();
132 ComponentDto another = newPrivateProjectDto();
134 newDocForProject("openhotspot1", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
135 newDocForProject("openhotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
136 newDocForProject("anotherProject", another).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
137 newDocForProject("anotherProject2021", another).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
139 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
140 assertThat(owaspTop10Report)
141 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
143 tuple("a1", 1L /* openhotspot1 */));
145 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
146 assertThat(owaspTop10For2021Report)
147 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
149 tuple("a2", 1L /* openhotspot1 */));
153 void getOwaspTop10Report_dont_count_closed_hotspots() {
154 ComponentDto project = newPrivateProjectDto();
156 newDocForProject("openhotspot1", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
157 newDocForProject("openhotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
158 newDocForProject("closedHotspot", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
159 .setResolution(Issue.RESOLUTION_FIXED),
160 newDocForProject("closedHotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
161 .setResolution(Issue.RESOLUTION_FIXED));
163 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
164 assertThat(owaspTop10Report)
165 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
167 tuple("a1", 1L /* openhotspot1 */));
169 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
170 assertThat(owaspTop10For2021Report)
171 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
173 tuple("a2", 1L /* openhotspot1 */));
177 void getOwaspTop10Report_aggregation_no_cwe() {
178 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(false);
180 assertThat(owaspTop10Report)
182 .allMatch(category -> category.getChildren().isEmpty());
186 void getPciDss32Report_aggregation() {
187 List<SecurityStandardCategoryStatistics> pciDss32Report = indexIssuesAndAssertPciDss32Report();
189 assertThat(pciDss32Report)
192 assertThat(pciDss32Report.get(0).getChildren()).hasSize(2);
193 assertThat(pciDss32Report.get(1).getChildren()).isEmpty();
194 assertThat(pciDss32Report.get(2).getChildren()).hasSize(4);
195 assertThat(pciDss32Report.get(3).getChildren()).isEmpty();
196 assertThat(pciDss32Report.get(4).getChildren()).isEmpty();
197 assertThat(pciDss32Report.get(5).getChildren()).hasSize(2);
198 assertThat(pciDss32Report.get(6).getChildren()).isEmpty();
199 assertThat(pciDss32Report.get(7).getChildren()).hasSize(1);
200 assertThat(pciDss32Report.get(8).getChildren()).isEmpty();
201 assertThat(pciDss32Report.get(9).getChildren()).hasSize(1);
202 assertThat(pciDss32Report.get(10).getChildren()).isEmpty();
203 assertThat(pciDss32Report.get(11).getChildren()).isEmpty();
207 void getOwaspAsvs40Report_aggregation() {
208 List<SecurityStandardCategoryStatistics> owaspAsvsReport = indexIssuesAndAssertOwaspAsvsReport();
210 assertThat(owaspAsvsReport)
213 assertThat(owaspAsvsReport.get(0).getChildren()).isEmpty();
214 assertThat(owaspAsvsReport.get(1).getChildren()).hasSize(2);
215 assertThat(owaspAsvsReport.get(2).getChildren()).hasSize(4);
216 assertThat(owaspAsvsReport.get(3).getChildren()).isEmpty();
217 assertThat(owaspAsvsReport.get(4).getChildren()).isEmpty();
218 assertThat(owaspAsvsReport.get(5).getChildren()).hasSize(2);
219 assertThat(owaspAsvsReport.get(6).getChildren()).hasSize(1);
220 assertThat(owaspAsvsReport.get(7).getChildren()).hasSize(1);
221 assertThat(owaspAsvsReport.get(8).getChildren()).isEmpty();
222 assertThat(owaspAsvsReport.get(9).getChildren()).hasSize(1);
223 assertThat(owaspAsvsReport.get(10).getChildren()).isEmpty();
224 assertThat(owaspAsvsReport.get(11).getChildren()).isEmpty();
225 assertThat(owaspAsvsReport.get(12).getChildren()).isEmpty();
226 assertThat(owaspAsvsReport.get(13).getChildren()).isEmpty();
230 void getOwaspAsvs40ReportGroupedByLevel_aggregation() {
231 List<SecurityStandardCategoryStatistics> owaspAsvsReportGroupedByLevel = indexIssuesAndAssertOwaspAsvsReportGroupedByLevel();
233 assertThat(owaspAsvsReportGroupedByLevel)
236 assertThat(owaspAsvsReportGroupedByLevel.get(0).getChildren()).hasSize(3);
237 assertThat(owaspAsvsReportGroupedByLevel.get(1).getChildren()).hasSize(7);
238 assertThat(owaspAsvsReportGroupedByLevel.get(2).getChildren()).hasSize(11);
242 void getOwaspTop10Report_aggregation_with_cwe() {
243 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(true);
245 Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
246 .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
248 assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
249 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
250 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
251 .containsExactlyInAnyOrder(
252 tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
253 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
254 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
255 assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
256 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
257 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
258 .containsExactlyInAnyOrder(
259 tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
260 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
261 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
265 void getOwaspTop10For2021Report_aggregation_with_cwe() {
266 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwasp2021Report(true);
268 Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
269 .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
271 assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
272 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
273 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
274 .containsExactlyInAnyOrder(
275 tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
276 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
277 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
278 assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
279 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
280 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
281 .containsExactlyInAnyOrder(
282 tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
283 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
284 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
287 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspReport(boolean includeCwe) {
288 ComponentDto project = newPrivateProjectDto();
290 newDocForProject("openvul1", project).setOwaspTop10(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
291 .setSeverity(Severity.MAJOR),
292 newDocForProject("openvul2", project).setOwaspTop10(asList("a3", "a6")).setCwe(List.of("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
293 .setSeverity(Severity.MINOR),
294 newDocForProject("notowaspvul", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
295 newDocForProject("toreviewhotspot1", project).setOwaspTop10(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
296 .setStatus(Issue.STATUS_TO_REVIEW),
297 newDocForProject("toreviewhotspot2", project).setOwaspTop10(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
298 newDocForProject("reviewedHotspot", project).setOwaspTop10(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
299 .setResolution(Issue.RESOLUTION_FIXED),
300 newDocForProject("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
302 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2017);
303 assertThat(owaspTop10Report)
304 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
305 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
306 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
307 .containsExactlyInAnyOrder(
308 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
309 tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
310 tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
311 tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
312 tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
313 tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
314 tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
315 tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
316 tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
317 tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
318 return owaspTop10Report;
321 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertPciDss32Report() {
322 ComponentDto project = newPrivateProjectDto();
324 newDocForProject("openvul1", project).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
325 .setSeverity(Severity.MAJOR),
326 newDocForProject("openvul2", project).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
327 .setSeverity(Severity.MINOR),
328 newDocForProject("openvul3", project).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
329 .setSeverity(Severity.MINOR),
330 newDocForProject("notpcidssvul", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
331 newDocForProject("toreviewhotspot1", project).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
332 .setStatus(Issue.STATUS_TO_REVIEW),
333 newDocForProject("toreviewhotspot2", project).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
334 newDocForProject("reviewedHotspot", project).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
335 .setResolution(Issue.RESOLUTION_FIXED),
336 newDocForProject("notpcidsshotspot", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
338 List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(project.uuid(), false, PciDssVersion.V3_2).stream()
339 .sorted(comparing(s -> parseInt(s.getCategory())))
341 assertThat(pciDssReport)
342 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
343 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
344 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
345 .containsExactlyInAnyOrder(
346 tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
347 tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
348 tuple("3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
349 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
350 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
351 tuple("6", 2L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
352 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
353 tuple("8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
354 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
355 tuple("10", 1L, OptionalInt.of(2), 0L, 0L, 1),
356 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
357 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
362 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReport() {
363 ComponentDto project = getProjectWithOwaspAsvsIssuesIndexed();
365 List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(project.uuid(), false, OwaspAsvsVersion.V4_0, 3).stream()
366 .sorted(comparing(s -> parseInt(s.getCategory())))
368 assertThat(owaspAsvsReport)
369 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
370 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
371 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
372 .containsExactlyInAnyOrder(
373 tuple("1", 0L, OptionalInt.empty(), 0L, 0L, 1),
374 tuple("2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
375 tuple("3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
376 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
377 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
378 tuple("6", 2L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 0L, 0L, 1),
379 tuple("7", 0L /* openvul2 */, OptionalInt.empty() /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
380 tuple("8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
381 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
382 tuple("10", 1L, OptionalInt.of(2), 0L, 0L, 1),
383 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
384 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1),
385 tuple("13", 0L, OptionalInt.empty(), 0L, 0L, 1),
386 tuple("14", 0L, OptionalInt.empty(), 0L, 0L, 1));
388 return owaspAsvsReport;
391 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReportGroupedByLevel() {
392 ComponentDto project = getProjectWithOwaspAsvsIssuesIndexed();
394 List<SecurityStandardCategoryStatistics> owaspAsvsReportGroupedByLevel = new ArrayList<>();
395 owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 1).stream()
396 .sorted(comparing(s -> parseInt(s.getCategory())))
398 owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 2).stream()
399 .sorted(comparing(s -> parseInt(s.getCategory())))
401 owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 3).stream()
402 .sorted(comparing(s -> parseInt(s.getCategory())))
405 assertThat(owaspAsvsReportGroupedByLevel)
406 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
407 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
408 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
409 .containsExactlyInAnyOrder(
410 tuple("l1", 1L /* openvul2 */, OptionalInt.of(2), 1L /* toreviewhotspot2 */, 0L, 5),
411 tuple("l2", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L /* toreviewhotspot1, toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
412 tuple("l3", 3L /* openvul1,openvul2,openvul3 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4));
414 return owaspAsvsReportGroupedByLevel;
418 private ComponentDto getProjectWithOwaspAsvsIssuesIndexed() {
419 ComponentDto project = newPrivateProjectDto();
421 newDocForProject("openvul1", project).setOwaspAsvs40(asList("2.4.1", "3.2.4")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
422 .setSeverity(Severity.MAJOR),
423 newDocForProject("openvul2", project).setOwaspAsvs40(asList("3.4.5", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
424 .setSeverity(Severity.MINOR),
425 newDocForProject("openvul3", project).setOwaspAsvs40(asList("10.2.4", "6.2.8")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
426 .setSeverity(Severity.MINOR),
427 newDocForProject("notowaspasvsvul", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
428 newDocForProject("toreviewhotspot1", project).setOwaspAsvs40(asList("2.2.5", "3.2.4")).setType(RuleType.SECURITY_HOTSPOT)
429 .setStatus(Issue.STATUS_TO_REVIEW),
430 newDocForProject("toreviewhotspot2", project).setOwaspAsvs40(asList("3.6.1", "7.1.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
431 newDocForProject("reviewedHotspot", project).setOwaspAsvs40(asList("3.3.3", "8.3.7")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
432 .setResolution(Issue.RESOLUTION_FIXED),
433 newDocForProject("notowaspasvshotspot", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
437 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwasp2021Report(boolean includeCwe) {
438 ComponentDto project = newPrivateProjectDto();
440 newDocForProject("openvul1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
441 .setSeverity(Severity.MAJOR),
442 newDocForProject("openvul2", project).setOwaspTop10For2021(asList("a3", "a6")).setCwe(List.of("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
443 .setSeverity(Severity.MINOR),
444 newDocForProject("notowaspvul", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
445 newDocForProject("toreviewhotspot1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
446 .setStatus(Issue.STATUS_TO_REVIEW),
447 newDocForProject("toreviewhotspot2", project).setOwaspTop10For2021(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
448 newDocForProject("reviewedHotspot", project).setOwaspTop10For2021(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
449 .setResolution(Issue.RESOLUTION_FIXED),
450 newDocForProject("notowasphotspot", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
452 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2021);
453 assertThat(owaspTop10Report)
454 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
455 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
456 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
457 .containsExactlyInAnyOrder(
458 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
459 tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
460 tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
461 tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
462 tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
463 tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
464 tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
465 tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
466 tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
467 tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
468 return owaspTop10Report;
472 void getPciDssReport_aggregation_on_portfolio() {
473 ComponentDto portfolio1 = db.components().insertPrivateApplication().getMainBranchComponent();
474 ComponentDto portfolio2 = db.components().insertPrivateApplication().getMainBranchComponent();
475 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
476 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
479 newDocForProject("openvul1", project1).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
480 .setSeverity(Severity.MAJOR),
481 newDocForProject("openvul2", project2).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
482 .setSeverity(Severity.MINOR),
483 newDocForProject("openvul3", project1).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
484 .setSeverity(Severity.MINOR),
485 newDocForProject("notpcidssvul", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
486 newDocForProject("toreviewhotspot1", project2).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
487 .setStatus(Issue.STATUS_TO_REVIEW),
488 newDocForProject("toreviewhotspot2", project1).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
489 newDocForProject("reviewedHotspot", project2).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
490 .setResolution(Issue.RESOLUTION_FIXED),
491 newDocForProject("notpcidsshotspot", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
493 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
494 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
496 List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(portfolio1.uuid(), true, PciDssVersion.V3_2).stream()
497 .sorted(comparing(s -> parseInt(s.getCategory())))
499 assertThat(pciDssReport)
500 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
501 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
502 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
503 .containsExactlyInAnyOrder(
504 tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
505 tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
506 tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
507 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
508 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
509 tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
510 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
511 tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
512 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
513 tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
514 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
515 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
519 void getOwaspAsvsReport_aggregation_on_portfolio() {
520 ComponentDto portfolio1 = db.components().insertPrivateApplication().getMainBranchComponent();
521 ComponentDto portfolio2 = db.components().insertPrivateApplication().getMainBranchComponent();
522 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
523 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
526 newDocForProject("openvul1", project1).setOwaspAsvs40(asList("2.1.1", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
527 .setSeverity(Severity.MAJOR),
528 newDocForProject("openvul2", project2).setOwaspAsvs40(asList("3.3.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
529 .setSeverity(Severity.MINOR),
530 newDocForProject("openvul3", project1).setOwaspAsvs40(asList("10.3.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
531 .setSeverity(Severity.MINOR),
532 newDocForProject("notowaspasvsvul", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
533 newDocForProject("toreviewhotspot1", project2).setOwaspAsvs40(asList("2.1.3", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
534 .setStatus(Issue.STATUS_TO_REVIEW),
535 newDocForProject("toreviewhotspot2", project1).setOwaspAsvs40(asList("3.4.4", "6.2.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
536 newDocForProject("reviewedHotspot", project2).setOwaspAsvs40(asList("3.1.1", "8.3.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
537 .setResolution(Issue.RESOLUTION_FIXED),
538 newDocForProject("notowaspasvshotspot", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
540 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
541 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
543 List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(portfolio1.uuid(), true, OwaspAsvsVersion.V4_0, 1).stream()
544 .sorted(comparing(s -> parseInt(s.getCategory())))
546 assertThat(owaspAsvsReport)
547 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
548 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
549 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
550 .containsExactlyInAnyOrder(
551 tuple("1", 0L, OptionalInt.empty(), 0L, 0L, 1),
552 tuple("2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
553 tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
554 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
555 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
556 tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
557 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
558 tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
559 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
560 tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
561 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
562 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1),
563 tuple("13", 0L, OptionalInt.empty(), 0L, 0L, 1),
564 tuple("14", 0L, OptionalInt.empty(), 0L, 0L, 1));
568 void getCWETop25Report_aggregation() {
569 ComponentDto project = newPrivateProjectDto();
571 newDocForProject("openvul", project).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
572 .setSeverity(Severity.MAJOR),
573 newDocForProject("notopenvul", project).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
574 .setResolution(Issue.RESOLUTION_FIXED)
575 .setSeverity(Severity.BLOCKER),
576 newDocForProject("toreviewhotspot", project).setCwe(List.of("89")).setType(RuleType.SECURITY_HOTSPOT)
577 .setStatus(Issue.STATUS_TO_REVIEW),
578 newDocForProject("only2020", project).setCwe(List.of("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
579 .setSeverity(Severity.MINOR),
580 newDocForProject("unknown", project).setCwe(List.of("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
581 .setSeverity(Severity.MINOR));
583 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(project.uuid(), false);
585 List<String> listOfYears = cweTop25Reports.stream()
586 .map(SecurityStandardCategoryStatistics::getCategory)
589 assertThat(listOfYears).contains("2021", "2022", "2023");
591 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
592 .filter(s -> s.getCategory().equals("2021"))
594 assertThat(cwe2021.getChildren()).hasSize(25);
595 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
596 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
597 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
598 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
599 .containsExactlyInAnyOrder(1L, 0L, 0L);
600 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
601 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
602 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
603 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
604 .containsExactlyInAnyOrder(0L, 1L, 0L);
605 assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
606 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
608 SecurityStandardCategoryStatistics cwe2022 = cweTop25Reports.stream()
609 .filter(s -> s.getCategory().equals("2022"))
611 assertThat(cwe2022.getChildren()).hasSize(25);
612 assertThat(findRuleInCweByYear(cwe2022, "119")).isNotNull()
613 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
614 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
615 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
616 .containsExactlyInAnyOrder(1L, 0L, 0L);
617 assertThat(findRuleInCweByYear(cwe2022, "89")).isNotNull()
618 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
619 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
620 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
621 .containsExactlyInAnyOrder(0L, 1L, 0L);
622 assertThat(findRuleInCweByYear(cwe2022, "950")).isNull();
623 assertThat(findRuleInCweByYear(cwe2022, "999")).isNull();
625 SecurityStandardCategoryStatistics cwe2023 = cweTop25Reports.stream()
626 .filter(s -> s.getCategory().equals("2023"))
628 assertThat(cwe2023.getChildren()).hasSize(25);
629 assertThat(findRuleInCweByYear(cwe2023, "119")).isNotNull()
630 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
631 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
632 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
633 .containsExactlyInAnyOrder(1L, 0L, 0L);
634 assertThat(findRuleInCweByYear(cwe2023, "89")).isNotNull()
635 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
636 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
637 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
638 .containsExactlyInAnyOrder(0L, 1L, 0L);
639 assertThat(findRuleInCweByYear(cwe2023, "862")).isNotNull()
640 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
641 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
642 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
643 .containsExactlyInAnyOrder(1L, 0L, 0L);
644 assertThat(findRuleInCweByYear(cwe2023, "999")).isNull();
648 void getCWETop25Report_aggregation_on_portfolio() {
649 ComponentDto application = db.components().insertPrivateApplication().getMainBranchComponent();
650 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
651 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
654 newDocForProject("openvul1", project1).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
655 .setSeverity(Severity.MAJOR),
656 newDocForProject("openvul2", project2).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
657 .setSeverity(Severity.MINOR),
658 newDocForProject("toreviewhotspot", project1).setCwe(List.of("89")).setType(RuleType.SECURITY_HOTSPOT)
659 .setStatus(Issue.STATUS_TO_REVIEW),
660 newDocForProject("only2020", project2).setCwe(List.of("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
661 .setSeverity(Severity.MINOR),
662 newDocForProject("unknown", project2).setCwe(List.of("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
663 .setSeverity(Severity.MINOR));
665 indexView(application.uuid(), asList(project1.uuid(), project2.uuid()));
667 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(application.uuid(), true);
669 List<String> listOfYears = cweTop25Reports.stream()
670 .map(SecurityStandardCategoryStatistics::getCategory)
673 assertThat(listOfYears).contains("2021", "2022", "2023");
675 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
676 .filter(s -> s.getCategory().equals("2021"))
678 assertThat(cwe2021.getChildren()).hasSize(25);
679 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
680 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
681 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
682 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
683 .containsExactlyInAnyOrder(2L, 0L, 0L);
684 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
685 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
686 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
687 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
688 .containsExactlyInAnyOrder(0L, 1L, 0L);
689 assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
690 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
693 SecurityStandardCategoryStatistics cwe2022 = cweTop25Reports.stream()
694 .filter(s -> s.getCategory().equals("2022"))
696 assertThat(cwe2022.getChildren()).hasSize(25);
697 assertThat(findRuleInCweByYear(cwe2022, "119")).isNotNull()
698 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
699 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
700 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
701 .containsExactlyInAnyOrder(2L, 0L, 0L);
702 assertThat(findRuleInCweByYear(cwe2022, "89")).isNotNull()
703 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
704 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
705 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
706 .containsExactlyInAnyOrder(0L, 1L, 0L);
707 assertThat(findRuleInCweByYear(cwe2022, "295")).isNull();
708 assertThat(findRuleInCweByYear(cwe2022, "999")).isNull();
710 SecurityStandardCategoryStatistics cwe2023 = cweTop25Reports.stream()
711 .filter(s -> s.getCategory().equals("2023"))
713 assertThat(cwe2023.getChildren()).hasSize(25);
714 assertThat(findRuleInCweByYear(cwe2023, "119")).isNotNull()
715 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
716 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
717 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
718 .containsExactlyInAnyOrder(2L, 0L, 0L);
719 assertThat(findRuleInCweByYear(cwe2023, "89")).isNotNull()
720 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
721 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
722 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
723 .containsExactlyInAnyOrder(0L, 1L, 0L);
724 assertThat(findRuleInCweByYear(cwe2023, "862")).isNotNull()
725 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
726 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
727 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
728 .containsExactlyInAnyOrder(1L, 0L, 0L);
729 assertThat(findRuleInCweByYear(cwe2023, "999")).isNull();
732 private SecurityStandardCategoryStatistics findRuleInCweByYear(SecurityStandardCategoryStatistics statistics, String cweId) {
733 return statistics.getChildren().stream().filter(stat -> stat.getCategory().equals(cweId)).findAny().orElse(null);
736 private void indexView(String viewUuid, List<String> projectBranchUuids) {
737 viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjectBranchUuids(projectBranchUuids));