diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2018-07-11 13:14:07 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-07-17 20:21:25 +0200 |
commit | c5840ddb4b2138e02aa18275a81c2c16679520eb (patch) | |
tree | 69e2ff5d35f1267940dcaed1e3917fbe537061bd /server | |
parent | d4b8f895197f7044a68377d47a7bf559c8116a05 (diff) | |
download | sonarqube-c5840ddb4b2138e02aa18275a81c2c16679520eb.tar.gz sonarqube-c5840ddb4b2138e02aa18275a81c2c16679520eb.zip |
SONAR-10978 Fix support of branches and applications for security reports
Diffstat (limited to 'server')
4 files changed, 128 insertions, 18 deletions
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndex.java index a9794254c77..48bbecfb3c3 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -798,8 +798,8 @@ public class IssueIndex { .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() @@ -809,8 +809,8 @@ public class IssueIndex { 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() @@ -898,11 +898,21 @@ public class IssueIndex { 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); diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java index ea07a6587bf..eddde5f4622 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java @@ -1459,7 +1459,7 @@ public class IssueIndexTest { 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) @@ -1477,7 +1477,7 @@ public class IssueIndexTest { 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) @@ -1493,7 +1493,7 @@ public class IssueIndexTest { // 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) @@ -1510,7 +1510,7 @@ public class IssueIndexTest { 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( @@ -1526,7 +1526,7 @@ public class IssueIndexTest { 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( @@ -1583,7 +1583,7 @@ public class IssueIndexTest { .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, @@ -1627,7 +1627,7 @@ public class IssueIndexTest { .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, @@ -1641,6 +1641,49 @@ public class IssueIndexTest { 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))); diff --git a/server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/ShowAction.java index 5f5ea130375..280e9100227 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/ShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/ShowAction.java @@ -21,6 +21,7 @@ package org.sonar.server.securityreport.ws; 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; @@ -72,7 +73,7 @@ public class ShowAction implements SecurityReportsWsAction { .setInternal(true); action.createParam(PARAM_PROJECT) - .setDescription("Project key") + .setDescription("Project, view or application key") .setRequired(true); action.createParam(PARAM_BRANCH) .setDescription("Branch name") @@ -95,18 +96,32 @@ public class ShowAction implements SecurityReportsWsAction { 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 + "'"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/securityreport/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/securityreport/ws/ShowActionTest.java index 9cd3054a64f..d87519555ee 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/securityreport/ws/ShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/securityreport/ws/ShowActionTest.java @@ -207,7 +207,6 @@ public class ShowActionTest { @Test public void sans_with_cwe() { - ComponentDto project = insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), "PROJECT_ID").setDbKey("PROJECT_KEY")); userSessionRule.addProjectPermission(UserRole.USER, project); indexPermissions(); @@ -244,6 +243,49 @@ public class ShowActionTest { .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() |