aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine
diff options
context:
space:
mode:
authorAurelien Poscia <aurelien.poscia@sonarsource.com>2022-11-10 16:59:36 +0100
committersonartech <sonartech@sonarsource.com>2022-11-15 20:02:59 +0000
commit4d156d7cffcd7379b3dcac2a36946bcbeab8077f (patch)
tree301806036ff9effd5ba0fe6b09d081ed58f036d7 /sonar-scanner-engine
parent8f9eb2135150b0a77aea302e17afa4ba238560ce (diff)
downloadsonarqube-4d156d7cffcd7379b3dcac2a36946bcbeab8077f.tar.gz
sonarqube-4d156d7cffcd7379b3dcac2a36946bcbeab8077f.zip
SONAR-17564 Add statistics on imported sarif files/issues count
Diffstat (limited to 'sonar-scanner-engine')
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210Importer.java28
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/Sarif210Importer.java8
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifImportResults.java78
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensor.java25
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210ImporterTest.java10
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensorTest.java93
6 files changed, 206 insertions, 36 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210Importer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210Importer.java
index 840843160f9..41503d2079f 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210Importer.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210Importer.java
@@ -19,9 +19,7 @@
*/
package org.sonar.scanner.externalissue.sarif;
-import java.util.Collection;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
import javax.annotation.CheckForNull;
import org.sonar.api.batch.sensor.issue.NewExternalIssue;
@@ -44,13 +42,27 @@ public class DefaultSarif210Importer implements Sarif210Importer {
}
@Override
- public void importSarif(Sarif210 sarif210) {
+ public SarifImportResults importSarif(Sarif210 sarif210) {
+ int successFullyImportedIssues = 0;
+ int successFullyImportedRuns = 0;
+ int failedRuns = 0;
+
Set<Run> runs = requireNonNull(sarif210.getRuns(), "The runs section of the Sarif report is null");
- runs.stream()
- .map(this::toNewExternalIssues)
- .filter(Objects::nonNull)
- .flatMap(Collection::stream)
- .forEach(NewExternalIssue::save);
+ for (Run run : runs) {
+ List<NewExternalIssue> newExternalIssues = toNewExternalIssues(run);
+ if (newExternalIssues == null) {
+ failedRuns += 1;
+ } else {
+ successFullyImportedRuns += 1;
+ successFullyImportedIssues += newExternalIssues.size();
+ newExternalIssues.forEach(NewExternalIssue::save);
+ }
+ }
+ return SarifImportResults.builder()
+ .successFullyImportedIssues(successFullyImportedIssues)
+ .successFullyImportedRuns(successFullyImportedRuns)
+ .failedRuns(failedRuns)
+ .build();
}
@CheckForNull
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/Sarif210Importer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/Sarif210Importer.java
index 9e88be77a18..b73ea0e9e92 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/Sarif210Importer.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/Sarif210Importer.java
@@ -22,5 +22,11 @@ package org.sonar.scanner.externalissue.sarif;
import org.sonar.core.sarif.Sarif210;
public interface Sarif210Importer {
- void importSarif(Sarif210 sarif210);
+
+ /**
+ *
+ * @param sarif210 the deserialized sarif report
+ * @return the number of issues imported
+ */
+ SarifImportResults importSarif(Sarif210 sarif210);
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifImportResults.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifImportResults.java
new file mode 100644
index 00000000000..bfc7e64feb7
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifImportResults.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scanner.externalissue.sarif;
+
+class SarifImportResults {
+
+ private final int successFullyImportedIssues;
+ private final int successFullyImportedRuns;
+ private final int failedRuns;
+
+ SarifImportResults(int successFullyImportedIssues, int successFullyImportedRuns, int failedRuns) {
+ this.successFullyImportedIssues = successFullyImportedIssues;
+ this.successFullyImportedRuns = successFullyImportedRuns;
+ this.failedRuns = failedRuns;
+ }
+
+ int getSuccessFullyImportedIssues() {
+ return successFullyImportedIssues;
+ }
+
+ int getSuccessFullyImportedRuns() {
+ return successFullyImportedRuns;
+ }
+
+ int getFailedRuns() {
+ return failedRuns;
+ }
+
+ static SarifImportResultBuilder builder() {
+ return new SarifImportResultBuilder();
+ }
+
+ static final class SarifImportResultBuilder {
+ private int successFullyImportedIssues;
+ private int successFullyImportedRuns;
+
+ private int failedRuns;
+
+ private SarifImportResultBuilder() {
+ }
+
+ SarifImportResultBuilder successFullyImportedIssues(int successFullyImportedIssues) {
+ this.successFullyImportedIssues = successFullyImportedIssues;
+ return this;
+ }
+
+ SarifImportResultBuilder successFullyImportedRuns(int successFullyImportedRuns) {
+ this.successFullyImportedRuns = successFullyImportedRuns;
+ return this;
+ }
+
+ SarifImportResultBuilder failedRuns(int failedRuns) {
+ this.failedRuns = failedRuns;
+ return this;
+ }
+
+ SarifImportResults build() {
+ return new SarifImportResults(successFullyImportedIssues, successFullyImportedRuns, failedRuns);
+ }
+ }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensor.java
index cb95123ed9f..9e8e386263c 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensor.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensor.java
@@ -22,7 +22,9 @@ package org.sonar.scanner.externalissue.sarif;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.sonar.api.CoreProperties;
@@ -73,19 +75,32 @@ public class SarifIssuesImportSensor implements Sensor {
@Override
public void execute(SensorContext context) {
Set<String> reportPaths = loadReportPaths();
+ Map<String, SarifImportResults> filePathToImportResults = new HashMap<>();
+
for (String reportPath : reportPaths) {
try {
- LOG.debug("Importing SARIF issues from '{}'", reportPath);
- Path reportFilePath = context.fileSystem().resolvePath(reportPath).toPath();
- Sarif210 sarifReport = sarifSerializer.deserialize(reportFilePath);
- sarifImporter.importSarif(sarifReport);
+ SarifImportResults sarifImportResults = processReport(context, reportPath);
+ filePathToImportResults.put(reportPath, sarifImportResults);
} catch (Exception exception) {
- LOG.warn("Failed to process SARIF report from file '{}', error '{}'", reportPath, exception.getMessage());
+ LOG.warn("Failed to process SARIF report from file '{}', error: '{}'", reportPath, exception.getMessage());
}
}
+ filePathToImportResults.forEach(SarifIssuesImportSensor::displayResults);
}
private Set<String> loadReportPaths() {
return Arrays.stream(config.getStringArray(SARIF_REPORT_PATHS_PROPERTY_KEY)).collect(Collectors.toSet());
}
+
+ private SarifImportResults processReport(SensorContext context, String reportPath) {
+ LOG.debug("Importing SARIF issues from '{}'", reportPath);
+ Path reportFilePath = context.fileSystem().resolvePath(reportPath).toPath();
+ Sarif210 sarifReport = sarifSerializer.deserialize(reportFilePath);
+ return sarifImporter.importSarif(sarifReport);
+ }
+
+ private static void displayResults(String filePath, SarifImportResults sarifImportResults) {
+ LOG.info("File {}: successfully imported {} vulnerabilities spread in {} runs. {} failed run(s).",
+ filePath, sarifImportResults.getSuccessFullyImportedIssues(), sarifImportResults.getSuccessFullyImportedRuns(), sarifImportResults.getFailedRuns());
+ }
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210ImporterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210ImporterTest.java
index 8493eb091d8..0d3b69288e1 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210ImporterTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/DefaultSarif210ImporterTest.java
@@ -67,8 +67,11 @@ public class DefaultSarif210ImporterTest extends TestCase {
when(runMapper.mapRun(run1)).thenReturn(List.of(issue1run1, issue2run1));
when(runMapper.mapRun(run2)).thenReturn(List.of(issue1run2));
- sarif210Importer.importSarif(sarif210);
+ SarifImportResults sarifImportResults = sarif210Importer.importSarif(sarif210);
+ assertThat(sarifImportResults.getSuccessFullyImportedIssues()).isEqualTo(3);
+ assertThat(sarifImportResults.getSuccessFullyImportedRuns()).isEqualTo(2);
+ assertThat(sarifImportResults.getFailedRuns()).isZero();
verify(issue1run1).save();
verify(issue2run1).save();
verify(issue1run2).save();
@@ -87,8 +90,11 @@ public class DefaultSarif210ImporterTest extends TestCase {
NewExternalIssue issue1run2 = mock(NewExternalIssue.class);
when(runMapper.mapRun(run2)).thenReturn(List.of(issue1run2));
- sarif210Importer.importSarif(sarif210);
+ SarifImportResults sarifImportResults = sarif210Importer.importSarif(sarif210);
+ assertThat(sarifImportResults.getSuccessFullyImportedIssues()).isOne();
+ assertThat(sarifImportResults.getSuccessFullyImportedRuns()).isOne();
+ assertThat(sarifImportResults.getFailedRuns()).isOne();
assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Failed to import a sarif run, error: " + testException.getMessage());
verify(issue1run2).save();
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensorTest.java
index 043189f0d6d..c7dedb9da17 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensorTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensorTest.java
@@ -19,7 +19,12 @@
*/
package org.sonar.scanner.externalissue.sarif;
+import com.google.common.collect.MoreCollectors;
import java.nio.file.Path;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.commons.lang.math.RandomUtils;
+import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -28,6 +33,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.utils.log.LogAndArguments;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.core.sarif.Sarif210;
@@ -59,7 +65,7 @@ public class SarifIssuesImportSensorTest {
}
@Rule
- public LogTester logTester = new LogTester();
+ public final LogTester logTester = new LogTester();
private final SensorContextTester sensorContext = SensorContextTester.create(Path.of("."));
@@ -67,12 +73,15 @@ public class SarifIssuesImportSensorTest {
public void execute_single_files() {
sensorSettings.setProperty("sonar.sarifReportPaths", FILE_1);
- Sarif210 sarifReport = mockReport(FILE_1);
+ ReportAndResults reportAndResults = mockReportAndResults(FILE_1);
SarifIssuesImportSensor sensor = new SarifIssuesImportSensor(sarifSerializer, sarifImporter, sensorSettings.asConfig());
sensor.execute(sensorContext);
- verify(sarifImporter).importSarif(sarifReport);
+ verify(sarifImporter).importSarif(reportAndResults.getSarifReport());
+
+ assertThat(logTester.logs(LoggerLevel.INFO)).hasSize(1);
+ assertSummaryIsCorrectlyDisplayed(FILE_1, reportAndResults.getSarifImportResults());
}
@Test
@@ -80,30 +89,34 @@ public class SarifIssuesImportSensorTest {
sensorSettings.setProperty("sonar.sarifReportPaths", SARIF_REPORT_PATHS_PARAM);
- Sarif210 sarifReport1 = mockReport(FILE_1);
- Sarif210 sarifReport2 = mockReport(FILE_2);
+ ReportAndResults reportAndResults1 = mockReportAndResults(FILE_1);
+ ReportAndResults reportAndResults2 = mockReportAndResults(FILE_2);
SarifIssuesImportSensor sensor = new SarifIssuesImportSensor(sarifSerializer, sarifImporter, sensorSettings.asConfig());
sensor.execute(sensorContext);
- verify(sarifImporter).importSarif(sarifReport1);
- verify(sarifImporter).importSarif(sarifReport2);
+ verify(sarifImporter).importSarif(reportAndResults1.getSarifReport());
+ verify(sarifImporter).importSarif(reportAndResults2.getSarifReport());
+
+ assertSummaryIsCorrectlyDisplayed(FILE_1, reportAndResults1.getSarifImportResults());
+ assertSummaryIsCorrectlyDisplayed(FILE_2, reportAndResults2.getSarifImportResults());
}
@Test
public void skip_report_when_import_fails() {
sensorSettings.setProperty("sonar.sarifReportPaths", SARIF_REPORT_PATHS_PARAM);
- Sarif210 sarifReport1 = mockReport(FILE_1);
- Sarif210 sarifReport2 = mockReport(FILE_2);
+ ReportAndResults reportAndResults1 = mockReportAndResults(FILE_1);
+ ReportAndResults reportAndResults2 = mockReportAndResults(FILE_2);
- doThrow(new NullPointerException("import failed")).when(sarifImporter).importSarif(sarifReport1);
+ doThrow(new NullPointerException("import failed")).when(sarifImporter).importSarif(reportAndResults1.getSarifReport());
SarifIssuesImportSensor sensor = new SarifIssuesImportSensor(sarifSerializer, sarifImporter, sensorSettings.asConfig());
sensor.execute(sensorContext);
- verify(sarifImporter).importSarif(sarifReport2);
- assertThat(logTester.logs(LoggerLevel.WARN)).contains("Failed to process SARIF report from file 'path/to/sarif/file.sarif', error 'import failed'");
+ verify(sarifImporter).importSarif(reportAndResults2.getSarifReport());
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains("Failed to process SARIF report from file 'path/to/sarif/file.sarif', error: 'import failed'");
+ assertSummaryIsCorrectlyDisplayed(FILE_2, reportAndResults2.getSarifImportResults());
}
@Test
@@ -111,25 +124,65 @@ public class SarifIssuesImportSensorTest {
sensorSettings.setProperty("sonar.sarifReportPaths", SARIF_REPORT_PATHS_PARAM);
failDeserializingReport(FILE_1);
- Sarif210 sarifReport2 = mockReport(FILE_2);
+ ReportAndResults reportAndResults2 = mockReportAndResults(FILE_2);
SarifIssuesImportSensor sensor = new SarifIssuesImportSensor(sarifSerializer, sarifImporter, sensorSettings.asConfig());
sensor.execute(sensorContext);
- verify(sarifImporter).importSarif(sarifReport2);
- assertThat(logTester.logs(LoggerLevel.WARN)).contains("Failed to process SARIF report from file 'path/to/sarif/file.sarif', error 'deserialization failed'");
+ verify(sarifImporter).importSarif(reportAndResults2.getSarifReport());
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains("Failed to process SARIF report from file 'path/to/sarif/file.sarif', error: 'deserialization failed'");
+ assertSummaryIsCorrectlyDisplayed(FILE_2, reportAndResults2.getSarifImportResults());
+ }
+ private void failDeserializingReport(String path) {
+ Path reportFilePath = sensorContext.fileSystem().resolvePath(path).toPath();
+ when(sarifSerializer.deserialize(reportFilePath)).thenThrow(new NullPointerException("deserialization failed"));
}
- private Sarif210 mockReport(String path) {
+ private ReportAndResults mockReportAndResults(String path) {
Sarif210 report = mock(Sarif210.class);
Path reportFilePath = sensorContext.fileSystem().resolvePath(path).toPath();
when(sarifSerializer.deserialize(reportFilePath)).thenReturn(report);
- return report;
+
+ SarifImportResults sarifImportResults = mock(SarifImportResults.class);
+ when(sarifImportResults.getSuccessFullyImportedIssues()).thenReturn(RandomUtils.nextInt());
+ when(sarifImportResults.getSuccessFullyImportedRuns()).thenReturn(RandomUtils.nextInt());
+ when(sarifImportResults.getFailedRuns()).thenReturn(RandomUtils.nextInt());
+
+ when(sarifImporter.importSarif(report)).thenReturn(sarifImportResults);
+ return new ReportAndResults(report, sarifImportResults);
}
- private void failDeserializingReport(String path) {
- Path reportFilePath = sensorContext.fileSystem().resolvePath(path).toPath();
- when(sarifSerializer.deserialize(reportFilePath)).thenThrow(new NullPointerException("deserialization failed"));
+ private void assertSummaryIsCorrectlyDisplayed(String filePath, SarifImportResults sarifImportResults) {
+ LogAndArguments logAndArguments = findLogEntry(filePath);
+ assertThat(logAndArguments.getRawMsg()).isEqualTo("File {}: successfully imported {} vulnerabilities spread in {} runs. {} failed run(s).");
+ assertThat(logAndArguments.getArgs()).isPresent()
+ .contains(new Object[] {filePath, sarifImportResults.getSuccessFullyImportedIssues(), sarifImportResults.getSuccessFullyImportedRuns(), sarifImportResults.getFailedRuns()});
+ }
+
+ private LogAndArguments findLogEntry(String filePath) {
+ Optional<LogAndArguments> optLogAndArguments = logTester.getLogs(LoggerLevel.INFO).stream()
+ .filter(log -> log.getFormattedMsg().contains(filePath))
+ .collect(MoreCollectors.toOptional());
+ assertThat(optLogAndArguments).as("Log entry missing for file %s", filePath).isPresent();
+ return optLogAndArguments.get();
+ }
+
+ private static class ReportAndResults {
+ private final Sarif210 sarifReport;
+ private final SarifImportResults sarifImportResults;
+
+ private ReportAndResults(Sarif210 sarifReport, SarifImportResults sarifImportResults) {
+ this.sarifReport = sarifReport;
+ this.sarifImportResults = sarifImportResults;
+ }
+
+ private Sarif210 getSarifReport() {
+ return sarifReport;
+ }
+
+ private SarifImportResults getSarifImportResults() {
+ return sarifImportResults;
+ }
}
}