]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17560 Introduce SARIF version validation
authorKlaudio Sinani <klaudio.sinani@sonarsource.com>
Thu, 10 Nov 2022 16:58:30 +0000 (17:58 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 15 Nov 2022 20:02:59 +0000 (20:02 +0000)
sonar-core/src/main/java/org/sonar/core/sarif/Sarif210.java
sonar-core/src/main/java/org/sonar/core/sarif/SarifSerializerImpl.java
sonar-core/src/main/java/org/sonar/core/sarif/SarifVersionValidator.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/sarif/SarifSerializerTest.java
sonar-core/src/test/java/org/sonar/core/sarif/SarifVersionValidatorTest.java [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/sarif/unsupported-sarif-version-abc.json [new file with mode: 0644]
sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/sarif/SarifIssuesImportSensorTest.java

index 0df616af2e223fbe3dfd62c8d5c7842cb46f5389..df4247bb5676a1dce351ae6ad4ec03ebd23940ac 100644 (file)
  */
 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")
index 3f33419cb8705c21941a842ff1e3b67756dba410..091953b1c87abc501c00d22eb060108e3a8fefee 100644 (file)
@@ -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 (file)
index 0000000..e82e1a4
--- /dev/null
@@ -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);
+  }
+}
index 8350226ce752554fe47fbc5b42d168d0a34a8f4b..fbc0e080ecec30f4c796062fc6f62031d20304ac 100644 (file)
@@ -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 (file)
index 0000000..4791abe
--- /dev/null
@@ -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 (file)
index 0000000..f996279
--- /dev/null
@@ -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"
+    }
+  ]
+}
index 8d6d10db9451ed8b6edcdc696214de318202d9f0..043189f0d6d5b6e4ae15b3d6aa9c522df4a6474e 100644 (file)
@@ -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() {