]> source.dussan.org Git - sonarqube.git/blob
8a7952c6e1c165388f7ca5105eb5698f6b826699
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.issue.index;
21
22 import java.util.List;
23 import java.util.Map;
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.api.server.rule.RulesDefinition;
31 import org.sonar.db.component.ComponentDto;
32 import org.sonar.server.view.index.ViewDoc;
33
34 import static java.lang.Integer.parseInt;
35 import static java.util.Arrays.asList;
36 import static java.util.Collections.singletonList;
37 import static java.util.Comparator.comparing;
38 import static java.util.stream.Collectors.toList;
39 import static org.assertj.core.api.Assertions.assertThat;
40 import static org.assertj.core.api.Assertions.tuple;
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.db.component.ComponentTesting.newPrivateProjectDto;
44 import static org.sonar.server.issue.IssueDocTesting.newDoc;
45 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION;
46 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES;
47 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE;
48 import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD;
49
50 public class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
51
52   @Test
53   public void getOwaspTop10Report_dont_count_vulnerabilities_from_other_projects() {
54     ComponentDto project = newPrivateProjectDto();
55     ComponentDto another = newPrivateProjectDto();
56
57     IssueDoc openVulDoc = newDoc("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
58     openVulDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR);
59
60     IssueDoc otherProjectDoc = newDoc("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
61     otherProjectDoc.setOwaspTop10For2021(singletonList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL);
62
63     indexIssues(openVulDoc, otherProjectDoc);
64
65     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
66     assertThat(owaspTop10Report)
67       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
68         SecurityStandardCategoryStatistics::getVulnerabilityRating)
69       .contains(
70         tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
71
72     List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
73     assertThat(owaspTop10For2021Report)
74       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
75         SecurityStandardCategoryStatistics::getVulnerabilityRating)
76       .contains(
77         tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
78
79   }
80
81   @Test
82   public void getOwaspTop10Report_dont_count_closed_vulnerabilities() {
83     ComponentDto project = newPrivateProjectDto();
84     indexIssues(
85       newDoc("openvul1", project).setOwaspTop10(asList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
86       newDoc("openvul12021", project).setOwaspTop10For2021(asList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
87       newDoc("notopenvul", project).setOwaspTop10(asList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
88         .setSeverity(Severity.BLOCKER),
89       newDoc("notopenvul2021", project).setOwaspTop10For2021(asList("a2")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
90         .setSeverity(Severity.BLOCKER));
91
92     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
93     assertThat(owaspTop10Report)
94       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
95         SecurityStandardCategoryStatistics::getVulnerabilityRating)
96       .contains(
97         tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
98
99     List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
100     assertThat(owaspTop10For2021Report)
101       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
102         SecurityStandardCategoryStatistics::getVulnerabilityRating)
103       .contains(
104         tuple("a2", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
105   }
106
107   @Test
108   public void getOwaspTop10Report_dont_count_old_vulnerabilities() {
109     ComponentDto project = newPrivateProjectDto();
110     indexIssues(
111       // Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
112       newDoc("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
113
114     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
115     assertThat(owaspTop10Report)
116       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
117         SecurityStandardCategoryStatistics::getVulnerabilityRating)
118       .containsOnly(
119         tuple(0L, OptionalInt.empty()));
120
121     List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
122     assertThat(owaspTop10For2021Report)
123       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
124         SecurityStandardCategoryStatistics::getVulnerabilityRating)
125       .containsOnly(
126         tuple(0L, OptionalInt.empty()));
127   }
128
129   @Test
130   public void getOwaspTop10Report_dont_count_hotspots_from_other_projects() {
131     ComponentDto project = newPrivateProjectDto();
132     ComponentDto another = newPrivateProjectDto();
133     indexIssues(
134       newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
135       newDoc("openhotspot2021", project).setOwaspTop10For2021(asList("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
136       newDoc("anotherProject", another).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
137       newDoc("anotherProject2021", another).setOwaspTop10For2021(asList("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
138
139     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
140     assertThat(owaspTop10Report)
141       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
142       .contains(
143         tuple("a1", 1L /* openhotspot1 */));
144
145     List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
146     assertThat(owaspTop10For2021Report)
147       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
148       .contains(
149         tuple("a2", 1L /* openhotspot1 */));
150   }
151
152   @Test
153   public void getOwaspTop10Report_dont_count_closed_hotspots() {
154     ComponentDto project = newPrivateProjectDto();
155     indexIssues(
156       newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
157       newDoc("openhotspot2021", project).setOwaspTop10For2021(asList("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
158       newDoc("closedHotspot", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
159         .setResolution(Issue.RESOLUTION_FIXED),
160     newDoc("closedHotspot2021", project).setOwaspTop10For2021(asList("a2")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
161       .setResolution(Issue.RESOLUTION_FIXED));
162
163     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
164     assertThat(owaspTop10Report)
165       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
166       .contains(
167         tuple("a1", 1L /* openhotspot1 */));
168
169     List<SecurityStandardCategoryStatistics> owaspTop10For2021Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2021);
170     assertThat(owaspTop10For2021Report)
171       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
172       .contains(
173         tuple("a2", 1L /* openhotspot1 */));
174   }
175
176   @Test
177   public void getOwaspTop10Report_aggregation_no_cwe() {
178     List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(false);
179
180     assertThat(owaspTop10Report)
181       .isNotEmpty()
182       .allMatch(category -> category.getChildren().isEmpty());
183   }
184
185   @Test
186   public void getPciDss32Report_aggregation() {
187     List<SecurityStandardCategoryStatistics> pciDss32Report = indexIssuesAndAssertPciDss32Report();
188
189     assertThat(pciDss32Report)
190       .isNotEmpty();
191
192     assertThat(pciDss32Report.get(0).getChildren()).hasSize(2);
193     assertThat(pciDss32Report.get(1).getChildren()).isEmpty();
194     assertThat(pciDss32Report.get(2).getChildren()).hasSize(4);
195     assertThat(pciDss32Report.get(3).getChildren()).isEmpty();
196     assertThat(pciDss32Report.get(4).getChildren()).isEmpty();
197     assertThat(pciDss32Report.get(5).getChildren()).hasSize(2);
198     assertThat(pciDss32Report.get(6).getChildren()).isEmpty();
199     assertThat(pciDss32Report.get(7).getChildren()).hasSize(1);
200     assertThat(pciDss32Report.get(8).getChildren()).isEmpty();
201     assertThat(pciDss32Report.get(9).getChildren()).hasSize(1);
202     assertThat(pciDss32Report.get(10).getChildren()).isEmpty();
203     assertThat(pciDss32Report.get(11).getChildren()).isEmpty();
204   }
205
206   @Test
207   public void getOwaspTop10Report_aggregation_with_cwe() {
208     List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(true);
209
210     Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
211       .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
212
213     assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
214       SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
215       SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
216       .containsExactlyInAnyOrder(
217         tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
218         tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
219         tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
220     assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
221       SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
222       SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
223       .containsExactlyInAnyOrder(
224         tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
225         tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
226         tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
227   }
228
229   @Test
230   public void getOwaspTop10For2021Report_aggregation_with_cwe() {
231     List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwasp2021Report(true);
232
233     Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
234       .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
235
236     assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
237         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
238         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
239       .containsExactlyInAnyOrder(
240         tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
241         tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
242         tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
243     assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
244         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
245         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
246       .containsExactlyInAnyOrder(
247         tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
248         tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
249         tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
250   }
251
252   private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspReport(boolean includeCwe) {
253     ComponentDto project = newPrivateProjectDto();
254     indexIssues(
255       newDoc("openvul1", project).setOwaspTop10(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
256         .setSeverity(Severity.MAJOR),
257       newDoc("openvul2", project).setOwaspTop10(asList("a3", "a6")).setCwe(asList("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
258         .setSeverity(Severity.MINOR),
259       newDoc("notowaspvul", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
260       newDoc("toreviewhotspot1", project).setOwaspTop10(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
261         .setStatus(Issue.STATUS_TO_REVIEW),
262       newDoc("toreviewhotspot2", project).setOwaspTop10(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
263       newDoc("reviewedHotspot", project).setOwaspTop10(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
264         .setResolution(Issue.RESOLUTION_FIXED),
265       newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
266
267     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2017);
268     assertThat(owaspTop10Report)
269       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
270         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
271         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
272       .containsExactlyInAnyOrder(
273         tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
274         tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
275         tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
276         tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
277         tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
278         tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
279         tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
280         tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
281         tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
282         tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
283     return owaspTop10Report;
284   }
285
286   private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertPciDss32Report() {
287     ComponentDto project = newPrivateProjectDto();
288     indexIssues(
289       newDoc("openvul1", project).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
290         .setSeverity(Severity.MAJOR),
291       newDoc("openvul2", project).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
292         .setSeverity(Severity.MINOR),
293       newDoc("openvul3", project).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
294         .setSeverity(Severity.MINOR),
295       newDoc("notpcidssvul", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
296       newDoc("toreviewhotspot1", project).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
297         .setStatus(Issue.STATUS_TO_REVIEW),
298       newDoc("toreviewhotspot2", project).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
299       newDoc("reviewedHotspot", project).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
300         .setResolution(Issue.RESOLUTION_FIXED),
301       newDoc("notpcidsshotspot", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
302
303     List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(project.uuid(), false, RulesDefinition.PciDssVersion.V3_2).stream()
304       .sorted(comparing(s -> parseInt(s.getCategory())))
305       .collect(toList());
306     assertThat(pciDssReport)
307       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
308         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
309         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
310       .containsExactlyInAnyOrder(
311         tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
312         tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
313         tuple("3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
314         tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
315         tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
316         tuple("6", 2L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
317         tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
318         tuple("8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
319         tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
320         tuple("10", 1L, OptionalInt.of(2), 0L, 0L, 1),
321         tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
322         tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
323
324     return pciDssReport;
325   }
326
327   private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwasp2021Report(boolean includeCwe) {
328     ComponentDto project = newPrivateProjectDto();
329     indexIssues(
330       newDoc("openvul1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
331         .setSeverity(Severity.MAJOR),
332       newDoc("openvul2", project).setOwaspTop10For2021(asList("a3", "a6")).setCwe(asList("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
333         .setSeverity(Severity.MINOR),
334       newDoc("notowaspvul", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
335       newDoc("toreviewhotspot1", project).setOwaspTop10For2021(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
336         .setStatus(Issue.STATUS_TO_REVIEW),
337       newDoc("toreviewhotspot2", project).setOwaspTop10For2021(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
338       newDoc("reviewedHotspot", project).setOwaspTop10For2021(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
339         .setResolution(Issue.RESOLUTION_FIXED),
340       newDoc("notowasphotspot", project).setOwaspTop10For2021(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
341
342     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2021);
343     assertThat(owaspTop10Report)
344       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
345         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
346         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
347       .containsExactlyInAnyOrder(
348         tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
349         tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
350         tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
351         tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
352         tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
353         tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
354         tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
355         tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
356         tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
357         tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
358     return owaspTop10Report;
359   }
360
361   @Test
362   public void getSansTop25Report_aggregation() {
363     ComponentDto project = newPrivateProjectDto();
364     indexIssues(
365       newDoc("openvul1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
366         .setSeverity(Severity.MAJOR),
367       newDoc("openvul2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
368         .setSeverity(Severity.MINOR),
369       newDoc("notopenvul", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
370         .setResolution(Issue.RESOLUTION_FIXED)
371         .setSeverity(Severity.BLOCKER),
372       newDoc("notsansvul", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
373       newDoc("toreviewhotspot1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
374         .setStatus(Issue.STATUS_TO_REVIEW),
375       newDoc("toreviewhotspot2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
376         .setStatus(Issue.STATUS_TO_REVIEW),
377       newDoc("inReviewHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW),
378       newDoc("reviewedHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
379         .setResolution(Issue.RESOLUTION_FIXED),
380       newDoc("notowasphotspot", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
381
382     List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false, false);
383     assertThat(sansTop25Report)
384       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
385         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
386         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
387       .containsExactlyInAnyOrder(
388         tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
389         tuple(SANS_TOP_25_RISKY_RESOURCE, 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */,
390           1L /* reviewedHotspot */, 4),
391         tuple(SANS_TOP_25_POROUS_DEFENSES, 1L /* openvul2 */, OptionalInt.of(2)/* MINOR = B */, 1L/* openhotspot2 */, 0L, 5));
392
393     assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
394   }
395
396   @Test
397   public void getSansTop25Report_aggregation_on_portfolio() {
398     ComponentDto portfolio1 = db.components().insertPrivateApplication();
399     ComponentDto portfolio2 = db.components().insertPrivateApplication();
400     ComponentDto project1 = db.components().insertPrivateProject();
401     ComponentDto project2 = db.components().insertPrivateProject();
402
403     indexIssues(
404       newDoc("openvul1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
405         .setSeverity(Severity.MAJOR),
406       newDoc("openvul2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
407         .setSeverity(Severity.MINOR),
408       newDoc("notopenvul", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
409         .setResolution(Issue.RESOLUTION_FIXED)
410         .setSeverity(Severity.BLOCKER),
411       newDoc("notsansvul", project2).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
412       newDoc("toreviewhotspot1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
413         .setStatus(Issue.STATUS_TO_REVIEW),
414       newDoc("toreviewhotspot2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
415         .setStatus(Issue.STATUS_TO_REVIEW),
416       newDoc("reviewedHotspot", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
417         .setResolution(Issue.RESOLUTION_FIXED),
418       newDoc("notowasphotspot", project1).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
419
420     indexView(portfolio1.uuid(), singletonList(project1.uuid()));
421     indexView(portfolio2.uuid(), singletonList(project2.uuid()));
422
423     List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(portfolio1.uuid(), true, false);
424     assertThat(sansTop25Report)
425       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
426         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
427         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
428       .containsExactlyInAnyOrder(
429         tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
430         tuple(SANS_TOP_25_RISKY_RESOURCE, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot1 */, 0L, 5),
431         tuple(SANS_TOP_25_POROUS_DEFENSES, 0L, OptionalInt.empty(), 0L, 0L, 1));
432
433     assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
434   }
435
436   @Test
437   public void getPciDssReport_aggregation_on_portfolio() {
438     ComponentDto portfolio1 = db.components().insertPrivateApplication();
439     ComponentDto portfolio2 = db.components().insertPrivateApplication();
440     ComponentDto project1 = db.components().insertPrivateProject();
441     ComponentDto project2 = db.components().insertPrivateProject();
442
443     indexIssues(
444       newDoc("openvul1", project1).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
445         .setSeverity(Severity.MAJOR),
446       newDoc("openvul2", project2).setPciDss32(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
447         .setSeverity(Severity.MINOR),
448       newDoc("openvul3", project1).setPciDss32(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
449         .setSeverity(Severity.MINOR),
450       newDoc("notpcidssvul", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
451       newDoc("toreviewhotspot1", project2).setPciDss32(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
452         .setStatus(Issue.STATUS_TO_REVIEW),
453       newDoc("toreviewhotspot2", project1).setPciDss32(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
454       newDoc("reviewedHotspot", project2).setPciDss32(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
455         .setResolution(Issue.RESOLUTION_FIXED),
456       newDoc("notpcidsshotspot", project1).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
457
458     indexView(portfolio1.uuid(), singletonList(project1.uuid()));
459     indexView(portfolio2.uuid(), singletonList(project2.uuid()));
460
461     List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(portfolio1.uuid(), true, RulesDefinition.PciDssVersion.V3_2).stream()
462       .sorted(comparing(s -> parseInt(s.getCategory())))
463       .collect(toList());
464     assertThat(pciDssReport)
465       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
466         SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
467         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
468       .containsExactlyInAnyOrder(
469         tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
470         tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
471         tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
472         tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
473         tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
474         tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
475         tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
476         tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
477         tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
478         tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
479         tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
480         tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
481   }
482
483   @Test
484   public void getCWETop25Report_aggregation() {
485     ComponentDto project = newPrivateProjectDto();
486     indexIssues(
487       newDoc("openvul", project).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
488         .setSeverity(Severity.MAJOR),
489       newDoc("notopenvul", project).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
490         .setResolution(Issue.RESOLUTION_FIXED)
491         .setSeverity(Severity.BLOCKER),
492       newDoc("toreviewhotspot", project).setCwe(asList("89")).setType(RuleType.SECURITY_HOTSPOT)
493         .setStatus(Issue.STATUS_TO_REVIEW),
494       newDoc("only2020", project).setCwe(asList("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
495         .setSeverity(Severity.MINOR),
496       newDoc("unknown", project).setCwe(asList("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
497         .setSeverity(Severity.MINOR));
498
499     List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(project.uuid(), false);
500
501     List<String> listOfYears = cweTop25Reports.stream()
502       .map(SecurityStandardCategoryStatistics::getCategory)
503       .collect(toList());
504
505     assertThat(listOfYears).contains("2019", "2020", "2021");
506
507     SecurityStandardCategoryStatistics cwe2019 = cweTop25Reports.stream()
508       .filter(s -> s.getCategory().equals("2019"))
509       .findAny().get();
510     assertThat(cwe2019.getChildren()).hasSize(25);
511     assertThat(findRuleInCweByYear(cwe2019, "119")).isNotNull()
512       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
513         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
514         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
515       .containsExactlyInAnyOrder(1L, 0L, 0L);
516     assertThat(findRuleInCweByYear(cwe2019, "89")).isNotNull()
517       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
518         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
519         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
520       .containsExactlyInAnyOrder(0L, 1L, 0L);
521     assertThat(findRuleInCweByYear(cwe2019, "862")).isNull();
522     assertThat(findRuleInCweByYear(cwe2019, "999")).isNull();
523
524     SecurityStandardCategoryStatistics cwe2020 = cweTop25Reports.stream()
525       .filter(s -> s.getCategory().equals("2020"))
526       .findAny().get();
527     assertThat(cwe2020.getChildren()).hasSize(25);
528     assertThat(findRuleInCweByYear(cwe2020, "119")).isNotNull()
529       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
530         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
531         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
532       .containsExactlyInAnyOrder(1L, 0L, 0L);
533     assertThat(findRuleInCweByYear(cwe2020, "89")).isNotNull()
534       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
535         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
536         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
537       .containsExactlyInAnyOrder(0L, 1L, 0L);
538     assertThat(findRuleInCweByYear(cwe2020, "862")).isNotNull()
539       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
540         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
541         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
542       .containsExactlyInAnyOrder(1L, 0L, 0L);
543     assertThat(findRuleInCweByYear(cwe2020, "999")).isNull();
544
545     SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
546       .filter(s -> s.getCategory().equals("2021"))
547       .findAny().get();
548     assertThat(cwe2021.getChildren()).hasSize(25);
549     assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
550       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
551         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
552         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
553       .containsExactlyInAnyOrder(1L, 0L, 0L);
554     assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
555       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
556         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
557         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
558       .containsExactlyInAnyOrder(0L, 1L, 0L);
559     assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
560     assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
561   }
562
563   @Test
564   public void getCWETop25Report_aggregation_on_portfolio() {
565     ComponentDto application = db.components().insertPrivateApplication();
566     ComponentDto project1 = db.components().insertPrivateProject();
567     ComponentDto project2 = db.components().insertPrivateProject();
568
569     indexIssues(
570       newDoc("openvul1", project1).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
571         .setSeverity(Severity.MAJOR),
572       newDoc("openvul2", project2).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
573         .setSeverity(Severity.MINOR),
574       newDoc("toreviewhotspot", project1).setCwe(asList("89")).setType(RuleType.SECURITY_HOTSPOT)
575         .setStatus(Issue.STATUS_TO_REVIEW),
576       newDoc("only2020", project2).setCwe(asList("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
577         .setSeverity(Severity.MINOR),
578       newDoc("unknown", project2).setCwe(asList("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
579         .setSeverity(Severity.MINOR));
580
581     indexView(application.uuid(), asList(project1.uuid(), project2.uuid()));
582
583     List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(application.uuid(), true);
584
585     List<String> listOfYears = cweTop25Reports.stream()
586       .map(SecurityStandardCategoryStatistics::getCategory)
587       .collect(toList());
588
589     assertThat(listOfYears).contains("2019", "2020", "2021");
590
591     SecurityStandardCategoryStatistics cwe2019 = cweTop25Reports.stream()
592       .filter(s -> s.getCategory().equals("2019"))
593       .findAny().get();
594     assertThat(cwe2019.getChildren()).hasSize(25);
595     assertThat(findRuleInCweByYear(cwe2019, "119")).isNotNull()
596       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
597         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
598         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
599       .containsExactlyInAnyOrder(2L, 0L, 0L);
600     assertThat(findRuleInCweByYear(cwe2019, "89")).isNotNull()
601       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
602         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
603         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
604       .containsExactlyInAnyOrder(0L, 1L, 0L);
605     assertThat(findRuleInCweByYear(cwe2019, "862")).isNull();
606     assertThat(findRuleInCweByYear(cwe2019, "999")).isNull();
607
608     SecurityStandardCategoryStatistics cwe2020 = cweTop25Reports.stream()
609       .filter(s -> s.getCategory().equals("2020"))
610       .findAny().get();
611     assertThat(cwe2020.getChildren()).hasSize(25);
612     assertThat(findRuleInCweByYear(cwe2020, "119")).isNotNull()
613       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
614         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
615         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
616       .containsExactlyInAnyOrder(2L, 0L, 0L);
617     assertThat(findRuleInCweByYear(cwe2020, "89")).isNotNull()
618       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
619         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
620         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
621       .containsExactlyInAnyOrder(0L, 1L, 0L);
622     assertThat(findRuleInCweByYear(cwe2020, "862")).isNotNull()
623       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
624         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
625         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
626       .containsExactlyInAnyOrder(1L, 0L, 0L);
627     assertThat(findRuleInCweByYear(cwe2020, "999")).isNull();
628
629     SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
630       .filter(s -> s.getCategory().equals("2021"))
631       .findAny().get();
632     assertThat(cwe2021.getChildren()).hasSize(25);
633     assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
634       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
635         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
636         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
637       .containsExactlyInAnyOrder(2L, 0L, 0L);
638     assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
639       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
640         SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
641         SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
642       .containsExactlyInAnyOrder(0L, 1L, 0L);
643     assertThat(findRuleInCweByYear(cwe2021, "295")).isNull();
644     assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
645   }
646
647   private SecurityStandardCategoryStatistics findRuleInCweByYear(SecurityStandardCategoryStatistics statistics, String cweId) {
648     return statistics.getChildren().stream().filter(stat -> stat.getCategory().equals(cweId)).findAny().orElse(null);
649   }
650
651   private void indexView(String viewUuid, List<String> projects) {
652     viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects));
653   }
654
655 }