diff options
author | Klaudio Sinani <klaudio.sinani@sonarsource.com> | 2022-11-25 12:01:31 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-11-28 11:29:35 +0000 |
commit | 92ce215524cf11c0dee49d2f4b6981497f197020 (patch) | |
tree | a14388e846d0418c80430eecb1aa350c84f05199 /sonar-scanner-engine/src/main/java/org/sonar | |
parent | 74d8a254ab6c9090f4d4abf4d5bc0490454a87b8 (diff) | |
download | sonarqube-92ce215524cf11c0dee49d2f4b6981497f197020.tar.gz sonarqube-92ce215524cf11c0dee49d2f4b6981497f197020.zip |
SONAR-17560 Improve SARIF rule vulnerability detection
Diffstat (limited to 'sonar-scanner-engine/src/main/java/org/sonar')
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(); + } } |