]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17560 Add Sarif deserialization
authorAntoine Vinot <antoine.vinot@sonarsource.com>
Tue, 8 Nov 2022 11:14:43 +0000 (12:14 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 15 Nov 2022 20:02:59 +0000 (20:02 +0000)
24 files changed:
sonar-core/src/main/java/org/sonar/core/sarif/ArtifactLocation.java
sonar-core/src/main/java/org/sonar/core/sarif/CodeFlow.java
sonar-core/src/main/java/org/sonar/core/sarif/Driver.java
sonar-core/src/main/java/org/sonar/core/sarif/Location.java
sonar-core/src/main/java/org/sonar/core/sarif/LocationWrapper.java
sonar-core/src/main/java/org/sonar/core/sarif/PartialFingerprints.java
sonar-core/src/main/java/org/sonar/core/sarif/PhysicalLocation.java
sonar-core/src/main/java/org/sonar/core/sarif/PropertiesBag.java
sonar-core/src/main/java/org/sonar/core/sarif/Region.java
sonar-core/src/main/java/org/sonar/core/sarif/Result.java
sonar-core/src/main/java/org/sonar/core/sarif/Rule.java
sonar-core/src/main/java/org/sonar/core/sarif/Run.java
sonar-core/src/main/java/org/sonar/core/sarif/Sarif210.java
sonar-core/src/main/java/org/sonar/core/sarif/SarifSerializer.java
sonar-core/src/main/java/org/sonar/core/sarif/SarifSerializerImpl.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/sarif/ThreadFlow.java
sonar-core/src/main/java/org/sonar/core/sarif/Tool.java
sonar-core/src/main/java/org/sonar/core/sarif/WrappedText.java
sonar-core/src/test/java/org/sonar/core/sarif/RuleTest.java
sonar-core/src/test/java/org/sonar/core/sarif/Sarif210SerializationDeserializationTest.java
sonar-core/src/test/java/org/sonar/core/sarif/SarifSerializerTest.java
sonar-core/src/test/resources/org/sonar/core/sarif/eslint-sarif210.json [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/sarif/invalid-json-syntax.json [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/BatchComponents.java

index b9839979c74604623a5a3a2e2660743e4e8cdb07..f12413c339da07741ba2f871ea076a0b243fa35e 100644 (file)
@@ -1,33 +1,45 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.google.gson.annotations.SerializedName;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 public class ArtifactLocation {
-  private static final String URI_BASE_ID = "%SRCROOT";
 
   @SerializedName("uri")
   private final String uri;
   @SerializedName("uriBaseId")
   private final String uriBaseId;
 
-  private ArtifactLocation(String uriBaseId, String uri) {
+  public ArtifactLocation(@Nullable String uriBaseId, String uri) {
     this.uriBaseId = uriBaseId;
     this.uri = uri;
   }
 
-  public static ArtifactLocation of(String uri) {
-    return new ArtifactLocation(URI_BASE_ID, uri);
-  }
-
   public String getUri() {
     return uri;
   }
 
+  @CheckForNull
   public String getUriBaseId() {
     return uriBaseId;
   }
index 0ee6471714056b7df2a099d9af3552f20cc08185..887ba202215e103c0930859dc77fab2042dbed7b 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index cbbbe3b32a35feb266953cf4d14415abd7c89705..aaa8d9faac22d7923f2a73bcc07a4a91caf2bde1 100644 (file)
@@ -1,16 +1,30 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.google.gson.annotations.SerializedName;
 import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 public class Driver {
-  private static final String TOOL_NAME = "SonarQube";
-  private static final String ORGANIZATION_NAME = "SonarSource";
 
   @SerializedName("name")
   private final String name;
@@ -21,11 +35,7 @@ public class Driver {
   @SerializedName("rules")
   private final Set<Rule> rules;
 
-  public Driver(String semanticVersion, Set<Rule> rules) {
-    this(TOOL_NAME, ORGANIZATION_NAME, semanticVersion, rules);
-  }
-
-  private Driver(String name, String organization, String semanticVersion, Set<Rule> rules) {
+  private Driver(String name, @Nullable String organization, @Nullable String semanticVersion, Set<Rule> rules) {
     this.name = name;
     this.organization = organization;
     this.semanticVersion = semanticVersion;
@@ -36,10 +46,12 @@ public class Driver {
     return name;
   }
 
+  @CheckForNull
   public String getOrganization() {
     return organization;
   }
 
+  @CheckForNull
   public String getSemanticVersion() {
     return semanticVersion;
   }
@@ -47,4 +59,42 @@ public class Driver {
   public Set<Rule> getRules() {
     return rules;
   }
+
+  public static DriverBuilder builder() {
+    return new DriverBuilder();
+  }
+
+  public static final class DriverBuilder {
+    private String name;
+    private String organization;
+    private String semanticVersion;
+    private Set<Rule> rules;
+
+    private DriverBuilder() {
+    }
+
+    public DriverBuilder name(String name) {
+      this.name = name;
+      return this;
+    }
+
+    public DriverBuilder organization(String organization) {
+      this.organization = organization;
+      return this;
+    }
+
+    public DriverBuilder semanticVersion(String semanticVersion) {
+      this.semanticVersion = semanticVersion;
+      return this;
+    }
+
+    public DriverBuilder rules(Set<Rule> rules) {
+      this.rules = rules;
+      return this;
+    }
+
+    public Driver build() {
+      return new Driver(name, organization, semanticVersion, rules);
+    }
+  }
 }
index c2a334c9bd922ad74bd004b8b767325f56f0d958..ae1db296947445d3d385eaa4586f91aff38aecde 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index 2c551204713d5a96159e77ac483f3c333867f059..91fa55680fe8251c48eed6b0f48735c9e7d4a5a0 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index 67cbbdef1d619f1a732b96e4faf69b3b4eeec2ce..ffef750f531a645b04d6fff360140a41a02f2838 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index c4c1e8339446a0a3cb8dc67c10edd654f77d29a7..0d9b7582c841af84ee1ac24393a1910d172e993a 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index 1f44afc39ac019d8c3f61cf299ebead81281ae41..949f3fea3efc7dd41970b46beb8ab008253d3ca1 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index 8fd89fa4c41d4cfeb7a2b7b03c247d2c8ef61bde..e909fceb9c3d0563a2eab10a6595df023804ffa2 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
@@ -45,10 +59,10 @@ public class Region {
   }
 
   public static final class RegionBuilder {
-    private int startLine;
-    private int endLine;
-    private int startColumn;
-    private int endColumn;
+    private Integer startLine;
+    private Integer endLine;
+    private Integer startColumn;
+    private Integer endColumn;
 
     public RegionBuilder startLine(int startLine) {
       this.startLine = startLine;
index 3815da4a48025893aca1e47d5d6c2ebc50e2a6ed..60f980de875c0ef9319383a4fe52115a53d5349d 100644 (file)
@@ -1,14 +1,30 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.google.gson.annotations.SerializedName;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
-import org.sonar.api.rule.RuleKey;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 public class Result {
   @SerializedName("ruleId")
@@ -16,19 +32,19 @@ public class Result {
   @SerializedName("message")
   private final WrappedText message;
   @SerializedName("locations")
-  private final Set<Location> locations;
+  private final LinkedHashSet<Location> locations;
   @SerializedName("partialFingerprints")
   private final PartialFingerprints partialFingerprints;
   @SerializedName("codeFlows")
   private final List<CodeFlow> codeFlows;
 
-
-  private Result(RuleKey ruleKey, String message, Location location, String primaryLocationLineHash, List<CodeFlow> codeFlows) {
-    this.ruleId = ruleKey.toString();
+  private Result(String ruleId, String message, LinkedHashSet<Location> locations,
+    @Nullable String primaryLocationLineHash, @Nullable List<CodeFlow> codeFlows) {
+    this.ruleId = ruleId;
     this.message = WrappedText.of(message);
-    this.locations = Set.of(location);
-    this.partialFingerprints = new PartialFingerprints(primaryLocationLineHash);
-    this.codeFlows = List.copyOf(codeFlows);
+    this.locations = locations;
+    this.partialFingerprints = primaryLocationLineHash == null ? null : new PartialFingerprints(primaryLocationLineHash);
+    this.codeFlows = codeFlows == null ? null : List.copyOf(codeFlows);
   }
 
   public String getRuleId() {
@@ -43,10 +59,12 @@ public class Result {
     return locations;
   }
 
+  @CheckForNull
   public PartialFingerprints getPartialFingerprints() {
     return partialFingerprints;
   }
 
+  @CheckForNull
   public List<CodeFlow> getCodeFlows() {
     return codeFlows;
   }
@@ -56,17 +74,17 @@ public class Result {
   }
 
   public static final class ResultBuilder {
-    private RuleKey ruleKey;
+    private String ruleId;
     private String message;
-    private Location location;
+    private LinkedHashSet<Location> locations;
     private String hash;
     private List<CodeFlow> codeFlows;
 
     private ResultBuilder() {
     }
 
-    public ResultBuilder ruleKey(RuleKey ruleKey) {
-      this.ruleKey = ruleKey;
+    public ResultBuilder ruleId(String ruleId) {
+      this.ruleId = ruleId;
       return this;
     }
 
@@ -75,8 +93,8 @@ public class Result {
       return this;
     }
 
-    public ResultBuilder locations(Location location) {
-      this.location = location;
+    public ResultBuilder locations(Set<Location> locations) {
+      this.locations = new LinkedHashSet<>(locations);
       return this;
     }
 
@@ -91,7 +109,7 @@ public class Result {
     }
 
     public Result build() {
-      return new Result(ruleKey, message, location, hash, codeFlows);
+      return new Result(ruleId, message, locations, hash, codeFlows);
     }
   }
 }
index ce234118159565ea1fc9fec1b20abc62afb1f103..eb4e140ae647e59834a3e4cf35e063bfc2e80045 100644 (file)
@@ -1,13 +1,26 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.google.gson.annotations.SerializedName;
 import java.util.Objects;
-import org.sonar.api.rule.RuleKey;
 
 public class Rule {
   @SerializedName("id")
@@ -23,12 +36,12 @@ public class Rule {
   @SerializedName("properties")
   private final PropertiesBag properties;
 
-  public Rule(RuleKey ruleKey, String ruleName, String ruleDescription, PropertiesBag properties) {
-    id = ruleKey.toString();
-    name = ruleKey.toString();
-    shortDescription = WrappedText.of(ruleName);
-    fullDescription = WrappedText.of(ruleName);
-    help = WrappedText.of(ruleDescription);
+  private Rule(String id, String name, WrappedText shortDescription, WrappedText fullDescription, WrappedText help, PropertiesBag properties) {
+    this.id = id;
+    this.name = name;
+    this.shortDescription = shortDescription;
+    this.fullDescription = fullDescription;
+    this.help = help;
     this.properties = properties;
   }
 
@@ -73,4 +86,53 @@ public class Rule {
     return Objects.hash(id);
   }
 
+  public static RuleBuilder builder() {
+    return new RuleBuilder();
+  }
+
+  public static final class RuleBuilder {
+    private String id;
+    private String name;
+    private WrappedText shortDescription;
+    private WrappedText fullDescription;
+    private WrappedText help;
+    private PropertiesBag properties;
+
+    private RuleBuilder() {
+    }
+
+    public RuleBuilder id(String id) {
+      this.id = id;
+      return this;
+    }
+
+    public RuleBuilder name(String name) {
+      this.name = name;
+      return this;
+    }
+
+    public RuleBuilder shortDescription(String shortDescription) {
+      this.shortDescription = WrappedText.of(shortDescription);
+      return this;
+    }
+
+    public RuleBuilder fullDescription(String fullDescription) {
+      this.fullDescription = WrappedText.of(fullDescription);
+      return this;
+    }
+
+    public RuleBuilder help(String help) {
+      this.help = WrappedText.of(help);
+      return this;
+    }
+
+    public RuleBuilder properties(PropertiesBag properties) {
+      this.properties = properties;
+      return this;
+    }
+
+    public Rule build() {
+      return new Rule(id, name, shortDescription, fullDescription, help, properties);
+    }
+  }
 }
index ae8cc0a78e4cb5e99f148c3357a9e834cb992167..ebcf81055fa24f478e7f0412968370b4599770ba 100644 (file)
@@ -1,21 +1,31 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.google.common.annotations.VisibleForTesting;
 import com.google.gson.annotations.SerializedName;
 import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 public class Run {
 
-  @VisibleForTesting
-  public static final String LANGUAGE_EN_US = "en-us";
-  @VisibleForTesting
-  public static final String COLUMN_KIND = "utf16CodeUnits";
-
   @SerializedName("tool")
   private final Tool tool;
   @SerializedName("results")
@@ -25,21 +35,19 @@ public class Run {
   @SerializedName("columnKind")
   private final String columnKind;
 
-  public Run(Tool tool, Set<Result> results) {
-    this(tool, results, LANGUAGE_EN_US, COLUMN_KIND);
-  }
-
-  private Run(Tool tool, Set<Result> results, String language, String columnKind) {
+  private Run(Tool tool, Set<Result> results, @Nullable String language, @Nullable String columnKind) {
     this.tool = tool;
     this.results = Set.copyOf(results);
     this.language = language;
     this.columnKind = columnKind;
   }
 
+  @CheckForNull
   public String getLanguage() {
     return language;
   }
 
+  @CheckForNull
   public String getColumnKind() {
     return columnKind;
   }
@@ -51,4 +59,43 @@ public class Run {
   public Set<Result> getResults() {
     return results;
   }
+
+  public static RunBuilder builder() {
+    return new RunBuilder();
+  }
+
+  public static final class RunBuilder {
+    private Tool tool;
+    private Set<Result> results;
+    private String language;
+    private String columnKind;
+
+    private RunBuilder() {
+    }
+
+    public RunBuilder tool(Tool tool) {
+      this.tool = tool;
+      return this;
+    }
+
+    public RunBuilder results(Set<Result> results) {
+      this.results = results;
+      return this;
+    }
+
+    public RunBuilder language(String language) {
+      this.language = language;
+      return this;
+    }
+
+    public RunBuilder columnKind(String columnKind) {
+      this.columnKind = columnKind;
+      return this;
+    }
+
+    public Run build() {
+      return new Run(tool, results, language, columnKind);
+    }
+  }
+
 }
index d19c386461fe4f9ecb482c27180e7262704a748d..0df616af2e223fbe3dfd62c8d5c7842cb46f5389 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
@@ -11,8 +25,6 @@ import java.util.Set;
 
 public class Sarif210 {
 
-  @VisibleForTesting
-  public static final String SARIF_SCHEMA_URL = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json";
   @VisibleForTesting
   public static final String SARIF_VERSION = "2.1.0";
 
@@ -23,13 +35,9 @@ public class Sarif210 {
   @SerializedName("runs")
   private final Set<Run> runs;
 
-  public Sarif210(Run run) {
-    this(SARIF_SCHEMA_URL, SARIF_VERSION, run);
-  }
-
-  private Sarif210(String schema, String version, Run run) {
+  public Sarif210(String schema, Run run) {
     this.schema = schema;
-    this.version = version;
+    this.version = SARIF_VERSION;
     this.runs = Set.of(run);
   }
 
index 62c2452d896fd87567e164e538e5b0cf96374c4a..1f984da096ef7620387da70bee29b267aa56a727 100644 (file)
@@ -1,49 +1,29 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.google.common.annotations.VisibleForTesting;
-import com.google.gson.Gson;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.Base64;
-import java.util.zip.GZIPOutputStream;
-import javax.inject.Inject;
-import org.sonar.api.ce.ComputeEngineSide;
+import java.nio.file.Path;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
+public interface SarifSerializer {
 
-@ComputeEngineSide
-public class SarifSerializer {
-  private final Gson gson;
+  String serialize(Sarif210 sarif210);
 
-  @Inject
-  public SarifSerializer() {
-    this(new Gson());
-  }
-
-  @VisibleForTesting
-  SarifSerializer(Gson gson) {
-    this.gson = gson;
-  }
-
-  public String serializeAndEncode(Sarif210 sarif210) {
-    String serializedSarif = gson.toJson(sarif210);
-    return compressToGzipAndEncodeBase64(serializedSarif);
-  }
-
-  private static String compressToGzipAndEncodeBase64(String input) {
-    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-      GZIPOutputStream gzipStream = new GZIPOutputStream(outputStream)) {
-      gzipStream.write(input.getBytes(UTF_8));
-      gzipStream.finish();
-      return Base64.getEncoder().encodeToString(outputStream.toByteArray());
-    } catch (IOException e) {
-      throw new UncheckedIOException(String.format("Failed to compress and encode the input: %s", input), e);
-    }
-  }
+  Sarif210 deserialize(Path sarifPath);
 }
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
new file mode 100644 (file)
index 0000000..3f33419
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.google.common.annotations.VisibleForTesting;
+import com.google.gson.Gson;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonSyntaxException;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.file.Path;
+import javax.inject.Inject;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.scanner.ScannerSide;
+
+import static java.lang.String.format;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.Files.newBufferedReader;
+
+@ScannerSide
+@ComputeEngineSide
+public class SarifSerializerImpl implements SarifSerializer {
+  private static final String SARIF_REPORT_ERROR = "Failed to read SARIF report at '%s'";
+  private static final String SARIF_JSON_SYNTAX_ERROR = SARIF_REPORT_ERROR + ": invalid JSON syntax";
+
+  private final Gson gson;
+
+  @Inject
+  public SarifSerializerImpl() {
+    this(new Gson());
+  }
+
+  @VisibleForTesting
+  SarifSerializerImpl(Gson gson) {
+    this.gson = gson;
+  }
+
+  @Override
+  public String serialize(Sarif210 sarif210) {
+    return gson.toJson(sarif210);
+  }
+
+  @Override
+  public Sarif210 deserialize(Path reportPath) {
+    try (Reader reader = newBufferedReader(reportPath, UTF_8)) {
+      return gson.fromJson(reader, Sarif210.class);
+    } 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);
+    }
+  }
+}
index 45d75c0bdc0f6ad9acd40464b48d5b3a201e99e1..e8cd950fda6ce5086de5869d8bba1b360c872286 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index 3990f8447f8b41336c47c28ad1dbfcda3c511197..7a10feea395bb58374bc80367f898e0b7586e2c4 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index ddf259342f618af5c8f47bd5035dbf0d1097a50d..06539aaa6d034669caa7eb3d0cbe2e3f28fc9b69 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index e579f8c35716b1381f3254658902789f06694355..6714bbcb3e17a5c8710d583c05e418094b33fc94 100644 (file)
@@ -1,17 +1,28 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.Set;
-import org.apache.commons.lang.RandomStringUtils;
 import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.core.sarif.PropertiesBag;
-import org.sonar.core.sarif.Rule;
 
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
 import static org.assertj.core.api.Assertions.assertThat;
 
 
@@ -19,34 +30,51 @@ public class RuleTest {
 
   @Test
   public void equals_matchOnlyOnId() {
-    Rule rule1 = createRule("rep1", "rule1");
-    Rule rule1Bis = createRule("rep1", "rule1");
-    Rule rule2 = withRuleId(rule1, "rep1", "rule2");
+    Rule rule1 = createRule();
+    Rule rule1Bis = createRule(rule1.getId()) ;
+    Rule rule2 = withRuleId(rule1, rule1.getId() + randomAlphanumeric(3));
 
     assertThat(rule1).isEqualTo(rule1Bis).isNotEqualTo(rule2);
   }
 
   @Test
   public void equals_notMatchWithNull(){
-    Rule rule1 = createRule("rep1", "rule2");
+    Rule rule1 = createRule();
 
     assertThat(rule1).isNotEqualTo(null);
   }
 
   @Test
   public void equals_matchWithSameObject(){
-    Rule rule1 = createRule("rep5", "rule2");
+    Rule rule1 = createRule();
 
     assertThat(rule1).isEqualTo(rule1);
   }
 
-  private static Rule withRuleId(Rule rule, String repoName, String ruleName) {
-    return new Rule(RuleKey.of(repoName, ruleName), rule.getName(), rule.getFullDescription().getText(), rule.getProperties());
+  private static Rule withRuleId(Rule rule, String id) {
+    return Rule.builder()
+      .id(id)
+      .name(rule.getName())
+      .shortDescription(rule.getName())
+      .fullDescription(rule.getName())
+      .help(rule.getFullDescription().getText())
+      .properties(rule.getProperties())
+      .build();
   }
 
-  private static Rule createRule(String repoName, String ruleName) {
-    return new Rule(RuleKey.of(repoName, ruleName), RandomStringUtils.randomAlphanumeric(5), RandomStringUtils.randomAlphanumeric(5),
-      PropertiesBag.of(RandomStringUtils.randomAlphanumeric(3), Set.of(RandomStringUtils.randomAlphanumeric(4))));
+  private static Rule createRule() {
+    return createRule(randomAlphanumeric(5));
+  }
+
+  private static Rule createRule(String id) {
+    return Rule.builder()
+      .id(id)
+      .name(randomAlphanumeric(5))
+      .shortDescription(randomAlphanumeric(5))
+      .fullDescription(randomAlphanumeric(10))
+      .help(randomAlphanumeric(10))
+      .properties(PropertiesBag.of(randomAlphanumeric(3), Set.of(randomAlphanumeric(4))))
+      .build();
   }
 
 }
index ef40fe8dcd51641b7b4cbdd29b4c4590d6c5d7b8..e603ada42e92d29d6d746d1344c7c2ffb766ee96 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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;
 
index e239a5e859f20cbc879aa176e9b70c10cff7dddd..8350226ce752554fe47fbc5b42d168d0a34a8f4b 100644 (file)
 /*
- * Copyright (C) 2017-2022 SonarSource SA
- * All rights reserved
+ * 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.google.gson.Gson;
-import org.sonar.core.sarif.Sarif210;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+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;
 
 @RunWith(MockitoJUnitRunner.class)
 public class SarifSerializerTest {
 
-  private static final String SARIF_JSON = "{\"message\" : \"A sarif in json format as String.\"}";
-  private static final String SARIF_JSON_ENCODED = "H4sIAAAAAAAAAKtWyk0tLk5MT1VSsFJQclQoTizKTFPIzFPIKs7PU0jLL8pNLFFILFYILinKzEvXU6oFACgK7/YxAAAA";
+  private static final String SARIF_JSON = "{\"version\":\"2.1.0\",\"$schema\":\"http://json.schemastore.org/sarif-2.1.0-rtm.4\",\"runs\":[{\"results\":[]}]}";
 
-  @Mock
-  private Gson gson;
+  private final SarifSerializerImpl serializer = new SarifSerializerImpl();
 
-  @InjectMocks
-  private SarifSerializer serializer;
+  @Test
+  public void serialize() {
+    Run.builder().results(Set.of()).build();
+    Sarif210 sarif210 = new Sarif210("http://json.schemastore.org/sarif-2.1.0-rtm.4", Run.builder().results(Set.of()).build());
+
+    String result = serializer.serialize(sarif210);
+
+    assertThat(result).isEqualTo(SARIF_JSON);
+  }
+
+  @Test
+  public void deserialize() throws URISyntaxException {
+    URL sarifResource = requireNonNull(getClass().getResource("eslint-sarif210.json"));
+    Path sarif = Paths.get(sarifResource.toURI());
+
+    Sarif210 deserializationResult = serializer.deserialize(sarif);
+
+    verifySarif(deserializationResult);
+  }
 
   @Test
-  public void serializeAndEncode_should_compressInGZipAndEncodeBase64() {
-    when(gson.toJson(any(Sarif210.class))).thenReturn(SARIF_JSON);
-    Sarif210 sarif210 = mock(Sarif210.class);
+  public void deserialize_shouldFail_whenFileCantBeFound() {
+    String file = "wrongPathToFile";
+    Path sarif = Paths.get(file);
+
+    assertThatThrownBy(() -> serializer.deserialize(sarif))
+      .isInstanceOf(IllegalStateException.class)
+      .hasMessage(String.format("Failed to read SARIF report at '%s'", file));
+  }
+
+  @Test
+  public void deserialize_shouldFail_whenJsonSyntaxIsIncorrect() throws URISyntaxException {
+    URL sarifResource = requireNonNull(getClass().getResource("invalid-json-syntax.json"));
+    Path sarif = Paths.get(sarifResource.toURI());
+
+    assertThatThrownBy(() -> serializer.deserialize(sarif))
+      .isInstanceOf(IllegalStateException.class)
+      .hasMessage(String.format("Failed to read SARIF report at '%s': invalid JSON syntax", sarif));
+  }
+
+  private void verifySarif(Sarif210 deserializationResult) {
+    Sarif210 expected = buildExpectedSarif210();
+
+    assertThat(deserializationResult).isNotNull();
+    assertThat(deserializationResult).usingRecursiveComparison().ignoringFields("runs").isEqualTo(expected);
+
+    Run run = getRun(deserializationResult);
+    Run expectedRun = getRun(expected);
+    assertThat(run).usingRecursiveComparison().ignoringFields("results", "tool.driver.rules").isEqualTo(expectedRun);
+
+    Result result = getResult(run);
+    Result expectedResult = getResult(expectedRun);
+    assertThat(result).usingRecursiveComparison().isEqualTo(expectedResult);
 
-    String encoded = serializer.serializeAndEncode(sarif210);
+    Rule rule = getRule(run);
+    Rule expectedRule = getRule(expectedRun);
+    assertThat(rule).usingRecursiveComparison().ignoringFields("properties").isEqualTo(expectedRule);
+  }
+
+  private static Sarif210 buildExpectedSarif210() {
+    return new Sarif210("http://json.schemastore.org/sarif-2.1.0-rtm.4", buildExpectedRun());
+  }
+
+  private static Run buildExpectedRun() {
+    Tool tool = new Tool(buildExpectedDriver());
+    return Run.builder()
+      .tool(tool)
+      .results(Set.of(buildExpectedResult())).build();
+  }
+
+  private static Driver buildExpectedDriver() {
+    return Driver.builder()
+      .name("ESLint")
+      .rules(Set.of(buildExpectedRule()))
+      .build();
+  }
+
+  private static Rule buildExpectedRule() {
+    return Rule.builder()
+      .id("no-unused-vars")
+      .shortDescription("disallow unused variables")
+      .build();
+  }
+
+  private static Result buildExpectedResult() {
+    return Result.builder()
+      .ruleId("no-unused-vars")
+      .locations(Set.of(buildExpectedLocation()))
+      .message("'x' is assigned a value but never used.")
+      .level("error")
+      .build();
+  }
+
+  private static Location buildExpectedLocation() {
+    ArtifactLocation artifactLocation = new ArtifactLocation(null, "file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js");
+    PhysicalLocation physicalLocation = PhysicalLocation.of(artifactLocation, buildExpectedRegion());
+    return Location.of(physicalLocation);
+  }
+
+  private static Region buildExpectedRegion() {
+    return Region.builder()
+      .startLine(1)
+      .startColumn(5)
+      .build();
+  }
+
+  private static Run getRun(Sarif210 sarif210) {
+    return sarif210.getRuns().stream().findFirst().orElseGet(() -> fail("runs property is missing"));
+  }
+
+  private static Result getResult(Run run) {
+    return run.getResults().stream().findFirst().orElseGet(() -> fail("results property is missing"));
+  }
 
-    assertThat(encoded).isEqualTo(SARIF_JSON_ENCODED);
+  private static Rule getRule(Run run) {
+    return run.getTool().getDriver().getRules().stream().findFirst().orElseGet(() -> fail("rules property is missing"));
   }
 
 }
diff --git a/sonar-core/src/test/resources/org/sonar/core/sarif/eslint-sarif210.json b/sonar-core/src/test/resources/org/sonar/core/sarif/eslint-sarif210.json
new file mode 100644 (file)
index 0000000..3d7d344
--- /dev/null
@@ -0,0 +1,57 @@
+{
+  "version": "2.1.0",
+  "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.4",
+  "runs": [
+    {
+      "tool": {
+        "driver": {
+          "name": "ESLint",
+          "informationUri": "https://eslint.org",
+          "rules": [
+            {
+              "id": "no-unused-vars",
+              "shortDescription": {
+                "text": "disallow unused variables"
+              },
+              "helpUri": "https://eslint.org/docs/rules/no-unused-vars",
+              "properties": {
+                "category": "Variables"
+              }
+            }
+          ]
+        }
+      },
+      "artifacts": [
+        {
+          "location": {
+            "uri": "file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js"
+          }
+        }
+      ],
+      "results": [
+        {
+          "level": "error",
+          "message": {
+            "text": "'x' is assigned a value but never used."
+          },
+          "locations": [
+            {
+              "physicalLocation": {
+                "artifactLocation": {
+                  "uri": "file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js",
+                  "index": 0
+                },
+                "region": {
+                  "startLine": 1,
+                  "startColumn": 5
+                }
+              }
+            }
+          ],
+          "ruleId": "no-unused-vars",
+          "ruleIndex": 0
+        }
+      ]
+    }
+  ]
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/sarif/invalid-json-syntax.json b/sonar-core/src/test/resources/org/sonar/core/sarif/invalid-json-syntax.json
new file mode 100644 (file)
index 0000000..eff20eb
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "version": "2.1.0",
+  "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.4",
+  "runs":
+    {
+
+    }
+}
index 9241acae7ab8a0785147fea3f7b39b7f013318a1..41144e68466e879903bfb74dc0cf87171d7711cb 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.List;
 import org.sonar.core.component.DefaultResourceTypes;
 import org.sonar.core.config.CorePropertyDefinitions;
+import org.sonar.core.sarif.SarifSerializerImpl;
 import org.sonar.scanner.cpd.JavaCpdBlockIndexerSensor;
 import org.sonar.scanner.deprecated.test.TestPlanBuilder;
 import org.sonar.scanner.externalissue.ExternalIssuesImportSensor;
@@ -53,6 +54,7 @@ public class BatchComponents {
     // External issues
     components.add(ExternalIssuesImportSensor.class);
     components.add(ExternalIssuesImportSensor.properties());
+    components.add(SarifSerializerImpl.class);
 
     return components;
   }