Skip to content

Commit

Permalink
Fix duplications in results cache (#129)
Browse files Browse the repository at this point in the history
* Fix duplications of VulnerabilityNodes in the cache, that caused errors while trying to read it.
  • Loading branch information
asafgabai authored Aug 10, 2023
1 parent 70d210f commit 38320da
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 47 deletions.
20 changes: 15 additions & 5 deletions src/main/java/com/jfrog/ide/common/nodes/ApplicableIssueNode.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.jfrog.ide.common.nodes;

import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;
import lombok.Getter;

import java.util.Objects;

@Getter
public class ApplicableIssueNode extends FileIssueNode {
@JsonProperty()
private String scannerSearchTarget;
@JsonProperty()
@JsonIdentityReference(alwaysAsId = true)
private VulnerabilityNode issue;

// Empty constructor for deserialization
Expand All @@ -20,12 +26,16 @@ public ApplicableIssueNode(String name, int rowStart, int colStart, int rowEnd,
this.issue = issue;
}

public VulnerabilityNode getIssue() {
return issue;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ApplicableIssueNode that = (ApplicableIssueNode) o;
return super.equals(o) && Objects.equals(scannerSearchTarget, that.scannerSearchTarget) && Objects.equals(issue, that.issue);
}


public String getScannerSearchTarget() {
return scannerSearchTarget;
@Override
public int hashCode() {
return Objects.hash(scannerSearchTarget, issue);
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/jfrog/ide/common/nodes/DependencyNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import javax.swing.tree.TreeNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static com.jfrog.ide.common.utils.Utils.removeComponentIdPrefix;

Expand Down Expand Up @@ -149,4 +150,17 @@ public Object clone() {
public String toString() {
return getTitle();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DependencyNode that = (DependencyNode) o;
return indirect == that.indirect && Objects.equals(componentId, that.componentId) && Objects.equals(impactTree, that.impactTree) && Objects.equals(licenses, that.licenses) && Objects.equals(children, that.children);
}

@Override
public int hashCode() {
return Objects.hash(componentId, indirect, impactTree, licenses, children);
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/jfrog/ide/common/nodes/FileIssueNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;
import com.jfrog.ide.common.nodes.subentities.Severity;

import java.util.Objects;

public class FileIssueNode extends IssueNode implements SubtitledTreeNode {
@JsonProperty()
private String title;
Expand Down Expand Up @@ -102,4 +104,17 @@ public String getTitle() {
public String getIcon() {
return getSeverity().getIconName();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FileIssueNode that = (FileIssueNode) o;
return rowStart == that.rowStart && colStart == that.colStart && rowEnd == that.rowEnd && colEnd == that.colEnd && Objects.equals(title, that.title) && Objects.equals(reason, that.reason) && Objects.equals(lineSnippet, that.lineSnippet) && Objects.equals(filePath, that.filePath) && severity == that.severity && reporterType == that.reporterType;
}

@Override
public int hashCode() {
return Objects.hash(title, reason, lineSnippet, rowStart, colStart, rowEnd, colEnd, filePath, severity, reporterType);
}
}
20 changes: 16 additions & 4 deletions src/main/java/com/jfrog/ide/common/nodes/FileTreeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import com.fasterxml.jackson.annotation.JsonProperty;
import com.jfrog.ide.common.nodes.subentities.Severity;
import lombok.Getter;

import java.io.File;
import java.util.Objects;

public class FileTreeNode extends SortableChildrenTreeNode implements SubtitledTreeNode, Comparable<FileTreeNode> {
@JsonProperty()
protected String fileName = "";
@Getter
@JsonProperty()
protected String filePath = "";
@JsonProperty()
Expand Down Expand Up @@ -47,10 +50,6 @@ public String getIcon() {
return topSeverity.getIconName();
}

public String getFilePath() {
return filePath;
}

public void addIssue(IssueNode issue) {
add(issue);
if (issue.getSeverity().isHigherThan(topSeverity)) {
Expand All @@ -62,4 +61,17 @@ public void addIssue(IssueNode issue) {
public int compareTo(FileTreeNode other) {
return other.getSeverity().ordinal() - this.getSeverity().ordinal();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FileTreeNode that = (FileTreeNode) o;
return Objects.equals(fileName, that.fileName) && Objects.equals(filePath, that.filePath) && topSeverity == that.topSeverity && Objects.equals(children, that.children);
}

@Override
public int hashCode() {
return Objects.hash(fileName, filePath, topSeverity, children);
}
}
54 changes: 16 additions & 38 deletions src/main/java/com/jfrog/ide/common/nodes/subentities/Severity.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.jfrog.ide.common.nodes.subentities;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;

/**
* @author yahavi
*/
@Getter
public enum Severity {
Normal("Scanned - No Issues", "normal"),
Pending("Pending Scan", "pending"),
Expand All @@ -29,54 +30,31 @@ public enum Severity {
this.iconName = iconName;
}

public String getSeverityName() {
return this.severityName;
}

public String getIconName() {
return iconName;
}

public boolean isHigherThan(Severity other) {
return this.ordinal() > other.ordinal();
}

@JsonCreator
public static Severity fromString(String inputSeverity) {
for (Severity severity : Severity.values()) {
if (severity.getSeverityName().equalsIgnoreCase(inputSeverity)) {
return severity;
}
}
throw new IllegalArgumentException("Severity " + inputSeverity + " doesn't exist");
return Severity.valueOf(inputSeverity);
}

public static Severity fromSarif(String level) {
switch (level) {
case "error":
return Severity.High;
case "note":
return Severity.Low;
case "none":
return Severity.Unknown;
default:
return Severity.Medium;
}
return switch (level) {
case "error" -> Severity.High;
case "note" -> Severity.Low;
case "none" -> Severity.Unknown;
default -> Severity.Medium;
};
}

public static Severity getNotApplicableSeverity(Severity severity) {
switch (severity) {
case Low:
return LowNotApplic;
case Medium:
return MediumNotApplic;
case High:
return HighNotApplic;
case Critical:
return CriticalNotApplic;
case Unknown:
default:
return UnknownNotApplic;
}
return switch (severity) {
case Low -> LowNotApplic;
case Medium -> MediumNotApplic;
case High -> HighNotApplic;
case Critical -> CriticalNotApplic;
default -> UnknownNotApplic;
};
}
}
100 changes: 100 additions & 0 deletions src/test/java/com/jfrog/ide/common/persistency/ScanCacheTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.jfrog.ide.common.persistency;

import com.jfrog.ide.common.nodes.*;
import com.jfrog.ide.common.nodes.subentities.Cve;
import com.jfrog.ide.common.nodes.subentities.ResearchInfo;
import com.jfrog.ide.common.nodes.subentities.Severity;
import com.jfrog.ide.common.nodes.subentities.SeverityReason;
import org.apache.commons.io.FileUtils;
import org.jfrog.build.api.util.NullLog;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

public class ScanCacheTest {
private Path tempCacheDirPath;

@BeforeMethod
public void setUp(Object[] testArgs) throws IOException {
tempCacheDirPath = Files.createTempDirectory("ide-plugins-common-test-cache");
FileUtils.forceDeleteOnExit(tempCacheDirPath.toFile());
}

@AfterMethod
public void tearDown() throws IOException {
FileUtils.forceDelete(tempCacheDirPath.toFile());
}

@Test
public void cacheTest() throws IOException {
final String TEST_PROJECT_ID = "test-project-id";
List<FileTreeNode> fileTreeNodes = getFileTreeNode();

// Create a new cache and validate it's empty
ScanCache cache = new ScanCache(TEST_PROJECT_ID, tempCacheDirPath, new NullLog());
Assert.assertNull(cache.getScanCacheObject());

// Save FileTreeNodes in cache and read them for it
cache.cacheNodes(fileTreeNodes);
ScanCache newCache = new ScanCache(TEST_PROJECT_ID, tempCacheDirPath, new NullLog());
List<FileTreeNode> actual = newCache.getScanCacheObject().getFileTreeNodes();
Assert.assertEquals(actual, fileTreeNodes);
}

private static List<FileTreeNode> getFileTreeNode() {
List<FileTreeNode> fileTreeNodes = new ArrayList<>();
DescriptorFileTreeNode descriptorFileTreeNode = new DescriptorFileTreeNode("path/to/descriptor.xml");
VulnerabilityNode vulnerabilityNode = new VulnerabilityNode(
"issueId",
Severity.LowNotApplic,
"summary",
new ArrayList<>(List.of("fixedVersions")),
new ArrayList<>(List.of("infectedVersions")),
new Cve("cveId",
"cvssV2Score",
"cvssV2Vector",
"cvssV3Score",
"cvssV3Vector"),
"lastUpdated",
new ArrayList<>(List.of("watchNames")),
new ArrayList<>(List.of("references")),
new ResearchInfo(Severity.Low,
"shortDescription",
"fullDescription",
"remediation",
new ArrayList<>(List.of(new SeverityReason("name", "description", true)))),
"ignoreRuleUrl");
DependencyNode dependencyNode = new DependencyNode().componentId("componentId");
dependencyNode.addIssue(vulnerabilityNode);
descriptorFileTreeNode.addDependency(dependencyNode);
FileTreeNode fileTreeNode = new FileTreeNode("path/to/file.txt");
ApplicableIssueNode applicableIssueNode = new ApplicableIssueNode(
"name",
1,
2,
3,
4,
"filePath",
"reason",
"lineSnippet",
"scannerSearchTarget",
vulnerabilityNode
);
vulnerabilityNode.updateApplicableInfo(applicableIssueNode);
fileTreeNode.addIssue(applicableIssueNode);

// The order of the adding to the list is important! We add the ApplicableIssueNode first to make sure its
// 'issue' field is saved in the cache file as a UUID only (a pointer), even though the VulnerabilityNode that
// it refers to is written later in the file.
fileTreeNodes.add(fileTreeNode);
fileTreeNodes.add(descriptorFileTreeNode);
return fileTreeNodes;
}
}

0 comments on commit 38320da

Please sign in to comment.