Browse Source

SONAR-12962 Compute Security Review measures on Applications

tags/8.2.0.32929
Julien Lancelot 4 years ago
parent
commit
c51fa2468c
12 changed files with 378 additions and 119 deletions
  1. 14
    2
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java
  2. 4
    4
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewCounter.java
  3. 10
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java
  4. 6
    7
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java
  5. 95
    41
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java
  6. 92
    39
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java
  7. 3
    21
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosTest.java
  8. 1
    1
      server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java
  9. 12
    0
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImpl.java
  10. 49
    1
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImplTest.java
  11. 4
    2
      sonar-core/src/main/resources/org/sonar/l10n/core.properties
  12. 88
    0
      sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java

+ 14
- 2
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java View File

@@ -30,6 +30,8 @@ import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
import org.sonar.ce.task.projectanalysis.period.PeriodHolder;

import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
@@ -45,6 +47,8 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
private final AnalysisMetadataHolder analysisMetadataHolder;
private final Metric newSecurityReviewRatingMetric;
private final Metric newSecurityHotspotsReviewedMetric;
private final Metric newSecurityHotspotsReviewedStatusMetric;
private final Metric newSecurityHotspotsToReviewStatusMetric;

public NewSecurityReviewMeasuresVisitor(ComponentIssuesRepository componentIssuesRepository, MeasureRepository measureRepository, PeriodHolder periodHolder,
AnalysisMetadataHolder analysisMetadataHolder, MetricRepository metricRepository) {
@@ -55,11 +59,19 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
this.analysisMetadataHolder = analysisMetadataHolder;
this.newSecurityReviewRatingMetric = metricRepository.getByKey(NEW_SECURITY_REVIEW_RATING_KEY);
this.newSecurityHotspotsReviewedMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY);
this.newSecurityHotspotsReviewedStatusMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY);
this.newSecurityHotspotsToReviewStatusMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY);
}

@Override
public void visitProject(Component project, Path<SecurityReviewCounter> path) {
computeMeasure(project, path);
if (!periodHolder.hasPeriod() && !analysisMetadataHolder.isPullRequest()) {
return;
}
// The following measures are only computed on projects level as they are required to compute the others measures on applications
measureRepository.add(project, newSecurityHotspotsReviewedStatusMetric, Measure.newMeasureBuilder().setVariation(path.current().getHotspotsReviewed()).createNoValue());
measureRepository.add(project, newSecurityHotspotsToReviewStatusMetric, Measure.newMeasureBuilder().setVariation(path.current().getHotspotsToReview()).createNoValue());
}

@Override
@@ -79,10 +91,10 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
componentIssuesRepository.getIssues(component)
.stream()
.filter(issue -> issue.type().equals(SECURITY_HOTSPOT))
.filter(issue -> analysisMetadataHolder.isPullRequest() || periodHolder.getPeriod().isOnPeriod(issue.creationDate()) )
.filter(issue -> analysisMetadataHolder.isPullRequest() || periodHolder.getPeriod().isOnPeriod(issue.creationDate()))
.forEach(issue -> path.current().processHotspot(issue));

Double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
measureRepository.add(component, newSecurityHotspotsReviewedMetric, Measure.newMeasureBuilder().setVariation(percent).createNoValue());
measureRepository.add(component, newSecurityReviewRatingMetric, Measure.newMeasureBuilder().setVariation(computeRating(percent).getIndex()).createNoValue());


+ 4
- 4
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewCounter.java View File

@@ -25,8 +25,8 @@ import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;

