* Compute Security Review Rating measures on projects * Live update Security Review Rating measurestags/7.8
import org.sonar.ce.task.projectanalysis.issue.RuleTagsCopier; | import org.sonar.ce.task.projectanalysis.issue.RuleTagsCopier; | ||||
import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUser; | import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUser; | ||||
import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUserLoader; | import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUserLoader; | ||||
import org.sonar.ce.task.projectanalysis.issue.ShortBranchOrPullRequestTrackerExecution; | |||||
import org.sonar.ce.task.projectanalysis.issue.SiblingsIssueMerger; | import org.sonar.ce.task.projectanalysis.issue.SiblingsIssueMerger; | ||||
import org.sonar.ce.task.projectanalysis.issue.SiblingsIssuesLoader; | import org.sonar.ce.task.projectanalysis.issue.SiblingsIssuesLoader; | ||||
import org.sonar.ce.task.projectanalysis.issue.ShortBranchOrPullRequestTrackerExecution; | |||||
import org.sonar.ce.task.projectanalysis.issue.TrackerBaseInputFactory; | import org.sonar.ce.task.projectanalysis.issue.TrackerBaseInputFactory; | ||||
import org.sonar.ce.task.projectanalysis.issue.TrackerExecution; | import org.sonar.ce.task.projectanalysis.issue.TrackerExecution; | ||||
import org.sonar.ce.task.projectanalysis.issue.TrackerMergeOrTargetBranchInputFactory; | import org.sonar.ce.task.projectanalysis.issue.TrackerMergeOrTargetBranchInputFactory; | ||||
import org.sonar.ce.task.projectanalysis.qualitymodel.NewReliabilityAndSecurityRatingMeasuresVisitor; | import org.sonar.ce.task.projectanalysis.qualitymodel.NewReliabilityAndSecurityRatingMeasuresVisitor; | ||||
import org.sonar.ce.task.projectanalysis.qualitymodel.RatingSettings; | import org.sonar.ce.task.projectanalysis.qualitymodel.RatingSettings; | ||||
import org.sonar.ce.task.projectanalysis.qualitymodel.ReliabilityAndSecurityRatingMeasuresVisitor; | import org.sonar.ce.task.projectanalysis.qualitymodel.ReliabilityAndSecurityRatingMeasuresVisitor; | ||||
import org.sonar.ce.task.projectanalysis.qualitymodel.SecurityReviewRatingVisitor; | |||||
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderImpl; | import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderImpl; | ||||
import org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepositoryImpl; | import org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepositoryImpl; | ||||
import org.sonar.ce.task.projectanalysis.scm.ScmInfoDbLoader; | import org.sonar.ce.task.projectanalysis.scm.ScmInfoDbLoader; | ||||
NewMaintainabilityMeasuresVisitor.class, | NewMaintainabilityMeasuresVisitor.class, | ||||
ReliabilityAndSecurityRatingMeasuresVisitor.class, | ReliabilityAndSecurityRatingMeasuresVisitor.class, | ||||
NewReliabilityAndSecurityRatingMeasuresVisitor.class, | NewReliabilityAndSecurityRatingMeasuresVisitor.class, | ||||
SecurityReviewRatingVisitor.class, | |||||
LastCommitVisitor.class, | LastCommitVisitor.class, | ||||
MeasureComputersVisitor.class, | MeasureComputersVisitor.class, | ||||
* Retrieves the base measure (ie. the one currently existing in DB) for the specified {@link Component} for | * Retrieves the base measure (ie. the one currently existing in DB) for the specified {@link Component} for | ||||
* the specified {@link MetricImpl} if it exists. | * the specified {@link MetricImpl} if it exists. | ||||
* <p> | * <p> | ||||
* This method searches for Measure which are specific to the Component and not associated to a rule or a | |||||
* characteristic. | |||||
* This method searches for Measure which are specific to the Component. | |||||
* </p> | * </p> | ||||
* | * | ||||
* @throws NullPointerException if either argument is {@code null} | * @throws NullPointerException if either argument is {@code null} | ||||
/** | /** | ||||
* Retrieves the measure created during the current analysis for the specified {@link Component} for the specified | * Retrieves the measure created during the current analysis for the specified {@link Component} for the specified | ||||
* {@link Metric} if it exists (ie. one created by the Compute Engine or the Batch) and which is <strong>not</strong> | |||||
* associated to a rule, a characteristic, or a developer. | |||||
* {@link Metric} if it exists (ie. one created by the Compute Engine or the Scanner). | |||||
*/ | */ | ||||
Optional<Measure> getRawMeasure(Component component, Metric metric); | Optional<Measure> getRawMeasure(Component component, Metric metric); | ||||
/** | /** | ||||
* Returns the {@link Measure}s for the specified {@link Component} and the specified {@link Metric}. | * Returns the {@link Measure}s for the specified {@link Component} and the specified {@link Metric}. | ||||
* <p> | |||||
* Their will be one measure not associated to rules, characteristics or developers, the other ones will be associated to rules or to characteristics | |||||
* (see {@link Measure#equals(Object)}. | |||||
* </p> | |||||
*/ | */ | ||||
Set<Measure> getRawMeasures(Component component, Metric metric); | Set<Measure> getRawMeasures(Component component, Metric metric); | ||||
/** | /** | ||||
* Returns the {@link Measure}s for the specified {@link Component} mapped by their metric key. | * Returns the {@link Measure}s for the specified {@link Component} mapped by their metric key. | ||||
* <p> | |||||
* Their can be multiple measures for the same Metric but only one which has no rule nor characteristic, one with a | |||||
* specific ruleId and one with specific characteristicId (see {@link Measure#equals(Object)}. | |||||
* </p> | |||||
*/ | */ | ||||
SetMultimap<String, Measure> getRawMeasures(Component component); | SetMultimap<String, Measure> getRawMeasures(Component component); | ||||
/** | /** | ||||
* Adds the specified measure for the specified Component and Metric. There can be no more than one measure for a | * Adds the specified measure for the specified Component and Metric. There can be no more than one measure for a | ||||
* specific combination of Component, Metric and association to a specific rule or characteristic. | |||||
* specific combination of Component, Metric. | |||||
* | * | ||||
* @throws NullPointerException if any of the arguments is null | * @throws NullPointerException if any of the arguments is null | ||||
* @throws UnsupportedOperationException when trying to add a measure when one already exists for the specified Component/Metric paar | * @throws UnsupportedOperationException when trying to add a measure when one already exists for the specified Component/Metric paar | ||||
/** | /** | ||||
* Updates the specified measure for the specified Component and Metric. There can be no more than one measure for a | * Updates the specified measure for the specified Component and Metric. There can be no more than one measure for a | ||||
* specific combination of Component, Metric and association to a specific rule or characteristic. | |||||
* specific combination of Component, Metric. | |||||
* | * | ||||
* @throws NullPointerException if any of the arguments is null | * @throws NullPointerException if any of the arguments is null | ||||
* @throws UnsupportedOperationException when trying to update a non existing measure | * @throws UnsupportedOperationException when trying to update a non existing measure |
/* | |||||
* 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.ce.task.projectanalysis.qualitymodel; | |||||
import java.util.Optional; | |||||
import org.sonar.ce.task.projectanalysis.component.Component; | |||||
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter; | |||||
import org.sonar.ce.task.projectanalysis.measure.Measure; | |||||
import org.sonar.ce.task.projectanalysis.measure.MeasureRepository; | |||||
import org.sonar.ce.task.projectanalysis.metric.Metric; | |||||
import org.sonar.ce.task.projectanalysis.metric.MetricRepository; | |||||
import org.sonar.server.measure.Rating; | |||||
import org.sonar.server.security.SecurityReviewRating; | |||||
import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY; | |||||
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY; | |||||
import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY; | |||||
import static org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit.PROJECT; | |||||
import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder; | |||||
public class SecurityReviewRatingVisitor extends TypeAwareVisitorAdapter { | |||||
private final MeasureRepository measureRepository; | |||||
private final Metric nclocMetric; | |||||
private final Metric securityHostspotsMetric; | |||||
private final Metric securityReviewRatingMetric; | |||||
public SecurityReviewRatingVisitor(MeasureRepository measureRepository, MetricRepository metricRepository) { | |||||
super(PROJECT, Order.POST_ORDER); | |||||
this.measureRepository = measureRepository; | |||||
this.nclocMetric = metricRepository.getByKey(NCLOC_KEY); | |||||
this.securityHostspotsMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_KEY); | |||||
this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY); | |||||
} | |||||
@Override | |||||
public void visitProject(Component project) { | |||||
Optional<Measure> nclocMeasure = measureRepository.getRawMeasure(project, nclocMetric); | |||||
Optional<Measure> securityHostspotsMeasure = measureRepository.getRawMeasure(project, securityHostspotsMetric); | |||||
if (!nclocMeasure.isPresent() || !securityHostspotsMeasure.isPresent()) { | |||||
return; | |||||
} | |||||
int ncloc = nclocMeasure.get().getIntValue(); | |||||
int securityHotspots = securityHostspotsMeasure.get().getIntValue(); | |||||
Rating rating = SecurityReviewRating.compute(ncloc, securityHotspots); | |||||
measureRepository.add(project, securityReviewRatingMetric, newMeasureBuilder().create(rating.getIndex(), rating.name())); | |||||
} | |||||
} |
import com.google.common.base.Predicate; | import com.google.common.base.Predicate; | ||||
import com.google.common.collect.ImmutableSetMultimap; | import com.google.common.collect.ImmutableSetMultimap; | ||||
import com.google.common.collect.SetMultimap; | import com.google.common.collect.SetMultimap; | ||||
import java.util.Collection; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Objects; | import java.util.Objects; | ||||
private final Map<InternalKey, Measure> baseMeasures = new HashMap<>(); | private final Map<InternalKey, Measure> baseMeasures = new HashMap<>(); | ||||
private final Map<InternalKey, Measure> rawMeasures = new HashMap<>(); | private final Map<InternalKey, Measure> rawMeasures = new HashMap<>(); | ||||
private final Map<InternalKey, Measure> initialRawMeasures = new HashMap<>(); | private final Map<InternalKey, Measure> initialRawMeasures = new HashMap<>(); | ||||
private Collection<Component> loadedAsRawComponents; | |||||
private Collection<Metric> loadedAsRawMetrics; | |||||
private final Predicate<Map.Entry<InternalKey, Measure>> isAddedMeasure = new Predicate<Map.Entry<InternalKey, Measure>>() { | |||||
@Override | |||||
public boolean apply(@Nonnull Map.Entry<InternalKey, Measure> input) { | |||||
return !initialRawMeasures.containsKey(input.getKey()) | |||||
|| !MeasureRepoEntry.deepEquals(input.getValue(), initialRawMeasures.get(input.getKey())); | |||||
} | |||||
}; | |||||
private final Predicate<Map.Entry<InternalKey, Measure>> isAddedMeasure = input -> !initialRawMeasures.containsKey(input.getKey()) | |||||
|| !MeasureRepoEntry.deepEquals(input.getValue(), initialRawMeasures.get(input.getKey())); | |||||
private MeasureRepositoryRule(ComponentProvider componentProvider, @Nullable MetricRepositoryRule metricRepositoryRule) { | private MeasureRepositoryRule(ComponentProvider componentProvider, @Nullable MetricRepositoryRule metricRepositoryRule) { | ||||
this.componentProvider = componentProvider; | this.componentProvider = componentProvider; | ||||
return new MeasureRepositoryRule(new TreeComponentProvider(treeRoot), requireNonNull(metricRepositoryRule)); | return new MeasureRepositoryRule(new TreeComponentProvider(treeRoot), requireNonNull(metricRepositoryRule)); | ||||
} | } | ||||
public MeasureRepositoryRule addBaseMeasure(Component component, Metric metric, Measure measure) { | |||||
checkAndInitProvidersState(); | |||||
InternalKey internalKey = new InternalKey(component, metric); | |||||
checkState(!baseMeasures.containsKey(internalKey), | |||||
format("Can not add a BaseMeasure twice for a Component (ref=%s) and Metric (key=%s)", getRef(component), metric.getKey())); | |||||
baseMeasures.put(internalKey, measure); | |||||
return this; | |||||
} | |||||
public MeasureRepositoryRule addBaseMeasure(int componentRef, String metricKey, Measure measure) { | public MeasureRepositoryRule addBaseMeasure(int componentRef, String metricKey, Measure measure) { | ||||
checkAndInitProvidersState(); | checkAndInitProvidersState(); | ||||
return Optional.ofNullable(baseMeasures.get(new InternalKey(component, metric))); | return Optional.ofNullable(baseMeasures.get(new InternalKey(component, metric))); | ||||
} | } | ||||
public Collection<Component> getComponentsLoadedAsRaw() { | |||||
return loadedAsRawComponents; | |||||
} | |||||
public Collection<Metric> getMetricsLoadedAsRaw() { | |||||
return loadedAsRawMetrics; | |||||
} | |||||
@Override | @Override | ||||
public Optional<Measure> getRawMeasure(Component component, Metric metric) { | public Optional<Measure> getRawMeasure(Component component, Metric metric) { | ||||
return Optional.ofNullable(rawMeasures.get(new InternalKey(component, metric))); | return Optional.ofNullable(rawMeasures.get(new InternalKey(component, metric))); | ||||
} | } | ||||
public Optional<Measure> getRawRuleMeasure(Component component, Metric metric, int ruleId) { | |||||
return Optional.ofNullable(rawMeasures.get(new InternalKey(component, metric))); | |||||
} | |||||
@Override | @Override | ||||
public Set<Measure> getRawMeasures(Component component, Metric metric) { | public Set<Measure> getRawMeasures(Component component, Metric metric) { | ||||
return from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(new MatchMetric(metric)).transform(ToMeasure.INSTANCE).toSet(); | return from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(new MatchMetric(metric)).transform(ToMeasure.INSTANCE).toSet(); |
/* | |||||
* 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.ce.task.projectanalysis.qualitymodel; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.sonar.ce.task.projectanalysis.component.Component; | |||||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | |||||
import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler; | |||||
import org.sonar.ce.task.projectanalysis.measure.Measure; | |||||
import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule; | |||||
import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule; | |||||
import org.sonar.server.measure.Rating; | |||||
import static java.util.Collections.singletonList; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.sonar.api.measures.CoreMetrics.NCLOC; | |||||
import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY; | |||||
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS; | |||||
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY; | |||||
import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING; | |||||
import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY; | |||||
import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder; | |||||
import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder; | |||||
public class SecurityReviewRatingVisitorTest { | |||||
private static final int PROJECT_REF = 1; | |||||
private static final Component PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project").build(); | |||||
@Rule | |||||
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); | |||||
@Rule | |||||
public MetricRepositoryRule metricRepository = new MetricRepositoryRule() | |||||
.add(NCLOC) | |||||
.add(SECURITY_HOTSPOTS) | |||||
.add(SECURITY_REVIEW_RATING); | |||||
@Rule | |||||
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); | |||||
private VisitorsCrawler underTest = new VisitorsCrawler(singletonList(new SecurityReviewRatingVisitor(measureRepository, metricRepository))); | |||||
@Test | |||||
public void compute_security_review_rating_on_project() { | |||||
treeRootHolder.setRoot(PROJECT); | |||||
measureRepository.addRawMeasure(PROJECT_REF, NCLOC_KEY, newMeasureBuilder().create(1000)); | |||||
measureRepository.addRawMeasure(PROJECT_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(12)); | |||||
underTest.visit(PROJECT); | |||||
Measure measure = measureRepository.getAddedRawMeasure(PROJECT_REF, SECURITY_REVIEW_RATING_KEY).get(); | |||||
assertThat(measure.getIntValue()).isEqualTo(Rating.C.getIndex()); | |||||
assertThat(measure.getData()).isEqualTo(Rating.C.name()); | |||||
} | |||||
@Test | |||||
public void compute_nothing_when_no_ncloc() { | |||||
treeRootHolder.setRoot(PROJECT); | |||||
measureRepository.addRawMeasure(PROJECT_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(2)); | |||||
underTest.visit(PROJECT); | |||||
assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, SECURITY_REVIEW_RATING_KEY)).isEmpty(); | |||||
} | |||||
@Test | |||||
public void compute_nothing_when_no_security_hotspot() { | |||||
treeRootHolder.setRoot(PROJECT); | |||||
measureRepository.addRawMeasure(PROJECT_REF, NCLOC_KEY, newMeasureBuilder().create(1000)); | |||||
underTest.visit(PROJECT); | |||||
assertThat(measureRepository.getAddedRawMeasure(PROJECT_REF, SECURITY_REVIEW_RATING_KEY)).isEmpty(); | |||||
} | |||||
} |
/* | |||||
* 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.security; | |||||
import org.sonar.server.measure.Rating; | |||||
public class SecurityReviewRating { | |||||
private SecurityReviewRating() { | |||||
// Only static method | |||||
} | |||||
public static Rating compute(int ncloc, int securityHotspots) { | |||||
if (ncloc == 0) { | |||||
return Rating.A; | |||||
} | |||||
double ratio = (double) securityHotspots * 1000d / (double) ncloc; | |||||
if (ratio <= 3d) { | |||||
return Rating.A; | |||||
} else if (ratio <= 10) { | |||||
return Rating.B; | |||||
} else if (ratio <= 15) { | |||||
return Rating.C; | |||||
} else if (ratio <= 25) { | |||||
return Rating.D; | |||||
} else { | |||||
return Rating.E; | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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.security; | |||||
import com.tngtech.java.junit.dataprovider.DataProvider; | |||||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | |||||
import com.tngtech.java.junit.dataprovider.UseDataProvider; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.junit.Test; | |||||
import org.junit.runner.RunWith; | |||||
import org.sonar.server.measure.Rating; | |||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
import static org.sonar.server.measure.Rating.A; | |||||
import static org.sonar.server.measure.Rating.B; | |||||
import static org.sonar.server.measure.Rating.C; | |||||
import static org.sonar.server.measure.Rating.D; | |||||
import static org.sonar.server.measure.Rating.E; | |||||
@RunWith(DataProviderRunner.class) | |||||
public class SecurityReviewRatingTest { | |||||
@DataProvider | |||||
public static Object[][] values() { | |||||
List<Object[]> res = new ArrayList<>(); | |||||
res.add(new Object[] {1000, 0, A}); | |||||
res.add(new Object[] {1000, 3, A}); | |||||
res.add(new Object[] {1000, 4, B}); | |||||
res.add(new Object[] {1000, 10, B}); | |||||
res.add(new Object[] {1000, 11, C}); | |||||
res.add(new Object[] {1000, 15, C}); | |||||
res.add(new Object[] {1000, 16, D}); | |||||
res.add(new Object[] {1000, 25, D}); | |||||
res.add(new Object[] {1000, 26, E}); | |||||
res.add(new Object[] {1000, 900, E}); | |||||
res.add(new Object[] {0, 2, A}); | |||||
res.add(new Object[] {1001, 3, A}); | |||||
res.add(new Object[] {999, 3, B}); | |||||
res.add(new Object[] {Integer.MAX_VALUE, Integer.MAX_VALUE, E}); | |||||
return res.toArray(new Object[res.size()][3]); | |||||
} | |||||
@Test | |||||
@UseDataProvider("values") | |||||
public void compute_security_review_rating_on_project(int ncloc, int securityHotspots, Rating expectedRating) { | |||||
assertThat(SecurityReviewRating.compute(ncloc, securityHotspots)).isEqualTo(expectedRating); | |||||
} | |||||
} |
import org.sonar.api.rule.Severity; | import org.sonar.api.rule.Severity; | ||||
import org.sonar.api.rules.RuleType; | import org.sonar.api.rules.RuleType; | ||||
import org.sonar.server.measure.Rating; | import org.sonar.server.measure.Rating; | ||||
import org.sonar.server.security.SecurityReviewRating; | |||||
import static java.util.Arrays.asList; | import static java.util.Arrays.asList; | ||||
import static org.sonar.server.measure.Rating.RATING_BY_SEVERITY; | import static org.sonar.server.measure.Rating.RATING_BY_SEVERITY; | ||||
new IssueMetricFormula(CoreMetrics.SECURITY_RATING, false, | new IssueMetricFormula(CoreMetrics.SECURITY_RATING, false, | ||||
(context, issues) -> context.setValue(RATING_BY_SEVERITY.get(issues.getHighestSeverityOfUnresolved(RuleType.VULNERABILITY, false).orElse(Severity.INFO)))), | (context, issues) -> context.setValue(RATING_BY_SEVERITY.get(issues.getHighestSeverityOfUnresolved(RuleType.VULNERABILITY, false).orElse(Severity.INFO)))), | ||||
new IssueMetricFormula(CoreMetrics.SECURITY_REVIEW_RATING, false, | |||||
(context, issues) -> context.setValue(SecurityReviewRating.compute(context.getValue(CoreMetrics.NCLOC).orElse(0d).intValue(), | |||||
context.getValue(CoreMetrics.SECURITY_HOTSPOTS).orElse(0d).intValue())), | |||||
asList(CoreMetrics.NCLOC, CoreMetrics.SECURITY_HOTSPOTS)), | |||||
new IssueMetricFormula(CoreMetrics.NEW_CODE_SMELLS, true, | new IssueMetricFormula(CoreMetrics.NEW_CODE_SMELLS, true, | ||||
(context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, true))), | (context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, true))), | ||||
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 3 + 5); | .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 3 + 5); | ||||
} | } | ||||
@Test | |||||
public void test_security_review_rating() { | |||||
withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REVIEW_RATING, Rating.A); | |||||
with(CoreMetrics.SECURITY_HOTSPOTS, 12.0) | |||||
.and(CoreMetrics.NCLOC, 1000.0) | |||||
.assertThatValueIs(CoreMetrics.SECURITY_REVIEW_RATING, Rating.C); | |||||
} | |||||
@Test | @Test | ||||
public void count_unresolved_by_severity() { | public void count_unresolved_by_severity() { | ||||
withNoIssues() | withNoIssues() |
metric.security_remediation_effort.description=Security remediation effort | metric.security_remediation_effort.description=Security remediation effort | ||||
metric.security_remediation_effort.name=Security Remediation Effort | metric.security_remediation_effort.name=Security Remediation Effort | ||||
metric.security_remediation_effort.extra_short_name=Remediation Effort | metric.security_remediation_effort.extra_short_name=Remediation Effort | ||||
metric.security_review_rating.description=Security Review Rating | |||||
metric.security_review_rating.name=Security Review Rating | |||||
metric.security_review_rating.extra_short_name=Review Rating | |||||
metric.skipped_tests.description=Number of skipped unit tests | metric.skipped_tests.description=Number of skipped unit tests | ||||
metric.skipped_tests.name=Skipped Unit Tests | metric.skipped_tests.name=Skipped Unit Tests | ||||
metric.skipped_tests.short_name=Skipped | metric.skipped_tests.short_name=Skipped |
.setWorstValue(5.0) | .setWorstValue(5.0) | ||||
.create(); | .create(); | ||||
/** | |||||
* @since 7.8 | |||||
*/ | |||||
public static final String SECURITY_REVIEW_RATING_KEY = "security_review_rating"; | |||||
/** | |||||
* @since 7.8 | |||||
*/ | |||||
public static final Metric<Integer> SECURITY_REVIEW_RATING = new Metric.Builder(SECURITY_REVIEW_RATING_KEY, "Security Review Rating", Metric.ValueType.RATING) | |||||
.setDescription("Security Review Rating") | |||||
.setDomain(DOMAIN_SECURITY) | |||||
.setDirection(Metric.DIRECTION_WORST) | |||||
.setQualitative(true) | |||||
.setBestValue(1d) | |||||
.setWorstValue(5d) | |||||
.create(); | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
// | // | ||||
// FILE DATA | // FILE DATA |