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;
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());
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
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;
+ }
+ }
}
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;
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;
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;
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()
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);
}
}