diff options
author | Aurelien Poscia <aurelien.poscia@sonarsource.com> | 2022-11-10 16:59:36 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-11-15 20:02:59 +0000 |
commit | 4d156d7cffcd7379b3dcac2a36946bcbeab8077f (patch) | |
tree | 301806036ff9effd5ba0fe6b09d081ed58f036d7 /sonar-scanner-engine | |
parent | 8f9eb2135150b0a77aea302e17afa4ba238560ce (diff) | |
download | sonarqube-4d156d7cffcd7379b3dcac2a36946bcbeab8077f.tar.gz sonarqube-4d156d7cffcd7379b3dcac2a36946bcbeab8077f.zip |
SONAR-17564 Add statistics on imported sarif files/issues count
Diffstat (limited to 'sonar-scanner-engine')
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; + } } } |