/*
- * 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;
}
/*
- * 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;
/*
- * 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;
@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;
return name;
}
+ @CheckForNull
public String getOrganization() {
return organization;
}
+ @CheckForNull
public String getSemanticVersion() {
return semanticVersion;
}
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);
+ }
+ }
}
/*
- * 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;
/*
- * 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;
/*
- * 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;
/*
- * 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;
/*
- * 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;
/*
- * 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;
}
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;
/*
- * 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")
@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() {
return locations;
}
+ @CheckForNull
public PartialFingerprints getPartialFingerprints() {
return partialFingerprints;
}
+ @CheckForNull
public List<CodeFlow> getCodeFlows() {
return codeFlows;
}
}
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;
}
return this;
}
- public ResultBuilder locations(Location location) {
- this.location = location;
+ public ResultBuilder locations(Set<Location> locations) {
+ this.locations = new LinkedHashSet<>(locations);
return this;
}
}
public Result build() {
- return new Result(ruleKey, message, location, hash, codeFlows);
+ return new Result(ruleId, message, locations, hash, codeFlows);
}
}
}
/*
- * 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")
@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;
}
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);
+ }
+ }
}
/*
- * 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")
@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;
}
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);
+ }
+ }
+
}
/*
- * 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;
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";
@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);
}
/*
- * 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);
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
/*
- * 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;
/*
- * 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;
/*
- * 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;
/*
- * 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;
@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();
}
}
/*
- * 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;
/*
- * 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"));
}
}
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "version": "2.1.0",
+ "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.4",
+ "runs":
+ {
+
+ }
+}
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;
// External issues
components.add(ExternalIssuesImportSensor.class);
components.add(ExternalIssuesImportSensor.properties());
+ components.add(SarifSerializerImpl.class);
return components;
}