3 * Copyright (C) 2009-2022 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.List;
24 import java.util.OptionalInt;
25 import java.util.stream.Collectors;
26 import org.junit.Test;
27 import org.sonar.api.issue.Issue;
28 import org.sonar.api.rule.Severity;
29 import org.sonar.api.rules.RuleType;
30 import org.sonar.db.component.ComponentDto;
31 import org.sonar.server.view.index.ViewDoc;
33 import static java.lang.Integer.parseInt;
34 import static java.util.Arrays.asList;
35 import static java.util.Collections.singletonList;
36 import static java.util.Comparator.comparing;
37 import static java.util.stream.Collectors.toList;
38 import static org.assertj.core.api.Assertions.assertThat;
39 import static org.assertj.core.api.Assertions.tuple;
40 import static org.sonar.api.server.rule.RulesDefinition.OwaspAsvsVersion;
41 import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017;
42 import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021;
43 import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion;
44 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
45 import static org.sonar.server.issue.IssueDocTesting.newDoc;
46 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION;
47 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES;
48 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE;
49 import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD;
51 public class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
54 public void getOwaspTop10Report_dont_count_vulnerabilities_from_other_projects() {
55 ComponentDto project = newPrivateProjectDto();
56 ComponentDto another = newPrivateProjectDto();
58 IssueDoc openVulDoc = newDoc("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
59 openVulDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
61 IssueDoc otherProjectDoc = newDoc("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
62 otherProjectDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
64 indexIssues(openVulDoc, otherProjectDoc);
66 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
67 assertThat(owaspTop10Report)
68 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
69 SecurityStandardCategoryStatistics::getVulnerabilityRating)
71 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
73 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
74 assertThat(owaspTop10For2021Report)
75 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
76 SecurityStandardCategoryStatistics::getVulnerabilityRating)
78 tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
83 public void getOwaspTop10Report_dont_count_closed_vulnerabilities() {
84 ComponentDto project = newPrivateProjectDto();
86 newDoc("openvul1", project).setOwaspTop10(List.of("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
87 newDoc("openvul12021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
88 newDoc("notopenvul", project).setOwaspTop10(List.of("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
89 .setSeverity(Severity.BLOCKER),
90 newDoc("notopenvul2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
91 .setSeverity(Severity.BLOCKER));
93 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
94 assertThat(owaspTop10Report)
95 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
96 SecurityStandardCategoryStatistics::getVulnerabilityRating)
98 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
100 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
101 assertThat(owaspTop10For2021Report)
102 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
103 SecurityStandardCategoryStatistics::getVulnerabilityRating)
105 tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
109 public void getOwaspTop10Report_dont_count_old_vulnerabilities() {
110 ComponentDto project = newPrivateProjectDto();
112 // Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
113 newDoc("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
115 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
116 assertThat(owaspTop10Report)
117 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
118 SecurityStandardCategoryStatistics::getVulnerabilityRating)
120 tuple(0L, OptionalInt.empty()));
122 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
123 assertThat(owaspTop10For2021Report)
124 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
125 SecurityStandardCategoryStatistics::getVulnerabilityRating)
127 tuple(0L, OptionalInt.empty()));
131 public void getOwaspTop10Report_dont_count_hotspots_from_other_projects() {
132 ComponentDto project = newPrivateProjectDto();
133 ComponentDto another = newPrivateProjectDto();
135 newDoc("openhotspot1", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
136 newDoc("openhotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
137 newDoc("anotherProject", another).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
138 newDoc("anotherProject2021", another).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
140 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
141 assertThat(owaspTop10Report)
142 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
144 tuple("a1", 1L /* openhotspot1 */));
146 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
147 assertThat(owaspTop10For2021Report)
148 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
150 tuple("a2", 1L /* openhotspot1 */));
154 public void getOwaspTop10Report_dont_count_closed_hotspots() {
155 ComponentDto project = newPrivateProjectDto();
157 newDoc("openhotspot1", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
158 newDoc("openhotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
159 newDoc("closedHotspot", project).setOwaspTop10(List.of("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
160 .setResolution(Issue.RESOLUTION_FIXED),
161 newDoc("closedHotspot2021", project).setOwaspTop10For2021(List.of("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
162 .setResolution(Issue.RESOLUTION_FIXED));
164 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
165 assertThat(owaspTop10Report)
166 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
168 tuple("a1", 1L /* openhotspot1 */));
170 List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
171 assertThat(owaspTop10For2021Report)
172 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
174 tuple("a2", 1L /* openhotspot1 */));
178 public void getOwaspTop10Report_aggregation_no_cwe() {
179 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(false);
181 assertThat(owaspTop10Report)
183 .allMatch(category -> category.getChildren().isEmpty());
187 public void getPciDss32Report_aggregation() {
188 List<SecurityStandardCategoryStatistics> pciDss32Report = indexIssuesAndAssertPciDss32Report();
190 assertThat(pciDss32Report)
193 assertThat(pciDss32Report.get(0).getChildren()).hasSize(2);
194 assertThat(pciDss32Report.get(1).getChildren()).isEmpty();
195 assertThat(pciDss32Report.get(2).getChildren()).hasSize(4);
196 assertThat(pciDss32Report.get(3).getChildren()).isEmpty();
197 assertThat(pciDss32Report.get(4).getChildren()).isEmpty();
198 assertThat(pciDss32Report.get(5).getChildren()).hasSize(2);
199 assertThat(pciDss32Report.get(6).getChildren()).isEmpty();
200 assertThat(pciDss32Report.get(7).getChildren()).hasSize(1);
201 assertThat(pciDss32Report.get(8).getChildren()).isEmpty();
202 assertThat(pciDss32Report.get(9).getChildren()).hasSize(1);
203 assertThat(pciDss32Report.get(10).getChildren()).isEmpty();
204 assertThat(pciDss32Report.get(11).getChildren()).isEmpty();
208 public void getOwaspAsvs40Report_aggregation() {
209 List<SecurityStandardCategoryStatistics> owaspAsvsReport = indexIssuesAndAssertOwaspAsvsReport();
211 assertThat(owaspAsvsReport)
214 assertThat(owaspAsvsReport.get(0).getChildren()).isEmpty();
215 assertThat(owaspAsvsReport.get(1).getChildren()).hasSize(2);
216 assertThat(owaspAsvsReport.get(2).getChildren()).hasSize(3);
217 assertThat(owaspAsvsReport.get(3).getChildren()).isEmpty();
218 assertThat(owaspAsvsReport.get(4).getChildren()).isEmpty();
219 assertThat(owaspAsvsReport.get(5).getChildren()).hasSize(1);
220 assertThat(owaspAsvsReport.get(6).getChildren()).hasSize(1);
221 assertThat(owaspAsvsReport.get(7).getChildren()).hasSize(1);
222 assertThat(owaspAsvsReport.get(8).getChildren()).isEmpty();
223 assertThat(owaspAsvsReport.get(9).getChildren()).hasSize(1);
224 assertThat(owaspAsvsReport.get(10).getChildren()).isEmpty();
225 assertThat(owaspAsvsReport.get(11).getChildren()).isEmpty();
226 assertThat(owaspAsvsReport.get(12).getChildren()).isEmpty();
227 assertThat(owaspAsvsReport.get(13).getChildren()).isEmpty();
231 public void getOwaspTop10Report_aggregation_with_cwe() {
232 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(true);
234 Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
235 .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
237 assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
238 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
239 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
240 .containsExactlyInAnyOrder(
241 tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
242 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
243 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
244 assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
245 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
246 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
247 .containsExactlyInAnyOrder(
248 tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
249 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
250 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
254 public void getOwaspTop10For2021Report_aggregation_with_cwe() {
255 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwasp2021Report(true);
257 Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
258 .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
260 assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
261 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
262 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
263 .containsExactlyInAnyOrder(
264 tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
265 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
266 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
267 assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
268 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
269 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
270 .containsExactlyInAnyOrder(
271 tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
272 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
273 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
276 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspReport(boolean includeCwe) {
277 ComponentDto project = newPrivateProjectDto();
279 newDoc("openvul1", project).setOwaspTop10(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
280 .setSeverity(Severity.MAJOR),
281 newDoc("openvul2", project).setOwaspTop10(asList("a3", "a6")).setCwe(List.of("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
282 .setSeverity(Severity.MINOR),
283 newDoc("notowaspvul", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
284 newDoc("toreviewhotspot1", project).setOwaspTop10(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
285 .setStatus(Issue.STATUS_TO_REVIEW),
286 newDoc("toreviewhotspot2", project).setOwaspTop10(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
287 newDoc("reviewedHotspot", project).setOwaspTop10(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
288 .setResolution(Issue.RESOLUTION_FIXED),
289 newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
291 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2017);
292 assertThat(owaspTop10Report)
293 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
294 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
295 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
296 .containsExactlyInAnyOrder(
297 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
298 tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
299 tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
300 tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
301 tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
302 tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
303 tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
304 tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
305 tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
306 tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
307 return owaspTop10Report;
310 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertPciDss32Report() {
311 ComponentDto project = newPrivateProjectDto();
313 newDoc("openvul1", project).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
314 .setSeverity(Severity.MAJOR),
315 newDoc("openvul2", project).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
316 .setSeverity(Severity.MINOR),
317 newDoc("openvul3", project).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
318 .setSeverity(Severity.MINOR),
319 newDoc("notpcidssvul", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
320 newDoc("toreviewhotspot1", project).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
321 .setStatus(Issue.STATUS_TO_REVIEW),
322 newDoc("toreviewhotspot2", project).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
323 newDoc("reviewedHotspot", project).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
324 .setResolution(Issue.RESOLUTION_FIXED),
325 newDoc("notpcidsshotspot", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
327 List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(project.uuid(), false, PciDssVersion.V3_2).stream()
328 .sorted(comparing(s -> parseInt(s.getCategory())))
330 assertThat(pciDssReport)
331 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
332 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
333 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
334 .containsExactlyInAnyOrder(
335 tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
336 tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
337 tuple("3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
338 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
339 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
340 tuple("6", 2L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
341 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
342 tuple("8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
343 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
344 tuple("10", 1L, OptionalInt.of(2), 0L, 0L, 1),
345 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
346 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
351 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReport() {
352 ComponentDto project = newPrivateProjectDto();
354 newDoc("openvul1", project).setOwaspAsvs40(asList("2.1.1", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
355 .setSeverity(Severity.MAJOR),
356 newDoc("openvul2", project).setOwaspAsvs40(asList("3.2.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
357 .setSeverity(Severity.MINOR),
358 newDoc("openvul3", project).setOwaspAsvs40(asList("10.3.1", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
359 .setSeverity(Severity.MINOR),
360 newDoc("notowaspasvsvul", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
361 newDoc("toreviewhotspot1", project).setOwaspAsvs40(asList("2.1.2", "3.2.2")).setType(RuleType.SECURITY_HOTSPOT)
362 .setStatus(Issue.STATUS_TO_REVIEW),
363 newDoc("toreviewhotspot2", project).setOwaspAsvs40(asList("3.4.5", "7.1.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
364 newDoc("reviewedHotspot", project).setOwaspAsvs40(asList("3.1.1", "8.3.4")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
365 .setResolution(Issue.RESOLUTION_FIXED),
366 newDoc("notowaspasvshotspot", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
368 List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(project.uuid(), false, OwaspAsvsVersion.V4_0, 1).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> indexIssuesAndAssertOwasp2021Report(boolean includeCwe) {
395 ComponentDto project = newPrivateProjectDto();
397 newDoc("openvul1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
398 .setSeverity(Severity.MAJOR),
399 newDoc("openvul2", project).setOwaspTop10For2021(asList("a3", "a6")).setCwe(List.of("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
400 .setSeverity(Severity.MINOR),
401 newDoc("notowaspvul", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
402 newDoc("toreviewhotspot1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
403 .setStatus(Issue.STATUS_TO_REVIEW),
404 newDoc("toreviewhotspot2", project).setOwaspTop10For2021(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
405 newDoc("reviewedHotspot", project).setOwaspTop10For2021(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
406 .setResolution(Issue.RESOLUTION_FIXED),
407 newDoc("notowasphotspot", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
409 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2021);
410 assertThat(owaspTop10Report)
411 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
412 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
413 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
414 .containsExactlyInAnyOrder(
415 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
416 tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
417 tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
418 tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
419 tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
420 tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
421 tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
422 tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
423 tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
424 tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
425 return owaspTop10Report;
429 public void getSansTop25Report_aggregation() {
430 ComponentDto project = newPrivateProjectDto();
432 newDoc("openvul1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
433 .setSeverity(Severity.MAJOR),
434 newDoc("openvul2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
435 .setSeverity(Severity.MINOR),
436 newDoc("notopenvul", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
437 .setResolution(Issue.RESOLUTION_FIXED)
438 .setSeverity(Severity.BLOCKER),
439 newDoc("notsansvul", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
440 newDoc("toreviewhotspot1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
441 .setStatus(Issue.STATUS_TO_REVIEW),
442 newDoc("toreviewhotspot2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
443 .setStatus(Issue.STATUS_TO_REVIEW),
444 newDoc("inReviewHotspot", project).setSansTop25(List.of(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW),
445 newDoc("reviewedHotspot", project).setSansTop25(List.of(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
446 .setResolution(Issue.RESOLUTION_FIXED),
447 newDoc("notowasphotspot", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
449 List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false, false);
450 assertThat(sansTop25Report)
451 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
452 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
453 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
454 .containsExactlyInAnyOrder(
455 tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
456 tuple(SANS_TOP_25_RISKY_RESOURCE, 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */,
457 1L /* reviewedHotspot */, 4),
458 tuple(SANS_TOP_25_POROUS_DEFENSES, 1L /* openvul2 */, OptionalInt.of(2)/* MINOR = B */, 1L/* openhotspot2 */, 0L, 5));
460 assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
464 public void getSansTop25Report_aggregation_on_portfolio() {
465 ComponentDto portfolio1 = db.components().insertPrivateApplication();
466 ComponentDto portfolio2 = db.components().insertPrivateApplication();
467 ComponentDto project1 = db.components().insertPrivateProject();
468 ComponentDto project2 = db.components().insertPrivateProject();
471 newDoc("openvul1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
472 .setSeverity(Severity.MAJOR),
473 newDoc("openvul2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
474 .setSeverity(Severity.MINOR),
475 newDoc("notopenvul", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
476 .setResolution(Issue.RESOLUTION_FIXED)
477 .setSeverity(Severity.BLOCKER),
478 newDoc("notsansvul", project2).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
479 newDoc("toreviewhotspot1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
480 .setStatus(Issue.STATUS_TO_REVIEW),
481 newDoc("toreviewhotspot2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
482 .setStatus(Issue.STATUS_TO_REVIEW),
483 newDoc("reviewedHotspot", project2).setSansTop25(List.of(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
484 .setResolution(Issue.RESOLUTION_FIXED),
485 newDoc("notowasphotspot", project1).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
487 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
488 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
490 List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(portfolio1.uuid(), true, false);
491 assertThat(sansTop25Report)
492 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
493 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
494 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
495 .containsExactlyInAnyOrder(
496 tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
497 tuple(SANS_TOP_25_RISKY_RESOURCE, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot1 */, 0L, 5),
498 tuple(SANS_TOP_25_POROUS_DEFENSES, 0L, OptionalInt.empty(), 0L, 0L, 1));
500 assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
504 public void getPciDssReport_aggregation_on_portfolio() {
505 ComponentDto portfolio1 = db.components().insertPrivateApplication();
506 ComponentDto portfolio2 = db.components().insertPrivateApplication();
507 ComponentDto project1 = db.components().insertPrivateProject();
508 ComponentDto project2 = db.components().insertPrivateProject();
511 newDoc("openvul1", project1).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
512 .setSeverity(Severity.MAJOR),
513 newDoc("openvul2", project2).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
514 .setSeverity(Severity.MINOR),
515 newDoc("openvul3", project1).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
516 .setSeverity(Severity.MINOR),
517 newDoc("notpcidssvul", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
518 newDoc("toreviewhotspot1", project2).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
519 .setStatus(Issue.STATUS_TO_REVIEW),
520 newDoc("toreviewhotspot2", project1).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
521 newDoc("reviewedHotspot", project2).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
522 .setResolution(Issue.RESOLUTION_FIXED),
523 newDoc("notpcidsshotspot", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
525 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
526 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
528 List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(portfolio1.uuid(), true, PciDssVersion.V3_2).stream()
529 .sorted(comparing(s -> parseInt(s.getCategory())))
531 assertThat(pciDssReport)
532 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
533 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
534 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
535 .containsExactlyInAnyOrder(
536 tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
537 tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
538 tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
539 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
540 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
541 tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
542 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
543 tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
544 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
545 tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
546 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
547 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
551 public void getOwaspAsvsReport_aggregation_on_portfolio() {
552 ComponentDto portfolio1 = db.components().insertPrivateApplication();
553 ComponentDto portfolio2 = db.components().insertPrivateApplication();
554 ComponentDto project1 = db.components().insertPrivateProject();
555 ComponentDto project2 = db.components().insertPrivateProject();
558 newDoc("openvul1", project1).setOwaspAsvs40(asList("2.1.1", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
559 .setSeverity(Severity.MAJOR),
560 newDoc("openvul2", project2).setOwaspAsvs40(asList("3.3.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
561 .setSeverity(Severity.MINOR),
562 newDoc("openvul3", project1).setOwaspAsvs40(asList("10.3.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
563 .setSeverity(Severity.MINOR),
564 newDoc("notowaspasvsvul", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
565 newDoc("toreviewhotspot1", project2).setOwaspAsvs40(asList("2.1.3", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
566 .setStatus(Issue.STATUS_TO_REVIEW),
567 newDoc("toreviewhotspot2", project1).setOwaspAsvs40(asList("3.4.4", "6.2.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
568 newDoc("reviewedHotspot", project2).setOwaspAsvs40(asList("3.1.1", "8.3.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
569 .setResolution(Issue.RESOLUTION_FIXED),
570 newDoc("notowaspasvshotspot", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
572 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
573 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
575 List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(portfolio1.uuid(), true, OwaspAsvsVersion.V4_0, 1).stream()
576 .sorted(comparing(s -> parseInt(s.getCategory())))
578 assertThat(owaspAsvsReport)
579 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
580 SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
581 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
582 .containsExactlyInAnyOrder(
583 tuple("1", 0L, OptionalInt.empty(), 0L, 0L, 1),
584 tuple("2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
585 tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
586 tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
587 tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
588 tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
589 tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
590 tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
591 tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
592 tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
593 tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
594 tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1),
595 tuple("13", 0L, OptionalInt.empty(), 0L, 0L, 1),
596 tuple("14", 0L, OptionalInt.empty(), 0L, 0L, 1));
600 public void getCWETop25Report_aggregation() {
601 ComponentDto project = newPrivateProjectDto();
603 newDoc("openvul", project).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
604 .setSeverity(Severity.MAJOR),
605 newDoc("notopenvul", project).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
606 .setResolution(Issue.RESOLUTION_FIXED)
607 .setSeverity(Severity.BLOCKER),
608 newDoc("toreviewhotspot", project).setCwe(List.of("89")).setType(RuleType.SECURITY_HOTSPOT)
609 .setStatus(Issue.STATUS_TO_REVIEW),
610 newDoc("only2020", project).setCwe(List.of("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
611 .setSeverity(Severity.MINOR),
612 newDoc("unknown", project).setCwe(List.of("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
613 .setSeverity(Severity.MINOR));
615 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(project.uuid(), false);
617 List<String> listOfYears = cweTop25Reports.stream()
618 .map(SecurityStandardCategoryStatistics::getCategory)
621 assertThat(listOfYears).contains("2019", "2020", "2021");
623 SecurityStandardCategoryStatistics cwe2019 = cweTop25Reports.stream()
624 .filter(s -> s.getCategory().equals("2019"))
626 assertThat(cwe2019.getChildren()).hasSize(25);
627 assertThat(findRuleInCweByYear(cwe2019, "119")).isNotNull()
628 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
629 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
630 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
631 .containsExactlyInAnyOrder(1L, 0L, 0L);
632 assertThat(findRuleInCweByYear(cwe2019, "89")).isNotNull()
633 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
634 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
635 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
636 .containsExactlyInAnyOrder(0L, 1L, 0L);
637 assertThat(findRuleInCweByYear(cwe2019, "862")).isNull();
638 assertThat(findRuleInCweByYear(cwe2019, "999")).isNull();
640 SecurityStandardCategoryStatistics cwe2020 = cweTop25Reports.stream()
641 .filter(s -> s.getCategory().equals("2020"))
643 assertThat(cwe2020.getChildren()).hasSize(25);
644 assertThat(findRuleInCweByYear(cwe2020, "119")).isNotNull()
645 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
646 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
647 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
648 .containsExactlyInAnyOrder(1L, 0L, 0L);
649 assertThat(findRuleInCweByYear(cwe2020, "89")).isNotNull()
650 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
651 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
652 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
653 .containsExactlyInAnyOrder(0L, 1L, 0L);
654 assertThat(findRuleInCweByYear(cwe2020, "862")).isNotNull()
655 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
656 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
657 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
658 .containsExactlyInAnyOrder(1L, 0L, 0L);
659 assertThat(findRuleInCweByYear(cwe2020, "999")).isNull();
661 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
662 .filter(s -> s.getCategory().equals("2021"))
664 assertThat(cwe2021.getChildren()).hasSize(25);
665 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
666 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
667 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
668 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
669 .containsExactlyInAnyOrder(1L, 0L, 0L);
670 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
671 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
672 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
673 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
674 .containsExactlyInAnyOrder(0L, 1L, 0L);
675 assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
676 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
680 public void getCWETop25Report_aggregation_on_portfolio() {
681 ComponentDto application = db.components().insertPrivateApplication();
682 ComponentDto project1 = db.components().insertPrivateProject();
683 ComponentDto project2 = db.components().insertPrivateProject();
686 newDoc("openvul1", project1).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
687 .setSeverity(Severity.MAJOR),
688 newDoc("openvul2", project2).setCwe(List.of("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
689 .setSeverity(Severity.MINOR),
690 newDoc("toreviewhotspot", project1).setCwe(List.of("89")).setType(RuleType.SECURITY_HOTSPOT)
691 .setStatus(Issue.STATUS_TO_REVIEW),
692 newDoc("only2020", project2).setCwe(List.of("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
693 .setSeverity(Severity.MINOR),
694 newDoc("unknown", project2).setCwe(List.of("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
695 .setSeverity(Severity.MINOR));
697 indexView(application.uuid(), asList(project1.uuid(), project2.uuid()));
699 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(application.uuid(), true);
701 List<String> listOfYears = cweTop25Reports.stream()
702 .map(SecurityStandardCategoryStatistics::getCategory)
705 assertThat(listOfYears).contains("2019", "2020", "2021");
707 SecurityStandardCategoryStatistics cwe2019 = cweTop25Reports.stream()
708 .filter(s -> s.getCategory().equals("2019"))
710 assertThat(cwe2019.getChildren()).hasSize(25);
711 assertThat(findRuleInCweByYear(cwe2019, "119")).isNotNull()
712 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
713 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
714 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
715 .containsExactlyInAnyOrder(2L, 0L, 0L);
716 assertThat(findRuleInCweByYear(cwe2019, "89")).isNotNull()
717 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
718 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
719 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
720 .containsExactlyInAnyOrder(0L, 1L, 0L);
721 assertThat(findRuleInCweByYear(cwe2019, "862")).isNull();
722 assertThat(findRuleInCweByYear(cwe2019, "999")).isNull();
724 SecurityStandardCategoryStatistics cwe2020 = cweTop25Reports.stream()
725 .filter(s -> s.getCategory().equals("2020"))
727 assertThat(cwe2020.getChildren()).hasSize(25);
728 assertThat(findRuleInCweByYear(cwe2020, "119")).isNotNull()
729 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
730 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
731 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
732 .containsExactlyInAnyOrder(2L, 0L, 0L);
733 assertThat(findRuleInCweByYear(cwe2020, "89")).isNotNull()
734 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
735 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
736 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
737 .containsExactlyInAnyOrder(0L, 1L, 0L);
738 assertThat(findRuleInCweByYear(cwe2020, "862")).isNotNull()
739 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
740 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
741 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
742 .containsExactlyInAnyOrder(1L, 0L, 0L);
743 assertThat(findRuleInCweByYear(cwe2020, "999")).isNull();
745 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
746 .filter(s -> s.getCategory().equals("2021"))
748 assertThat(cwe2021.getChildren()).hasSize(25);
749 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
750 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
751 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
752 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
753 .containsExactlyInAnyOrder(2L, 0L, 0L);
754 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
755 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
756 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
757 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
758 .containsExactlyInAnyOrder(0L, 1L, 0L);
759 assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
760 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
763 private SecurityStandardCategoryStatistics findRuleInCweByYear(SecurityStandardCategoryStatistics statistics, String cweId) {
764 return statistics.getChildren().stream().filter(stat -> stat.getCategory().equals(cweId)).findAny().orElse(null);
767 private void indexView(String viewUuid, List<String> projects) {
768 viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects));