import org.sonar.scanner.externalissue.sarif.LocationMapper;
import org.sonar.scanner.externalissue.sarif.RegionMapper;
import org.sonar.scanner.externalissue.sarif.ResultMapper;
+import org.sonar.scanner.externalissue.sarif.RuleMapper;
import org.sonar.scanner.externalissue.sarif.RunMapper;
import org.sonar.scanner.externalissue.sarif.SarifIssuesImportSensor;
import org.sonar.scanner.genericcoverage.GenericCoverageSensor;
components.add(ResultMapper.class);
components.add(LocationMapper.class);
components.add(RegionMapper.class);
+ components.add(RuleMapper.class);
return components;
}
import java.util.List;
import java.util.Set;
-import javax.annotation.CheckForNull;
-import org.sonar.api.batch.sensor.issue.NewExternalIssue;
-import org.sonar.api.scanner.ScannerSide;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.sensor.issue.NewExternalIssue;
+import org.sonar.api.batch.sensor.rule.NewAdHocRule;
+import org.sonar.api.scanner.ScannerSide;
import org.sonar.core.sarif.Run;
import org.sonar.core.sarif.Sarif210;
+import org.sonar.scanner.externalissue.sarif.RunMapper.RunMapperResult;
import static java.util.Objects.requireNonNull;
Set<Run> runs = requireNonNull(sarif210.getRuns(), "The runs section of the Sarif report is null");
for (Run run : runs) {
- List<NewExternalIssue> newExternalIssues = toNewExternalIssues(run);
- if (newExternalIssues == null) {
- failedRuns += 1;
- } else {
+ RunMapperResult runMapperResult = tryMapRun(run);
+ if (runMapperResult.isSuccess()) {
+ List<NewAdHocRule> newAdHocRules = runMapperResult.getNewAdHocRules();
+ newAdHocRules.forEach(NewAdHocRule::save);
+
+ List<NewExternalIssue> newExternalIssues = runMapperResult.getNewExternalIssues();
successFullyImportedRuns += 1;
successFullyImportedIssues += newExternalIssues.size();
newExternalIssues.forEach(NewExternalIssue::save);
+ } else {
+ failedRuns += 1;
}
}
return SarifImportResults.builder()
.build();
}
- @CheckForNull
- private List<NewExternalIssue> toNewExternalIssues(Run run) {
+ private RunMapperResult tryMapRun(Run run) {
try {
return runMapper.mapRun(run);
} catch (Exception exception) {
LOG.warn("Failed to import a sarif run, error: {}", exception.getMessage());
- return null;
+ return new RunMapperResult().success(false);
+
}
}
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.issue.NewExternalIssue;
import org.sonar.api.batch.sensor.issue.NewIssueLocation;
+import org.sonar.api.issue.impact.SoftwareQuality;
+import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
import org.sonar.api.scanner.ScannerSide;
import org.sonar.core.sarif.Location;
import org.sonar.core.sarif.Result;
import static java.util.Objects.requireNonNull;
+import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.api.issue.impact.Severity.LOW;
+import static org.sonar.api.issue.impact.Severity.MEDIUM;
+import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
+import static org.sonar.api.rules.CleanCodeAttribute.CONVENTIONAL;
@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)
.put("none", Severity.INFO)
.build();
- private static final RuleType DEFAULT_TYPE = RuleType.VULNERABILITY;
+ private static final Map<String, org.sonar.api.issue.impact.Severity> IMPACT_SEVERITY_MAPPING = ImmutableMap.<String, org.sonar.api.issue.impact.Severity>builder()
+ .put("error", HIGH)
+ .put("warning", MEDIUM)
+ .put("note", LOW)
+ .put("none", LOW)
+ .build();
+
+ public static final Severity DEFAULT_SEVERITY = Severity.MAJOR;
+ public static final RuleType DEFAULT_TYPE = RuleType.VULNERABILITY;
+ public static final CleanCodeAttribute DEFAULT_CLEAN_CODE_ATTRIBUTE = CONVENTIONAL;
+ public static final SoftwareQuality DEFAULT_SOFTWARE_QUALITY = SECURITY;
+ public static final org.sonar.api.issue.impact.Severity DEFAULT_IMPACT_SEVERITY = MEDIUM;
private final SensorContext sensorContext;
private final LocationMapper locationMapper;
this.locationMapper = locationMapper;
}
- NewExternalIssue mapResult(String driverName, @Nullable String ruleSeverity, Result result) {
+ NewExternalIssue mapResult(String driverName, @Nullable String ruleSeverity, @Nullable String ruleSeverityForNewTaxonomy, Result result) {
NewExternalIssue newExternalIssue = sensorContext.newExternalIssue();
newExternalIssue.type(DEFAULT_TYPE);
newExternalIssue.engineId(driverName);
newExternalIssue.severity(toSonarQubeSeverity(ruleSeverity));
newExternalIssue.ruleId(requireNonNull(result.getRuleId(), "No ruleId found for issue thrown by driver " + driverName));
+ newExternalIssue.cleanCodeAttribute(DEFAULT_CLEAN_CODE_ATTRIBUTE);
+ newExternalIssue.addImpact(DEFAULT_SOFTWARE_QUALITY, toSonarQubeImpactSeverity(ruleSeverityForNewTaxonomy));
mapLocations(result, newExternalIssue);
return newExternalIssue;
}
- private static Severity toSonarQubeSeverity(@Nullable String ruleSeverity) {
+ protected static org.sonar.api.issue.impact.Severity toSonarQubeImpactSeverity(@Nullable String ruleSeverity) {
+ return IMPACT_SEVERITY_MAPPING.getOrDefault(ruleSeverity, DEFAULT_IMPACT_SEVERITY);
+ }
+
+ protected static Severity toSonarQubeSeverity(@Nullable String ruleSeverity) {
return SEVERITY_MAPPING.getOrDefault(ruleSeverity, DEFAULT_SEVERITY);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 javax.annotation.Nullable;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.rule.NewAdHocRule;
+import org.sonar.api.scanner.ScannerSide;
+import org.sonar.core.sarif.Rule;
+
+import static java.lang.String.join;
+
+@ScannerSide
+public class RuleMapper {
+
+ private final SensorContext sensorContext;
+
+ public RuleMapper(SensorContext sensorContext) {
+ this.sensorContext = sensorContext;
+ }
+
+ NewAdHocRule mapRule(Rule rule, String driverName, @Nullable String ruleSeverity, @Nullable String ruleSeverityForNewTaxonomy) {
+ return sensorContext.newAdHocRule()
+ .severity(ResultMapper.toSonarQubeSeverity(ruleSeverity))
+ .type(ResultMapper.DEFAULT_TYPE)
+ .ruleId(rule.getId())
+ .engineId(driverName)
+ .name(join(":", driverName, rule.getId()))
+ .cleanCodeAttribute(ResultMapper.DEFAULT_CLEAN_CODE_ATTRIBUTE)
+ .addDefaultImpact(ResultMapper.DEFAULT_SOFTWARE_QUALITY, ResultMapper.toSonarQubeImpactSeverity(ruleSeverityForNewTaxonomy));
+ }
+}
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nullable;
-import org.sonar.api.batch.rule.Severity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.core.sarif.DefaultConfiguration;
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_IMPACT_SEVERITY;
import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_SEVERITY;
public class RulesSeverityDetector {
private static final Logger LOG = LoggerFactory.getLogger(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 static final String UNSUPPORTED_RULE_SEVERITIES_WARNING = "Unable to detect rules severity for issue detected by tool {}, falling back to default rule severity: {}";
private RulesSeverityDetector() {}
return extensionDefinedRuleSeverities;
}
- LOG.warn(composeUnsupportedRuleSeveritiesDefinitionWarningMessage(driverName, DEFAULT_SEVERITY));
+ LOG.warn(UNSUPPORTED_RULE_SEVERITIES_WARNING, driverName, DEFAULT_SEVERITY.name());
+ return emptyMap();
+ }
+
+ public static Map<String, String> detectRulesSeveritiesForNewTaxonomy(Run run, String driverName) {
+ Map<String, String> driverDefinedRuleSeverities = getDriverDefinedRuleSeverities(run);
+
+ if (!driverDefinedRuleSeverities.isEmpty()) {
+ return driverDefinedRuleSeverities;
+ }
+
+ Map<String, String> extensionDefinedRuleSeverities = getExtensionsDefinedRuleSeverities(run);
+
+ if (!extensionDefinedRuleSeverities.isEmpty()) {
+ return extensionDefinedRuleSeverities;
+ }
+
+ LOG.warn(UNSUPPORTED_RULE_SEVERITIES_WARNING, driverName, DEFAULT_IMPACT_SEVERITY.name());
return emptyMap();
}
.isPresent();
}
- private static String composeUnsupportedRuleSeveritiesDefinitionWarningMessage(String driverName, Severity defaultSeverity) {
- return format(UNSUPPORTED_RULE_SEVERITIES_WARNING, driverName, defaultSeverity);
- }
}
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
import javax.annotation.Nullable;
-import org.sonar.api.batch.sensor.issue.NewExternalIssue;
-import org.sonar.api.scanner.ScannerSide;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.sensor.issue.NewExternalIssue;
+import org.sonar.api.batch.sensor.rule.NewAdHocRule;
+import org.sonar.api.scanner.ScannerSide;
import org.sonar.core.sarif.Driver;
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.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
import static org.sonar.api.utils.Preconditions.checkArgument;
+import static org.sonar.scanner.externalissue.sarif.RulesSeverityDetector.detectRulesSeverities;
+import static org.sonar.scanner.externalissue.sarif.RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy;
@ScannerSide
public class RunMapper {
private static final Logger LOG = LoggerFactory.getLogger(RunMapper.class);
private final ResultMapper resultMapper;
+ private final RuleMapper ruleMapper;
- RunMapper(ResultMapper resultMapper) {
+ RunMapper(ResultMapper resultMapper, RuleMapper ruleMapper) {
this.resultMapper = resultMapper;
+ this.ruleMapper = ruleMapper;
}
- List<NewExternalIssue> mapRun(Run run) {
+ RunMapperResult mapRun(Run run) {
if (run.getResults().isEmpty()) {
- return emptyList();
+ return new RunMapperResult();
}
+
String driverName = getToolDriverName(run);
- Map<String, String> ruleSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, driverName);
+ Map<String, String> ruleSeveritiesByRuleId = detectRulesSeverities(run, driverName);
+ Map<String, String> ruleSeveritiesByRuleIdForNewCCT = detectRulesSeveritiesForNewTaxonomy(run, driverName);
- return run.getResults()
- .stream()
- .map(result -> toNewExternalIssue(driverName, ruleSeveritiesByRuleId.get(result.getRuleId()), result))
- .filter(Optional::isPresent)
- .map(Optional::get)
- .collect(toList());
+ return new RunMapperResult()
+ .newAdHocRules(toNewAdHocRules(run, driverName, ruleSeveritiesByRuleId, ruleSeveritiesByRuleIdForNewCCT))
+ .newExternalIssues(toNewExternalIssues(run, driverName, ruleSeveritiesByRuleId, ruleSeveritiesByRuleIdForNewCCT));
}
private static String getToolDriverName(Run run) throws IllegalArgumentException {
return run.getTool().getDriver().getName();
}
- private Optional<NewExternalIssue> toNewExternalIssue(String driverName, @Nullable String ruleSeverity, Result result) {
+ private static boolean hasToolDriverNameDefined(Run run) {
+ return Optional.ofNullable(run)
+ .map(Run::getTool)
+ .map(Tool::getDriver)
+ .map(Driver::getName)
+ .isPresent();
+ }
+
+ private List<NewAdHocRule> toNewAdHocRules(Run run, String driverName, Map<String, String> ruleSeveritiesByRuleId, Map<String, String> ruleSeveritiesByRuleIdForNewCCT) {
+ Set<Rule> driverRules = run.getTool().getDriver().getRules();
+ Set<Rule> extensionRules = hasExtensions(run.getTool())
+ ? run.getTool().getExtensions().stream().flatMap(extension -> extension.getRules().stream()).collect(toSet())
+ : Set.of();
+ return Stream.concat(driverRules.stream(), extensionRules.stream())
+ .distinct()
+ .map(rule -> ruleMapper.mapRule(rule, driverName, ruleSeveritiesByRuleId.get(rule.getId()), ruleSeveritiesByRuleIdForNewCCT.get(rule.getId())))
+ .collect(toList());
+ }
+
+ private static boolean hasExtensions(Tool tool) {
+ return tool.getExtensions() != null && !tool.getExtensions().isEmpty();
+ }
+
+ private List<NewExternalIssue> toNewExternalIssues(Run run, String driverName, Map<String, String> ruleSeveritiesByRuleId, Map<String, String> ruleSeveritiesByRuleIdForNewCCT) {
+ return run.getResults()
+ .stream()
+ .map(result -> toNewExternalIssue(driverName, ruleSeveritiesByRuleId.get(result.getRuleId()), ruleSeveritiesByRuleIdForNewCCT.get(result.getRuleId()), result))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toList());
+ }
+
+ private Optional<NewExternalIssue> toNewExternalIssue(String driverName, @Nullable String ruleSeverity, @Nullable String ruleSeverityForNewTaxonomy, Result result) {
try {
- return Optional.of(resultMapper.mapResult(driverName, ruleSeverity, result));
+ return Optional.of(resultMapper.mapResult(driverName, ruleSeverity, ruleSeverityForNewTaxonomy, 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();
+ static class RunMapperResult {
+ private List<NewExternalIssue> newExternalIssues;
+ private List<NewAdHocRule> newAdHocRules;
+ private boolean success;
+
+ public RunMapperResult() {
+ this.newExternalIssues = emptyList();
+ this.newAdHocRules = emptyList();
+ this.success = true;
+ }
+
+ public RunMapperResult newExternalIssues(List<NewExternalIssue> newExternalIssues) {
+ this.newExternalIssues = newExternalIssues;
+ return this;
+ }
+
+ public RunMapperResult newAdHocRules(List<NewAdHocRule> newAdHocRules) {
+ this.newAdHocRules = newAdHocRules;
+ return this;
+ }
+
+ public RunMapperResult success(boolean success) {
+ this.success = success;
+ return this;
+ }
+
+ public List<NewExternalIssue> getNewExternalIssues() {
+ return newExternalIssues;
+ }
+
+ public List<NewAdHocRule> getNewAdHocRules() {
+ return newAdHocRules;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
}
+
}
import org.sonar.api.testfixtures.log.LogTester;
import org.sonar.core.sarif.Run;
import org.sonar.core.sarif.Sarif210;
+import org.sonar.scanner.externalissue.sarif.RunMapper.RunMapperResult;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
NewExternalIssue issue1run1 = mock(NewExternalIssue.class);
NewExternalIssue issue2run1 = mock(NewExternalIssue.class);
NewExternalIssue issue1run2 = mock(NewExternalIssue.class);
- when(runMapper.mapRun(run1)).thenReturn(List.of(issue1run1, issue2run1));
- when(runMapper.mapRun(run2)).thenReturn(List.of(issue1run2));
+ when(runMapper.mapRun(run1)).thenReturn(new RunMapperResult().newExternalIssues(List.of(issue1run1, issue2run1)));
+ when(runMapper.mapRun(run2)).thenReturn(new RunMapperResult().newExternalIssues(List.of(issue1run2)));
SarifImportResults sarifImportResults = sarif210Importer.importSarif(sarif210);
Exception testException = new RuntimeException("test");
when(runMapper.mapRun(run1)).thenThrow(testException);
NewExternalIssue issue1run2 = mock(NewExternalIssue.class);
- when(runMapper.mapRun(run2)).thenReturn(List.of(issue1run2));
+ when(runMapper.mapRun(run2)).thenReturn(new RunMapperResult().newExternalIssues(List.of(issue1run2)));
SarifImportResults sarifImportResults = sarif210Importer.importSarif(sarif210);
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_IMPACT_SEVERITY;
import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_SEVERITY;
+import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_SOFTWARE_QUALITY;
@RunWith(DataProviderRunner.class)
public class ResultMapperTest {
@Test
public void mapResult_mapsSimpleFieldsCorrectly() {
- NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, result);
+ NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, WARNING, result);
verify(newExternalIssue).type(RuleType.VULNERABILITY);
verify(newExternalIssue).engineId(DRIVER_NAME);
public void mapResult_ifRuleIdMissing_fails() {
when(result.getRuleId()).thenReturn(null);
assertThatNullPointerException()
- .isThrownBy(() -> resultMapper.mapResult(DRIVER_NAME, WARNING, result))
+ .isThrownBy(() -> resultMapper.mapResult(DRIVER_NAME, WARNING, WARNING, result))
.withMessage("No ruleId found for issue thrown by driver driverName");
}
Location location = mock(Location.class);
when(result.getLocations()).thenReturn(Set.of(location));
- NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, result);
+ NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, WARNING, result);
verify(locationMapper).fillIssueInFileLocation(result, newExternalIssueLocation, location);
verifyNoMoreInteractions(locationMapper);
when(result.getLocations()).thenReturn(Set.of(location));
when(locationMapper.fillIssueInFileLocation(any(), any(), any())).thenReturn(null);
- NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, result);
+ NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, WARNING, result);
verify(locationMapper).fillIssueInProjectLocation(result, newExternalIssueLocation);
verify(newExternalIssue).at(newExternalIssueLocation);
@Test
public void mapResult_whenLocationsIsEmpty_createsProjectLocation() {
- NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, result);
+ NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, WARNING, result);
verify(locationMapper).fillIssueInProjectLocation(result, newExternalIssueLocation);
verifyNoMoreInteractions(locationMapper);
@Test
public void mapResult_whenLocationsIsNull_createsProjectLocation() {
when(result.getLocations()).thenReturn(null);
- NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, result);
+ NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, WARNING, result);
verify(locationMapper).fillIssueInProjectLocation(result, newExternalIssueLocation);
verifyNoMoreInteractions(locationMapper);
verify(newExternalIssue, never()).addFlow(any());
}
- @Test
- public void mapResult_mapsErrorLevel_toCriticalSeverity() {
- NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, ERROR, result);
- verify(newExternalIssue).severity(Severity.CRITICAL);
- }
-
@DataProvider
public static Object[][] rule_severity_to_sonarqube_severity_mapping() {
return new Object[][] {
- {"error", Severity.CRITICAL},
- {"warning", Severity.MAJOR},
- {"note", Severity.MINOR},
- {"none", Severity.INFO},
- {"anything else", DEFAULT_SEVERITY},
- {null, DEFAULT_SEVERITY},
+ {"error", Severity.CRITICAL, org.sonar.api.issue.impact.Severity.HIGH},
+ {"warning", Severity.MAJOR, org.sonar.api.issue.impact.Severity.MEDIUM},
+ {"note", Severity.MINOR, org.sonar.api.issue.impact.Severity.LOW},
+ {"none", Severity.INFO, org.sonar.api.issue.impact.Severity.LOW},
+ {"anything else", DEFAULT_SEVERITY, DEFAULT_IMPACT_SEVERITY},
+ {null, DEFAULT_SEVERITY, DEFAULT_IMPACT_SEVERITY},
};
}
@Test
@UseDataProvider("rule_severity_to_sonarqube_severity_mapping")
- public void mapResult_mapsCorrectlyLevelToSeverity(String ruleSeverity, Severity sonarQubeSeverity) {
- NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, ruleSeverity, result);
+ public void mapResult_mapsCorrectlyLevelToSeverity(String ruleSeverity, Severity sonarQubeSeverity, org.sonar.api.issue.impact.Severity impactSeverity) {
+ NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, ruleSeverity, ruleSeverity, result);
verify(newExternalIssue).severity(sonarQubeSeverity);
+ verify(newExternalIssue).addImpact(DEFAULT_SOFTWARE_QUALITY, impactSeverity);
+ }
+
+ @Test
+ public void mapResult_mapsCorrectlyCleanCodeAttribute() {
+ NewExternalIssue newExternalIssue = resultMapper.mapResult(DRIVER_NAME, WARNING, WARNING, result);
+ verify(newExternalIssue).cleanCodeAttribute(ResultMapper.DEFAULT_CLEAN_CODE_ATTRIBUTE);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.rule.NewAdHocRule;
+import org.sonar.api.batch.sensor.rule.internal.DefaultAdHocRule;
+import org.sonar.core.sarif.Rule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(DataProviderRunner.class)
+public class RuleMapperTest {
+
+ private static final String WARNING = "warning";
+ private static final String RULE_ID = "test_rules_id";
+ private static final String DRIVER_NAME = "driverName";
+
+ @Mock
+ private SensorContext sensorContext;
+
+ @InjectMocks
+ RuleMapper ruleMapper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.openMocks(this);
+ when(sensorContext.newAdHocRule()).thenReturn(new DefaultAdHocRule());
+ }
+
+ @Test
+ public void mapRule_shouldCorrectlyMapToNewAdHocRule() {
+ Rule rule = Rule.builder().id(RULE_ID).build();
+ NewAdHocRule expected = new DefaultAdHocRule()
+ .severity(ResultMapper.DEFAULT_SEVERITY)
+ .type(ResultMapper.DEFAULT_TYPE)
+ .ruleId(RULE_ID)
+ .engineId(DRIVER_NAME)
+ .name(String.join(":", DRIVER_NAME, RULE_ID))
+ .cleanCodeAttribute(ResultMapper.DEFAULT_CLEAN_CODE_ATTRIBUTE)
+ .addDefaultImpact(ResultMapper.DEFAULT_SOFTWARE_QUALITY, org.sonar.api.issue.impact.Severity.MEDIUM);
+
+ NewAdHocRule result = ruleMapper.mapRule(rule, DRIVER_NAME, WARNING, WARNING);
+
+ assertThat(result).usingRecursiveComparison().isEqualTo(expected);
+ }
+
+}
import java.util.Set;
import org.assertj.core.api.Assertions;
import org.assertj.core.groups.Tuple;
+import org.junit.Before;
import org.junit.Test;
import org.slf4j.event.Level;
-import org.sonar.api.batch.rule.Severity;
import org.sonar.api.testfixtures.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.core.sarif.DefaultConfiguration;
import org.sonar.core.sarif.Driver;
import org.sonar.core.sarif.Extension;
import static org.assertj.core.api.AssertionsForClassTypes.tuple;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_IMPACT_SEVERITY;
import static org.sonar.scanner.externalissue.sarif.ResultMapper.DEFAULT_SEVERITY;
-import static org.sonar.scanner.externalissue.sarif.RulesSeverityDetector.UNSUPPORTED_RULE_SEVERITIES_WARNING;
public class RulesSeverityDetectorTest {
private static final String DRIVER_NAME = "Test";
private static final String RULE_ID = "RULE_ID";
@org.junit.Rule
- public LogTester logTester = new LogTester().setLevel(LoggerLevel.TRACE);
+ public LogTester logTester = new LogTester().setLevel(Level.TRACE);
private final Run run = mock(Run.class);
private final Rule rule = mock(Rule.class);
private final Extension extension = mock(Extension.class);
private final DefaultConfiguration defaultConfiguration = mock(DefaultConfiguration.class);
+ @Before
+ public void setUp() {
+ when(run.getResults()).thenReturn(Set.of(result));
+ when(run.getTool()).thenReturn(tool);
+ when(tool.getDriver()).thenReturn(driver);
+ }
+
+ // We keep this test for backward compatibility until we remove the deprecated severity
@Test
public void detectRulesSeverities_detectsCorrectlyResultDefinedRuleSeverities() {
Run run = mockResultDefinedRuleSeverities();
assertDetectedRuleSeverities(rulesSeveritiesByRuleId, tuple(RULE_ID, WARNING));
}
+ @Test
+ public void detectRulesSeveritiesForNewTaxonomy_shouldReturnsEmptyMapAndLogsWarning_whenOnlyResultDefinedRuleSeverities() {
+ Run run = mockResultDefinedRuleSeverities();
+
+ Map<String, String> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME);
+
+ assertWarningLog(DEFAULT_IMPACT_SEVERITY.name());
+ assertDetectedRuleSeverities(rulesSeveritiesByRuleId);
+ }
+
@Test
public void detectRulesSeverities_detectsCorrectlyDriverDefinedRuleSeverities() {
Run run = mockDriverDefinedRuleSeverities();
- Map<String, String> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, DRIVER_NAME);
+ Map<String, String> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME);
+
+ assertNoLogs();
+ assertDetectedRuleSeverities(rulesSeveritiesByRuleId, tuple(RULE_ID, WARNING));
+
+ // We keep this below for backward compatibility until we remove the deprecated severity
+ rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, DRIVER_NAME);
assertNoLogs();
assertDetectedRuleSeverities(rulesSeveritiesByRuleId, tuple(RULE_ID, WARNING));
public void detectRulesSeverities_detectsCorrectlyExtensionsDefinedRuleSeverities() {
Run run = mockExtensionsDefinedRuleSeverities();
- Map<String, String> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, DRIVER_NAME);
+ Map<String, String> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME);
+
+ assertNoLogs();
+ assertDetectedRuleSeverities(rulesSeveritiesByRuleId, tuple(RULE_ID, WARNING));
+
+ // We keep this below for backward compatibility until we remove the deprecated severity
+ rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, DRIVER_NAME);
assertNoLogs();
assertDetectedRuleSeverities(rulesSeveritiesByRuleId, tuple(RULE_ID, WARNING));
public void detectRulesSeverities_returnsEmptyMapAndLogsWarning_whenUnableToDetectSeverities() {
Run run = mockUnsupportedRuleSeveritiesDefinition();
- Map<String, String> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, DRIVER_NAME);
+ Map<String, String> rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, DRIVER_NAME);
+
+ assertWarningLog(DEFAULT_IMPACT_SEVERITY.name());
+ assertDetectedRuleSeverities(rulesSeveritiesByRuleId);
+
+ // We keep this below for backward compatibility until we remove the deprecated severity
+ rulesSeveritiesByRuleId = RulesSeverityDetector.detectRulesSeverities(run, DRIVER_NAME);
- assertWarningLog(DRIVER_NAME, DEFAULT_SEVERITY);
+ assertWarningLog(DEFAULT_SEVERITY.name());
assertDetectedRuleSeverities(rulesSeveritiesByRuleId);
}
when(run.getResults()).thenReturn(Set.of(result));
when(result.getLevel()).thenReturn(WARNING);
when(result.getRuleId()).thenReturn(RULE_ID);
-
return run;
}
private Run mockDriverDefinedRuleSeverities() {
- when(run.getTool()).thenReturn(tool);
- when(tool.getDriver()).thenReturn(driver);
when(driver.getRules()).thenReturn(Set.of(rule));
when(rule.getId()).thenReturn(RULE_ID);
when(rule.getDefaultConfiguration()).thenReturn(defaultConfiguration);
when(defaultConfiguration.getLevel()).thenReturn(WARNING);
-
return run;
}
private Run mockExtensionsDefinedRuleSeverities() {
- when(run.getTool()).thenReturn(tool);
- when(tool.getDriver()).thenReturn(driver);
when(driver.getRules()).thenReturn(Set.of());
when(tool.getExtensions()).thenReturn(Set.of(extension));
when(extension.getRules()).thenReturn(Set.of(rule));
when(rule.getId()).thenReturn(RULE_ID);
when(rule.getDefaultConfiguration()).thenReturn(defaultConfiguration);
when(defaultConfiguration.getLevel()).thenReturn(WARNING);
-
return run;
}
when(driver.getRules()).thenReturn(Set.of());
when(tool.getExtensions()).thenReturn(Set.of(extension));
when(extension.getRules()).thenReturn(Set.of());
-
return run;
}
.containsExactly(expectedSeverities);
}
- private void assertWarningLog(String driverName, Severity defaultSeverity) {
- assertThat(logTester.logs()).hasSize(1);
+ private void assertWarningLog(String defaultSeverity) {
assertThat(logTester.logs(Level.WARN))
- .containsOnly(format(UNSUPPORTED_RULE_SEVERITIES_WARNING, driverName, defaultSeverity));
+ .contains(format("Unable to detect rules severity for issue detected by tool %s, falling back to default rule severity: %s", DRIVER_NAME, defaultSeverity));
}
}
*/
package org.sonar.scanner.externalissue.sarif;
-import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.slf4j.event.Level;
import org.sonar.api.batch.sensor.issue.NewExternalIssue;
+import org.sonar.api.batch.sensor.rule.NewAdHocRule;
import org.sonar.api.testfixtures.log.LogTester;
+import org.sonar.core.sarif.Extension;
import org.sonar.core.sarif.Result;
import org.sonar.core.sarif.Run;
+import org.sonar.scanner.externalissue.sarif.RunMapper.RunMapperResult;
import static java.util.Collections.emptySet;
import static org.assertj.core.api.Assertions.assertThat;
@Mock
private ResultMapper resultMapper;
+ @Mock
+ private RuleMapper ruleMapper;
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Run run;
+ @Mock
+ private org.sonar.core.sarif.Rule rule;
+
@Rule
public LogTester logTester = new LogTester();
@InjectMocks
private RunMapper runMapper;
- @Test
- public void mapRun_delegatesToMapResult() {
+ @Before
+ public void setUp() {
when(run.getTool().getDriver().getName()).thenReturn(TEST_DRIVER);
+ when(run.getTool().getExtensions()).thenReturn(null);
+ when(rule.getId()).thenReturn(RULE_ID);
+ }
+
+ @Test
+ public void mapRun_shouldMapExternalIssues() {
Result result1 = mock(Result.class);
Result result2 = mock(Result.class);
when(run.getResults()).thenReturn(Set.of(result1, result2));
- NewExternalIssue externalIssue1 = mockMappedResult(result1);
- NewExternalIssue externalIssue2 = mockMappedResult(result2);
+ NewExternalIssue externalIssue1 = mockMappedExternalIssue(result1);
+ NewExternalIssue externalIssue2 = mockMappedExternalIssue(result2);
try (MockedStatic<RulesSeverityDetector> detector = mockStatic(RulesSeverityDetector.class)) {
detector.when(() -> RulesSeverityDetector.detectRulesSeverities(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
+ detector.when(() -> RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
- List<NewExternalIssue> newExternalIssues = runMapper.mapRun(run);
+ RunMapperResult runMapperResult = runMapper.mapRun(run);
- assertThat(newExternalIssues).containsOnly(externalIssue1, externalIssue2);
+ assertThat(runMapperResult.getNewExternalIssues()).containsOnly(externalIssue1, externalIssue2);
+ assertThat(logTester.logs()).isEmpty();
+ }
+ }
+
+ @Test
+ public void mapRun_shouldMapExternalRules_whenDriverHasRulesAndNoExtensions() {
+ when(run.getTool().getDriver().getRules()).thenReturn(Set.of(rule));
+ NewAdHocRule externalRule = mockMappedExternalRule();
+
+ try (MockedStatic<RulesSeverityDetector> detector = mockStatic(RulesSeverityDetector.class)) {
+ detector.when(() -> RulesSeverityDetector.detectRulesSeverities(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
+ detector.when(() -> RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
+
+ RunMapperResult runMapperResult = runMapper.mapRun(run);
+
+ assertThat(runMapperResult.getNewAdHocRules()).containsOnly(externalRule);
+ assertThat(logTester.logs()).isEmpty();
+ }
+ }
+
+ @Test
+ public void mapRun_shouldMapExternalRules_whenRulesInExtensions() {
+ when(run.getTool().getDriver().getRules()).thenReturn(Set.of());
+ Extension extension = mock(Extension.class);
+ when(extension.getRules()).thenReturn(Set.of(rule));
+ when(run.getTool().getExtensions()).thenReturn(Set.of(extension));
+ NewAdHocRule externalRule = mockMappedExternalRule();
+
+ try (MockedStatic<RulesSeverityDetector> detector = mockStatic(RulesSeverityDetector.class)) {
+ detector.when(() -> RulesSeverityDetector.detectRulesSeverities(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
+ detector.when(() -> RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
+
+ RunMapperResult runMapperResult = runMapper.mapRun(run);
+
+ assertThat(runMapperResult.getNewAdHocRules()).containsOnly(externalRule);
assertThat(logTester.logs()).isEmpty();
}
}
public void mapRun_ifRunIsEmpty_returnsEmptyList() {
when(run.getResults()).thenReturn(emptySet());
- List<NewExternalIssue> newExternalIssues = runMapper.mapRun(run);
+ RunMapperResult runMapperResult = runMapper.mapRun(run);
- assertThat(newExternalIssues).isEmpty();
+ assertThat(runMapperResult.getNewExternalIssues()).isEmpty();
}
@Test
public void mapRun_ifExceptionThrownByResultMapper_logsThemAndContinueProcessing() {
- when(run.getTool().getDriver().getName()).thenReturn(TEST_DRIVER);
Result result1 = mock(Result.class);
Result result2 = mock(Result.class);
when(run.getResults()).thenReturn(Set.of(result1, result2));
- NewExternalIssue externalIssue2 = mockMappedResult(result2);
+ NewExternalIssue externalIssue2 = mockMappedExternalIssue(result2);
when(result1.getRuleId()).thenReturn(RULE_ID);
- when(resultMapper.mapResult(TEST_DRIVER, WARNING, result1)).thenThrow(new IllegalArgumentException("test"));
+ when(resultMapper.mapResult(TEST_DRIVER, WARNING, WARNING, result1)).thenThrow(new IllegalArgumentException("test"));
try (MockedStatic<RulesSeverityDetector> detector = mockStatic(RulesSeverityDetector.class)) {
detector.when(() -> RulesSeverityDetector.detectRulesSeverities(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
+ detector.when(() -> RulesSeverityDetector.detectRulesSeveritiesForNewTaxonomy(run, TEST_DRIVER)).thenReturn(Map.of(RULE_ID, WARNING));
- List<NewExternalIssue> newExternalIssues = runMapper.mapRun(run);
+ RunMapperResult runMapperResult = runMapper.mapRun(run);
- assertThat(newExternalIssues)
+ assertThat(runMapperResult.getNewExternalIssues())
.containsExactly(externalIssue2);
assertThat(logTester.logs(Level.WARN)).containsOnly("Failed to import an issue raised by tool Test driver, error: test");
}
.withMessage("The run does not have a tool driver name defined.");
}
- private NewExternalIssue mockMappedResult(Result result) {
+ private NewExternalIssue mockMappedExternalIssue(Result result) {
NewExternalIssue externalIssue = mock(NewExternalIssue.class);
when(result.getRuleId()).thenReturn(RULE_ID);
- when(resultMapper.mapResult(TEST_DRIVER, WARNING, result)).thenReturn(externalIssue);
+ when(resultMapper.mapResult(TEST_DRIVER, WARNING, WARNING, result)).thenReturn(externalIssue);
return externalIssue;
}
+ private NewAdHocRule mockMappedExternalRule() {
+ NewAdHocRule externalRule = mock(NewAdHocRule.class);
+ when(ruleMapper.mapRule(rule, TEST_DRIVER, WARNING, WARNING)).thenReturn(externalRule);
+ return externalRule;
+ }
+
}