final class SecurityReviewCounter {
private long hotspotsReviewed;
private long hotspotsToReview;
private int hotspotsReviewed;
private int hotspotsToReview;

SecurityReviewCounter() {
// prevents instantiation
@@ -45,11 +45,11 @@ final class SecurityReviewCounter {
hotspotsToReview += otherCounter.hotspotsToReview;
}

public long getHotspotsReviewed() {
public int getHotspotsReviewed() {
return hotspotsReviewed;
}

public long getHotspotsToReview() {
public int getHotspotsToReview() {
return hotspotsToReview;
}
}

+ 10
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java View File

@@ -30,6 +30,8 @@ import org.sonar.ce.task.projectanalysis.metric.Metric;
import org.sonar.ce.task.projectanalysis.metric.MetricRepository;

import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
@@ -43,6 +45,8 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
private final MeasureRepository measureRepository;
private final Metric securityReviewRatingMetric;
private final Metric securityHotspotsReviewedMetric;
private final Metric securityHotspotsReviewedStatusMetric;
private final Metric securityHotspotsToReviewStatusMetric;

public SecurityReviewMeasuresVisitor(ComponentIssuesRepository componentIssuesRepository, MeasureRepository measureRepository, MetricRepository metricRepository) {
super(FILE, POST_ORDER, SecurityReviewMeasuresVisitor.CounterFactory.INSTANCE);
@@ -50,11 +54,16 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
this.measureRepository = measureRepository;
this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY);
this.securityHotspotsReviewedMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_REVIEWED_KEY);
this.securityHotspotsReviewedStatusMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY);
this.securityHotspotsToReviewStatusMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY);
}

@Override
public void visitProject(Component project, Path<SecurityReviewCounter> path) {
computeMeasure(project, path);
// The following measures are only computed on projects level as they are required to compute the others measures on applications
measureRepository.add(project, securityHotspotsReviewedStatusMetric, Measure.newMeasureBuilder().create(path.current().getHotspotsReviewed()));
measureRepository.add(project, securityHotspotsToReviewStatusMetric, Measure.newMeasureBuilder().create(path.current().getHotspotsToReview()));
}

@Override
@@ -73,7 +82,7 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
.filter(issue -> issue.type().equals(SECURITY_HOTSPOT))
.forEach(issue -> path.current().processHotspot(issue));

Double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
double percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
measureRepository.add(component, securityHotspotsReviewedMetric, Measure.newMeasureBuilder().create(percent, securityHotspotsReviewedMetric.getDecimalScale()));
measureRepository.add(component, securityReviewRatingMetric, RatingMeasures.get(computeRating(percent)));


server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplications.java → server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfolios.java View File

@@ -34,15 +34,16 @@ 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.ViewAttributes.Type.APPLICATION;

public class SecurityReviewRatingVisitorForPortfoliosAndApplications extends TypeAwareVisitorAdapter {
public class SecurityReviewRatingVisitorForPortfolios extends TypeAwareVisitorAdapter {

private final MeasureRepository measureRepository;
private final Metric nclocMetric;
private final Metric securityHostspotsMetric;
private final Metric securityReviewRatingMetric;

public SecurityReviewRatingVisitorForPortfoliosAndApplications(MeasureRepository measureRepository, MetricRepository metricRepository) {
public SecurityReviewRatingVisitorForPortfolios(MeasureRepository measureRepository, MetricRepository metricRepository) {
super(CrawlerDepthLimit.SUBVIEW, Order.POST_ORDER);
this.measureRepository = measureRepository;
this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
@@ -50,13 +51,11 @@ public class SecurityReviewRatingVisitorForPortfoliosAndApplications extends Typ
this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY);
}

@Override
public void visitProject(Component project) {
// Do nothing
}

@Override
public void visitView(Component view) {
if (view.getViewAttributes().getType().equals(APPLICATION)) {
return;
}
computeMeasure(view);
}


+ 95
- 41
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java View File

@@ -22,6 +22,7 @@ package org.sonar.ce.task.projectanalysis.qualitymodel;
import java.util.Arrays;
import java.util.Date;
import javax.annotation.Nullable;
import org.assertj.core.api.Assertions;
import org.assertj.core.data.Offset;
import org.junit.Rule;
import org.junit.Test;
@@ -34,7 +35,6 @@ import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryRule;
import org.sonar.ce.task.projectanalysis.issue.FillComponentIssuesVisitorRule;
import org.sonar.ce.task.projectanalysis.measure.MeasureAssert;
import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
import org.sonar.ce.task.projectanalysis.period.Period;
@@ -52,6 +52,10 @@ import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY;
import static org.sonar.api.rule.Severity.MAJOR;
@@ -59,6 +63,7 @@ import static org.sonar.api.rule.Severity.MINOR;
import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
import static org.sonar.ce.task.projectanalysis.measure.MeasureAssert.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;
@@ -99,7 +104,9 @@ public class NewSecurityReviewMeasuresVisitorTest {
@Rule
public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
.add(NEW_SECURITY_REVIEW_RATING)
.add(NEW_SECURITY_HOTSPOTS_REVIEWED);
.add(NEW_SECURITY_HOTSPOTS_REVIEWED)
.add(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS)
.add(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
@Rule
@@ -131,11 +138,11 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, A, 100.0);
verifyMeasures(FILE_2_REF, A, 100.0);
verifyMeasures(DIRECTORY_REF, A, 100.0);
verifyMeasures(ROOT_DIR_REF, A, 100.0);
verifyMeasures(PROJECT_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
}

@Test
@@ -160,11 +167,11 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, A, 100.0);
verifyMeasures(FILE_2_REF, A, 80.0);
verifyMeasures(DIRECTORY_REF, A, 87.5);
verifyMeasures(ROOT_DIR_REF, A, 87.5);
verifyMeasures(PROJECT_REF, A, 87.5);
verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
}

@Test
@@ -189,11 +196,11 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, A, 100.0);
verifyMeasures(FILE_2_REF, B, 71.42);
verifyMeasures(DIRECTORY_REF, B, 75.0);
verifyMeasures(ROOT_DIR_REF, B, 75.0);
verifyMeasures(PROJECT_REF, B, 75.0);
verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.42);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
}

@Test
@@ -217,11 +224,11 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, C, 50.0);
verifyMeasures(FILE_2_REF, C, 60.0);
verifyMeasures(DIRECTORY_REF, C, 57.14);
verifyMeasures(ROOT_DIR_REF, C, 57.14);
verifyMeasures(PROJECT_REF, C, 57.14);
verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.14);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.14);
verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.14);
}

@Test
@@ -246,11 +253,11 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, D, 33.33);
verifyMeasures(FILE_2_REF, D, 40.0);
verifyMeasures(DIRECTORY_REF, D, 37.5);
verifyMeasures(ROOT_DIR_REF, D, 37.5);
verifyMeasures(PROJECT_REF, D, 37.5);
verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
}

@Test
@@ -273,11 +280,11 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, D, 33.33);
verifyMeasures(FILE_2_REF, E, 0.0);
verifyMeasures(DIRECTORY_REF, E, 16.66);
verifyMeasures(ROOT_DIR_REF, E, 16.66);
verifyMeasures(PROJECT_REF, E, 16.66);
verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.66);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.66);
verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.66);
}

@Test
@@ -290,7 +297,7 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(PROJECT_REF, A, 100.0);
verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
}

@Test
@@ -317,11 +324,45 @@ public class NewSecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, C, 50.0);
verifyMeasures(FILE_2_REF, C, 57.14);
verifyMeasures(DIRECTORY_REF, C, 55.55);
verifyMeasures(ROOT_DIR_REF, C, 55.55);
verifyMeasures(PROJECT_REF, C, 55.55);
verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, C, 57.14);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 55.55);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 55.55);
verifyRatingAndReviewedMeasures(PROJECT_REF, C, 55.55);
}

@Test
public void compute_status_related_measures() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
// Should not be taken into account
newIssue());
fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
newIssue());

underTest.visit(ROOT_PROJECT);

verifyHotspotStatusMeasures(FILE_1_REF, null, null);
verifyHotspotStatusMeasures(FILE_2_REF, null, null);
verifyHotspotStatusMeasures(DIRECTORY_REF, null, null);
verifyHotspotStatusMeasures(ROOT_DIR_REF, null, null);
verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
}

@Test
public void compute_0_status_related_measures_when_no_hotspot() {
treeRootHolder.setRoot(ROOT_PROJECT);

underTest.visit(ROOT_PROJECT);

verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
}

@Test
@@ -337,12 +378,25 @@ public class NewSecurityReviewMeasuresVisitorTest {
assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).values()).isEmpty();
}

private void verifyMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
MeasureAssert.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasVariation(expectedReviewRating.getIndex());
MeasureAssert.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasVariation(expectedHotspotsReviewed,
private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasVariation(expectedReviewRating.getIndex());
assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasVariation(expectedHotspotsReviewed,
VARIATION_COMPARISON_OFFSET);
}

private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
if (hotspotsReviewed == null) {
Assertions.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
} else {
assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).hasVariation(hotspotsReviewed);
}
if (hotspotsReviewed == null) {
Assertions.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
} else {
assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).hasVariation(hotspotsToReview);
}
}

private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
return new DefaultIssue()
.setKey(Uuids.create())

+ 92
- 39
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java View File

@@ -41,6 +41,10 @@ import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
import static org.sonar.api.issue.Issue.RESOLUTION_SAFE;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_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.api.rule.Severity.MAJOR;
@@ -81,7 +85,9 @@ public class SecurityReviewMeasuresVisitorTest {
@Rule
public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
.add(SECURITY_REVIEW_RATING)
.add(SECURITY_HOTSPOTS_REVIEWED);
.add(SECURITY_HOTSPOTS_REVIEWED)
.add(SECURITY_HOTSPOTS_REVIEWED_STATUS)
.add(SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
@Rule
public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
@Rule
@@ -93,7 +99,7 @@ public class SecurityReviewMeasuresVisitorTest {
new SecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository)));

@Test
public void compute_measures_when_100_percent_hotspots_reviewed() {
public void compute_rating_and_reviewed_measures_when_100_percent_hotspots_reviewed() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
@@ -106,15 +112,15 @@ public class SecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, A, 100.0);
verifyMeasures(FILE_2_REF, A, 100.0);
verifyMeasures(DIRECTORY_REF, A, 100.0);
verifyMeasures(ROOT_DIR_REF, A, 100.0);
verifyMeasures(PROJECT_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
}

@Test
public void compute_measures_when_more_than_80_percent_hotspots_reviewed() {
public void compute_rating_and_reviewed__measures_when_more_than_80_percent_hotspots_reviewed() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
@@ -132,15 +138,15 @@ public class SecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, A, 100.0);
verifyMeasures(FILE_2_REF, A, 80.0);
verifyMeasures(DIRECTORY_REF, A, 87.5);
verifyMeasures(ROOT_DIR_REF, A, 87.5);
verifyMeasures(PROJECT_REF, A, 87.5);
verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
}

@Test
public void compute_measures_when_more_than_70_percent_hotspots_reviewed() {
public void compute_rating_and_reviewed__measures_when_more_than_70_percent_hotspots_reviewed() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
@@ -158,15 +164,15 @@ public class SecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, A, 100.0);
verifyMeasures(FILE_2_REF, B, 71.4);
verifyMeasures(DIRECTORY_REF, B, 75.0);
verifyMeasures(ROOT_DIR_REF, B, 75.0);
verifyMeasures(PROJECT_REF, B, 75.0);
verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.4);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
}

@Test
public void compute_measures_when_more_than_50_percent_hotspots_reviewed() {
public void compute_rating_and_reviewed__measures_when_more_than_50_percent_hotspots_reviewed() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_TO_REVIEW, null),
@@ -183,15 +189,15 @@ public class SecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, C, 50.0);
verifyMeasures(FILE_2_REF, C, 60.0);
verifyMeasures(DIRECTORY_REF, C, 57.1);
verifyMeasures(ROOT_DIR_REF, C, 57.1);
verifyMeasures(PROJECT_REF, C, 57.1);
verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.1);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.1);
verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.1);
}

