aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine/src/main/java/org/sonar
diff options
context:
space:
mode:
authorKlaudio Sinani <klaudio.sinani@sonarsource.com>2022-11-25 12:01:31 +0100
committersonartech <sonartech@sonarsource.com>2022-11-28 11:29:35 +0000
commit92ce215524cf11c0dee49d2f4b6981497f197020 (patch)
treea14388e846d0418c80430eecb1aa350c84f05199 /sonar-scanner-engine/src/main/java/org/sonar
parent74d8a254ab6c9090f4d4abf4d5bc0490454a87b8 (diff)
downloadsonarqube-92ce215524cf11c0dee49d2f4b6981497f197020.tar.gz
sonarqube-92ce215524cf11c0dee49d2f4b6981497f197020.zip
SONAR-17560 Improve SARIF rule vulnerability detection
Diffstat (limited to 'sonar-scanner-engine/src/main/java/org/sonar')
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/ResultMapper.java12
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetector.java117
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RunMapper.java31
3 files changed, 143 insertions, 17 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/ResultMapper.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/ResultMapper.java
index 4bef14c9af1..a8c8b690750 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/ResultMapper.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/ResultMapper.java
@@ -19,7 +19,6 @@
*/
package org.sonar.scanner.externalissue.sarif;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Optional;
@@ -37,6 +36,7 @@ import static java.util.Objects.requireNonNull;
@ScannerSide
public class ResultMapper {
+ public static final Severity DEFAULT_SEVERITY = Severity.MAJOR;
private static final Map<String, Severity> SEVERITY_MAPPING = ImmutableMap.<String, Severity>builder()
.put("error", Severity.CRITICAL)
@@ -45,8 +45,6 @@ public class ResultMapper {
.put("none", Severity.INFO)
.build();
- @VisibleForTesting
- static final Severity DEFAULT_SEVERITY = Severity.MAJOR;
private static final RuleType DEFAULT_TYPE = RuleType.VULNERABILITY;
private final SensorContext sensorContext;
@@ -57,19 +55,19 @@ public class ResultMapper {
this.locationMapper = locationMapper;
}
- NewExternalIssue mapResult(String driverName, Result result) {
+ NewExternalIssue mapResult(String driverName, @Nullable String ruleSeverity, Result result) {
NewExternalIssue newExternalIssue = sensorContext.newExternalIssue();
newExternalIssue.type(DEFAULT_TYPE);
newExternalIssue.engineId(driverName);
- newExternalIssue.severity(toSonarQubeSeverity(result.getLevel()));
+ newExternalIssue.severity(toSonarQubeSeverity(ruleSeverity));
newExternalIssue.ruleId(requireNonNull(result.getRuleId(), "No ruleId found for issue thrown by driver " + driverName));
mapLocations(result, newExternalIssue);
return newExternalIssue;
}
- private static Severity toSonarQubeSeverity(@Nullable String level) {
- return SEVERITY_MAPPING.getOrDefault(level, DEFAULT_SEVERITY);
+ private static Severity toSonarQubeSeverity(@Nullable String ruleSeverity) {
+ return SEVERITY_MAPPING.getOrDefault(ruleSeverity, DEFAULT_SEVERITY);
}
private void mapLocations(Result result, NewExternalIssue newExternalIssue) {
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetector.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetector.java
new file mode 100644
index 00000000000..d889af0c1aa
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RulesSeverityDetector.java
@@ -0,0 +1,117 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.scanner.externalissue.sarif;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import javax.annotation.Nullable;
+import org.sonar.api.batch.rule.Severity;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.sarif.DefaultConfiguration;
+import org.sonar.core.sarif.Extension;
+import org.sonar.core.sarif.Result;
+import org.sonar.core.sarif.Rule;
+import org.sonar.core.sarif.Run;
+import org.sonar.core.sarif.Tool;
+
+import static java.lang.String.format;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static java.util.stream.Collectors.toMap;
+import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_SEVERITY;
+
+public class RulesSeverityDetector {
+ private static final Logger LOG = Loggers.get(RulesSeverityDetector.class);
+ public static final String UNSUPPORTED_RULE_SEVERITIES_WARNING = "Unable to detect rules severity for issue detected by tool %s, falling back to default rule severity: %s";
+
+ private RulesSeverityDetector() {}
+
+ public static Map<String, String> detectRulesSeverities(Run run, String driverName) {
+ Map<String, String> resultDefinedRuleSeverities = getResultDefinedRuleSeverities(run);
+
+ if (!resultDefinedRuleSeverities.isEmpty()) {
+ return resultDefinedRuleSeverities;
+ }
+
+ Map<String, String> driverDefinedRuleSeverities = getDriverDefinedRuleSeverities(run);
+
+ if (!driverDefinedRuleSeverities.isEmpty()) {
+ return driverDefinedRuleSeverities;
+ }
+
+ Map<String, String> extensionDefinedRuleSeverities = getExtensionsDefinedRuleSeverities(run);
+
+ if (!extensionDefinedRuleSeverities.isEmpty()) {
+ return extensionDefinedRuleSeverities;
+ }
+
+ LOG.warn(composeUnsupportedRuleSeveritiesDefinitionWarningMessage(driverName, DEFAULT_SEVERITY));
+ return emptyMap();
+ }
+
+ private static Map<String, String> getResultDefinedRuleSeverities(Run run) {
+ Predicate<Result> hasResultDefinedLevel = result -> Optional.ofNullable(result).map(Result::getLevel).isPresent();
+
+ return run.getResults()
+ .stream()
+ .filter(hasResultDefinedLevel)
+ .collect(toMap(Result::getRuleId, Result::getLevel, (x, y) -> y));
+ }
+
+ private static Map<String, String> getDriverDefinedRuleSeverities(Run run) {
+ return run.getTool().getDriver().getRules()
+ .stream()
+ .filter(RulesSeverityDetector::hasRuleDefinedLevel)
+ .collect(toMap(Rule::getId, x -> x.getDefaultConfiguration().getLevel()));
+ }
+
+ private static Map<String, String> getExtensionsDefinedRuleSeverities(Run run) {
+ return getExtensions(run)
+ .stream()
+ .map(Extension::getRules)
+ .filter(Objects::nonNull)
+ .flatMap(Collection::stream)
+ .filter(RulesSeverityDetector::hasRuleDefinedLevel)
+ .collect(toMap(Rule::getId, rule -> rule.getDefaultConfiguration().getLevel()));
+ }
+
+ private static Set<Extension> getExtensions(Run run) {
+ return Optional.of(run)
+ .map(Run::getTool)
+ .map(Tool::getExtensions)
+ .orElse(emptySet());
+ }
+
+ private static boolean hasRuleDefinedLevel(@Nullable Rule rule) {
+ return Optional.ofNullable(rule)
+ .map(Rule::getDefaultConfiguration)
+ .map(DefaultConfiguration::getLevel)
+ .isPresent();
+ }
+
+ private static String composeUnsupportedRuleSeveritiesDefinitionWarningMessage(String driverName, Severity defaultSeverity) {
+ return format(UNSUPPORTED_RULE_SEVERITIES_WARNING, driverName, defaultSeverity);
+ }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RunMapper.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RunMapper.java
index 623c04d5705..a9d616a02e4 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RunMapper.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/RunMapper.java
@@ -20,13 +20,17 @@
package org.sonar.scanner.externalissue.sarif;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import javax.annotation.Nullable;
import org.sonar.api.batch.sensor.issue.NewExternalIssue;
import org.sonar.api.scanner.ScannerSide;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.sarif.Driver;
import org.sonar.core.sarif.Result;
import org.sonar.core.sarif.Run;
+import org.sonar.core.sarif.Tool;
import static java.util.stream.Collectors.toList;
import static org.sonar.api.utils.Preconditions.checkArgument;
@@ -42,29 +46,36 @@ public class RunMapper {
}
List<NewExternalIssue> mapRun(Run run) {
- String driverName = getToolDriverNameOrThrow(run);
- return run.getResults().stream()
- .map(result -> toNewExternalIssue(driverName, result))
+ String driverName = getToolDriverName(run);
+ Map<String, String> ruleSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, driverName);
+
+ return run.getResults()
+ .stream()
+ .map(result -> toNewExternalIssue(driverName, ruleSeveritiesByRuleId.get(result.getRuleId()), result))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
}
- private static String getToolDriverNameOrThrow(Run run) {
- checkArgument(run.getTool() != null
- && run.getTool().getDriver() != null
- && run.getTool().getDriver().getName() != null,
- "The run does not have a tool driver name defined.");
+ private static String getToolDriverName(Run run) throws IllegalArgumentException {
+ checkArgument(hasToolDriverNameDefined(run), "The run does not have a tool driver name defined.");
return run.getTool().getDriver().getName();
}
- private Optional<NewExternalIssue> toNewExternalIssue(String driverName, Result result) {
+ private Optional<NewExternalIssue> toNewExternalIssue(String driverName, @Nullable String ruleSeverity, Result result) {
try {
- return Optional.of(resultMapper.mapResult(driverName, result));
+ return Optional.of(resultMapper.mapResult(driverName, ruleSeverity, result));
} catch (Exception exception) {
LOG.warn("Failed to import an issue raised by tool {}, error: {}", driverName, exception.getMessage());
return Optional.empty();
}
}
+ private static boolean hasToolDriverNameDefined(Run run) {
+ return Optional.ofNullable(run)
+ .map(Run::getTool)
+ .map(Tool::getDriver)
+ .map(Driver::getName)
+ .isPresent();
+ }
}