From 7ffdf0a08ac0557bcf1c29035500bc0f7cabed3a Mon Sep 17 00:00:00 2001 From: "antoine.vinot" Date: Tue, 10 Sep 2024 16:14:30 +0200 Subject: [PATCH] SONAR-22914 Issues on different CVEs are different issues --- .../projectanalysis/issue/SiblingIssue.java | 13 ++++++ .../issue/TrackerRawInputFactory.java | 4 +- .../java/org/sonar/db/issue/IssueDto.java | 1 + .../org/sonar/db/issue/IssueMapper.xml | 25 +++++++++-- .../core/issue/AnticipatedTransition.java | 7 ++++ .../org/sonar/core/issue/DefaultIssue.java | 2 + .../core/issue/tracking/AbstractTracker.java | 41 ++++++++++++++---- .../sonar/core/issue/tracking/Trackable.java | 3 ++ .../core/issue/tracking/TrackerTest.java | 42 +++++++++++++++++++ 9 files changed, 125 insertions(+), 13 deletions(-) diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java index 11d896a0c6a..c8825883bc5 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java @@ -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(); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java index 93b6054564e..4f014fde255 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java @@ -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; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java index 0107b8c399c..8f78835585b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java @@ -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; } } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml index eba1d964f01..b8f3a77b8d6 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml @@ -45,6 +45,7 @@ i.prioritized_rule as prioritizedRule, + @@ -119,11 +120,16 @@ ii.software_quality as "ii_softwareQuality", ii.severity as "ii_severity", + rdi.software_quality as "rdi_softwareQuality", rdi.severity as "rdi_severity", + + cve.id as cveId, + + @@ -273,6 +279,8 @@ 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} @@ -287,6 +295,8 @@ 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 <> 'CLOSED' @@ -315,6 +325,8 @@ 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' @@ -339,7 +351,7 @@ - select , - 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 @@ -430,6 +444,8 @@ 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 @@ -652,6 +668,7 @@ i.prioritized_rule as prioritizedRule, + i.clean_code_attribute as cleanCodeAttribute, r.clean_code_attribute as ruleCleanCodeAttribute @@ -666,6 +683,8 @@ 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 i.kee IN diff --git a/sonar-core/src/main/java/org/sonar/core/issue/AnticipatedTransition.java b/sonar-core/src/main/java/org/sonar/core/issue/AnticipatedTransition.java index e5e1b246b11..50c91c18134 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/AnticipatedTransition.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/AnticipatedTransition.java @@ -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) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index 7480488b75e..b8c46a299af 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -774,6 +774,8 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. return updateDate; } + @Override + @CheckForNull public String getCveId() { return cveId; } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java index 46978ee5fb1..1c1e9394f48 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java @@ -61,11 +61,13 @@ public class AbstractTracker { 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 { } 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 { 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 { } 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 { 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 { } 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 { 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 { } 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 { 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 { } 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); } } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java index f637627ad81..63e6a919287 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java @@ -50,4 +50,7 @@ public interface Trackable { * Functional update date for the issue. See {@link DefaultIssue#updateDate()} */ Date getUpdateDate(); + + @CheckForNull + String getCveId(); } diff --git a/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java b/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java index a32e8eb6681..675eef1cc26 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java @@ -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 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 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 { -- 2.39.5