3 * Copyright (C) 2009-2021 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;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.rules.ExpectedException;
30 import org.sonar.api.impl.utils.TestSystem2;
31 import org.sonar.api.issue.Issue;
32 import org.sonar.api.rule.Severity;
33 import org.sonar.api.rules.RuleType;
34 import org.sonar.api.utils.System2;
35 import org.sonar.db.DbTester;
36 import org.sonar.db.component.ComponentDto;
37 import org.sonar.server.es.EsTester;
38 import org.sonar.server.permission.index.IndexPermissions;
39 import org.sonar.server.permission.index.PermissionIndexerTester;
40 import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
41 import org.sonar.server.tester.UserSessionRule;
42 import org.sonar.server.view.index.ViewDoc;
43 import org.sonar.server.view.index.ViewIndexer;
45 import static java.util.Arrays.asList;
46 import static java.util.Arrays.stream;
47 import static java.util.Collections.singletonList;
48 import static java.util.TimeZone.getTimeZone;
49 import static java.util.stream.Collectors.toList;
50 import static org.assertj.core.api.Assertions.assertThat;
51 import static org.assertj.core.api.Assertions.tuple;
52 import static org.junit.rules.ExpectedException.none;
53 import static org.sonar.api.resources.Qualifiers.PROJECT;
54 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
55 import static org.sonar.server.issue.IssueDocTesting.newDoc;
56 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION;
57 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES;
58 import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE;
59 import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD;
61 public class IssueIndexSecurityReportsTest {
64 public EsTester es = EsTester.create();
66 public UserSessionRule userSessionRule = UserSessionRule.standalone();
68 public ExpectedException expectedException = none();
69 private System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(getTimeZone("GMT-01:00"));
71 public DbTester db = DbTester.create(system2);
73 private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), null);
74 private ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client());
75 private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer);
77 private IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule));
80 public void getOwaspTop10Report_dont_count_vulnerabilities_from_other_projects() {
81 ComponentDto project = newPrivateProjectDto();
82 ComponentDto another = newPrivateProjectDto();
84 newDoc("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
85 newDoc("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
87 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
88 assertThat(owaspTop10Report)
89 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
90 SecurityStandardCategoryStatistics::getVulnerabiliyRating)
92 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
97 public void getOwaspTop10Report_dont_count_closed_vulnerabilities() {
98 ComponentDto project = newPrivateProjectDto();
100 newDoc("openvul1", project).setOwaspTop10(asList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
101 newDoc("notopenvul", project).setOwaspTop10(asList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
102 .setSeverity(Severity.BLOCKER));
104 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
105 assertThat(owaspTop10Report)
106 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
107 SecurityStandardCategoryStatistics::getVulnerabiliyRating)
109 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */));
113 public void getOwaspTop10Report_dont_count_old_vulnerabilities() {
114 ComponentDto project = newPrivateProjectDto();
116 // Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
117 newDoc("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
119 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
120 assertThat(owaspTop10Report)
121 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
122 SecurityStandardCategoryStatistics::getVulnerabiliyRating)
124 tuple(0L, OptionalInt.empty()));
128 public void getOwaspTop10Report_dont_count_hotspots_from_other_projects() {
129 ComponentDto project = newPrivateProjectDto();
130 ComponentDto another = newPrivateProjectDto();
132 newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
133 newDoc("anotherProject", another).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
135 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
136 assertThat(owaspTop10Report)
137 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
139 tuple("a1", 1L /* openhotspot1 */));
143 public void getOwaspTop10Report_dont_count_closed_hotspots() {
144 ComponentDto project = newPrivateProjectDto();
146 newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
147 newDoc("closedHotspot", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
148 .setResolution(Issue.RESOLUTION_FIXED));
150 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
151 assertThat(owaspTop10Report)
152 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
154 tuple("a1", 1L /* openhotspot1 */));
158 public void getOwaspTop10Report_aggregation_no_cwe() {
159 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(false);
161 assertThat(owaspTop10Report)
163 .allMatch(category -> category.getChildren().isEmpty());
167 public void getOwaspTop10Report_aggregation_with_cwe() {
168 List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(true);
170 Map<String, List<SecurityStandardCategoryStatistics>> cweByOwasp = owaspTop10Report.stream()
171 .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
173 assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
174 SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
175 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
176 .containsExactlyInAnyOrder(
177 tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
178 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
179 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
180 assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
181 SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
182 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
183 .containsExactlyInAnyOrder(
184 tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
185 tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
186 tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 5));
189 private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspReport(boolean includeCwe) {
190 ComponentDto project = newPrivateProjectDto();
192 newDoc("openvul1", project).setOwaspTop10(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
193 .setSeverity(Severity.MAJOR),
194 newDoc("openvul2", project).setOwaspTop10(asList("a3", "a6")).setCwe(asList("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
195 .setSeverity(Severity.MINOR),
196 newDoc("notowaspvul", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
197 newDoc("toreviewhotspot1", project).setOwaspTop10(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
198 .setStatus(Issue.STATUS_TO_REVIEW),
199 newDoc("toreviewhotspot2", project).setOwaspTop10(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
200 newDoc("reviewedHotspot", project).setOwaspTop10(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
201 .setResolution(Issue.RESOLUTION_FIXED),
202 newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
204 List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe);
205 assertThat(owaspTop10Report)
206 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
207 SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
208 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
209 .containsExactlyInAnyOrder(
210 tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
211 tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 1),
212 tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
213 tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 1),
214 tuple("a5", 0L, OptionalInt.empty(), 0L, 0L, 1),
215 tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
216 tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 1),
217 tuple("a8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
218 tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 1),
219 tuple("a10", 0L, OptionalInt.empty(), 0L, 0L, 1));
220 return owaspTop10Report;
224 public void getSansTop25Report_aggregation() {
225 ComponentDto project = newPrivateProjectDto();
227 newDoc("openvul1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
228 .setSeverity(Severity.MAJOR),
229 newDoc("openvul2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
230 .setSeverity(Severity.MINOR),
231 newDoc("notopenvul", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
232 .setResolution(Issue.RESOLUTION_FIXED)
233 .setSeverity(Severity.BLOCKER),
234 newDoc("notsansvul", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
235 newDoc("toreviewhotspot1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
236 .setStatus(Issue.STATUS_TO_REVIEW),
237 newDoc("toreviewhotspot2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
238 .setStatus(Issue.STATUS_TO_REVIEW),
239 newDoc("inReviewHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW),
240 newDoc("reviewedHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
241 .setResolution(Issue.RESOLUTION_FIXED),
242 newDoc("notowasphotspot", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
244 List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false, false);
245 assertThat(sansTop25Report)
246 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
247 SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
248 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
249 .containsExactlyInAnyOrder(
250 tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
251 tuple(SANS_TOP_25_RISKY_RESOURCE, 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */,
252 1L /* reviewedHotspot */, 4),
253 tuple(SANS_TOP_25_POROUS_DEFENSES, 1L /* openvul2 */, OptionalInt.of(2)/* MINOR = B */, 1L/* openhotspot2 */, 0L, 5));
255 assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
259 public void getSansTop25Report_aggregation_on_portfolio() {
260 ComponentDto portfolio1 = db.components().insertPrivateApplication();
261 ComponentDto portfolio2 = db.components().insertPrivateApplication();
262 ComponentDto project1 = db.components().insertPrivateProject();
263 ComponentDto project2 = db.components().insertPrivateProject();
266 newDoc("openvul1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
267 .setSeverity(Severity.MAJOR),
268 newDoc("openvul2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
269 .setSeverity(Severity.MINOR),
270 newDoc("notopenvul", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
271 .setResolution(Issue.RESOLUTION_FIXED)
272 .setSeverity(Severity.BLOCKER),
273 newDoc("notsansvul", project2).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
274 newDoc("toreviewhotspot1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
275 .setStatus(Issue.STATUS_TO_REVIEW),
276 newDoc("toreviewhotspot2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
277 .setStatus(Issue.STATUS_TO_REVIEW),
278 newDoc("reviewedHotspot", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
279 .setResolution(Issue.RESOLUTION_FIXED),
280 newDoc("notowasphotspot", project1).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
282 indexView(portfolio1.uuid(), singletonList(project1.uuid()));
283 indexView(portfolio2.uuid(), singletonList(project2.uuid()));
285 List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(portfolio1.uuid(), true, false);
286 assertThat(sansTop25Report)
287 .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
288 SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
289 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
290 .containsExactlyInAnyOrder(
291 tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
292 tuple(SANS_TOP_25_RISKY_RESOURCE, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot1 */, 0L, 5),
293 tuple(SANS_TOP_25_POROUS_DEFENSES, 0L, OptionalInt.empty(), 0L, 0L, 1));
295 assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
299 public void getCWETop25Report_aggregation() {
300 ComponentDto project = newPrivateProjectDto();
302 newDoc("openvul", project).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
303 .setSeverity(Severity.MAJOR),
304 newDoc("notopenvul", project).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
305 .setResolution(Issue.RESOLUTION_FIXED)
306 .setSeverity(Severity.BLOCKER),
307 newDoc("toreviewhotspot", project).setCwe(asList("89")).setType(RuleType.SECURITY_HOTSPOT)
308 .setStatus(Issue.STATUS_TO_REVIEW),
309 newDoc("only2020", project).setCwe(asList("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
310 .setSeverity(Severity.MINOR),
311 newDoc("unknown", project).setCwe(asList("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
312 .setSeverity(Severity.MINOR));
314 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(project.uuid(), false);
316 List<String> listOfYears = cweTop25Reports.stream()
317 .map(SecurityStandardCategoryStatistics::getCategory)
320 assertThat(listOfYears).contains("2019", "2020", "2021");
322 SecurityStandardCategoryStatistics cwe2019 = cweTop25Reports.stream()
323 .filter(s -> s.getCategory().equals("2019"))
325 assertThat(cwe2019.getChildren()).hasSize(25);
326 assertThat(findRuleInCweByYear(cwe2019, "119")).isNotNull()
327 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
328 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
329 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
330 .containsExactlyInAnyOrder(1L, 0L, 0L);
331 assertThat(findRuleInCweByYear(cwe2019, "89")).isNotNull()
332 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
333 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
334 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
335 .containsExactlyInAnyOrder(0L, 1L, 0L);
336 assertThat(findRuleInCweByYear(cwe2019, "862")).isNull();
337 assertThat(findRuleInCweByYear(cwe2019, "999")).isNull();
339 SecurityStandardCategoryStatistics cwe2020 = cweTop25Reports.stream()
340 .filter(s -> s.getCategory().equals("2020"))
342 assertThat(cwe2020.getChildren()).hasSize(25);
343 assertThat(findRuleInCweByYear(cwe2020, "119")).isNotNull()
344 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
345 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
346 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
347 .containsExactlyInAnyOrder(1L, 0L, 0L);
348 assertThat(findRuleInCweByYear(cwe2020, "89")).isNotNull()
349 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
350 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
351 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
352 .containsExactlyInAnyOrder(0L, 1L, 0L);
353 assertThat(findRuleInCweByYear(cwe2020, "862")).isNotNull()
354 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
355 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
356 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
357 .containsExactlyInAnyOrder(1L, 0L, 0L);
358 assertThat(findRuleInCweByYear(cwe2020, "999")).isNull();
360 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
361 .filter(s -> s.getCategory().equals("2021"))
363 assertThat(cwe2021.getChildren()).hasSize(25);
364 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
365 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
366 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
367 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
368 .containsExactlyInAnyOrder(1L, 0L, 0L);
369 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
370 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
371 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
372 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
373 .containsExactlyInAnyOrder(0L, 1L, 0L);
374 assertThat(findRuleInCweByYear(cwe2021, "862")).isNull();
375 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
379 public void getCWETop25Report_aggregation_on_portfolio() {
380 ComponentDto application = db.components().insertPrivateApplication();
381 ComponentDto project1 = db.components().insertPrivateProject();
382 ComponentDto project2 = db.components().insertPrivateProject();
385 newDoc("openvul1", project1).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
386 .setSeverity(Severity.MAJOR),
387 newDoc("openvul2", project2).setCwe(asList("119")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
388 .setSeverity(Severity.MINOR),
389 newDoc("toreviewhotspot", project1).setCwe(asList("89")).setType(RuleType.SECURITY_HOTSPOT)
390 .setStatus(Issue.STATUS_TO_REVIEW),
391 newDoc("only2020", project2).setCwe(asList("862")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
392 .setSeverity(Severity.MINOR),
393 newDoc("unknown", project2).setCwe(asList("999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
394 .setSeverity(Severity.MINOR));
396 indexView(application.uuid(), asList(project1.uuid(), project2.uuid()));
398 List<SecurityStandardCategoryStatistics> cweTop25Reports = underTest.getCweTop25Reports(application.uuid(), true);
400 List<String> listOfYears = cweTop25Reports.stream()
401 .map(SecurityStandardCategoryStatistics::getCategory)
404 assertThat(listOfYears).contains("2019", "2020", "2021");
406 SecurityStandardCategoryStatistics cwe2019 = cweTop25Reports.stream()
407 .filter(s -> s.getCategory().equals("2019"))
409 assertThat(cwe2019.getChildren()).hasSize(25);
410 assertThat(findRuleInCweByYear(cwe2019, "119")).isNotNull()
411 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
412 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
413 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
414 .containsExactlyInAnyOrder(2L, 0L, 0L);
415 assertThat(findRuleInCweByYear(cwe2019, "89")).isNotNull()
416 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
417 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
418 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
419 .containsExactlyInAnyOrder(0L, 1L, 0L);
420 assertThat(findRuleInCweByYear(cwe2019, "862")).isNull();
421 assertThat(findRuleInCweByYear(cwe2019, "999")).isNull();
423 SecurityStandardCategoryStatistics cwe2020 = cweTop25Reports.stream()
424 .filter(s -> s.getCategory().equals("2020"))
426 assertThat(cwe2020.getChildren()).hasSize(25);
427 assertThat(findRuleInCweByYear(cwe2020, "119")).isNotNull()
428 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
429 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
430 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
431 .containsExactlyInAnyOrder(2L, 0L, 0L);
432 assertThat(findRuleInCweByYear(cwe2020, "89")).isNotNull()
433 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
434 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
435 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
436 .containsExactlyInAnyOrder(0L, 1L, 0L);
437 assertThat(findRuleInCweByYear(cwe2020, "862")).isNotNull()
438 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
439 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
440 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
441 .containsExactlyInAnyOrder(1L, 0L, 0L);
442 assertThat(findRuleInCweByYear(cwe2020, "999")).isNull();
444 SecurityStandardCategoryStatistics cwe2021 = cweTop25Reports.stream()
445 .filter(s -> s.getCategory().equals("2021"))
447 assertThat(cwe2021.getChildren()).hasSize(25);
448 assertThat(findRuleInCweByYear(cwe2021, "119")).isNotNull()
449 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
450 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
451 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
452 .containsExactlyInAnyOrder(2L, 0L, 0L);
453 assertThat(findRuleInCweByYear(cwe2021, "89")).isNotNull()
454 .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
455 SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
456 SecurityStandardCategoryStatistics::getReviewedSecurityHotspots)
457 .containsExactlyInAnyOrder(0L, 1L, 0L);
458 assertThat(findRuleInCweByYear(cwe2021, "862")).isNull();
459 assertThat(findRuleInCweByYear(cwe2021, "999")).isNull();
462 private SecurityStandardCategoryStatistics findRuleInCweByYear(SecurityStandardCategoryStatistics statistics, String cweId) {
463 return statistics.getChildren().stream().filter(stat -> stat.getCategory().equals(cweId)).findAny().orElse(null);
466 private void indexIssues(IssueDoc... issues) {
467 issueIndexer.index(asList(issues).iterator());
468 authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList()));
471 private void indexView(String viewUuid, List<String> projects) {
472 viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects));