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.api.server.rule.RulesDefinition.StigVersion;
33 import org.sonar.db.component.ComponentDto;
34 import org.sonar.server.view.index.ViewDoc;
36 import static java.lang.Integer.parseInt;
37 import static java.util.Arrays.asList;
38 import static java.util.Collections.singletonList;
39 import static java.util.Comparator.comparing;
40 import static java.util.stream.Collectors.toList;
41 import static org.assertj.core.api.Assertions.assertThat;
42 import static org.assertj.core.api.Assertions.tuple;
43 import static org.sonar.api.server.rule.RulesDefinition.OwaspAsvsVersion;
44 import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017;
45 import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021;
46 import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion;
47 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
48 import static org.sonar.server.issue.IssueDocTesting.newDocForProject;
49 import static org.sonar.server.security.SecurityStandards.StigSupportedRequirement.V222391;
50 import static org.sonar.server.security.SecurityStandards.StigSupportedRequirement.V222397;
51 import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD;
53 class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
56 void getOwaspTop10Report_dont_count_vulnerabilities_from_other_projects() {
57 ComponentDto project = newPrivateProjectDto();
58 ComponentDto another = newPrivateProjectDto();
60 IssueDoc openVulDoc = newDocForProject("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
61 openVulDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
63 IssueDoc otherProjectDoc = newDocForProject("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
64 otherProjectDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
66 indexIssues(openVulDoc, otherProjectDoc);
68 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
69 assertThat(owaspTop10Report)
70 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
71 SecurityStandardCategoryStatistics::getVulnerabilityRating)
73 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
75 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
76 assertThat(owaspTop10For2021Report)
77 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
78 SecurityStandardCategoryStatistics::getVulnerabilityRating)
80 tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
85 void getOwaspTop10Report_dont_count_closed_vulnerabilities() {
86 ComponentDto project = newPrivateProjectDto();
88 newDocForProject("openvul1", project).setOwaspTop10(List.of("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
89 newDocForProject("openvul12021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
90 newDocForProject("notopenvul", project).setOwaspTop10(List.of("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
91 .setSeverity(Severity.BLOCKER),
92 newDocForProject("notopenvul2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
93 .setSeverity(Severity.BLOCKER));
95 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
96 assertThat(owaspTop10Report)
97 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
98 SecurityStandardCategoryStatistics::getVulnerabilityRating)
100 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
102 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
103 assertThat(owaspTop10For2021Report)
104 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
105 SecurityStandardCategoryStatistics::getVulnerabilityRating)
107 tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
111 void getOwaspTop10Report_dont_count_old_vulnerabilities() {
112 ComponentDto project = newPrivateProjectDto();
114 // Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
115 newDocForProject("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
117 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
118 assertThat(owaspTop10Report)
119 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
120 SecurityStandardCategoryStatistics::getVulnerabilityRating)
122 tuple(0L, OptionalInt.empty()));
124 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
125 assertThat(owaspTop10For2021Report)
126 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
127 SecurityStandardCategoryStatistics::getVulnerabilityRating)
129 tuple(0L, OptionalInt.empty()));
133 void getOwaspTop10Report_dont_count_hotspots_from_other_projects() {
134 ComponentDto project = newPrivateProjectDto();
135 ComponentDto another = newPrivateProjectDto();
137 newDocForProject("openhotspot1", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
138 newDocForProject("openhotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
139 newDocForProject("anotherProject", another).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
140 newDocForProject("anotherProject2021", another).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
142 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
143 assertThat(owaspTop10Report)
144 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
146 tuple("a1", 1L /* openhotspot1 */));
148 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
149 assertThat(owaspTop10For2021Report)
150 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
152 tuple("a2", 1L /* openhotspot1 */));
156 void getOwaspTop10Report_dont_count_closed_hotspots() {
157 ComponentDto project = newPrivateProjectDto();
159 newDocForProject("openhotspot1", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
160 newDocForProject("openhotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
161 newDocForProject("closedHotspot", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
162 .setResolution(Issue.RESOLUTION_FIXED),
163 newDocForProject("closedHotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
164 .setResolution(Issue.RESOLUTION_FIXED));
166 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
167 assertThat(owaspTop10Report)
168 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
170 tuple("a1", 1L /* openhotspot1 */));
172 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
173 assertThat(owaspTop10For2021Report)
174 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
176 tuple("a2", 1L /* openhotspot1 */));
180 void getOwaspTop10Report_aggregation_no_cwe() {
181 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(false);
183 assertThat(owaspTop10Report)
185 .allMatch(category -> category.getChildren().isEmpty());
189 void getPciDss32Report_aggregation() {
190 List<SecurityStandardCategoryStatistics> pciDss32Report = indexIssuesAndAssertPciDss32Report();
192 assertThat(pciDss32Report)
195 assertThat(pciDss32Report.get(0).getChildren()).hasSize(2);
196 assertThat(pciDss32Report.get(1).getChildren()).isEmpty();
197 assertThat(pciDss32Report.get(2).getChildren()).hasSize(4);
198 assertThat(pciDss32Report.get(3).getChildren()).isEmpty();
199 assertThat(pciDss32Report.get(4).getChildren()).isEmpty();
200 assertThat(pciDss32Report.get(5).getChildren()).hasSize(2);
201 assertThat(pciDss32Report.get(6).getChildren()).isEmpty();
202 assertThat(pciDss32Report.get(7).getChildren()).hasSize(1);
203 assertThat(pciDss32Report.get(8).getChildren()).isEmpty();
204 assertThat(pciDss32Report.get(9).getChildren()).hasSize(1);
205 assertThat(pciDss32Report.get(10).getChildren()).isEmpty();
206 assertThat(pciDss32Report.get(11).getChildren()).isEmpty();
210 void getOwaspAsvs40Report_aggregation() {
211 List<SecurityStandardCategoryStatistics> owaspAsvsReport = indexIssuesAndAssertOwaspAsvsReport();
213 assertThat(owaspAsvsReport)
216 assertThat(owaspAsvsReport.get(0).getChildren()).isEmpty();
217 assertThat(owaspAsvsReport.get(1).getChildren()).hasSize(2);
218 assertThat(owaspAsvsReport.get(2).getChildren()).hasSize(4);
219 assertThat(owaspAsvsReport.get(3).getChildren()).isEmpty();
220 assertThat(owaspAsvsReport.get(4).getChildren()).isEmpty();
221 assertThat(owaspAsvsReport.get(5).getChildren()).hasSize(2);
222 assertThat(owaspAsvsReport.get(6).getChildren()).hasSize(1);
223 assertThat(owaspAsvsReport.get(7).getChildren()).hasSize(1);
224 assertThat(owaspAsvsReport.get(8).getChildren()).isEmpty();
225 assertThat(owaspAsvsReport.get(9).getChildren()).hasSize(1);
226 assertThat(owaspAsvsReport.get(10).getChildren()).isEmpty();
227 assertThat(owaspAsvsReport.get(11).getChildren()).isEmpty();
228 assertThat(owaspAsvsReport.get(12).getChildren()).isEmpty();
229 assertThat(owaspAsvsReport.get(13).getChildren()).isEmpty();
233 void getOwaspAsvs40ReportGroupedByLevel_aggregation() {
234 List<SecurityStandardCategoryStatistics> owaspAsvsReportGroupedByLevel = indexIssuesAndAssertOwaspAsvsReportGroupedByLevel();
236 assertThat(owaspAsvsReportGroupedByLevel)
239 assertThat(owaspAsvsReportGroupedByLevel.get(0).getChildren()).hasSize(3);
240 assertThat(owaspAsvsReportGroupedByLevel.get(1).getChildren()).hasSize(7);
241 assertThat(owaspAsvsReportGroupedByLevel.get(2).getChildren()).hasSize(11);
245 void getOwaspTop10Report_aggregation_with_cwe() {
246 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(true);
248 Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
249 .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
251 assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
252 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
253 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
254 .containsExactlyInAnyOrder(
255 tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
256 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
257 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
258 assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
259 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
260 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
261 .containsExactlyInAnyOrder(
262 tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
263 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
264 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
268 void getOwaspTop10For2021Report_aggregation_with_cwe() {
269 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwasp2021Report(true);
271 Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
272 .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
274 assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
275 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
276 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
277 .containsExactlyInAnyOrder(
278 tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
279 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
280 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
281 assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
282 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
283 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
284 .containsExactlyInAnyOrder(
285 tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
286 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
287 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
290 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspReport(boolean includeCwe) {
291 ComponentDto project = newPrivateProjectDto();
293 newDocForProject("openvul1", project).setOwaspTop10(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
294 .setSeverity(Severity.MAJOR),
295 newDocForProject("openvul2", project).setOwaspTop10(asList("a3", "a6")).setCwe(List.of("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
296 .setSeverity(Severity.MINOR),
297 newDocForProject("notowaspvul", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
298 newDocForProject("toreviewhotspot1", project).setOwaspTop10(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
299 .setStatus(Issue.STATUS_TO_REVIEW),
300 newDocForProject("toreviewhotspot2", project).setOwaspTop10(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
301 newDocForProject("reviewedHotspot", project).setOwaspTop10(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
302 .setResolution(Issue.RESOLUTION_FIXED),
303 newDocForProject("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
305 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2017);
306 assertThat(owaspTop10Report)
307 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
308 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
309 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
310 .containsExactlyInAnyOrder(
311 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
312 tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
313 tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
314 tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
315 tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
316 tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
317 tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
318 tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
319 tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
320 tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
321 return owaspTop10Report;
324 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertPciDss32Report() {
325 ComponentDto project = newPrivateProjectDto();
327 newDocForProject("openvul1", project).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
328 .setSeverity(Severity.MAJOR),
329 newDocForProject("openvul2", project).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
330 .setSeverity(Severity.MINOR),
331 newDocForProject("openvul3", project).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
332 .setSeverity(Severity.MINOR),
333 newDocForProject("notpcidssvul", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
334 newDocForProject("toreviewhotspot1", project).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
335 .setStatus(Issue.STATUS_TO_REVIEW),
336 newDocForProject("toreviewhotspot2", project).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
337 newDocForProject("reviewedHotspot", project).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
338 .setResolution(Issue.RESOLUTION_FIXED),
339 newDocForProject("notpcidsshotspot", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
341 List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(project.uuid(), false, PciDssVersion.V3_2).stream()
342 .sorted(comparing(s -> parseInt(s.getCategory())))
344 assertThat(pciDssReport)
345 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
346 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
347 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
348 .containsExactlyInAnyOrder(
349 tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
350 tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
351 tuple("3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
352 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
353 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
354 tuple("6", 2L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
355 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
356 tuple("8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
357 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
358 tuple("10", 1L, OptionalInt.of(2), 0L, 0L, 1),
359 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
360 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
365 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReport() {
366 ComponentDto project = getProjectWithOwaspAsvsIssuesIndexed();
368 List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(project.uuid(), false, OwaspAsvsVersion.V4_0, 3).stream()
369 .sorted(comparing(s -> parseInt(s.getCategory())))
371 assertThat(owaspAsvsReport)
372 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
373 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
374 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
375 .containsExactlyInAnyOrder(
376 tuple("1", 0L, OptionalInt.empty(), 0L, 0L, 1),
377 tuple("2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
378 tuple("3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
379 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
380 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
381 tuple("6", 2L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 0L, 0L, 1),
382 tuple("7", 0L /* openvul2 */, OptionalInt.empty() /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
383 tuple("8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
384 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
385 tuple("10", 1L, OptionalInt.of(2), 0L, 0L, 1),
386 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
387 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1),
388 tuple("13", 0L, OptionalInt.empty(), 0L, 0L, 1),
389 tuple("14", 0L, OptionalInt.empty(), 0L, 0L, 1));
391 return owaspAsvsReport;
394 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReportGroupedByLevel() {
395 ComponentDto project = getProjectWithOwaspAsvsIssuesIndexed();
397 List<SecurityStandardCategoryStatistics> owaspAsvsReportGroupedByLevel = new ArrayList<>();
398 owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 1).stream()
399 .sorted(comparing(s -> parseInt(s.getCategory())))
401 owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 2).stream()
402 .sorted(comparing(s -> parseInt(s.getCategory())))
404 owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 3).stream()
405 .sorted(comparing(s -> parseInt(s.getCategory())))
408 assertThat(owaspAsvsReportGroupedByLevel)
409 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
410 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
411 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
412 .containsExactlyInAnyOrder(
413 tuple("l1", 1L /* openvul2 */, OptionalInt.of(2), 1L /* toreviewhotspot2 */, 0L, 5),
414 tuple("l2", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L /* toreviewhotspot1, toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
415 tuple("l3", 3L /* openvul1,openvul2,openvul3 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4));
417 return owaspAsvsReportGroupedByLevel;
421 private ComponentDto getProjectWithOwaspAsvsIssuesIndexed() {
422 ComponentDto project = newPrivateProjectDto();
424 newDocForProject("openvul1", project).setOwaspAsvs40(asList("2.4.1", "3.2.4")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
425 .setSeverity(Severity.MAJOR),
426 newDocForProject("openvul2", project).setOwaspAsvs40(asList("3.4.5", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
427 .setSeverity(Severity.MINOR),
428 newDocForProject("openvul3", project).setOwaspAsvs40(asList("10.2.4", "6.2.8")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
429 .setSeverity(Severity.MINOR),
430 newDocForProject("notowaspasvsvul", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
431 newDocForProject("toreviewhotspot1", project).setOwaspAsvs40(asList("2.2.5", "3.2.4")).setType(RuleType.SECURITY_HOTSPOT)
432 .setStatus(Issue.STATUS_TO_REVIEW),
433 newDocForProject("toreviewhotspot2", project).setOwaspAsvs40(asList("3.6.1", "7.1.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
434 newDocForProject("reviewedHotspot", project).setOwaspAsvs40(asList("3.3.3", "8.3.7")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
435 .setResolution(Issue.RESOLUTION_FIXED),
436 newDocForProject("notowaspasvshotspot", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
440 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwasp2021Report(boolean includeCwe) {
441 ComponentDto project = newPrivateProjectDto();
443 newDocForProject("openvul1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
444 .setSeverity(Severity.MAJOR),
445 newDocForProject("openvul2", project).setOwaspTop10For2021(asList("a3", "a6")).setCwe(List.of("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
446 .setSeverity(Severity.MINOR),
447 newDocForProject("notowaspvul", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
448 newDocForProject("toreviewhotspot1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
449 .setStatus(Issue.STATUS_TO_REVIEW),
450 newDocForProject("toreviewhotspot2", project).setOwaspTop10For2021(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
451 newDocForProject("reviewedHotspot", project).setOwaspTop10For2021(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
452 .setResolution(Issue.RESOLUTION_FIXED),
453 newDocForProject("notowasphotspot", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
455 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2021);
456 assertThat(owaspTop10Report)
457 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
458 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
459 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
460 .containsExactlyInAnyOrder(
461 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
462 tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
463 tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
464 tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
465 tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
466 tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
467 tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
468 tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
469 tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
470 tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
471 return owaspTop10Report;
475 void getPciDssReport_aggregation_on_portfolio() {
476 ComponentDto portfolio1 = db.components().insertPrivateApplication().getMainBranchComponent();
477 ComponentDto portfolio2 = db.components().insertPrivateApplication().getMainBranchComponent();
478 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
479 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
482 newDocForProject("openvul1", project1).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
483 .setSeverity(Severity.MAJOR),
484 newDocForProject("openvul2", project2).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
485 .setSeverity(Severity.MINOR),
486 newDocForProject("openvul3", project1).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
487 .setSeverity(Severity.MINOR),
488 newDocForProject("notpcidssvul", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
489 newDocForProject("toreviewhotspot1", project2).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
490 .setStatus(Issue.STATUS_TO_REVIEW),
491 newDocForProject("toreviewhotspot2", project1).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
492 newDocForProject("reviewedHotspot", project2).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
493 .setResolution(Issue.RESOLUTION_FIXED),
494 newDocForProject("notpcidsshotspot", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
496 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
497 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
499 List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(portfolio1.uuid(), true, PciDssVersion.V3_2).stream()
500 .sorted(comparing(s -> parseInt(s.getCategory())))
502 assertThat(pciDssReport)
503 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
504 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
505 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
506 .containsExactlyInAnyOrder(
507 tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
508 tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
509 tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
510 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
511 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
512 tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
513 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
514 tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
515 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
516 tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
517 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
518 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
522 void getOwaspAsvsReport_aggregation_on_portfolio() {
523 ComponentDto portfolio1 = db.components().insertPrivateApplication().getMainBranchComponent();
524 ComponentDto portfolio2 = db.components().insertPrivateApplication().getMainBranchComponent();
525 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
526 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
529 newDocForProject("openvul1", project1).setOwaspAsvs40(asList("2.1.1", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
530 .setSeverity(Severity.MAJOR),
531 newDocForProject("openvul2", project2).setOwaspAsvs40(asList("3.3.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
532 .setSeverity(Severity.MINOR),
533 newDocForProject("openvul3", project1).setOwaspAsvs40(asList("10.3.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
534 .setSeverity(Severity.MINOR),
535 newDocForProject("notowaspasvsvul", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
536 newDocForProject("toreviewhotspot1", project2).setOwaspAsvs40(asList("2.1.3", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
537 .setStatus(Issue.STATUS_TO_REVIEW),
538 newDocForProject("toreviewhotspot2", project1).setOwaspAsvs40(asList("3.4.4", "6.2.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
539 newDocForProject("reviewedHotspot", project2).setOwaspAsvs40(asList("3.1.1", "8.3.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
540 .setResolution(Issue.RESOLUTION_FIXED),
541 newDocForProject("notowaspasvshotspot", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
543 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
544 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
546 List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(portfolio1.uuid(), true, OwaspAsvsVersion.V4_0, 1).stream()
547 .sorted(comparing(s -> parseInt(s.getCategory())))
549 assertThat(owaspAsvsReport)
550 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
551 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
552 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
553 .containsExactlyInAnyOrder(
554 tuple("1", 0L, OptionalInt.empty(), 0L, 0L, 1),
555 tuple("2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
556 tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
557 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
558 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
559 tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
560 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
561 tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
562 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
563 tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
564 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
565 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1),
566 tuple("13", 0L, OptionalInt.empty(), 0L, 0L, 1),
567 tuple("14", 0L, OptionalInt.empty(), 0L, 0L, 1));
571 void getCWETop25Report_aggregation() {
572 ComponentDto project = newPrivateProjectDto();
574 newDocForProject("openvul", project).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
575 .setSeverity(Severity.MAJOR),
576 newDocForProject("notopenvul", project).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
577 .setResolution(Issue.RESOLUTION_FIXED)
578 .setSeverity(Severity.BLOCKER),
579 newDocForProject("toreviewhotspot", project).setCwe(List.of("89")).setType(RuleType.SECURITY_HOTSPOT)
580 .setStatus(Issue.STATUS_TO_REVIEW),
581 newDocForProject("only2020", project).setCwe(List.of("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
582 .setSeverity(Severity.MINOR),
583 newDocForProject("unknown", project).setCwe(List.of("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
584 .setSeverity(Severity.MINOR));
586 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(project.uuid(), false);
588 List<String> listOfYears = cweTop25Reports.stream()
589 .map(SecurityStandardCategoryStatistics::getCategory)
592 assertThat(listOfYears).contains("2021", "2022", "2023");
594 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
595 .filter(s -> s.getCategory().equals("2021"))
597 assertThat(cwe2021.getChildren()).hasSize(25);
598 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
599 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
600 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
601 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
602 .containsExactlyInAnyOrder(1L, 0L, 0L);
603 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
604 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
605 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
606 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
607 .containsExactlyInAnyOrder(0L, 1L, 0L);
608 assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
609 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
611 SecurityStandardCategoryStatistics cwe2022 = cweTop25Reports.stream()
612 .filter(s -> s.getCategory().equals("2022"))
614 assertThat(cwe2022.getChildren()).hasSize(25);
615 assertThat(findRuleInCweByYear(cwe2022, "119")).isNotNull()
616 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
617 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
618 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
619 .containsExactlyInAnyOrder(1L, 0L, 0L);
620 assertThat(findRuleInCweByYear(cwe2022, "89")).isNotNull()
621 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
622 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
623 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
624 .containsExactlyInAnyOrder(0L, 1L, 0L);
625 assertThat(findRuleInCweByYear(cwe2022, "950")).isNull();
626 assertThat(findRuleInCweByYear(cwe2022, "999")).isNull();
628 SecurityStandardCategoryStatistics cwe2023 = cweTop25Reports.stream()
629 .filter(s -> s.getCategory().equals("2023"))
631 assertThat(cwe2023.getChildren()).hasSize(25);
632 assertThat(findRuleInCweByYear(cwe2023, "119")).isNotNull()
633 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
634 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
635 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
636 .containsExactlyInAnyOrder(1L, 0L, 0L);
637 assertThat(findRuleInCweByYear(cwe2023, "89")).isNotNull()
638 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
639 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
640 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
641 .containsExactlyInAnyOrder(0L, 1L, 0L);
642 assertThat(findRuleInCweByYear(cwe2023, "862")).isNotNull()
643 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
644 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
645 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
646 .containsExactlyInAnyOrder(1L, 0L, 0L);
647 assertThat(findRuleInCweByYear(cwe2023, "999")).isNull();
651 void getCWETop25Report_aggregation_on_portfolio() {
652 ComponentDto application = db.components().insertPrivateApplication().getMainBranchComponent();
653 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
654 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
657 newDocForProject("openvul1", project1).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
658 .setSeverity(Severity.MAJOR),
659 newDocForProject("openvul2", project2).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
660 .setSeverity(Severity.MINOR),
661 newDocForProject("toreviewhotspot", project1).setCwe(List.of("89")).setType(RuleType.SECURITY_HOTSPOT)
662 .setStatus(Issue.STATUS_TO_REVIEW),
663 newDocForProject("only2020", project2).setCwe(List.of("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
664 .setSeverity(Severity.MINOR),
665 newDocForProject("unknown", project2).setCwe(List.of("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
666 .setSeverity(Severity.MINOR));
668 indexView(application.uuid(), asList(project1.uuid(), project2.uuid()));
670 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(application.uuid(), true);
672 List<String> listOfYears = cweTop25Reports.stream()
673 .map(SecurityStandardCategoryStatistics::getCategory)
676 assertThat(listOfYears).contains("2021", "2022", "2023");
678 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
679 .filter(s -> s.getCategory().equals("2021"))
681 assertThat(cwe2021.getChildren()).hasSize(25);
682 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
683 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
684 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
685 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
686 .containsExactlyInAnyOrder(2L, 0L, 0L);
687 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
688 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
689 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
690 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
691 .containsExactlyInAnyOrder(0L, 1L, 0L);
692 assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
693 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
696 SecurityStandardCategoryStatistics cwe2022 = cweTop25Reports.stream()
697 .filter(s -> s.getCategory().equals("2022"))
699 assertThat(cwe2022.getChildren()).hasSize(25);
700 assertThat(findRuleInCweByYear(cwe2022, "119")).isNotNull()
701 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
702 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
703 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
704 .containsExactlyInAnyOrder(2L, 0L, 0L);
705 assertThat(findRuleInCweByYear(cwe2022, "89")).isNotNull()
706 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
707 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
708 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
709 .containsExactlyInAnyOrder(0L, 1L, 0L);
710 assertThat(findRuleInCweByYear(cwe2022, "295")).isNull();
711 assertThat(findRuleInCweByYear(cwe2022, "999")).isNull();
713 SecurityStandardCategoryStatistics cwe2023 = cweTop25Reports.stream()
714 .filter(s -> s.getCategory().equals("2023"))
716 assertThat(cwe2023.getChildren()).hasSize(25);
717 assertThat(findRuleInCweByYear(cwe2023, "119")).isNotNull()
718 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
719 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
720 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
721 .containsExactlyInAnyOrder(2L, 0L, 0L);
722 assertThat(findRuleInCweByYear(cwe2023, "89")).isNotNull()
723 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
724 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
725 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
726 .containsExactlyInAnyOrder(0L, 1L, 0L);
727 assertThat(findRuleInCweByYear(cwe2023, "862")).isNotNull()
728 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
729 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
730 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
731 .containsExactlyInAnyOrder(1L, 0L, 0L);
732 assertThat(findRuleInCweByYear(cwe2023, "999")).isNull();
736 void getStigAsdV5R3_whenRequestingReportOnApplication_ShouldAggregateBasedOnStigRequirement() {
737 ComponentDto application = db.components().insertPrivateApplication().getMainBranchComponent();
738 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
739 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
742 newDocForProject("openvul1", project1).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
743 .setSeverity(Severity.MAJOR),
744 newDocForProject("openvul2", project2).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
745 .setSeverity(Severity.MINOR),
746 newDocForProject("toreviewhotspot", project1).setStigAsdV5R3(List.of(V222397.getRequirement())).setType(RuleType.SECURITY_HOTSPOT)
747 .setStatus(Issue.STATUS_TO_REVIEW),
749 newDocForProject("unknown", project2).setStigAsdV5R3(List.of("V-999999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
750 .setSeverity(Severity.MINOR));
752 indexView(application.uuid(), asList(project1.uuid(), project2.uuid()));
754 Map<String, SecurityStandardCategoryStatistics> statisticsToMap = underTest.getStig(application.uuid(), true, StigVersion.ASD_V5R3)
755 .stream().collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, e -> e));
757 assertThat(statisticsToMap).hasSize(41)
758 .hasEntrySatisfying(V222391.getRequirement(), stat -> {
759 assertThat(stat.getVulnerabilities()).isEqualTo(2);
760 assertThat(stat.getToReviewSecurityHotspots()).isZero();
761 assertThat(stat.getReviewedSecurityHotspots()).isZero();
763 .hasEntrySatisfying(V222397.getRequirement(), stat -> {
764 assertThat(stat.getVulnerabilities()).isZero();
765 assertThat(stat.getToReviewSecurityHotspots()).isEqualTo(1);
766 assertThat(stat.getReviewedSecurityHotspots()).isZero();
771 void getStigAsdV5R3_whenRequestingReportOnProject_ShouldAggregateBasedOnStigRequirement() {
772 ComponentDto branch = newPrivateProjectDto();
774 newDocForProject("openvul", branch).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
775 .setSeverity(Severity.MAJOR),
776 newDocForProject("openvul2", branch).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
777 .setSeverity(Severity.MAJOR),
778 newDocForProject("notopenvul", branch).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
779 .setResolution(Issue.RESOLUTION_FIXED)
780 .setSeverity(Severity.BLOCKER),
781 newDocForProject("toreviewhotspot", branch).setStigAsdV5R3(List.of(V222397.getRequirement())).setType(RuleType.SECURITY_HOTSPOT)
782 .setStatus(Issue.STATUS_TO_REVIEW),
783 newDocForProject("reviewedHostpot", branch).setStigAsdV5R3(List.of(V222397.getRequirement())).setType(RuleType.SECURITY_HOTSPOT)
784 .setStatus(Issue.STATUS_REVIEWED).setResolution(Issue.RESOLUTION_FIXED));
786 Map<String, SecurityStandardCategoryStatistics> statisticsToMap = underTest.getStig(branch.uuid(), false, StigVersion.ASD_V5R3)
787 .stream().collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, e -> e));
789 assertThat(statisticsToMap).hasSize(41)
790 .hasEntrySatisfying(V222391.getRequirement(), stat -> {
791 assertThat(stat.getVulnerabilities()).isEqualTo(2);
792 assertThat(stat.getToReviewSecurityHotspots()).isZero();
793 assertThat(stat.getReviewedSecurityHotspots()).isZero();
794 assertThat(stat.getVulnerabilityRating()).as("MAJOR = C").isEqualTo(OptionalInt.of(3));
796 .hasEntrySatisfying(V222397.getRequirement(), stat -> {
797 assertThat(stat.getVulnerabilities()).isZero();
798 assertThat(stat.getToReviewSecurityHotspots()).isEqualTo(1);
799 assertThat(stat.getReviewedSecurityHotspots()).isEqualTo(1);
800 assertThat(stat.getSecurityReviewRating()).as("50% of hotspots are reviewed, so rating is C").isEqualTo(3);
804 private SecurityStandardCategoryStatistics findRuleInCweByYear(SecurityStandardCategoryStatistics statistics, String cweId) {
805 return statistics.getChildren().stream().filter(stat -> stat.getCategory().equals(cweId)).findAny().orElse(null);
808 private void indexView(String viewUuid, List<String> projectBranchUuids) {
809 viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjectBranchUuids(projectBranchUuids));