]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22914 Issues on different CVEs are different issues
authorantoine.vinot <antoine.vinot@sonarsource.com>
Tue, 10 Sep 2024 14:14:30 +0000 (16:14 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 12 Sep 2024 20:02:55 +0000 (20:02 +0000)
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
sonar-core/src/main/java/org/sonar/core/issue/AnticipatedTransition.java
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java
sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java
sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java

index 11d896a0c6a8891c1424124849b41a5b582fe229..c8825883bc541789054049e56670aec8212e8d65 100644 (file)
@@ -38,9 +38,15 @@ public class SiblingIssue implements Trackable {
   private final String prKey;
   private final BranchType branchType;
   private final Date updateDate;
+  private final String cveId;
 
   SiblingIssue(String key, @Nullable Integer line, @Nullable String message, @Nullable String lineHash, RuleKey ruleKey, String status, String prKey, BranchType branchType,
     Date updateDate) {
+    this(key, line, message, lineHash, ruleKey, status, prKey, branchType, updateDate, null);
+  }
+
+  SiblingIssue(String key, @Nullable Integer line, @Nullable String message, @Nullable String lineHash, RuleKey ruleKey, String status, String prKey, BranchType branchType,
+    Date updateDate, @Nullable String cveId) {
     this.key = key;
     this.line = line;
     this.message = message;
@@ -50,6 +56,7 @@ public class SiblingIssue implements Trackable {
     this.prKey = prKey;
     this.branchType = branchType;
     this.updateDate = updateDate;
+    this.cveId = cveId;
   }
 
   public String getKey() {
@@ -97,6 +104,12 @@ public class SiblingIssue implements Trackable {
     return updateDate;
   }
 
+  @CheckForNull
+  @Override
+  public String getCveId() {
+    return cveId;
+  }
+
   @Override
   public int hashCode() {
     return key.hashCode();
index 93b6054564e8b8390fc3dcbe42c18da64c55603e..4f014fde255444dfcfd5e48ed24e657ff75cf09e 100644 (file)
@@ -270,7 +270,9 @@ public class TrackerRawInputFactory {
       }
       issue.setIsFromExternalRuleEngine(true);
       issue.setLocations(dbLocationsBuilder.build());
-      issue.setCveId(reportExternalIssue.getCveId());
+      if (reportExternalIssue.hasCveId()) {
+        issue.setCveId(reportExternalIssue.getCveId());
+      }
 
       return issue;
     }
index 0107b8c399c95e14e322f1b0dcab2f4c1d69ec4b..8f78835585b45b3e76c0ca2f0d68ec69dd422f35 100644 (file)
@@ -929,6 +929,7 @@ public final class IssueDto implements Serializable {
     issue.setCodeVariants(getCodeVariants());
     issue.setCleanCodeAttribute(cleanCodeAttribute);
     impacts.forEach(i -> issue.addImpact(i.getSoftwareQuality(), i.getSeverity()));
+    issue.setCveId(cveId);
     return issue;
   }
 }
index eba1d964f016179d1b5080282b3ae8b81c98fbed..b8f3a77b8d67ae3cae8c21e092f4e8bd6b385ca6 100644 (file)
@@ -45,6 +45,7 @@
     i.prioritized_rule as prioritizedRule,
     <include refid="issueImpactsColumns"/>
     <include refid="ruleDefaultImpactsColumns"/>
+    <include refid="issuesDependencyColumns"/>
     <include refid="isNewCodeReferenceIssue"/>
   </sql>
 
     ii.software_quality as "ii_softwareQuality",
     ii.severity as "ii_severity",
   </sql>
+
   <sql id="ruleDefaultImpactsColumns">
     rdi.software_quality as "rdi_softwareQuality",
     rdi.severity as "rdi_severity",
   </sql>
 
+  <sql id="issuesDependencyColumns">
+    cve.id as cveId,
+  </sql>
+
   <resultMap id="issueResultMap" type="Issue" autoMapping="true">
     <id property="kee" column="kee"/>
 
     left join new_code_reference_issues n on i.kee = n.issue_key
     left outer join issues_impacts ii on i.kee = ii.issue_key
     left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
+    left join issues_dependency dep on i.kee = dep.issue_uuid
+    left join cves cve on dep.cve_uuid = cve.uuid
     where i.kee=#{kee,jdbcType=VARCHAR}
   </select>
 
     left join new_code_reference_issues n on i.kee = n.issue_key
     left outer join issues_impacts ii on i.kee = ii.issue_key
     left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
+    left join issues_dependency dep on i.kee = dep.issue_uuid
+    left join cves cve on dep.cve_uuid = cve.uuid
     where
     i.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
     i.status &lt;&gt; 'CLOSED'
       i.kee = ii.issue_key
     left outer join rules_default_impacts rdi on
       r.uuid = rdi.rule_uuid
+    left join issues_dependency dep on i.kee = dep.issue_uuid
+    left join cves cve on dep.cve_uuid = cve.uuid
     where
       i.component_uuid = #{componentUuid,jdbcType=VARCHAR}
       and i.status = 'CLOSED'
     </collection>
   </resultMap>
 
-   <select id="scrollIssuesForIndexation" parameterType="map" resultMap="indexedIssueResultMap" fetchSize="${_scrollFetchSize}"
+  <select id="scrollIssuesForIndexation" parameterType="map" resultMap="indexedIssueResultMap" fetchSize="${_scrollFetchSize}"
            resultSetType="FORWARD_ONLY" resultOrdered="true">
     select
       i.kee as issueKey,
       i.prioritized_rule as prioritizedRule,
       <include refid="issueImpactsColumns"/>
       <include refid="ruleDefaultImpactsColumns"/>
+      <include refid="issuesDependencyColumns"/>
       <include refid="isNewCodeReferenceIssue"/>
     from issues i
     inner join rules r on r.uuid = i.rule_uuid
     left join new_code_reference_issues n on n.issue_key = i.kee
     left outer join issues_impacts ii on i.kee = ii.issue_key
     left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
+    left join issues_dependency dep on i.kee = dep.issue_uuid
+    left join cves cve on dep.cve_uuid = cve.uuid
     <where>
       <if test="branchUuid != null">
             and c.branch_uuid = #{branchUuid,jdbcType=VARCHAR} and i.project_uuid = #{branchUuid,jdbcType=VARCHAR}
   <select id="selectByKeys" parameterType="map" resultMap="issueResultMap">
     select
     <include refid="issueColumns"/>,
-    u.login as assigneeLogin,
-    cve.id as cveId
+    u.login as assigneeLogin
     from issues i
     inner join rules r on r.uuid=i.rule_uuid
     inner join components p on p.uuid=i.component_uuid
     left join new_code_reference_issues n on i.kee = n.issue_key
     left outer join issues_impacts ii on i.kee = ii.issue_key
     left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
+    left join issues_dependency dep on i.kee = dep.issue_uuid
+    left join cves cve on dep.cve_uuid = cve.uuid
     where
       i.kee in
         <foreach collection="keys" open="(" close=")" item="key" separator=",">
     i.prioritized_rule as prioritizedRule,
     <include refid="issueImpactsColumns"/>
     <include refid="ruleDefaultImpactsColumns"/>
+    <include refid="issuesDependencyColumns"/>
     i.clean_code_attribute as cleanCodeAttribute,
     r.clean_code_attribute as ruleCleanCodeAttribute
   </sql>
         left join users u on i.assignee = u.uuid
         left outer join issues_impacts ii on i.kee = ii.issue_key
         left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
+        left join issues_dependency dep on i.kee = dep.issue_uuid
+        left join cves cve on dep.cve_uuid = cve.uuid
     where
       <if test="keys.size() > 0">
         i.kee IN
index e5e1b246b111c44019dcaea276ab80a86c694d08..50c91c18134836b29d76f02b61071ae2b01b5338 100644 (file)
@@ -52,6 +52,7 @@ public class AnticipatedTransition implements Trackable {
     @Nullable String lineHash,
     String transition,
     @Nullable String comment) {
+
     this.uuid = uuid;
     this.projectKey = projectKey;
     this.transition = transition;
@@ -118,6 +119,12 @@ public class AnticipatedTransition implements Trackable {
     return Date.from(Instant.now());
   }
 
+  @Nullable
+  @Override
+  public String getCveId() {
+    return null;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
index 7480488b75e8f5c2762c65d7a74c7303b8902f26..b8c46a299af41bcddd91be722847ff5fdd220404 100644 (file)
@@ -774,6 +774,8 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
     return updateDate;
   }
 
+  @Override
+  @CheckForNull
   public String getCveId() {
     return cveId;
   }
index 46978ee5fb1ddc2c253d856b63eec6b6394e0e13..1c1e9394f486db9b1b1a81cf80e1e7363ced7d41 100644 (file)
@@ -61,11 +61,13 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
     private final RuleKey ruleKey;
     private final String lineHash;
     private final Integer line;
+    private final String cveId;
 
     protected LineAndLineHashKey(Trackable trackable) {
       this.ruleKey = trackable.getRuleKey();
       this.line = trackable.getLine();
       this.lineHash = Objects.toString(trackable.getLineHash(), "");
+      this.cveId = trackable.getCveId();
     }
 
     @Override
@@ -78,12 +80,15 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
       }
       LineAndLineHashKey that = (LineAndLineHashKey) o;
       // start with most discriminant field
-      return Objects.equals(line, that.line) && lineHash.equals(that.lineHash) && ruleKey.equals(that.ruleKey);
+      return Objects.equals(line, that.line)
+        && lineHash.equals(that.lineHash)
+        && ruleKey.equals(that.ruleKey)
+        && Objects.equals(cveId, that.cveId);
     }
 
     @Override
     public int hashCode() {
-      return Objects.hash(ruleKey, lineHash, line != null ? line : 0);
+      return Objects.hash(ruleKey, lineHash, line != null ? line : 0, cveId);
     }
   }
 
@@ -92,12 +97,14 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
     private final String lineHash;
     private final String message;
     private final Integer line;
+    private final String cveId;
 
     protected LineAndLineHashAndMessage(Trackable trackable) {
       this.ruleKey = trackable.getRuleKey();
       this.line = trackable.getLine();
       this.message = trackable.getMessage();
       this.lineHash = Objects.toString(trackable.getLineHash(), "");
+      this.cveId = trackable.getCveId();
     }
 
     @Override
@@ -110,12 +117,16 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
       }
       LineAndLineHashAndMessage that = (LineAndLineHashAndMessage) o;
       // start with most discriminant field
