Browse Source

SONAR-11970 Move Security Reports web service to core governance

tags/7.8
Julien Lancelot 5 years ago
parent
commit
45ef13fe96
14 changed files with 0 additions and 1009 deletions
  1. 0
    4
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
  2. 0
    43
      server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/SecurityReportsWs.java
  3. 0
    26
      server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/SecurityReportsWsAction.java
  4. 0
    31
      server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/SecurityReportsWsModule.java
  5. 0
    291
      server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/ShowAction.java
  6. 0
    23
      server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/package-info.java
  7. 0
    136
      server/sonar-server/src/main/resources/org/sonar/server/securityreport/ws/show-example.json
  8. 0
    455
      server/sonar-server/src/test/java/org/sonar/server/securityreport/ws/ShowActionTest.java
  9. 0
    0
      server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/empty.json
  10. 0
    0
      server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/owaspNoCwe.json
  11. 0
    0
      server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sansWithCwe.json
  12. 0
    0
      server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json
  13. 0
    0
      server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json
  14. 0
    0
      server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json

+ 0
- 4
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java View File

@@ -181,7 +181,6 @@ import org.sonar.server.rule.ws.RuleQueryFactory;
import org.sonar.server.rule.ws.RuleWsSupport;
import org.sonar.server.rule.ws.RulesWs;
import org.sonar.server.rule.ws.TagsAction;
import org.sonar.server.securityreport.ws.SecurityReportsWsModule;
import org.sonar.server.setting.ws.SettingsWsModule;
import org.sonar.server.source.HtmlSourceDecorator;
import org.sonar.server.source.SourceService;
@@ -416,9 +415,6 @@ public class PlatformLevel4 extends PlatformLevel {
MyNewIssuesNotificationHandler.class,
MyNewIssuesNotificationHandler.newMetadata(),

// Security reports
SecurityReportsWsModule.class,

// issues actions
AssignAction.class,
SetTypeAction.class,

+ 0
- 43
server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/SecurityReportsWs.java View File

@@ -1,43 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.securityreport.ws;

import org.sonar.api.server.ws.WebService;

public class SecurityReportsWs implements WebService {

private final SecurityReportsWsAction[] actions;

public SecurityReportsWs(SecurityReportsWsAction... actions) {
this.actions = actions;
}

@Override
public void define(Context context) {
NewController controller = context.createController("api/security_reports");
controller.setDescription("Return data needed by the security reports");
controller.setSince("7.3");
for (SecurityReportsWsAction action : actions) {
action.define(controller);
}
controller.done();
}

}

+ 0
- 26
server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/SecurityReportsWsAction.java View File

@@ -1,26 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.securityreport.ws;

import org.sonar.server.ws.WsAction;

interface SecurityReportsWsAction extends WsAction {
// Marker interface
}

+ 0
- 31
server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/SecurityReportsWsModule.java View File

@@ -1,31 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.securityreport.ws;

import org.sonar.core.platform.Module;

public class SecurityReportsWsModule extends Module {
@Override
protected void configureModule() {
add(
SecurityReportsWs.class,
ShowAction.class);
}
}

+ 0
- 291
server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/ShowAction.java View File

@@ -1,291 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.securityreport.ws;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.qualityprofile.OrgActiveRuleDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.issue.index.SecurityStandardCategoryStatistics;
import org.sonar.server.qualityprofile.QPMeasureData;
import org.sonar.server.qualityprofile.QualityProfile;
import org.sonar.server.security.SecurityStandardHelper;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.SecurityReports;

import static java.lang.Integer.parseInt;
import static java.util.Arrays.asList;
import static java.util.Collections.emptySortedSet;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import static org.sonar.api.measures.CoreMetrics.QUALITY_PROFILES_KEY;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.server.security.SecurityStandardHelper.UNKNOWN_STANDARD;
import static org.sonar.server.security.SecurityStandardHelper.getCwe;
import static org.sonar.server.security.SecurityStandardHelper.getOwaspTop10;
import static org.sonar.server.security.SecurityStandardHelper.getSansTop25;
import static org.sonar.server.security.SecurityStandardHelper.getSonarSourceSecurityCategories;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY;

public class ShowAction implements SecurityReportsWsAction {

private static final String UNSUPPORTED_STANDARD_MSG = "Unsupported standard: '%s'";
private static final String PARAM_PROJECT = "project";
private static final String PARAM_BRANCH = "branch";
private static final String PARAM_INCLUDE_DISTRIBUTION = "includeDistribution";
private static final String PARAM_STANDARD = "standard";
private static final String OWASP_CAT_PREFIX = "a";

private final UserSession userSession;
private final ComponentFinder componentFinder;
private final IssueIndex issueIndex;
private final DbClient dbClient;

public ShowAction(UserSession userSession, ComponentFinder componentFinder, IssueIndex issueIndex, DbClient dbClient) {
this.userSession = userSession;
this.componentFinder = componentFinder;
this.issueIndex = issueIndex;
this.dbClient = dbClient;
}

@Override
public void define(WebService.NewController controller) {
WebService.NewAction action = controller
.createAction("show")
.setResponseExample(getClass().getResource("show-example.json"))
.setHandler(this)
.setDescription("Return data used by security reports")
.setSince("7.3")
.setInternal(true);

action.createParam(PARAM_PROJECT)
.setDescription("Project, view or application key")
.setRequired(true);
action.createParam(PARAM_BRANCH)
.setDescription("Branch name")
.setExampleValue("branch-2.0");
action.createParam(PARAM_STANDARD)
.setDescription("Security standard")
.setPossibleValues(PARAM_OWASP_TOP_10, PARAM_SANS_TOP_25, PARAM_SONARSOURCE_SECURITY)
.setRequired(true);
action.createParam(PARAM_INCLUDE_DISTRIBUTION)
.setDescription("To return CWE distribution")
.setBooleanPossibleValues()
.setDefaultValue("false");
}

@Override
public final void handle(Request request, Response response) {
String projectKey = request.mandatoryParam(PARAM_PROJECT);
ComponentDto projectDto;
try (DbSession dbSession = dbClient.openSession(false)) {
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);
List<SecurityStandardCategoryStatistics> statistics;
switch (standard) {
case PARAM_OWASP_TOP_10:
statistics = issueIndex.getOwaspTop10Report(projectDto.uuid(), isViewOrApp, includeCwe)
.stream()
.sorted(comparing(ShowAction::index))
.collect(toList());
break;
case PARAM_SANS_TOP_25:
statistics = issueIndex.getSansTop25Report(projectDto.uuid(), isViewOrApp, includeCwe);
break;
case PARAM_SONARSOURCE_SECURITY:
statistics = issueIndex.getSonarSourceReport(projectDto.uuid(), isViewOrApp, includeCwe);
break;
default:
throw new IllegalArgumentException(String.format(UNSUPPORTED_STANDARD_MSG, standard));
}
if (!isViewOrApp) {
// App and Portfolios don't have a quality profile
completeRulesStatistics(statistics, projectDto, standard, includeCwe);
}
writeResponse(request, response, statistics, isViewOrApp);
}

private void completeRulesStatistics(List<SecurityStandardCategoryStatistics> input, ComponentDto project, String standard, boolean includeCwe) {
try (DbSession dbSession = dbClient.openSession(false)) {
Set<QualityProfile> qualityProfiles = dbClient.liveMeasureDao().selectMeasure(dbSession, project.projectUuid(), QUALITY_PROFILES_KEY)
.map(LiveMeasureDto::getDataAsString)
.map(data -> QPMeasureData.fromJson(data).getProfiles())
.orElse(emptySortedSet());

List<OrgActiveRuleDto> activeRuleDtos = dbClient.activeRuleDao().selectByTypeAndProfileUuids(dbSession,
asList(RuleType.SECURITY_HOTSPOT.getDbConstant(), RuleType.VULNERABILITY.getDbConstant()),
qualityProfiles.stream()
.map(QualityProfile::getQpKey)
.collect(toList()));

Multimap<String, OrgActiveRuleDto> activeRulesByCategory = ArrayListMultimap.create();
activeRuleDtos
.forEach(r -> {
List<String> cwe = getCwe(r.getSecurityStandards());
if (includeCwe) {
cwe.forEach(s -> activeRulesByCategory.put(s, r));
}
switch (standard) {
case PARAM_OWASP_TOP_10:
getOwaspTop10(r.getSecurityStandards()).forEach(s -> activeRulesByCategory.put(s, r));
break;
case PARAM_SANS_TOP_25:
getSansTop25(cwe).forEach(s -> activeRulesByCategory.put(s, r));
break;
case PARAM_SONARSOURCE_SECURITY:
SecurityStandardHelper.getSonarSourceSecurityCategories(cwe).forEach(s -> activeRulesByCategory.put(s, r));
break;
default:
throw new IllegalArgumentException(String.format(UNSUPPORTED_STANDARD_MSG, standard));
}
});

List<RuleDto> ruleDtos = dbClient.ruleDao().selectByTypeAndLanguages(dbSession,
project.getOrganizationUuid(),
asList(RuleType.SECURITY_HOTSPOT.getDbConstant(), RuleType.VULNERABILITY.getDbConstant()),
qualityProfiles.stream()
.map(QualityProfile::getLanguageKey)
.collect(toList()));

Multimap<String, RuleDto> rulesByCategory = ArrayListMultimap.create();
ruleDtos
.forEach(r -> {
List<String> cwe = getCwe(r.getSecurityStandards());
if (includeCwe) {
cwe.forEach(s -> rulesByCategory.put(s, r));
}
switch (standard) {
case PARAM_OWASP_TOP_10:
getOwaspTop10(r.getSecurityStandards()).forEach(s -> rulesByCategory.put(s, r));
break;
case PARAM_SANS_TOP_25:
getSansTop25(cwe).forEach(s -> rulesByCategory.put(s, r));
break;
case PARAM_SONARSOURCE_SECURITY:
getSonarSourceSecurityCategories(cwe).forEach(s -> rulesByCategory.put(s, r));
break;
default:
throw new IllegalArgumentException(String.format(UNSUPPORTED_STANDARD_MSG, standard));
}
});

input.forEach(c -> {
c.setTotalRules(rulesByCategory.get(c.getCategory()).size());
c.setActiveRules(activeRulesByCategory.get(c.getCategory()).size());
c.getChildren().forEach(child -> {
child.setTotalRules(rulesByCategory.get(child.getCategory()).size());
child.setActiveRules(activeRulesByCategory.get(child.getCategory()).size());
});
});
}
}

private static Integer index(SecurityStandardCategoryStatistics owaspCat) {
if (owaspCat.getCategory().startsWith(OWASP_CAT_PREFIX)) {
return parseInt(owaspCat.getCategory().substring(OWASP_CAT_PREFIX.length()));
}
// unknown
return 11;
}

private static void writeResponse(Request request, Response response, List<SecurityStandardCategoryStatistics> categories, boolean isViewOrApp) {
SecurityReports.ShowWsResponse.Builder builder = SecurityReports.ShowWsResponse.newBuilder();
categories.forEach(cat -> {
SecurityReports.SecurityStandardCategoryStatistics.Builder catBuilder = SecurityReports.SecurityStandardCategoryStatistics.newBuilder();
catBuilder
.setCategory(cat.getCategory())
.setVulnerabilities(cat.getVulnerabilities());
cat.getVulnerabiliyRating().ifPresent(catBuilder::setVulnerabilityRating);
catBuilder
.setOpenSecurityHotspots(cat.getOpenSecurityHotspots())
.setToReviewSecurityHotspots(cat.getToReviewSecurityHotspots())
.setWontFixSecurityHotspots(cat.getWontFixSecurityHotspots());
if (!isViewOrApp) {
catBuilder.setTotalRules(cat.getTotalRules())
.setActiveRules(cat.getActiveRules());
}
if (cat.getChildren() != null) {
cat.getChildren().stream()
.sorted(comparing(cweIndex()))
.forEach(cwe -> {
SecurityReports.CweStatistics.Builder cweBuilder = SecurityReports.CweStatistics.newBuilder();
cweBuilder
.setCwe(cwe.getCategory())
.setVulnerabilities(cwe.getVulnerabilities());
cwe.getVulnerabiliyRating().ifPresent(cweBuilder::setVulnerabilityRating);
cweBuilder
.setOpenSecurityHotspots(cwe.getOpenSecurityHotspots())
.setToReviewSecurityHotspots(cwe.getToReviewSecurityHotspots())
.setWontFixSecurityHotspots(cwe.getWontFixSecurityHotspots());
if (!isViewOrApp) {
cweBuilder.setActiveRules(cwe.getActiveRules())
.setTotalRules(cwe.getTotalRules());
}
catBuilder.addDistribution(cweBuilder);
});
}
builder.addCategories(catBuilder);
});

writeProtobuf(builder.build(), request, response);
}

private static Function<SecurityStandardCategoryStatistics, Integer> cweIndex() {
return securityStandardCategoryStatistics -> {
String category = securityStandardCategoryStatistics.getCategory();
return category.equals(UNKNOWN_STANDARD) ? Integer.MAX_VALUE : parseInt(category);
};
}

}

+ 0
- 23
server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/package-info.java View File

@@ -1,23 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
@ParametersAreNonnullByDefault
package org.sonar.server.securityreport.ws;

import javax.annotation.ParametersAreNonnullByDefault;

+ 0
- 136
server/sonar-server/src/main/resources/org/sonar/server/securityreport/ws/show-example.json View File

@@ -1,136 +0,0 @@
{
"categories": [
{
"category": "a1",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 1,
"totalRules": 1
},
{
"category": "a2",
"vulnerabilities": 1,
"vulnerabilityRating": 3,
"toReviewSecurityHotspots": 1,
"openSecurityHotspots": 1,
"wontFixSecurityHotspots": 1,
"distribution": [
{
"cwe": "89",
"vulnerabilities": 1,
"vulnerabilityRating": 3,
"toReviewSecurityHotspots": 1,
"openSecurityHotspots": 1,
"wontFixSecurityHotspots": 1,
"activeRules": 1,
"totalRules": 1
},
{
"cwe": "123",
"vulnerabilities": 1,
"vulnerabilityRating": 3,
"toReviewSecurityHotspots": 1,
"openSecurityHotspots": 1,
"wontFixSecurityHotspots": 1,
"activeRules": 1,
"totalRules": 1
}
],
"activeRules": 1,
"totalRules": 1
},
{
"category": "a3",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 1
},
{
"category": "a4",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 0
},
{
"category": "a5",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 0
},
{
"category": "a6",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 0
},
{
"category": "a7",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 0
},
{
"category": "a8",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 0
},
{
"category": "a9",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 0
},
{
"category": "a10",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 0,
"totalRules": 0
},
{
"category": "unknown",
"vulnerabilities": 0,
"toReviewSecurityHotspots": 0,
"openSecurityHotspots": 0,
"wontFixSecurityHotspots": 0,
"distribution": [],
"activeRules": 1,
"totalRules": 2
}
]
}

+ 0
- 455
server/sonar-server/src/test/java/org/sonar/server/securityreport/ws/ShowActionTest.java View File

@@ -1,455 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.securityreport.ws;

import java.util.Date;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.issue.Issue;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.StartupIndexer;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.issue.index.IssueIndexer;
import org.sonar.server.issue.index.IssueIteratorFactory;
import org.sonar.server.permission.index.PermissionIndexer;
import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
import org.sonar.server.qualityprofile.QPMeasureData;
import org.sonar.server.qualityprofile.QualityProfile;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.view.index.ViewDoc;
import org.sonar.server.view.index.ViewIndexer;
import org.sonar.server.ws.WsActionTester;

import static com.google.common.collect.Sets.newHashSet;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.api.measures.CoreMetrics.QUALITY_PROFILES_KEY;
import static org.sonar.api.measures.Metric.ValueType.STRING;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.issue.IssueTesting.newIssue;
import static org.sonar.server.tester.UserSessionRule.standalone;
import static org.sonar.test.JsonAssert.assertJson;

public class ShowActionTest {

@Rule
public UserSessionRule userSessionRule = standalone();
@Rule
public DbTester db = DbTester.create();
@Rule
public EsTester es = EsTester.create();
@Rule
public ExpectedException expectedException = none();

private DbClient dbClient = db.getDbClient();
private ComponentDbTester componentDbTester = db.components();
private DbSession session = db.getSession();
private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule));
private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient));
private ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client());
private WsActionTester ws = new WsActionTester(new ShowAction(userSessionRule, TestComponentFinder.from(db), issueIndex, dbClient));
private StartupIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);

