.collect(MoreCollectors.toList(branchUuids.size()));
}
- public List<SecurityStandardCategoryStatistics> getSansTop25Report(String projectUuid, boolean includeCwe) {
- SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid);
+ public List<SecurityStandardCategoryStatistics> getSansTop25Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
+ SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
Stream.of(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES).forEach(sansCategory -> {
AggregationBuilder sansCategoryAggs = AggregationBuilders
.filter(sansCategory, boolQuery()
return processSecurityReportSearchResults(request, includeCwe);
}
- public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean includeCwe) {
- SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid);
+ public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
+ SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
Stream.concat(IntStream.rangeClosed(1, 10).mapToObj(i -> "a" + i), Stream.of(UNKNOWN_STANDARD)).forEach(owaspCategory -> {
AggregationBuilder owaspCategoryAggs = AggregationBuilders
.filter(owaspCategory, boolQuery()
AggregationBuilders.count("count").field(IssueIndexDefinition.FIELD_ISSUE_KEY)));
}
- private SearchRequestBuilder prepareNonClosedVulnerabilitiesAndHotspotSearch(String projectUuid) {
+ private SearchRequestBuilder prepareNonClosedVulnerabilitiesAndHotspotSearch(String projectUuid, boolean isViewOrApp) {
+ BoolQueryBuilder componentFilter = boolQuery();
+ if (isViewOrApp) {
+ componentFilter.filter(QueryBuilders.termsLookupQuery(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID,
+ new TermsLookup(
+ ViewIndexDefinition.INDEX_TYPE_VIEW.getIndex(),
+ ViewIndexDefinition.INDEX_TYPE_VIEW.getType(),
+ projectUuid,
+ ViewIndexDefinition.FIELD_PROJECTS)));
+ } else {
+ componentFilter.filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, projectUuid));
+ }
return client.prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE)
.setQuery(
- boolQuery()
- .filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectUuid))
+ componentFilter
.filter(termsQuery(IssueIndexDefinition.FIELD_ISSUE_TYPE, RuleType.SECURITY_HOTSPOT.name(), RuleType.VULNERABILITY.name()))
.mustNot(termQuery(IssueIndexDefinition.FIELD_ISSUE_STATUS, Issue.STATUS_CLOSED)))
.setSize(0);
newDoc("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
newDoc("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating)
newDoc("notopenvul", project).setOwaspTop10(asList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
.setSeverity(Severity.BLOCKER));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating)
// Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
newDoc("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating)
newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN),
newDoc("anotherProject", another).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getOpenSecurityHotspots)
.contains(
newDoc("closedHotspot", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
.setResolution(Issue.RESOLUTION_FIXED));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getOpenSecurityHotspots)
.contains(
.setResolution(Issue.RESOLUTION_WONT_FIX),
newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), includeCwe);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
.setResolution(Issue.RESOLUTION_WONT_FIX),
newDoc("notowasphotspot", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
- List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false);
+ List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false, false);
assertThat(sansTop25Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
}
+ @Test
+ public void test_getSansTop25Report_aggregation_on_portfolio() {
+ ComponentDto portfolio1 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ ComponentDto portfolio2 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+
+ indexIssues(
+ newDoc("openvul1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
+ .setSeverity(Severity.MAJOR),
+ newDoc("openvul2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
+ .setSeverity(Severity.MINOR),
+ newDoc("notopenvul", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
+ .setResolution(Issue.RESOLUTION_FIXED)
+ .setSeverity(Severity.BLOCKER),
+ newDoc("notsansvul", project2).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
+ .setSeverity(Severity.CRITICAL),
+ newDoc("openhotspot1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
+ .setStatus(Issue.STATUS_OPEN),
+ newDoc("openhotspot2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
+ .setStatus(Issue.STATUS_REOPENED),
+ newDoc("toReviewHotspot", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
+ .setResolution(Issue.RESOLUTION_FIXED),
+ newDoc("WFHotspot", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
+ .setResolution(Issue.RESOLUTION_WONT_FIX),
+ newDoc("notowasphotspot", project1).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
+
+ indexView(portfolio1.uuid(), singletonList(project1.uuid()));
+ indexView(portfolio2.uuid(), singletonList(project2.uuid()));
+
+ List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(portfolio1.uuid(), true, false);
+ assertThat(sansTop25Report)
+ .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
+ SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
+ SecurityStandardCategoryStatistics::getToReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
+ .containsExactlyInAnyOrder(
+ tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* openhotspot1 */, 0L, 0L),
+ tuple(SANS_TOP_25_RISKY_RESOURCE, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* openhotspot1 */, 1L /* toReviewHotspot */, 0L),
+ tuple(SANS_TOP_25_POROUS_DEFENSES, 0L, OptionalInt.empty(), 0L, 0L, 0L));
+
+ assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
+ }
+
private void addIssues(ComponentDto component, int bugs, int vulnerabilities, int codeSmelles) {
List<IssueDoc> issues = new ArrayList<>();
IntStream.range(0, bugs).forEach(b -> issues.add(newDoc(component).setType(BUG).setResolution(null)));
import java.util.List;
import java.util.function.Function;
+import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
.setInternal(true);
action.createParam(PARAM_PROJECT)
- .setDescription("Project key")
+ .setDescription("Project, view or application key")
.setRequired(true);
action.createParam(PARAM_BRANCH)
.setDescription("Branch name")
projectDto = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, request.param(PARAM_BRANCH), null);
}
userSession.checkComponentPermission(USER, projectDto);
+ String qualifier = projectDto.qualifier();
+ boolean isViewOrApp;
+ switch (qualifier) {
+ case Qualifiers.VIEW:
+ case Qualifiers.SUBVIEW:
+ case Qualifiers.APP:
+ isViewOrApp = true;
+ break;
+ case Qualifiers.PROJECT:
+ isViewOrApp = false;
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported component type " + qualifier);
+ }
String standard = request.mandatoryParam(PARAM_STANDARD);
boolean includeCwe = request.mandatoryParamAsBoolean(PARAM_INCLUDE_DISTRIBUTION);
switch (standard) {
case PARAM_OWASP_TOP_10:
- List<SecurityStandardCategoryStatistics> owaspCategories = issueIndex.getOwaspTop10Report(projectDto.uuid(), includeCwe)
+ List<SecurityStandardCategoryStatistics> owaspCategories = issueIndex.getOwaspTop10Report(projectDto.uuid(), isViewOrApp, includeCwe)
.stream()
.sorted(comparing(ShowAction::index))
.collect(toList());
writeResponse(request, response, owaspCategories);
break;
case PARAM_SANS_TOP_25:
- writeResponse(request, response, issueIndex.getSansTop25Report(projectDto.uuid(), includeCwe));
+ writeResponse(request, response, issueIndex.getSansTop25Report(projectDto.uuid(), isViewOrApp, includeCwe));
break;
default:
throw new IllegalArgumentException("Unsupported standard: '" + standard + "'");
@Test
public void sans_with_cwe() {
-
ComponentDto project = insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), "PROJECT_ID").setDbKey("PROJECT_KEY"));
userSessionRule.addProjectPermission(UserRole.USER, project);
indexPermissions();
.isSimilarTo(this.getClass().getResource("ShowActionTest/sansWithCwe.json"));
}
+ @Test
+ public void sans_with_cwe_for_branches() {
+ ComponentDto project1 = db.components().insertPrivateProject(p -> p.setDbKey("prj1"));
+ ComponentDto project1Branch1 = db.components().insertProjectBranch(project1);
+ ComponentDto fileOnProject1Branch1 = db.components().insertComponent(newFileDto(project1Branch1));
+ ComponentDto project2 = db.components().insertPrivateProject(p -> p.setDbKey("prj2"));
+
+ userSessionRule.addProjectPermission(UserRole.USER, project1);
+ userSessionRule.addProjectPermission(UserRole.USER, project2);
+ indexPermissions();
+ RuleDefinitionDto rule = newRule();
+ IssueDto issue1 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+ .setStatus("OPEN")
+ .setSeverity("MAJOR")
+ .setType(RuleType.VULNERABILITY);
+ IssueDto issue2 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+ .setStatus("OPEN")
+ .setSeverity("MAJOR")
+ .setType(RuleType.SECURITY_HOTSPOT);
+ IssueDto issue3 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+ .setStatus(Issue.STATUS_RESOLVED)
+ .setResolution(Issue.RESOLUTION_FIXED)
+ .setSeverity("MAJOR")
+ .setType(RuleType.SECURITY_HOTSPOT);
+ IssueDto issue4 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+ .setStatus(Issue.STATUS_RESOLVED)
+ .setResolution(Issue.RESOLUTION_WONT_FIX)
+ .setSeverity("MAJOR")
+ .setType(RuleType.SECURITY_HOTSPOT);
+ dbClient.issueDao().insert(session, issue1, issue2, issue3, issue4);
+ session.commit();
+ indexIssues();
+
+ assertJson(ws.newRequest()
+ .setParam("standard", "sansTop25")
+ .setParam("project", project1Branch1.getKey())
+ .setParam("branch", project1Branch1.getBranch())
+ .setParam("includeDistribution", "true")
+ .execute().getInput())
+ .withStrictArrayOrder()
+ .isSimilarTo(this.getClass().getResource("ShowActionTest/sansWithCwe.json"));
+ }
+
private RuleDefinitionDto newRule() {
RuleDefinitionDto rule = RuleTesting.newRule()