@Test
public void compute_measures_when_more_30_than_percent_hotspots_reviewed() {
public void compute_rating_and_reviewed__measures_when_more_30_than_percent_hotspots_reviewed() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_TO_REVIEW, null),
@@ -209,15 +215,15 @@ public class SecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, D, 33.3);
verifyMeasures(FILE_2_REF, D, 40.0);
verifyMeasures(DIRECTORY_REF, D, 37.5);
verifyMeasures(ROOT_DIR_REF, D, 37.5);
verifyMeasures(PROJECT_REF, D, 37.5);
verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
}

@Test
public void compute_measures_when_less_than_30_percent_hotspots_reviewed() {
public void compute_rating_and_reviewed__measures_when_less_than_30_percent_hotspots_reviewed() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_TO_REVIEW, null),
@@ -233,11 +239,11 @@ public class SecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(FILE_1_REF, D, 33.3);
verifyMeasures(FILE_2_REF, E, 0.0);
verifyMeasures(DIRECTORY_REF, E, 16.7);
verifyMeasures(ROOT_DIR_REF, E, 16.7);
verifyMeasures(PROJECT_REF, E, 16.7);
verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.7);
verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.7);
verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.7);
}

@Test
@@ -246,10 +252,44 @@ public class SecurityReviewMeasuresVisitorTest {

underTest.visit(ROOT_PROJECT);

verifyMeasures(PROJECT_REF, A, 100.0);
verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
}

private void verifyMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
@Test
public void compute_status_related_measures() {
treeRootHolder.setRoot(ROOT_PROJECT);
fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
newHotspot(STATUS_TO_REVIEW, null),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
// Should not be taken into account
newIssue());
fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
newHotspot(STATUS_TO_REVIEW, null),
newHotspot(STATUS_TO_REVIEW, null),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
newIssue());

underTest.visit(ROOT_PROJECT);

verifyHotspotStatusMeasures(FILE_1_REF, null, null);
verifyHotspotStatusMeasures(FILE_2_REF, null, null);
verifyHotspotStatusMeasures(DIRECTORY_REF, null, null);
verifyHotspotStatusMeasures(ROOT_DIR_REF, null, null);
verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
}

@Test
public void compute_0_status_related_measures_when_no_hotspot() {
treeRootHolder.setRoot(ROOT_PROJECT);

underTest.visit(ROOT_PROJECT);

verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
}

private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
verifySecurityReviewRating(componentRef, expectedReviewRating);
verifySecurityHotspotsReviewed(componentRef, expectedHotspotsReviewed);
}
@@ -264,6 +304,19 @@ public class SecurityReviewMeasuresVisitorTest {
assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY).get().getDoubleValue()).isEqualTo(percent);
}

private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
if (hotspotsReviewed == null){
assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
} else {
assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsReviewed);
}
if (hotspotsReviewed == null){
assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
} else {
assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsToReview);
}
}

private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
return new DefaultIssue()
.setKey(Uuids.create())

server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest.java → server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewRatingVisitorForPortfoliosTest.java View File

@@ -26,7 +26,6 @@ import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.component.ViewAttributes;
import org.sonar.ce.task.projectanalysis.component.ViewsComponent;
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;

@@ -42,22 +41,18 @@ import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilde
import static org.sonar.server.measure.Rating.B;
import static org.sonar.server.measure.Rating.C;