private UserDto user;
private ComponentDto project;
private QProfileDto qualityProfile;
private RuleDefinitionDto rule1;
private RuleDefinitionDto rule2;
private RuleDefinitionDto rule3;
private RuleDefinitionDto rule4;
private RuleDefinitionDto rule5;
private MetricDto qpMetric;

@Before
public void setUp() {
user = db.users().insertUser("john");
userSessionRule.logIn(user);
project = componentDbTester.insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), "PROJECT_ID").setDbKey("PROJECT_KEY"));
qualityProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), qp -> qp.setLanguage("java"));
// owasp : a2 and TopSans25 insecure
rule1 = newRule(newHashSet("owaspTop10:a2", "cwe:123", "cwe:89"), RuleType.SECURITY_HOTSPOT);
// owasp : a1 and TopSans25 risky
rule2 = newRule(newHashSet("owaspTop10:a1", "cwe:22", "cwe:190"), RuleType.SECURITY_HOTSPOT);
// owasp : a4 and TopSans25 porous, not activated
rule3 = newRule(newHashSet("owaspTop10:a3", "cwe:759", "cwe:000"), RuleType.SECURITY_HOTSPOT);
// cwe with unknown, not activated
rule4 = newRule(newHashSet("cwe:999"), RuleType.SECURITY_HOTSPOT);
// TopSans25 insecure
rule5 = newRule(newHashSet("cwe:78"), RuleType.VULNERABILITY);
db.qualityProfiles().activateRule(qualityProfile, rule1);
db.qualityProfiles().activateRule(qualityProfile, rule2);
db.qualityProfiles().activateRule(qualityProfile, rule5);
qpMetric = db.measures().insertMetric(m -> m.setValueType(STRING.name()).setKey(QUALITY_PROFILES_KEY));

