diff options
author | Klaudio Sinani <klaudio.sinani@sonarsource.com> | 2022-11-10 17:58:30 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-11-15 20:02:59 +0000 |
commit | 8f9eb2135150b0a77aea302e17afa4ba238560ce (patch) | |
tree | 1412fb1e771c35d6bf149f54b3083ef3d18cd787 | |
parent | 53f94935f393750ba08a7e1fa00742acadbadafb (diff) | |
download | sonarqube-8f9eb2135150b0a77aea302e17afa4ba238560ce.tar.gz sonarqube-8f9eb2135150b0a77aea302e17afa4ba238560ce.zip |
SONAR-17560 Introduce SARIF version validation
7 files changed, 267 insertions, 7 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/sarif/Sarif210.java b/sonar-core/src/main/java/org/sonar/core/sarif/Sarif210.java index 0df616af2e2..df4247bb567 100644 --- a/sonar-core/src/main/java/org/sonar/core/sarif/Sarif210.java +++ b/sonar-core/src/main/java/org/sonar/core/sarif/Sarif210.java @@ -19,13 +19,11 @@ */ package org.sonar.core.sarif; -import com.google.common.annotations.VisibleForTesting; import com.google.gson.annotations.SerializedName; import java.util.Set; public class Sarif210 { - @VisibleForTesting public static final String SARIF_VERSION = "2.1.0"; @SerializedName("version") diff --git a/sonar-core/src/main/java/org/sonar/core/sarif/SarifSerializerImpl.java b/sonar-core/src/main/java/org/sonar/core/sarif/SarifSerializerImpl.java index 3f33419cb87..091953b1c87 100644 --- a/sonar-core/src/main/java/org/sonar/core/sarif/SarifSerializerImpl.java +++ b/sonar-core/src/main/java/org/sonar/core/sarif/SarifSerializerImpl.java @@ -60,11 +60,15 @@ public class SarifSerializerImpl implements SarifSerializer { @Override public Sarif210 deserialize(Path reportPath) { try (Reader reader = newBufferedReader(reportPath, UTF_8)) { - return gson.fromJson(reader, Sarif210.class); + Sarif210 sarif = gson.fromJson(reader, Sarif210.class); + SarifVersionValidator.validateSarifVersion(sarif.getVersion()); + return sarif; } catch (JsonIOException | IOException e) { throw new IllegalStateException(format(SARIF_REPORT_ERROR, reportPath), e); } catch (JsonSyntaxException e) { throw new IllegalStateException(format(SARIF_JSON_SYNTAX_ERROR, reportPath), e); + } catch (IllegalStateException e) { + throw new IllegalStateException(e.getMessage(), e); } } } diff --git a/sonar-core/src/main/java/org/sonar/core/sarif/SarifVersionValidator.java b/sonar-core/src/main/java/org/sonar/core/sarif/SarifVersionValidator.java new file mode 100644 index 00000000000..e82e1a4b8e1 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/sarif/SarifVersionValidator.java @@ -0,0 +1,50 @@ +/* + * 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.core.sarif; + +import java.util.Optional; +import java.util.Set; +import javax.annotation.Nullable; + +import static java.lang.String.format; +import static org.sonar.core.sarif.Sarif210.SARIF_VERSION; + +public class SarifVersionValidator { + public static final Set<String> SUPPORTED_SARIF_VERSIONS = Set.of(SARIF_VERSION); + public static final String UNSUPPORTED_VERSION_MESSAGE_TEMPLATE = "Version [%s] of SARIF is not supported"; + + private SarifVersionValidator() {} + + public static void validateSarifVersion(@Nullable String version) { + if (!isSupportedSarifVersion(version)) { + throw new IllegalStateException(composeUnsupportedVersionMessage(version)); + } + } + + private static boolean isSupportedSarifVersion(@Nullable String version) { + return Optional.ofNullable(version) + .filter(SUPPORTED_SARIF_VERSIONS::contains) + .isPresent(); + } + + private static String composeUnsupportedVersionMessage(@Nullable String version) { + return format(UNSUPPORTED_VERSION_MESSAGE_TEMPLATE, version); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/sarif/SarifSerializerTest.java b/sonar-core/src/test/java/org/sonar/core/sarif/SarifSerializerTest.java index 8350226ce75..fbc0e080ece 100644 --- a/sonar-core/src/test/java/org/sonar/core/sarif/SarifSerializerTest.java +++ b/sonar-core/src/test/java/org/sonar/core/sarif/SarifSerializerTest.java @@ -28,10 +28,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.fail; +import static org.sonar.core.sarif.SarifVersionValidator.UNSUPPORTED_VERSION_MESSAGE_TEMPLATE; @RunWith(MockitoJUnitRunner.class) public class SarifSerializerTest { @@ -67,7 +69,7 @@ public class SarifSerializerTest { assertThatThrownBy(() -> serializer.deserialize(sarif)) .isInstanceOf(IllegalStateException.class) - .hasMessage(String.format("Failed to read SARIF report at '%s'", file)); + .hasMessage(format("Failed to read SARIF report at '%s'", file)); } @Test @@ -77,7 +79,17 @@ public class SarifSerializerTest { assertThatThrownBy(() -> serializer.deserialize(sarif)) .isInstanceOf(IllegalStateException.class) - .hasMessage(String.format("Failed to read SARIF report at '%s': invalid JSON syntax", sarif)); + .hasMessage(format("Failed to read SARIF report at '%s': invalid JSON syntax", sarif)); + } + + @Test + public void deserialize_shouldFail_whenSarifVersionIsNotSupported() throws URISyntaxException { + URL sarifResource = requireNonNull(getClass().getResource("unsupported-sarif-version-abc.json")); + Path sarif = Paths.get(sarifResource.toURI()); + + assertThatThrownBy(() -> serializer.deserialize(sarif)) + .isInstanceOf(IllegalStateException.class) + .hasMessage(format(UNSUPPORTED_VERSION_MESSAGE_TEMPLATE, "A.B.C")); } private void verifySarif(Sarif210 deserializationResult) { diff --git a/sonar-core/src/test/java/org/sonar/core/sarif/SarifVersionValidatorTest.java b/sonar-core/src/test/java/org/sonar/core/sarif/SarifVersionValidatorTest.java new file mode 100644 index 00000000000..4791abeae57 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/sarif/SarifVersionValidatorTest.java @@ -0,0 +1,89 @@ +/* + * 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.core.sarif; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.apache.commons.lang.RandomStringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.sonar.core.sarif.SarifVersionValidator.SUPPORTED_SARIF_VERSIONS; +import static org.sonar.core.sarif.SarifVersionValidator.UNSUPPORTED_VERSION_MESSAGE_TEMPLATE; + +@RunWith(DataProviderRunner.class) +public class SarifVersionValidatorTest { + + @Test + @UseDataProvider("unsupportedSarifVersions") + public void sarif_version_validation_fails_if_version_is_not_supported(String version) { + assertThatThrownBy(() -> SarifVersionValidator.validateSarifVersion(version)) + .isExactlyInstanceOf(IllegalStateException.class) + .hasMessage(format(UNSUPPORTED_VERSION_MESSAGE_TEMPLATE, version)); + } + + @Test + @UseDataProvider("supportedSarifVersions") + public void sarif_version_validation_succeeds_if_version_is_supported(String version) { + assertThatCode(() -> SarifVersionValidator.validateSarifVersion(version)) + .doesNotThrowAnyException(); + } + + @DataProvider + public static List<String> unsupportedSarifVersions() { + List<String> unsupportedVersions = generateRandomUnsupportedSemanticVersions(10); + unsupportedVersions.add(null); + return unsupportedVersions; + } + + @DataProvider + public static Set<String> supportedSarifVersions() { + return SUPPORTED_SARIF_VERSIONS; + } + + private static List<String> generateRandomUnsupportedSemanticVersions(int amount) { + return Stream + .generate(SarifVersionValidatorTest::generateRandomSemanticVersion) + .takeWhile(SarifVersionValidatorTest::isUnsupportedVersion) + .limit(amount) + .collect(Collectors.toList()); + } + + private static String generateRandomSemanticVersion() { + return IntStream + .rangeClosed(1, 3) + .mapToObj(x -> RandomStringUtils.randomNumeric(1)) + .collect(Collectors.joining(".")); + } + + private static boolean isUnsupportedVersion(String version) { + return !SUPPORTED_SARIF_VERSIONS.contains(version); + } + +} diff --git a/sonar-core/src/test/resources/org/sonar/core/sarif/unsupported-sarif-version-abc.json b/sonar-core/src/test/resources/org/sonar/core/sarif/unsupported-sarif-version-abc.json new file mode 100644 index 00000000000..f9962795461 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/sarif/unsupported-sarif-version-abc.json @@ -0,0 +1,107 @@ +{ + "version": "A.B.C", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "runs": [ + { + "tool": { + "driver": { + "name": "SonarQube", + "organization": "SonarSource", + "semanticVersion": "9.6", + "rules": [ + { + "id": "java:S5132", + "name": "java:S5132", + "shortDescription": { + "text": "Make this final static field too." + }, + "fullDescription": { + "text": "Make this final static field too." + }, + "help": { + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam hendrerit nisi sed sollicitudin pellentesque. Nunc posuere purus rhoncus pulvinar aliquam. Ut aliquet tristique nisl vitae volutpat. Nulla aliquet porttitor venenatis. Donec a dui et dui fringilla consectetur id nec massa. Aliquam erat volutpat. Sed ut dui ut lacus dictum fermentum vel tincidunt neque. Sed sed lacinia lectus. Duis sit amet sodales felis. Duis nunc eros, mattis at dui ac, convallis semper risus. In adipiscing ultrices tellus, in suscipit massa vehicula eu." + }, + "properties": { + "tags": [ + "tag1", + "tag2" + ] + } + } + ] + } + }, + "results": [ + { + "ruleId": "java:S5132", + "message": { + "text": "this is the message" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "www.google.com", + "uriBaseId": "%SRCROOT" + }, + "region": { + "startLine": 11, + "endLine": 222, + "startColumn": 54, + "endColumn": 4 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "thisISTHEHAS" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "www.google.com", + "uriBaseId": "%SRCROOT" + }, + "region": { + "startLine": 11, + "endLine": 222, + "startColumn": 54, + "endColumn": 4 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "www.google.com", + "uriBaseId": "%SRCROOT" + }, + "region": { + "startLine": 22, + "endLine": 4323, + "startColumn": 545, + "endColumn": 4324 + } + } + } + } + ] + } + ] + } + ] + } + ], + "language": "en-us", + "columnKind": "utf16CodeUnits" + } + ] +} 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 8d6d10db945..043189f0d6d 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 @@ -33,8 +33,8 @@ import org.sonar.api.utils.log.LoggerLevel; import org.sonar.core.sarif.Sarif210; import org.sonar.core.sarif.SarifSerializer; -import static org.mockito.Mockito.doThrow; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -61,7 +61,7 @@ public class SarifIssuesImportSensorTest { @Rule public LogTester logTester = new LogTester(); - private SensorContextTester sensorContext = SensorContextTester.create(Path.of(".")); + private final SensorContextTester sensorContext = SensorContextTester.create(Path.of(".")); @Test public void execute_single_files() { |