public class SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest {
public class SecurityReviewRatingVisitorForPortfoliosTest {

private static final int PORTFOLIO_REF = 10;
private static final int SUB_PORTFOLIO_1_REF = 11;
private static final int SUB_PORTFOLIO_2_REF = 12;
private static final Component PORTFOLIO = ViewsComponent.builder(Component.Type.VIEW, Integer.toString(PORTFOLIO_REF))
.setViewAttributes(new ViewAttributes(ViewAttributes.Type.PORTFOLIO))
.addChildren(
ViewsComponent.builder(Component.Type.SUBVIEW, Integer.toString(SUB_PORTFOLIO_1_REF)).build(),
ViewsComponent.builder(Component.Type.SUBVIEW, Integer.toString(SUB_PORTFOLIO_2_REF)).build())
.build();

private static final int APPLICATION_REF = 20;
private static final Component APPLICATION = ViewsComponent.builder(Component.Type.VIEW, Integer.toString(APPLICATION_REF))
.setViewAttributes(new ViewAttributes(ViewAttributes.Type.APPLICATION))
.build();

@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();

@@ -70,7 +65,7 @@ public class SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest {
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);

private VisitorsCrawler underTest = new VisitorsCrawler(singletonList(new SecurityReviewRatingVisitorForPortfoliosAndApplications(measureRepository, metricRepository)));
private VisitorsCrawler underTest = new VisitorsCrawler(singletonList(new SecurityReviewRatingVisitorForPortfolios(measureRepository, metricRepository)));

@Test
public void compute_security_review_rating_on_portfolio() {
@@ -89,19 +84,6 @@ public class SecurityReviewRatingVisitorForPortfoliosAndApplicationsTest {
assertThat(measureRepository.getAddedRawMeasure(PORTFOLIO_REF, SECURITY_REVIEW_RATING_KEY).get().getIntValue()).isEqualTo(B.getIndex());
}

@Test
public void compute_security_review_rating_on_application() {
treeRootHolder.setRoot(APPLICATION);
measureRepository.addRawMeasure(APPLICATION_REF, NCLOC_KEY, newMeasureBuilder().create(1000));
measureRepository.addRawMeasure(APPLICATION_REF, SECURITY_HOTSPOTS_KEY, newMeasureBuilder().create(12));

underTest.visit(APPLICATION);

Measure measure = measureRepository.getAddedRawMeasure(APPLICATION_REF, SECURITY_REVIEW_RATING_KEY).get();
assertThat(measure.getIntValue()).isEqualTo(C.getIndex());
assertThat(measure.getData()).isEqualTo(C.name());
}

@Test
public void compute_nothing_when_no_ncloc() {
treeRootHolder.setRoot(PORTFOLIO);

+ 1
- 1
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java View File

@@ -55,7 +55,7 @@ public class SecurityReviewRating {
}
}

public static Double computePercent(long hotspotsToReview, long hotspotsReviewed) {
public static double computePercent(long hotspotsToReview, long hotspotsReviewed) {
long total = hotspotsToReview + hotspotsReviewed;
if (total == 0) {
return 100.0;

+ 12
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImpl.java View File

@@ -117,6 +117,12 @@ public class IssueMetricFormulaFactoryImpl implements IssueMetricFormulaFactory
(context, issues) -> context
.setValue(computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false)))),

new IssueMetricFormula(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, false,
(context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false))),

new IssueMetricFormula(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, false,
(context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false))),

new IssueMetricFormula(CoreMetrics.NEW_CODE_SMELLS, true,
(context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, true))),

@@ -180,6 +186,12 @@ public class IssueMetricFormulaFactoryImpl implements IssueMetricFormulaFactory
context.setLeakValue(percent);
}),

new IssueMetricFormula(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, true,
(context, issues) -> context.setLeakValue(issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true))),

new IssueMetricFormula(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, true,
(context, issues) -> context.setLeakValue(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true))),

new IssueMetricFormula(CoreMetrics.NEW_SQALE_DEBT_RATIO, true,
(context, issues) -> context.setLeakValue(100.0 * newDebtDensity(context)),
asList(CoreMetrics.NEW_TECHNICAL_DEBT, CoreMetrics.NEW_DEVELOPMENT_COST)),

+ 49
- 1
server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/IssueMetricFormulaFactoryImplTest.java View File

@@ -139,12 +139,34 @@ public class IssueMetricFormulaFactoryImplTest {
with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 75.0);
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 75.0);

withNoIssues()
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 100.0);
}

@Test
public void test_security_hotspots_reviewed_status() {
with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);

withNoIssues()
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
}

@Test
public void test_security_hotspots_to_review_status() {
with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);

withNoIssues()
.assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
}

@Test
public void count_unresolved_by_severity() {
withNoIssues()
@@ -678,6 +700,32 @@ public class IssueMetricFormulaFactoryImplTest {
.assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 100.0);
}

@Test
public void test_new_security_hotspots_reviewed_status() {
with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
// not in leak
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
.assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);

withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
}