insertQPLiveMeasure(project);
}

@Test
public void test_definition() {
WebService.Action def = ws.getDef();
assertThat(def.key()).isEqualTo("show");
assertThat(def.isInternal()).isTrue();
assertThat(def.isPost()).isFalse();
assertThat(def.since()).isEqualTo("7.3");

assertThat(def.params()).extracting("key").containsExactlyInAnyOrder("standard", "project", "branch", "includeDistribution");
}

@Test
public void owasp_empty() {
userSessionRule.addProjectPermission(UserRole.USER, project);
indexPermissions();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue1 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.CODE_SMELL);
IssueDto issue2 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.BUG);
IssueDto issue3 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.CODE_SMELL);
dbClient.issueDao().insert(session, issue1, issue2, issue3);
session.commit();
indexIssues();

assertJson(ws.newRequest()
.setParam("standard", "owaspTop10")
.setParam("project", project.getKey())
.execute().getInput())
.withStrictArrayOrder()
.isSimilarTo(this.getClass().getResource("ShowActionTest/empty.json"));
}

@Test
public void owasp_without_cwe() {
userSessionRule.addProjectPermission(UserRole.USER, project);
indexPermissions();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue1 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
IssueDto issue2 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue3 = newIssue(rule1, project, file)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue4 = newIssue(rule1, project, file)
.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", "owaspTop10")
.setParam("project", project.getKey())
.execute().getInput())
.withStrictArrayOrder()
.isSimilarTo(this.getClass().getResource("ShowActionTest/owaspNoCwe.json"));
}