-      return Objects.equals(line, that.line) && lineHash.equals(that.lineHash) && Objects.equals(message, that.message) && ruleKey.equals(that.ruleKey);
+      return Objects.equals(line, that.line)
+        && lineHash.equals(that.lineHash)
+        && Objects.equals(message, that.message)
+        && ruleKey.equals(that.ruleKey)
+        && Objects.equals(cveId, that.cveId);
     }
 
     @Override
     public int hashCode() {
-      return Objects.hash(ruleKey, lineHash, message, line != null ? line : 0);
+      return Objects.hash(ruleKey, lineHash, message, line != null ? line : 0, cveId);
     }
   }
 
@@ -123,11 +134,13 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
     private final RuleKey ruleKey;
     private final String message;
     private final String lineHash;
+    private final String cveId;
 
     LineHashAndMessageKey(Trackable trackable) {
       this.ruleKey = trackable.getRuleKey();
       this.message = trackable.getMessage();
       this.lineHash = Objects.toString(trackable.getLineHash(), "");
+      this.cveId = trackable.getCveId();
     }
 
     @Override
@@ -140,12 +153,15 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
       }
       LineHashAndMessageKey that = (LineHashAndMessageKey) o;
       // start with most discriminant field
-      return lineHash.equals(that.lineHash) && Objects.equals(message, that.message) && ruleKey.equals(that.ruleKey);
+      return lineHash.equals(that.lineHash)
+        && Objects.equals(message, that.message)
+        && ruleKey.equals(that.ruleKey)
+        && Objects.equals(cveId, that.cveId);
     }
 
     @Override
     public int hashCode() {
-      return Objects.hash(ruleKey, message, lineHash);
+      return Objects.hash(ruleKey, message, lineHash, cveId);
     }
   }
 
@@ -153,11 +169,13 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
     private final RuleKey ruleKey;
     private final String message;
     private final Integer line;
+    private final String cveId;
 
     LineAndMessageKey(Trackable trackable) {
       this.ruleKey = trackable.getRuleKey();
       this.message = trackable.getMessage();
       this.line = trackable.getLine();
+      this.cveId = trackable.getCveId();
     }
 
     @Override
@@ -170,7 +188,10 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
       }
       LineAndMessageKey that = (LineAndMessageKey) o;
       // start with most discriminant field
-      return Objects.equals(line, that.line) && Objects.equals(message, that.message) && ruleKey.equals(that.ruleKey);
+      return Objects.equals(line, that.line)
+        && Objects.equals(message, that.message)
+        && ruleKey.equals(that.ruleKey)
+        && Objects.equals(cveId, that.cveId);
     }
 
     @Override
@@ -182,10 +203,12 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
   protected static class LineHashKey implements SearchKey {
     private final RuleKey ruleKey;
     private final String lineHash;
+    private final String cveId;
 
     LineHashKey(Trackable trackable) {
       this.ruleKey = trackable.getRuleKey();
       this.lineHash = Objects.toString(trackable.getLineHash(), "");
+      this.cveId = trackable.getCveId();
     }
 
     @Override
@@ -198,12 +221,12 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
       }
       LineHashKey that = (LineHashKey) o;
       // start with most discriminant field
