]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20021 Add clean code information to tainted vulnerability endpoint
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>
Tue, 15 Aug 2023 12:40:53 +0000 (14:40 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 18 Aug 2023 20:02:49 +0000 (20:02 +0000)
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/Rule.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleImpl.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactory.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/pushevent/TaintVulnerabilityRaised.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/pushevent/PushEventFactoryTest.java

index 53e414758254fba9aed8ce8f94fdfc4cdb23ded8..94c0e3eee9ac8addadc71a53b3de3b9d9481ab6e 100644 (file)
@@ -71,6 +71,5 @@ public interface Rule {
 
   Map<SoftwareQuality, Severity> getDefaultImpacts();
 
-  @CheckForNull
   CleanCodeAttribute cleanCodeAttribute();
 }
index 42c5d5c26c30c4e7fc53436a9b55047aa2c28adb..2299f409a7eaf879baa92b9c7b6b7f765048c3de 100644 (file)
@@ -153,7 +153,6 @@ public class RuleImpl implements Rule {
     return defaultImpacts;
   }
 
-  @CheckForNull
   @Override
   public CleanCodeAttribute cleanCodeAttribute() {
     return cleanCodeAttribute;
index 8c3a5c7580ed404901acee8c6ab0613eec90025c..58c5e0ddeeafb4197ced2e009f9a4a2032f9252c 100644 (file)
@@ -21,10 +21,15 @@ package org.sonar.ce.task.projectanalysis.pushevent;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import org.jetbrains.annotations.NotNull;
 import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rules.RuleType;
 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.ce.task.projectanalysis.component.Component;
@@ -139,9 +144,26 @@ public class PushEventFactory {
     event.setMainLocation(prepareMainLocation(component, issue));
     event.setFlows(flowGenerator.convertFlows(component.getName(), requireNonNull(issue.getLocations())));
     issue.getRuleDescriptionContextKey().ifPresent(event::setRuleDescriptionContextKey);
+
+    Rule rule = ruleRepository.getByKey(issue.getRuleKey());
+    event.setCleanCodeAttribute(rule.cleanCodeAttribute().name());
+    event.setCleanCodeAttributeCategory(rule.cleanCodeAttribute().getAttributeCategory().name());
+    event.setImpacts(computeEffectiveImpacts(rule.getDefaultImpacts(), issue.impacts()));
     return event;
   }
 
+  private static List<TaintVulnerabilityRaised.Impact> computeEffectiveImpacts(Map<SoftwareQuality, Severity> defaultImpacts, Map<SoftwareQuality, Severity> impacts) {
+    Map<SoftwareQuality, Severity> impactMap = new EnumMap<>(defaultImpacts);
+    impacts.forEach((softwareQuality, severity) -> impactMap.computeIfPresent(softwareQuality, (existingSoftwareQuality, existingSeverity) -> severity));
+    return impactMap.entrySet().stream()
+      .map(e -> {
+        TaintVulnerabilityRaised.Impact impact = new TaintVulnerabilityRaised.Impact();
+        impact.setSoftwareQuality(e.getKey().name());
+        impact.setSeverity(e.getValue().name());
+        return impact;
+      }).toList();
+  }
+
   private static Location prepareMainLocation(Component component, DefaultIssue issue) {
     DbIssues.Locations issueLocations = requireNonNull(issue.getLocations());
     TextRange mainLocationTextRange = getTextRange(issueLocations.getTextRange(), issueLocations.getChecksum());
index 003718bdfc4354d04ea63e8ba6464189fa637a64..30e57047e0ff43f345d5c6ea5fb53758aac47d4c 100644 (file)
@@ -36,6 +36,9 @@ public class TaintVulnerabilityRaised extends IssueEvent {
   private Location mainLocation;
   private List<Flow> flows;
   private String ruleDescriptionContextKey;
+  private String cleanCodeAttribute;
+  private String cleanCodeAttributeCategory;
+  private List<Impact> impacts;
 
   public TaintVulnerabilityRaised() {
     // nothing to do
@@ -109,4 +112,49 @@ public class TaintVulnerabilityRaised extends IssueEvent {
   public void setRuleDescriptionContextKey(String ruleDescriptionContextKey) {
     this.ruleDescriptionContextKey = ruleDescriptionContextKey;
   }
+
+  public String getCleanCodeAttribute() {
+    return cleanCodeAttribute;
+  }
+
+  public void setCleanCodeAttribute(String cleanCodeAttribute) {
+    this.cleanCodeAttribute = cleanCodeAttribute;
+  }
+
+  public String getCleanCodeAttributeCategory() {
+    return cleanCodeAttributeCategory;
+  }
+
+  public void setCleanCodeAttributeCategory(String cleanCodeAttributeCategory) {
+    this.cleanCodeAttributeCategory = cleanCodeAttributeCategory;
+  }
+
+  public List<Impact> getImpacts() {
+    return impacts;
+  }
+
+  public void setImpacts(List<Impact> impacts) {
+    this.impacts = impacts;
+  }
+
+  public static class Impact {
+    String softwareQuality;
+    String severity;
+
+    public String getSoftwareQuality() {
+      return softwareQuality;
+    }
+
+    public void setSoftwareQuality(String softwareQuality) {
+      this.softwareQuality = softwareQuality;
+    }
+
+    public String getSeverity() {
+      return severity;
+    }
+
+    public void setSeverity(String severity) {
+      this.severity = severity;
+    }
+  }
 }
index 028a0c11843ebce0edb1b619b8ddd54bb010f090..c76207cfec2060d6397fa0424ff21b6fc5630e1c 100644 (file)
@@ -23,11 +23,15 @@ import com.google.gson.Gson;
 import java.nio.charset.StandardCharsets;
 import java.util.Date;
 import java.util.Set;
+import org.assertj.core.groups.Tuple;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
@@ -39,6 +43,8 @@ import org.sonar.ce.task.projectanalysis.issue.RuleRepository;
 import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.protobuf.DbCommons;
 import org.sonar.db.protobuf.DbIssues;
 import org.sonar.db.rule.RuleDto;
@@ -47,6 +53,7 @@ import org.sonar.server.issue.TaintChecker;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.fail;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -104,11 +111,36 @@ public class PushEventFactoryTest {
     assertThat(taintVulnerabilityRaised.getRuleKey()).isEqualTo(defaultIssue.ruleKey().toString());
     assertThat(taintVulnerabilityRaised.getType()).isEqualTo(defaultIssue.type().name());
     assertThat(taintVulnerabilityRaised.getBranch()).isEqualTo(BRANCH_NAME);
+    assertThat(taintVulnerabilityRaised.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CONVENTIONAL.name());
+    assertThat(taintVulnerabilityRaised.getCleanCodeAttributeCategory()).isEqualTo(CleanCodeAttribute.CONVENTIONAL.getAttributeCategory().name());
+    assertThat(taintVulnerabilityRaised.getImpacts()).extracting(TaintVulnerabilityRaised.Impact::getSoftwareQuality, TaintVulnerabilityRaised.Impact::getSeverity)
+      .containsExactlyInAnyOrder(Tuple.tuple(SoftwareQuality.MAINTAINABILITY.name(), Severity.MEDIUM.name()),
+        Tuple.tuple(SoftwareQuality.RELIABILITY.name(), Severity.HIGH.name()));
+
     String ruleDescriptionContextKey = taintVulnerabilityRaised.getRuleDescriptionContextKey().orElseGet(() -> fail("No rule description " +
-      "context key"));
+                                                                                                                    "context key"));
     assertThat(ruleDescriptionContextKey).isEqualTo(defaultIssue.getRuleDescriptionContextKey().orElse(null));
   }
 
+  @Test
+  public void raiseEventOnIssue_whenNewTaintVulnerabilityWithImpactAtRuleAndIssueLevel_shouldMergeImpacts() {
+    DefaultIssue defaultIssue = createDefaultIssue()
+      .setNew(true)
+      .addImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)
+      .setRuleDescriptionContextKey(randomAlphabetic(6));
+
+    when(taintChecker.isTaintVulnerability(any())).thenReturn(true);
+
+    assertThat(underTest.raiseEventOnIssue("some-project-uuid", defaultIssue))
+      .isNotEmpty()
+      .hasValueSatisfying(pushEventDto -> {
+        TaintVulnerabilityRaised taintVulnerabilityRaised = gson.fromJson(new String(pushEventDto.getPayload(), StandardCharsets.UTF_8),
+          TaintVulnerabilityRaised.class);
+        assertThat(taintVulnerabilityRaised.getImpacts()).extracting(TaintVulnerabilityRaised.Impact::getSoftwareQuality, TaintVulnerabilityRaised.Impact::getSeverity)
+          .containsExactlyInAnyOrder(tuple(SoftwareQuality.MAINTAINABILITY.name(), Severity.HIGH.name()), tuple(SoftwareQuality.RELIABILITY.name(), Severity.HIGH.name()));
+      });
+  }
+
   @Test
   public void raiseEventOnIssue_whenReopenedTaintVulnerability_shouldCreateRaisedEvent() {
     DefaultIssue defaultIssue = createDefaultIssue()
@@ -379,6 +411,9 @@ public class PushEventFactoryTest {
     RuleDto ruleDto = new RuleDto();
     ruleDto.setRuleKey(RuleKey.of("javasecurity", "S123"));
     ruleDto.setSecurityStandards(Set.of("owasp-a1"));
+    ruleDto.setCleanCodeAttribute(CleanCodeAttribute.CONVENTIONAL);
+    ruleDto.addDefaultImpact(new ImpactDto().setUuid(Uuids.createFast()).setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.MEDIUM));
+    ruleDto.addDefaultImpact(new ImpactDto().setUuid(Uuids.createFast()).setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.HIGH));
     return new org.sonar.ce.task.projectanalysis.issue.RuleImpl(ruleDto);
   }
 }