@Test
public void owasp_with_cwe__matching_json_example() {
userSessionRule.addProjectPermission(UserRole.USER, project);
indexPermissions();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue1 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
IssueDto issue2 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue3 = newIssue(rule1, project, file)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue4 = newIssue(rule1, project, file)
.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", "owaspTop10")
.setParam("project", project.getKey())
.setParam("includeDistribution", "true")
.execute().getInput())
.withStrictArrayOrder()
.isSimilarTo(getClass().getResource("show-example.json"));
}

@Test
public void sans_with_cwe() {
userSessionRule.addProjectPermission(UserRole.USER, project);
indexPermissions();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue1 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
IssueDto issue2 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue3 = newIssue(rule1, project, file)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue4 = newIssue(rule5, project, file)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_WONT_FIX)
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
dbClient.issueDao().insert(session, issue1, issue2, issue3, issue4);
session.commit();
indexIssues();

assertJson(ws.newRequest()
.setParam("standard", "sansTop25")
.setParam("project", project.getKey())
.setParam("includeDistribution", "true")
.execute().getInput())
.withStrictArrayOrder()
.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"));

insertQPLiveMeasure(project1);
insertQPLiveMeasure(project1Branch1);
insertQPLiveMeasure(project2);

userSessionRule.addProjectPermission(UserRole.USER, project1);
userSessionRule.addProjectPermission(UserRole.USER, project2);
indexPermissions();
IssueDto issue1 = newIssue(rule1, project1Branch1, fileOnProject1Branch1)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
IssueDto issue2 = newIssue(rule1, project1Branch1, fileOnProject1Branch1)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue3 = newIssue(rule1, project1Branch1, fileOnProject1Branch1)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue4 = newIssue(rule5, project1Branch1, fileOnProject1Branch1)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_WONT_FIX)
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
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"));
}

