* Compute Security Review Rating measures on projects * Live update Security Review Rating measurestags/7.8
@@ -77,9 +77,9 @@ import org.sonar.ce.task.projectanalysis.issue.RuleRepositoryImpl; | |||
import org.sonar.ce.task.projectanalysis.issue.RuleTagsCopier; | |||
import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUser; | |||
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.SiblingsIssuesLoader; | |||
import org.sonar.ce.task.projectanalysis.issue.ShortBranchOrPullRequestTrackerExecution; | |||
import org.sonar.ce.task.projectanalysis.issue.TrackerBaseInputFactory; | |||
import org.sonar.ce.task.projectanalysis.issue.TrackerExecution; | |||
import org.sonar.ce.task.projectanalysis.issue.TrackerMergeOrTargetBranchInputFactory; | |||
@@ -111,6 +111,7 @@ import org.sonar.ce.task.projectanalysis.qualitymodel.NewMaintainabilityMeasures | |||
import org.sonar.ce.task.projectanalysis.qualitymodel.NewReliabilityAndSecurityRatingMeasuresVisitor; | |||
import org.sonar.ce.task.projectanalysis.qualitymodel.RatingSettings; | |||
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.QProfileStatusRepositoryImpl; | |||
import org.sonar.ce.task.projectanalysis.scm.ScmInfoDbLoader; | |||
@@ -265,6 +266,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop | |||
NewMaintainabilityMeasuresVisitor.class, | |||
ReliabilityAndSecurityRatingMeasuresVisitor.class, | |||
NewReliabilityAndSecurityRatingMeasuresVisitor.class, | |||
SecurityReviewRatingVisitor.class, | |||
LastCommitVisitor.class, | |||
MeasureComputersVisitor.class, | |||
@@ -32,8 +32,7 @@ public interface MeasureRepository { | |||
* Retrieves the base measure (ie. the one currently existing in DB) for the specified {@link Component} for | |||
* the specified {@link MetricImpl} if it exists. | |||
* <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> | |||
* | |||
* @throws NullPointerException if either argument is {@code null} | |||
@@ -42,32 +41,23 @@ public interface MeasureRepository { | |||
/** | |||
* 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); | |||
/** | |||
* 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); | |||
/** | |||
* 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); | |||
/** | |||
* 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 UnsupportedOperationException when trying to add a measure when one already exists for the specified Component/Metric paar | |||
@@ -76,7 +66,7 @@ public interface MeasureRepository { | |||
/** | |||
* 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 UnsupportedOperationException when trying to update a non existing measure |
@@ -0,0 +1,67 @@ | |||
/* | |||
* 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())); | |||
} | |||
} |
@@ -23,7 +23,6 @@ import com.google.common.base.Function; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.ImmutableSetMultimap; | |||
import com.google.common.collect.SetMultimap; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Objects; | |||
@@ -61,15 +60,8 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe | |||
private final Map<InternalKey, Measure> baseMeasures = new HashMap<>(); | |||
private final Map<InternalKey, Measure> rawMeasures = 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) { | |||
this.componentProvider = componentProvider; | |||
@@ -95,18 +87,6 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe | |||
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) { | |||
checkAndInitProvidersState(); | |||
@@ -188,23 +168,11 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe | |||
return Optional.ofNullable(baseMeasures.get(new InternalKey(component, metric))); | |||
} | |||
public Collection<Component> getComponentsLoadedAsRaw() { | |||
return loadedAsRawComponents; | |||
} | |||
public Collection<Metric> getMetricsLoadedAsRaw() { | |||
return loadedAsRawMetrics; | |||
} | |||
@Override | |||
public Optional<Measure> getRawMeasure(Component component, Metric 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 | |||
public Set<Measure> getRawMeasures(Component component, Metric metric) { | |||
return from(filterKeys(rawMeasures, hasComponentRef(component)).entrySet()).filter(new MatchMetric(metric)).transform(ToMeasure.INSTANCE).toSet(); |
@@ -0,0 +1,95 @@ | |||
/* | |||
* 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(); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -28,6 +28,7 @@ import org.sonar.api.measures.Metric; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.api.rules.RuleType; | |||
import org.sonar.server.measure.Rating; | |||
import org.sonar.server.security.SecurityReviewRating; | |||
import static java.util.Arrays.asList; | |||
import static org.sonar.server.measure.Rating.RATING_BY_SEVERITY; | |||
@@ -107,6 +108,11 @@ public class IssueMetricFormulaFactoryImpl implements IssueMetricFormulaFactory | |||
new IssueMetricFormula(CoreMetrics.SECURITY_RATING, false, | |||
(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, | |||
(context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, true))), | |||
@@ -123,6 +123,15 @@ public class IssueMetricFormulaFactoryImplTest { | |||
.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 | |||
public void count_unresolved_by_severity() { | |||
withNoIssues() |
@@ -1988,6 +1988,9 @@ metric.security_rating.tooltip.E=Security rating is E when there is at least one | |||
metric.security_remediation_effort.description=Security remediation effort | |||
metric.security_remediation_effort.name=Security 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.name=Skipped Unit Tests | |||
metric.skipped_tests.short_name=Skipped |
@@ -1506,6 +1506,23 @@ public final class CoreMetrics { | |||
.setWorstValue(5.0) | |||
.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 |