diff options
author | Léo Geoffroy <leo.geoffroy@sonarsource.com> | 2024-10-10 18:14:38 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-16 20:03:01 +0000 |
commit | e2cb22069d25733459a1d058be1a7c1f3ca370ef (patch) | |
tree | 8174db0874a9c60e1ed26ae7cea13fb025d6d9e6 /sonar-scanner-engine | |
parent | 5038a353ca5917a647e5613859fbf62e756f4a21 (diff) | |
download | sonarqube-e2cb22069d25733459a1d058be1a7c1f3ca370ef.tar.gz sonarqube-e2cb22069d25733459a1d058be1a7c1f3ca370ef.zip |
SONAR-23250 handle impacts on active rules in scanner engine
Diffstat (limited to 'sonar-scanner-engine')
6 files changed, 107 insertions, 4 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java index d09209bb2c7..53d04e5faa5 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java @@ -21,6 +21,7 @@ package org.sonar.scanner.issue; import java.util.ArrayList; import java.util.Collection; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -77,7 +78,7 @@ public class IssuePublisher { return false; } - ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.scannerId(), activeRule.severity()); + ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.scannerId(), activeRule.severity(), activeRule.impacts()); if (filters.accept(inputComponent, rawIssue)) { write(inputComponent.scannerId(), rawIssue); @@ -107,7 +108,8 @@ public class IssuePublisher { return str; } - private static ScannerReport.Issue createReportIssue(Issue issue, int componentRef, String activeRuleSeverity) { + private static ScannerReport.Issue createReportIssue(Issue issue, int componentRef, String activeRuleSeverity, + Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> activeRuleImpacts) { String primaryMessage = nullToEmpty(issue.primaryLocation().message()); org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity(); Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRuleSeverity); @@ -121,7 +123,10 @@ public class IssuePublisher { builder.setRuleKey(issue.ruleKey().rule()); builder.setMsg(primaryMessage); builder.addAllMsgFormatting(toProtobufMessageFormattings(issue.primaryLocation().messageFormattings())); - builder.addAllOverridenImpacts(toProtobufImpacts(issue.overridenImpacts())); + Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> overriddenImpacts = new EnumMap<>(issue.overridenImpacts()); + activeRuleImpacts.entrySet().forEach(e -> overriddenImpacts.putIfAbsent(e.getKey(), e.getValue())); + builder.addAllOverridenImpacts(toProtobufImpacts(overriddenImpacts)); + locationBuilder.setMsg(primaryMessage); locationBuilder.addAllMsgFormatting(toProtobufMessageFormattings(issue.primaryLocation().messageFormattings())); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java index 4028fbbcd9a..8e0691a069e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java @@ -29,6 +29,8 @@ import org.sonar.api.batch.rule.LoadedActiveRule; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.DefaultActiveRules; import org.sonar.api.batch.rule.internal.NewActiveRule; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -91,6 +93,12 @@ public class ActiveRulesProvider { } } + if (activeRule.getImpacts() != null) { + for (Map.Entry<SoftwareQuality, Severity> impact : activeRule.getImpacts().entrySet()) { + builder.setImpact(impact.getKey(), impact.getValue()); + } + } + return builder.build(); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java index 1bc34c0198c..9c2bb5d86d1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java @@ -21,6 +21,7 @@ package org.sonar.scanner.rule; import java.io.IOException; import java.io.InputStream; +import java.util.EnumMap; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -29,9 +30,13 @@ import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; import org.sonar.api.batch.rule.LoadedActiveRule; import org.sonar.api.impl.utils.ScannerUtils; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; +import org.sonar.core.rule.ImpactFormatter; import org.sonar.scanner.http.ScannerWsClient; +import org.sonarqube.ws.Common; import org.sonarqube.ws.Common.Paging; import org.sonarqube.ws.Rules; import org.sonarqube.ws.Rules.Active; @@ -109,6 +114,7 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader { loadedRule.setRuleKey(RuleKey.parse(r.getKey())); loadedRule.setName(r.getName()); loadedRule.setSeverity(active.getSeverity()); + loadedRule.setCreatedAt(DateUtils.dateToLong(DateUtils.parseDateTime(active.getCreatedAt()))); loadedRule.setUpdatedAt(DateUtils.dateToLong(DateUtils.parseDateTime(active.getUpdatedAt()))); loadedRule.setLanguage(r.getLang()); @@ -128,7 +134,15 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader { for (Param param : active.getParamsList()) { params.put(param.getKey(), param.getValue()); } + loadedRule.setParams(params); + + Map<SoftwareQuality, Severity> impacts = new EnumMap<>(SoftwareQuality.class); + for (Common.Impact impact : active.getImpacts().getImpactsList()) { + impacts.put(SoftwareQuality.valueOf(impact.getSoftwareQuality().name()), ImpactFormatter.mapImpactSeverity(impact.getSeverity())); + } + loadedRule.setImpacts(impacts); + loadedRule.setDeprecatedKeys(r.getDeprecatedKeys().getDeprecatedKeyList() .stream() .map(RuleKey::parse) @@ -138,4 +152,5 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader { return loadedRules; } + } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java index 5a58ff47540..0c4b12443ec 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java @@ -62,6 +62,7 @@ import static org.mockito.Mockito.when; import static org.sonar.api.batch.sensor.issue.MessageFormatting.Type.CODE; import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY; +import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY; @RunWith(MockitoJUnitRunner.class) public class IssuePublisherTest { @@ -149,7 +150,7 @@ public class IssuePublisherTest { ScannerReport.Impact impact1 = ScannerReport.Impact.newBuilder().setSoftwareQuality(MAINTAINABILITY.name()).setSeverity("HIGH").build(); ScannerReport.Impact impact2 = ScannerReport.Impact.newBuilder().setSoftwareQuality(RELIABILITY.name()).setSeverity("LOW").build(); - assertThat(argument.getValue().getOverridenImpactsList()).containsExactly(impact1, impact2); + assertThat(argument.getValue().getOverridenImpactsList()).containsExactlyInAnyOrder(impact1, impact2); } @Test @@ -243,6 +244,58 @@ public class IssuePublisherTest { ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class); verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture()); assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO); + assertThat(argument.getValue().getOverridenImpactsList()).isEmpty(); + } + + @Test + public void initAndAddIssue_whenImpactsOverriddenOnActiveRule_shouldOverrideIssue() { + activeRulesBuilder.addRule(new NewActiveRule.Builder() + .setRuleKey(NOSONAR_RULE_KEY) + .setSeverity(Severity.INFO) + .setImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH) + .setQProfileKey("qp-1") + .build()); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue(project) + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) + .forRule(NOSONAR_RULE_KEY); + when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true); + moduleIssues.initAndAddIssue(issue); + + ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class); + verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture()); + assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO); + assertThat(argument.getValue().getOverridenImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity) + .containsExactly(tuple(MAINTAINABILITY.name(), org.sonar.api.issue.impact.Severity.HIGH.name())); + } + + @Test + public void initAndAddIssue_whenImpactsOverriddenOnActiveRuleAndInIssue_shouldCombineOverriddenImpacts() { + activeRulesBuilder.addRule(new NewActiveRule.Builder() + .setRuleKey(NOSONAR_RULE_KEY) + .setSeverity(Severity.INFO) + .setImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH) + .setImpact(SECURITY, org.sonar.api.issue.impact.Severity.INFO) + .setQProfileKey("qp-1") + .build()); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue(project) + .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) + .overrideImpact(RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM) + .overrideImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW) + .forRule(NOSONAR_RULE_KEY); + when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true); + moduleIssues.initAndAddIssue(issue); + + ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class); + verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture()); + assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO); + assertThat(argument.getValue().getOverridenImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity) + .containsExactlyInAnyOrder(tuple(MAINTAINABILITY.name(), org.sonar.api.issue.impact.Severity.LOW.name()), + tuple(RELIABILITY.name(), org.sonar.api.issue.impact.Severity.MEDIUM.name()), + tuple(SECURITY.name(), org.sonar.api.issue.impact.Severity.INFO.name())); } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java index 32611fb0cf2..14a5036e254 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java @@ -25,11 +25,16 @@ import com.google.common.collect.ImmutableSet; import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.assertj.core.groups.Tuple; import org.junit.Test; +import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.LoadedActiveRule; import org.sonar.api.batch.rule.internal.DefaultActiveRules; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; @@ -50,6 +55,8 @@ public class ActiveRulesProviderTest { LoadedActiveRule r2 = mockRule("rule2"); LoadedActiveRule r3 = mockRule("rule3"); + r1.setImpacts(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)); + List<LoadedActiveRule> qp1Rules = ImmutableList.of(r1, r2); List<LoadedActiveRule> qp2Rules = ImmutableList.of(r2, r3); List<LoadedActiveRule> qp3Rules = ImmutableList.of(r1, r3); @@ -65,6 +72,13 @@ public class ActiveRulesProviderTest { assertThat(activeRules.findAll()).extracting("ruleKey").containsOnly( RuleKey.of("rule1", "rule1"), RuleKey.of("rule2", "rule2"), RuleKey.of("rule3", "rule3")); + Map<String, ActiveRule> activeRuleByKey = activeRules.findAll().stream().collect(Collectors.toMap(e -> e.ruleKey().rule(), e -> e)); + assertThat(activeRuleByKey.get("rule1").impacts()) + .containsExactlyInAnyOrderEntriesOf(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)); + + assertThat(activeRuleByKey.get("rule2").impacts()).isEmpty(); + assertThat(activeRuleByKey.get("rule3").impacts()).isEmpty(); + verify(loader).load("qp1"); verify(loader).load("qp2"); verify(loader).load("qp3"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java index bae17ee866d..ad70dd74d47 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java @@ -27,6 +27,7 @@ import java.util.stream.IntStream; import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.rule.LoadedActiveRule; +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.scanner.WsTestUtil; @@ -82,6 +83,10 @@ public class DefaultActiveRulesLoaderTest { .filteredOn(r -> r.getRuleKey().equals(EXAMPLE_KEY)) .extracting(LoadedActiveRule::getSeverity) .containsExactly(SEVERITY_VALUE); + assertThat(activeRules) + .filteredOn(r -> r.getRuleKey().equals(EXAMPLE_KEY)) + .extracting(LoadedActiveRule::getImpacts) + .containsExactlyInAnyOrder(Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH)); WsTestUtil.verifyCall(wsClient, urlOfPage(1)); WsTestUtil.verifyCall(wsClient, urlOfPage(2)); @@ -119,6 +124,9 @@ public class DefaultActiveRulesLoaderTest { if (EXAMPLE_KEY.equals(key)) { activeBuilder.addParams(Rules.Active.Param.newBuilder().setKey(FORMAT_KEY).setValue(FORMAT_VALUE)); activeBuilder.setSeverity(SEVERITY_VALUE); + activeBuilder.setImpacts(Rules.Impacts.newBuilder().addImpacts(Common.Impact.newBuilder() + .setSoftwareQuality(Common.SoftwareQuality.MAINTAINABILITY) + .setSeverity(Common.ImpactSeverity.HIGH).build()).build()); } ActiveList activeList = Rules.ActiveList.newBuilder().addActiveList(activeBuilder).build(); actives.putAllActives(Map.of(key.toString(), activeList)); |