@Test
public void sonarsource_security_without_cwe() {
userSessionRule.addProjectPermission(UserRole.USER, project);
indexPermissions();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue1 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
IssueDto issue2 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue3 = newIssue(rule1, project, file)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue4 = newIssue(rule1, project, file)
.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", "sonarsourceSecurity")
.setParam("project", project.getKey())
.setParam("includeDistribution", "false")
.execute().getInput())
.withStrictArrayOrder()
.isSimilarTo(this.getClass().getResource("ShowActionTest/sonarsourceSecurityNoCwe.json"));
}

@Test
public void sonarsource_security_with_cwe() {
userSessionRule.addProjectPermission(UserRole.USER, project);
indexPermissions();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue1 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
IssueDto issue2 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue3 = newIssue(rule1, project, file)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue4 = newIssue(rule1, project, file)
.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", "sonarsourceSecurity")
.setParam("project", project.getKey())
.setParam("includeDistribution", "true")
.execute().getInput())
.withStrictArrayOrder()
.isSimilarTo(this.getClass().getResource("ShowActionTest/sonarsourceSecurityWithCwe.json"));
}

@Test
public void dont_return_rules_on_application() {
ComponentDto application = db.components().insertPrivateApplication(db.getDefaultOrganization());
indexView(application.uuid(), singletonList(project.uuid()));
userSessionRule.addProjectPermission(UserRole.USER, application);
indexPermissions();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue1 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.VULNERABILITY);
IssueDto issue2 = newIssue(rule1, project, file)
.setStatus("OPEN")
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue3 = newIssue(rule1, project, file)
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setType(RuleType.SECURITY_HOTSPOT);
IssueDto issue4 = newIssue(rule1, project, file)
.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", "sonarsourceSecurity")
.setParam("project", application.getKey())
.setParam("includeDistribution", "true")
.execute().getInput())
.withStrictArrayOrder()
.isSimilarTo(this.getClass().getResource("ShowActionTest/sonarsourceSecurityOnApplication.json"));
}