@Test
public void test_new_security_hotspots_to_review_status() {
with(
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
// not in leak
newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
.assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);

withNoIssues()
.assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
}

@Test
public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
withNoIssues()

+ 4
- 2
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -2115,11 +2115,13 @@ metric.new_security_hotspots.short_name=Security Hotspots
metric.new_security_hotspots_reviewed.description=Percentage of Security Hotspots Reviewed on New Code
metric.new_security_hotspots_reviewed.name=Security Hotspots Reviewed on New Code
metric.new_security_hotspots_reviewed.short_name=Security Hotspots Reviewed
metric.new_security_hotspots_reviewed_status.description=Security Review Reviewed Status on New Code
metric.new_security_hotspots_reviewed_status.name=Security Review Reviewed Status on New Code
metric.new_security_hotspots_to_review_status.description=Security Review To Review Status on New Code
metric.new_security_hotspots_to_review_status.name=Security Review To Review Status on New Code
metric.new_security_rating.description=Security rating on new code
metric.new_security_rating.name=Security Rating on New Code
metric.new_security_rating.extra_short_name=Rating
metric.new_security_review_rating.description=Security Review rating on new code
metric.new_security_review_rating.name=Security Review Rating on New Code
metric.new_security_remediation_effort.description=Security remediation effort on new code
metric.new_security_remediation_effort.name=Security Remediation Effort on New Code
metric.new_security_remediation_effort.extra_short_name=Remediation Effort

+ 88
- 0
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java View File

@@ -1590,6 +1590,94 @@ public final class CoreMetrics {
.setBestValue(100.0)
.create();

/**
* @since 8.2
*/
public static final String SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY = "security_hotspots_reviewed_status";

/**
* @since 8.2
*
* This hidden metric is only needed to compute 'security_review_rating' and 'security_hotspots_reviewed' on Applications.
*/
public static final Metric<Integer> SECURITY_HOTSPOTS_REVIEWED_STATUS = new Metric.Builder(SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY, "Security Review Reviewed Status",
Metric.ValueType.INT)
.setDescription("Security Review Reviewed Status")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(false)
.setDomain(DOMAIN_SECURITY_REVIEW)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.setHidden(true)
.create();

/**
* @since 8.2
*/
public static final String SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY = "security_hotspots_to_review_status";

/**
* @since 8.2
*
* This hidden metric is only needed to compute 'security_review_rating' and 'security_hotspots_reviewed' on Applications.
*/
public static final Metric<Integer> SECURITY_HOTSPOTS_TO_REVIEW_STATUS = new Metric.Builder(SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY, "Security Review To Review Status",
Metric.ValueType.INT)
.setDescription("Security Review To Review Status")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(false)
.setDomain(DOMAIN_SECURITY_REVIEW)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.setHidden(true)
.create();

/**
* @since 8.2
*/
public static final String NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY = "new_security_hotspots_reviewed_status";

/**
* @since 8.2
*
* This hidden metric is only needed to compute 'new_security_review_rating' and 'new_security_hotspots_reviewed' on Applications.
*/
public static final Metric<Integer> NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS = new Metric.Builder(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY,
"Security Review Reviewed Status on New Code", Metric.ValueType.INT)
.setDescription("Security Review Reviewed Status on New Code")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(false)
.setDomain(DOMAIN_SECURITY_REVIEW)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.setHidden(true)
.create();

/**
* @since 8.2
*/
public static final String NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY = "new_security_hotspots_to_review_status";

/**
* @since 8.2
*
* This hidden metric is only needed to compute 'new_security_review_rating' and 'new_security_hotspots_reviewed' on Applications.
*/
public static final Metric<Integer> NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS = new Metric.Builder(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY,
"Security Review To Review Status on New Code", Metric.ValueType.INT)
.setDescription("Security Review To Review Status on New Code")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(false)
.setDomain(DOMAIN_SECURITY_REVIEW)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.setHidden(true)
.create();

// --------------------------------------------------------------------------------------------------------------------
//
// FILE DATA

Loading…
Cancel
Save