-      return lineHash.equals(that.lineHash) && ruleKey.equals(that.ruleKey);
+      return lineHash.equals(that.lineHash) && ruleKey.equals(that.ruleKey) && Objects.equals(cveId, that.cveId);
     }
 
     @Override
     public int hashCode() {
-      return Objects.hash(ruleKey, lineHash);
+      return Objects.hash(ruleKey, lineHash, cveId);
     }
   }
 
index f637627ad813c5c8038a193a8acf48ad35e91ac7..63e6a919287105643f52c93538c753c0a1f6bd15 100644 (file)
@@ -50,4 +50,7 @@ public interface Trackable {
    * Functional update date for the issue. See {@link DefaultIssue#updateDate()}
    */
   Date getUpdateDate();
+
+  @CheckForNull
+  String getCveId();
 }
index a32e8eb66816bc7ee627561318739261cf37db22..675eef1cc264f64203ef38b93e7500aca9c5fc10 100644 (file)
@@ -459,20 +459,56 @@ public class TrackerTest {
     assertThat(tracking.baseFor(raw2)).isEqualTo(base1);
   }
 
+  @Test
+  public void match_issues_with_same_cve_id() {
+    FakeInput baseInput = new FakeInput();
+    Issue base = new Issue(null, "", RuleKey.of("sca", "use-of-vulnerable-dependency"), "Vulnerable", org.sonar.api.issue.Issue.STATUS_OPEN, new Date(), "CVE-1");
+    baseInput.addIssue(base);
+    FakeInput rawInput = new FakeInput();
+    Issue raw = new Issue(null, "", RuleKey.of("sca", "use-of-vulnerable-dependency"), "Vulnerable", org.sonar.api.issue.Issue.STATUS_OPEN, new Date(), "CVE-1");
+    rawInput.addIssue(raw);
+
+    Tracking<Issue, Issue> tracking = tracker.trackNonClosed(rawInput, baseInput);
+
+    assertThat(tracking.getUnmatchedBases()).isEmpty();
+    assertThat(tracking.baseFor(raw)).isEqualTo(base);
+  }
+
+  @Test
+  public void do_not_match_issues_with_different_cve_id() {
+    FakeInput baseInput = new FakeInput();
+    Issue base = new Issue(null, "", RuleKey.of("sca", "use-of-vulnerable-dependency"), "Vulnerable", org.sonar.api.issue.Issue.STATUS_OPEN, new Date(), "CVE-1");
+    baseInput.addIssue(base);
+    FakeInput rawInput = new FakeInput();
+    Issue raw = new Issue(null, "", RuleKey.of("sca", "use-of-vulnerable-dependency"), "Vulnerable", org.sonar.api.issue.Issue.STATUS_OPEN, new Date(), "CVE-2");
+    rawInput.addIssue(raw);
+
+    Tracking<Issue, Issue> tracking = tracker.trackNonClosed(rawInput, baseInput);
+
+    assertThat(tracking.getUnmatchedBases()).contains(base);
+    assertThat(tracking.baseFor(raw)).isNull();
+  }
+
   private static class Issue implements Trackable {
     private final RuleKey ruleKey;
     private final Integer line;
     private final String message, lineHash;
     private final String status;
     private final Date updateDate;
+    private final String cveId;
 
     Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, @Nullable String message, String status, Date updateDate) {
+      this(line, lineHash, ruleKey, message, status, updateDate, null);
+    }
+
+    Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, @Nullable String message, String status, Date updateDate, @Nullable String cveId) {
       this.line = line;
       this.lineHash = lineHash;
       this.ruleKey = ruleKey;
       this.status = status;
       this.updateDate = updateDate;
       this.message = trim(message);
+      this.cveId = cveId;
     }
 
     @Override
@@ -505,6 +541,12 @@ public class TrackerTest {
     public Date getUpdateDate() {
       return updateDate;
     }
+
+    @CheckForNull
+    @Override
+    public String getCveId() {
+      return cveId;
+    }
   }
 
   private static class FakeInput implements Input<Issue> {