diff --git a/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/Cve.java b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/Cve.java new file mode 100644 index 000000000..696ea94db --- /dev/null +++ b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/Cve.java @@ -0,0 +1,49 @@ +package org.jfrog.build.extractor.scan; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * @author yahavi + **/ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class Cve { + private String cveId; + private String cvssV1; + private String cvssV2; + + public Cve(String cveId, String cvssV1, String cvssV2) { + this.cveId = cveId; + this.cvssV1 = cvssV1; + this.cvssV2 = cvssV2; + } + + @SuppressWarnings("unused") + public String getCveId() { + return cveId; + } + + @SuppressWarnings("unused") + public void setCveId(String cveId) { + this.cveId = cveId; + } + + @SuppressWarnings("unused") + public String getCvssV1() { + return cvssV1; + } + + @SuppressWarnings("unused") + public void setCvssV1(String cvssV1) { + this.cvssV1 = cvssV1; + } + + @SuppressWarnings("unused") + public String getCvssV2() { + return cvssV2; + } + + @SuppressWarnings("unused") + public void setCvssV2(String cvssV2) { + this.cvssV2 = cvssV2; + } +} diff --git a/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/DependencyTree.java b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/DependencyTree.java index ef0202ce3..21eb2a456 100644 --- a/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/DependencyTree.java +++ b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/DependencyTree.java @@ -1,11 +1,12 @@ package org.jfrog.build.extractor.scan; -import javax.swing.tree.DefaultMutableTreeNode; - import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang3.StringUtils; +import javax.swing.tree.DefaultMutableTreeNode; import java.util.*; +import java.util.stream.Collectors; /** * Dependency tree for Xray scan. Used in 'Eclipse' and 'Idea' Xray plugins. @@ -15,6 +16,7 @@ @JsonFilter("xray-graph-filter") public class DependencyTree extends DefaultMutableTreeNode { + private Set violatedLicenses = new HashSet<>(); private Set licenses = new HashSet<>(); private Set issues = new HashSet<>(); private Set scopes = new HashSet<>(); @@ -38,6 +40,15 @@ public DependencyTree(Object userObject) { super(userObject); } + @SuppressWarnings("unused") + public void setViolatedLicenses(Set violatedLicenses) { + this.violatedLicenses = violatedLicenses; + } + + public Set getViolatedLicenses() { + return violatedLicenses; + } + public void setLicenses(Set licenses) { this.licenses = licenses; } @@ -86,17 +97,6 @@ public Issue getTopIssue() { return topIssue; } - /** - * @return if one or more of the licenses is violating define policy - */ - @SuppressWarnings("unused") - public boolean isLicenseViolating() { - if (licenses.stream().anyMatch(License::isViolate)) { - return true; - } - return getChildren().stream().anyMatch(DependencyTree::isLicenseViolating); - } - @SuppressWarnings("unused") public boolean isMetadata() { return metadata; @@ -178,6 +178,26 @@ private void setTopIssue() { }); } + /** + * 1. Populate current node's licenses components + * 2. Populate current node and subtree's violated licenses + * + * @return all violated licenses of the current node and its ancestors + */ + public Set processTreeViolatedLicenses() { + setViolatedLicensesComponent(); + violatedLicenses.addAll(licenses.stream().filter(License::isViolate).collect(Collectors.toSet())); + getChildren().forEach(child -> violatedLicenses.addAll(child.processTreeViolatedLicenses())); + return violatedLicenses; + } + + private void setViolatedLicensesComponent() { + Object userObject = getUserObject(); + if (userObject != null) { + licenses.forEach(license -> license.setComponent(userObject.toString())); + } + } + /** * Recursively, collect all scopes and licenses. * @@ -193,4 +213,21 @@ public void collectAllScopesAndLicenses(Set allScopes, Set allLi allLicenses.addAll(child.getLicenses()); } } + + /** + * Recursively find a node contains the input component ID. + * + * @param componentId - The component ID to search + * @return a node contains the input component ID or null. + */ + public DependencyTree find(String componentId) { + if (StringUtils.equals(toString(), componentId)) { + return this; + } + return getChildren().stream() + .map(child -> child.find(componentId)) + .filter(Objects::nonNull) + .findAny() + .orElse(null); + } } diff --git a/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/Issue.java b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/Issue.java index 02e7ae46d..82c96d0f2 100644 --- a/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/Issue.java +++ b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/Issue.java @@ -16,21 +16,26 @@ public class Issue implements Comparable { private Severity severity = Severity.Normal; private List fixedVersions; + private List references; private String component = ""; + private String ignoreRuleUrl; + private List cves; private String summary; private String issueId; - private String cve; public Issue() { } @SuppressWarnings("unused") - public Issue(String issueId, Severity severity, String summary, List fixedVersions, String cve) { + public Issue(String issueId, Severity severity, String summary, List fixedVersions, List cves, + List references, String ignoreRuleUrl) { this.issueId = issueId; this.severity = severity; this.summary = summary; this.fixedVersions = fixedVersions; - this.cve = cve; + this.cves = cves; + this.references = references; + this.ignoreRuleUrl = ignoreRuleUrl; } public String getIssueId() { @@ -65,8 +70,18 @@ public void setFixedVersions(List fixedVersions) { } @SuppressWarnings("unused") - public String getCve() { - return cve; + public List getCves() { + return cves; + } + + @SuppressWarnings("unused") + public List getReferences() { + return references; + } + + @SuppressWarnings("unused") + public String getIgnoreRuleUrl() { + return ignoreRuleUrl; } @JsonIgnore diff --git a/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/License.java b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/License.java index 0e2c44bff..91bc41a64 100644 --- a/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/License.java +++ b/build-info-extractor/src/main/java/org/jfrog/build/extractor/scan/License.java @@ -16,7 +16,7 @@ public class License { private static final String UNKNOWN_LICENCE_FULL_NAME = "Unknown license"; @SuppressWarnings("FieldCanBeLocal") private static final String UNKNOWN_LICENCE_NAME = "Unknown"; - private List components = new ArrayList<>(); + private String component = ""; private final String fullName; private final String name; private List moreInfoUrl = new ArrayList<>(); @@ -27,12 +27,11 @@ public License() { this.name = UNKNOWN_LICENCE_NAME; } - public License(List components, String fullName, String name, List moreInfoUrl) { - this(components, fullName, name, moreInfoUrl, false); + public License(String fullName, String name, List moreInfoUrl) { + this(fullName, name, moreInfoUrl, false); } - public License(List components, String fullName, String name, List moreInfoUrl, boolean violate) { - this.components = components; + public License(String fullName, String name, List moreInfoUrl, boolean violate) { this.fullName = StringUtils.trim(fullName); this.name = StringUtils.trim(name); this.moreInfoUrl = moreInfoUrl; @@ -40,8 +39,12 @@ public License(List components, String fullName, String name, List getComponents() { - return components; + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; } @SuppressWarnings("unused") diff --git a/build-info-extractor/src/test/java/org/jfrog/build/extractor/scan/DependencyTreeTest.java b/build-info-extractor/src/test/java/org/jfrog/build/extractor/scan/DependencyTreeTest.java index 8074fa94f..876412081 100644 --- a/build-info-extractor/src/test/java/org/jfrog/build/extractor/scan/DependencyTreeTest.java +++ b/build-info-extractor/src/test/java/org/jfrog/build/extractor/scan/DependencyTreeTest.java @@ -43,6 +43,8 @@ public void testInit() { // Sanity test - Check tree with no issues Set rootIssues = root.processTreeIssues(); assertTrue(rootIssues.isEmpty()); + Set rootViolatedLicenses = root.processTreeViolatedLicenses(); + assertTrue(rootViolatedLicenses.isEmpty()); assertEquals(Severity.Normal, root.getTopIssue().getSeverity()); } @@ -59,6 +61,10 @@ public void testOneNode() { assertEquals(1, rootIssues.size()); assertEquals(Severity.Normal, ((Issue) rootIssues.toArray()[0]).getSeverity()); + // Assert no violated licenses + Set rootViolatedLicenses = root.processTreeViolatedLicenses(); + assertTrue(rootViolatedLicenses.isEmpty()); + // Check isHigherSeverityThan() functionality assertTrue(createIssue(Severity.Unknown).isHigherSeverityThan(root.getTopIssue())); } @@ -126,22 +132,31 @@ public void testFiveNodes() { @Test(dependsOnMethods = {"testFiveNodes"}) public void testIsLicenseViolating() { - assertFalse(root.isLicenseViolating()); + assertTrue(root.getViolatedLicenses().isEmpty()); + + License violatedLicense = createLicense(true); // Populate node three with 4 licenses, one violation three.setLicenses(Sets.newHashSet(createLicense(false), createLicense(false), createLicense(false), - createLicense(true))); + violatedLicense)); // Populate node five with non violated license. five.setLicenses(Sets.newHashSet(createLicense(false))); - // Assert that all issues are in the tree + // Assert that all licenses are in the tree Set rootLicense = new HashSet<>(); root.collectAllScopesAndLicenses(new HashSet<>(), rootLicense); assertEquals(6, rootLicense.size()); - assertTrue(root.isLicenseViolating()); - assertFalse(four.isLicenseViolating()); - assertFalse(five.isLicenseViolating()); + + Set expectedViolatedLicenseSet = Sets.newHashSet(violatedLicense); + Set rootViolatedLicenses = root.processTreeViolatedLicenses(); + assertEquals(expectedViolatedLicenseSet, rootViolatedLicenses); + assertTrue(one.getViolatedLicenses().isEmpty()); + assertEquals(expectedViolatedLicenseSet, two.getViolatedLicenses()); + assertEquals(expectedViolatedLicenseSet, three.getViolatedLicenses()); + assertTrue(three.getLicenses().contains(violatedLicense)); + assertTrue(four.getViolatedLicenses().isEmpty()); + assertTrue(five.getViolatedLicenses().isEmpty()); } @Test @@ -178,6 +193,17 @@ public void testFixedVersions() { one.setIssues(Sets.newHashSet()); } + @Test + public void testFind() { + assertNotNull(root.find("0")); + assertNotNull(root.find("1")); + assertNotNull(root.find("2")); + assertNotNull(root.find("3")); + assertNotNull(root.find("4")); + assertNotNull(root.find("5")); + assertNull(root.find("non-existent")); + } + /** * Create a random issue * @@ -185,7 +211,8 @@ public void testFixedVersions() { * @return the random issue */ private Issue createIssue(Severity severity) { - return new Issue(generateUID(), severity, generateUID(), Lists.newArrayList(), generateUID()); + return new Issue(generateUID(), severity, generateUID(), Lists.newArrayList(), + Lists.newArrayList(), Lists.newArrayList(), generateUID()); } /** @@ -195,7 +222,7 @@ private Issue createIssue(Severity severity) { * @return the random issue */ private License createLicense(boolean violating) { - return new License(Lists.newArrayList(), generateUID(), generateUID(), Lists.newArrayList(), violating); + return new License(generateUID(), generateUID(), Lists.newArrayList(), violating); } private String generateUID() {