private RuleDefinitionDto newRule(Set<String> standards, RuleType type) {
RuleDefinitionDto rule = RuleTesting.newRule()
.setType(type)
.setLanguage("java")
.setSecurityStandards(standards);
db.rules().insert(rule);
return rule;
}

private void indexPermissions() {
permissionIndexer.indexOnStartup(permissionIndexer.getIndexTypes());
}

private void indexIssues() {
issueIndexer.indexOnStartup(issueIndexer.getIndexTypes());
}

private void indexView(String viewUuid, List<String> projects) {
viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects));
}

private void insertQPLiveMeasure(ComponentDto project) {
QualityProfile qp = new QualityProfile(qualityProfile.getKee(), qualityProfile.getName(), qualityProfile.getLanguage(), new Date());
db.measures().insertLiveMeasure(project, qpMetric, lm -> lm.setData(QPMeasureData.toJson(new QPMeasureData(singletonList(qp)))));
}

}

server/sonar-server/src/test/resources/org/sonar/server/securityreport/ws/ShowActionTest/empty.json → server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/empty.json View File


server/sonar-server/src/test/resources/org/sonar/server/securityreport/ws/ShowActionTest/owaspNoCwe.json → server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/owaspNoCwe.json View File


server/sonar-server/src/test/resources/org/sonar/server/securityreport/ws/ShowActionTest/sansWithCwe.json → server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sansWithCwe.json View File


server/sonar-server/src/test/resources/org/sonar/server/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json → server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json View File


server/sonar-server/src/test/resources/org/sonar/server/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json → server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json View File


server/sonar-server/src/test/resources/org/sonar/server/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json → server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json View File


Loading…
Cancel
Save