refactored the dependency object to be threadsafe

This commit is contained in:
Jeremy Long
2017-09-09 20:42:42 -04:00
parent 61390b200d
commit 1548f9a4b2
12 changed files with 193 additions and 186 deletions

View File

@@ -266,7 +266,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
d.getFileName()); d.getFileName());
d.setFilePath(displayPath); d.setFilePath(displayPath);
d.setFileName(displayName); d.setFileName(displayName);
d.setProjectReferences(dependency.getProjectReferences()); d.addAllProjectReferences(dependency.getProjectReferences());
//TODO - can we get more evidence from the parent? EAR contains module name, etc. //TODO - can we get more evidence from the parent? EAR contains module name, etc.
//analyze the dependency (i.e. extract files) if it is a supported type. //analyze the dependency (i.e. extract files) if it is a supported type.

View File

@@ -120,11 +120,11 @@ public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnaly
} else if (isShadedJar(dependency, nextDependency)) { } else if (isShadedJar(dependency, nextDependency)) {
if (dependency.getFileName().toLowerCase().endsWith("pom.xml")) { if (dependency.getFileName().toLowerCase().endsWith("pom.xml")) {
mergeDependencies(nextDependency, dependency, dependenciesToRemove); mergeDependencies(nextDependency, dependency, dependenciesToRemove);
nextDependency.getRelatedDependencies().remove(dependency); nextDependency.removeRelatedDependencies(dependency);
return true; return true;
} else { } else {
mergeDependencies(dependency, nextDependency, dependenciesToRemove); mergeDependencies(dependency, nextDependency, dependenciesToRemove);
dependency.getRelatedDependencies().remove(nextDependency); dependency.removeRelatedDependencies(nextDependency);
} }
} else if (cpeIdentifiersMatch(dependency, nextDependency) } else if (cpeIdentifiersMatch(dependency, nextDependency)
&& hasSameBasePath(dependency, nextDependency) && hasSameBasePath(dependency, nextDependency)
@@ -152,10 +152,9 @@ public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnaly
*/ */
private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set<Dependency> dependenciesToRemove) { private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set<Dependency> dependenciesToRemove) {
dependency.addRelatedDependency(relatedDependency); dependency.addRelatedDependency(relatedDependency);
final Iterator<Dependency> i = relatedDependency.getRelatedDependencies().iterator(); for (Dependency d : relatedDependency.getRelatedDependencies()) {
while (i.hasNext()) { dependency.addRelatedDependency(d);
dependency.addRelatedDependency(i.next()); relatedDependency.removeRelatedDependencies(d);
i.remove();
} }
if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) { if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) {
dependency.addAllProjectReferences(relatedDependency.getProjectReferences()); dependency.addAllProjectReferences(relatedDependency.getProjectReferences());

View File

@@ -132,10 +132,9 @@ public class DependencyMergingAnalyzer extends AbstractDependencyComparingAnalyz
dependency.addEvidence(EvidenceType.VERSION, e); dependency.addEvidence(EvidenceType.VERSION, e);
} }
final Iterator<Dependency> i = relatedDependency.getRelatedDependencies().iterator(); for (Dependency d : relatedDependency.getRelatedDependencies()) {
while (i.hasNext()) { dependency.addRelatedDependency(d);
dependency.addRelatedDependency(i.next()); relatedDependency.removeRelatedDependencies(d);
i.remove();
} }
if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) { if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) {
dependency.addAllProjectReferences(relatedDependency.getProjectReferences()); dependency.addAllProjectReferences(relatedDependency.getProjectReferences());

View File

@@ -22,6 +22,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
@@ -160,17 +161,18 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
} }
} }
if (mustContain != null) { if (mustContain != null) {
final Iterator<Identifier> itr = dependency.getIdentifiers().iterator(); final Set<Identifier> removalSet = new HashSet<>();
while (itr.hasNext()) { for (Identifier i : dependency.getIdentifiers()) {
final Identifier i = itr.next();
if ("cpe".contains(i.getType()) if ("cpe".contains(i.getType())
&& i.getValue() != null && i.getValue() != null
&& i.getValue().startsWith("cpe:/a:springsource:") && i.getValue().startsWith("cpe:/a:springsource:")
&& !i.getValue().toLowerCase().contains(mustContain)) { && !i.getValue().toLowerCase().contains(mustContain)) {
itr.remove(); removalSet.add(i);
//dependency.getIdentifiers().remove(i);
} }
} }
for (Identifier i : removalSet) {
dependency.removeIdentifier(i);
}
} }
} }
@@ -221,15 +223,15 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
//how did we get here? //how did we get here?
LOGGER.debug("currentVersion and nextVersion are both null?"); LOGGER.debug("currentVersion and nextVersion are both null?");
} else if (currentVersion == null && nextVersion != null) { } else if (currentVersion == null && nextVersion != null) {
dependency.getIdentifiers().remove(currentId); dependency.removeIdentifier(currentId);
} else if (nextVersion == null && currentVersion != null) { } else if (nextVersion == null && currentVersion != null) {
dependency.getIdentifiers().remove(nextId); dependency.removeIdentifier(nextId);
} else if (currentVersion.length() < nextVersion.length()) { } else if (currentVersion.length() < nextVersion.length()) {
if (nextVersion.startsWith(currentVersion) || "-".equals(currentVersion)) { if (nextVersion.startsWith(currentVersion) || "-".equals(currentVersion)) {
dependency.getIdentifiers().remove(currentId); dependency.removeIdentifier(currentId);
} }
} else if (currentVersion.startsWith(nextVersion) || "-".equals(nextVersion)) { } else if (currentVersion.startsWith(nextVersion) || "-".equals(nextVersion)) {
dependency.getIdentifiers().remove(nextId); dependency.removeIdentifier(nextId);
} }
} }
} }
@@ -244,21 +246,22 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
* @param dependency the dependency to remove JRE CPEs from * @param dependency the dependency to remove JRE CPEs from
*/ */
private void removeJreEntries(Dependency dependency) { private void removeJreEntries(Dependency dependency) {
final Set<Identifier> identifiers = dependency.getIdentifiers(); final Set<Identifier> removalSet = new HashSet<>();
final Iterator<Identifier> itr = identifiers.iterator(); for (Identifier i : dependency.getIdentifiers()) {
while (itr.hasNext()) {
final Identifier i = itr.next();
final Matcher coreCPE = CORE_JAVA.matcher(i.getValue()); final Matcher coreCPE = CORE_JAVA.matcher(i.getValue());
final Matcher coreFiles = CORE_FILES.matcher(dependency.getFileName()); final Matcher coreFiles = CORE_FILES.matcher(dependency.getFileName());
if (coreCPE.matches() && !coreFiles.matches()) { if (coreCPE.matches() && !coreFiles.matches()) {
itr.remove(); removalSet.add(i);
} }
final Matcher coreJsfCPE = CORE_JAVA_JSF.matcher(i.getValue()); final Matcher coreJsfCPE = CORE_JAVA_JSF.matcher(i.getValue());
final Matcher coreJsfFiles = CORE_JSF_FILES.matcher(dependency.getFileName()); final Matcher coreJsfFiles = CORE_JSF_FILES.matcher(dependency.getFileName());
if (coreJsfCPE.matches() && !coreJsfFiles.matches()) { if (coreJsfCPE.matches() && !coreJsfFiles.matches()) {
itr.remove(); removalSet.add(i);
} }
} }
for (Identifier i : removalSet) {
dependency.removeIdentifier(i);
}
} }
/** /**
@@ -290,8 +293,6 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
* @param dependency the dependency to analyze * @param dependency the dependency to analyze
*/ */
protected void removeBadMatches(Dependency dependency) { protected void removeBadMatches(Dependency dependency) {
final Set<Identifier> identifiers = dependency.getIdentifiers();
final Iterator<Identifier> itr = identifiers.iterator();
/* TODO - can we utilize the pom's groupid and artifactId to filter??? most of /* TODO - can we utilize the pom's groupid and artifactId to filter??? most of
* these are due to low quality data. Other idea would be to say any CPE * these are due to low quality data. Other idea would be to say any CPE
@@ -300,8 +301,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
*/ */
//Set<Evidence> groupId = dependency.getVendorEvidence().getEvidence("pom", "groupid"); //Set<Evidence> groupId = dependency.getVendorEvidence().getEvidence("pom", "groupid");
//Set<Evidence> artifactId = dependency.getVendorEvidence().getEvidence("pom", "artifactid"); //Set<Evidence> artifactId = dependency.getVendorEvidence().getEvidence("pom", "artifactid");
while (itr.hasNext()) { for (Identifier i : dependency.getIdentifiers()) {
final Identifier i = itr.next();
//TODO move this startsWith expression to the base suppression file //TODO move this startsWith expression to the base suppression file
if ("cpe".equals(i.getType())) { if ("cpe".equals(i.getType())) {
if ((i.getValue().matches(".*c\\+\\+.*") if ((i.getValue().matches(".*c\\+\\+.*")
@@ -325,7 +325,8 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|| dependency.getFileName().toLowerCase().endsWith(".tgz") || dependency.getFileName().toLowerCase().endsWith(".tgz")
|| dependency.getFileName().toLowerCase().endsWith(".ear") || dependency.getFileName().toLowerCase().endsWith(".ear")
|| dependency.getFileName().toLowerCase().endsWith(".war"))) { || dependency.getFileName().toLowerCase().endsWith(".war"))) {
itr.remove(); //itr.remove();
dependency.removeIdentifier(i);
} else if ((i.getValue().startsWith("cpe:/a:jquery:jquery") } else if ((i.getValue().startsWith("cpe:/a:jquery:jquery")
|| i.getValue().startsWith("cpe:/a:prototypejs:prototype") || i.getValue().startsWith("cpe:/a:prototypejs:prototype")
|| i.getValue().startsWith("cpe:/a:yahoo:yui")) || i.getValue().startsWith("cpe:/a:yahoo:yui"))
@@ -333,7 +334,8 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|| dependency.getFileName().toLowerCase().endsWith("pom.xml") || dependency.getFileName().toLowerCase().endsWith("pom.xml")
|| dependency.getFileName().toLowerCase().endsWith(".dll") || dependency.getFileName().toLowerCase().endsWith(".dll")
|| dependency.getFileName().toLowerCase().endsWith(".exe"))) { || dependency.getFileName().toLowerCase().endsWith(".exe"))) {
itr.remove(); //itr.remove();
dependency.removeIdentifier(i);
} else if ((i.getValue().startsWith("cpe:/a:microsoft:excel") } else if ((i.getValue().startsWith("cpe:/a:microsoft:excel")
|| i.getValue().startsWith("cpe:/a:microsoft:word") || i.getValue().startsWith("cpe:/a:microsoft:word")
|| i.getValue().startsWith("cpe:/a:microsoft:visio") || i.getValue().startsWith("cpe:/a:microsoft:visio")
@@ -344,10 +346,12 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|| dependency.getFileName().toLowerCase().endsWith(".ear") || dependency.getFileName().toLowerCase().endsWith(".ear")
|| dependency.getFileName().toLowerCase().endsWith(".war") || dependency.getFileName().toLowerCase().endsWith(".war")
|| dependency.getFileName().toLowerCase().endsWith("pom.xml"))) { || dependency.getFileName().toLowerCase().endsWith("pom.xml"))) {
itr.remove(); //itr.remove();
dependency.removeIdentifier(i);
} else if (i.getValue().startsWith("cpe:/a:apache:maven") } else if (i.getValue().startsWith("cpe:/a:apache:maven")
&& !dependency.getFileName().toLowerCase().matches("maven-core-[\\d\\.]+\\.jar")) { && !dependency.getFileName().toLowerCase().matches("maven-core-[\\d\\.]+\\.jar")) {
itr.remove(); //itr.remove();
dependency.removeIdentifier(i);
} else if (i.getValue().startsWith("cpe:/a:m-core:m-core")) { } else if (i.getValue().startsWith("cpe:/a:m-core:m-core")) {
boolean found = false; boolean found = false;
for (Evidence e : dependency.getEvidence(EvidenceType.PRODUCT)) { for (Evidence e : dependency.getEvidence(EvidenceType.PRODUCT)) {
@@ -365,11 +369,13 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
} }
} }
if (!found) { if (!found) {
itr.remove(); //itr.remove();
dependency.removeIdentifier(i);
} }
} else if (i.getValue().startsWith("cpe:/a:jboss:jboss") } else if (i.getValue().startsWith("cpe:/a:jboss:jboss")
&& !dependency.getFileName().toLowerCase().matches("jboss-?[\\d\\.-]+(GA)?\\.jar")) { && !dependency.getFileName().toLowerCase().matches("jboss-?[\\d\\.-]+(GA)?\\.jar")) {
itr.remove(); //itr.remove();
dependency.removeIdentifier(i);
} }
} }
} }
@@ -382,31 +388,30 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
* @param dependency the dependency to analyze * @param dependency the dependency to analyze
*/ */
private void removeWrongVersionMatches(Dependency dependency) { private void removeWrongVersionMatches(Dependency dependency) {
final Set<Identifier> identifiers = dependency.getIdentifiers(); final Set<Identifier> identifiersToRemove = new HashSet<>();
final Iterator<Identifier> itr = identifiers.iterator();
final String fileName = dependency.getFileName(); final String fileName = dependency.getFileName();
if (fileName != null && fileName.contains("axis2")) { if (fileName != null && fileName.contains("axis2")) {
while (itr.hasNext()) { for (Identifier i : dependency.getIdentifiers()) {
final Identifier i = itr.next();
if ("cpe".equals(i.getType())) { if ("cpe".equals(i.getType())) {
final String cpe = i.getValue(); final String cpe = i.getValue();
if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis:") || "cpe:/a:apache:axis".equals(cpe))) { if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis:") || "cpe:/a:apache:axis".equals(cpe))) {
itr.remove(); identifiersToRemove.add(i);
} }
} }
} }
} else if (fileName != null && fileName.contains("axis")) { } else if (fileName != null && fileName.contains("axis")) {
while (itr.hasNext()) { for (Identifier i : dependency.getIdentifiers()) {
final Identifier i = itr.next();
if ("cpe".equals(i.getType())) { if ("cpe".equals(i.getType())) {
final String cpe = i.getValue(); final String cpe = i.getValue();
if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis2:") || "cpe:/a:apache:axis2".equals(cpe))) { if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis2:") || "cpe:/a:apache:axis2".equals(cpe))) {
itr.remove(); identifiersToRemove.add(i);
} }
} }
} }
} }
for (Identifier i : identifiersToRemove) {
dependency.removeIdentifier(i);
}
} }
/** /**
@@ -430,17 +435,13 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
final String newCpe3 = String.format("cpe:/a:sun:opensso:%s", identifier.getValue().substring(22)); final String newCpe3 = String.format("cpe:/a:sun:opensso:%s", identifier.getValue().substring(22));
final String newCpe4 = String.format("cpe:/a:oracle:opensso:%s", identifier.getValue().substring(22)); final String newCpe4 = String.format("cpe:/a:oracle:opensso:%s", identifier.getValue().substring(22));
try { try {
dependency.addIdentifier("cpe", dependency.addIdentifier("cpe", newCpe,
newCpe,
String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe, "UTF-8"))); String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe, "UTF-8")));
dependency.addIdentifier("cpe", dependency.addIdentifier("cpe", newCpe2,
newCpe2,
String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe2, "UTF-8"))); String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe2, "UTF-8")));
dependency.addIdentifier("cpe", dependency.addIdentifier("cpe", newCpe3,
newCpe3,
String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe3, "UTF-8"))); String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe3, "UTF-8")));
dependency.addIdentifier("cpe", dependency.addIdentifier("cpe", newCpe4,
newCpe4,
String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe4, "UTF-8"))); String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe4, "UTF-8")));
} catch (UnsupportedEncodingException ex) { } catch (UnsupportedEncodingException ex) {
LOGGER.debug("", ex); LOGGER.debug("", ex);

View File

@@ -198,7 +198,7 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
vuln.setVulnerableSoftware(new HashSet<>(Arrays.asList(vs))); vuln.setVulnerableSoftware(new HashSet<>(Arrays.asList(vs)));
// Add the vulnerability to package.json // Add the vulnerability to package.json
dependency.getVulnerabilities().add(vuln); dependency.addVulnerability(vuln);
} }
/* /*
@@ -323,9 +323,12 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer {
* dependency will not actually exist but needs to be unique (due to the use of Set in Dependency). * dependency will not actually exist but needs to be unique (due to the use of Set in Dependency).
* The use of related dependencies is a way to specify the actual software BOM in package.json. * The use of related dependencies is a way to specify the actual software BOM in package.json.
*/ */
//TODO is this actually correct? or should these be transitive dependencies?
final Dependency nodeModule = new Dependency(new File(dependency.getActualFile() + "#" + entry.getKey()), true); final Dependency nodeModule = new Dependency(new File(dependency.getActualFile() + "#" + entry.getKey()), true);
nodeModule.setDisplayFileName(entry.getKey()); nodeModule.setDisplayFileName(entry.getKey());
nodeModule.setIdentifiers(new HashSet<>(Arrays.asList(moduleName, moduleVersion, moduleDepType))); nodeModule.addIdentifier(moduleName);
nodeModule.addIdentifier(moduleVersion);
nodeModule.addIdentifier(moduleDepType);
dependency.addRelatedDependency(nodeModule); dependency.addRelatedDependency(nodeModule);
} }
} }

View File

@@ -59,7 +59,7 @@ public class NvdCveAnalyzer extends AbstractAnalyzer {
try { try {
final String value = id.getValue(); final String value = id.getValue();
final List<Vulnerability> vulns = cveDB.getVulnerabilities(value); final List<Vulnerability> vulns = cveDB.getVulnerabilities(value);
dependency.getVulnerabilities().addAll(vulns); dependency.addVulnerabilities(vulns);
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
throw new AnalysisException(ex); throw new AnalysisException(ex);
} }
@@ -70,7 +70,7 @@ public class NvdCveAnalyzer extends AbstractAnalyzer {
try { try {
final String value = id.getValue(); final String value = id.getValue();
final List<Vulnerability> vulns = cveDB.getVulnerabilities(value); final List<Vulnerability> vulns = cveDB.getVulnerabilities(value);
dependency.getSuppressedVulnerabilities().addAll(vulns); dependency.addSuppressedVulnerabilities(vulns);
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
throw new AnalysisException(ex); throw new AnalysisException(ex);
} }

View File

@@ -365,7 +365,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
vulnerability.setName(advisory); vulnerability.setName(advisory);
} }
if (null != dependency) { if (null != dependency) {
dependency.getVulnerabilities().add(vulnerability); // needed to wait for vulnerability name to avoid NPE dependency.addVulnerability(vulnerability);
} }
LOGGER.debug("bundle-audit ({}): {}", parentName, nextLine); LOGGER.debug("bundle-audit ({}): {}", parentName, nextLine);
} }

View File

@@ -22,12 +22,13 @@ import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
@@ -44,7 +45,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@NotThreadSafe @ThreadSafe
public class Dependency extends EvidenceCollection implements Serializable, Comparable<Dependency> { public class Dependency extends EvidenceCollection implements Serializable, Comparable<Dependency> {
/** /**
@@ -82,7 +83,7 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
/** /**
* A list of Identifiers. * A list of Identifiers.
*/ */
private Set<Identifier> identifiers = new TreeSet<>(); private final Set<Identifier> identifiers = new TreeSet<>();
/** /**
* The file name to display in reports. * The file name to display in reports.
*/ */
@@ -90,11 +91,11 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
/** /**
* A set of identifiers that have been suppressed. * A set of identifiers that have been suppressed.
*/ */
private Set<Identifier> suppressedIdentifiers = new TreeSet<>(); private final Set<Identifier> suppressedIdentifiers = new TreeSet<>();
/** /**
* A set of vulnerabilities that have been suppressed. * A set of vulnerabilities that have been suppressed.
*/ */
private SortedSet<Vulnerability> suppressedVulnerabilities = new TreeSet<>(new VulnerabilityComparator()); private final SortedSet<Vulnerability> suppressedVulnerabilities = new TreeSet<>(new VulnerabilityComparator());
/** /**
* The description of the JAR file. * The description of the JAR file.
*/ */
@@ -106,19 +107,19 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
/** /**
* A list of vulnerabilities for this dependency. * A list of vulnerabilities for this dependency.
*/ */
private SortedSet<Vulnerability> vulnerabilities = new TreeSet<>(new VulnerabilityComparator()); private final SortedSet<Vulnerability> vulnerabilities = new TreeSet<>(new VulnerabilityComparator());
/** /**
* A collection of related dependencies. * A collection of related dependencies.
*/ */
private Set<Dependency> relatedDependencies = new TreeSet<>(); private final Set<Dependency> relatedDependencies = new TreeSet<>();
/** /**
* A list of projects that reference this dependency. * A list of projects that reference this dependency.
*/ */
private Set<String> projectReferences = new HashSet<>(); private final Set<String> projectReferences = new HashSet<>();
/** /**
* A list of available versions. * A list of available versions.
*/ */
private List<String> availableVersions = new ArrayList<>(); private final List<String> availableVersions = new ArrayList<>();
/** /**
* Defines an actual or virtual dependency. * Defines an actual or virtual dependency.
@@ -322,21 +323,22 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
} }
/** /**
* Returns a List of Identifiers. * Returns an unmodifiable List of Identifiers.
* *
* @return an ArrayList of Identifiers * @return an unmodifiable List of Identifiers
*/ */
public Set<Identifier> getIdentifiers() { public synchronized Set<Identifier> getIdentifiers() {
return this.identifiers; return Collections.unmodifiableSet(new HashSet<>(identifiers));
} }
/** /**
* Sets a List of Identifiers. * Adds a set of Identifiers to the current list of identifiers. Only used
* for testing.
* *
* @param identifiers A list of Identifiers * @param identifiers A set of Identifiers
*/ */
public void setIdentifiers(Set<Identifier> identifiers) { protected synchronized void addIdentifiers(Set<Identifier> identifiers) {
this.identifiers = identifiers; this.identifiers.addAll(identifiers);
} }
/** /**
@@ -347,7 +349,7 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* @param value the value of the identifier * @param value the value of the identifier
* @param url the URL of the identifier * @param url the URL of the identifier
*/ */
public void addIdentifier(String type, String value, String url) { public synchronized void addIdentifier(String type, String value, String url) {
final Identifier i = new Identifier(type, value, url); final Identifier i = new Identifier(type, value, url);
this.identifiers.add(i); this.identifiers.add(i);
} }
@@ -361,12 +363,21 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* @param url the URL of the identifier * @param url the URL of the identifier
* @param confidence the confidence in the Identifier being accurate * @param confidence the confidence in the Identifier being accurate
*/ */
public void addIdentifier(String type, String value, String url, Confidence confidence) { public synchronized void addIdentifier(String type, String value, String url, Confidence confidence) {
final Identifier i = new Identifier(type, value, url); final Identifier i = new Identifier(type, value, url);
i.setConfidence(confidence); i.setConfidence(confidence);
this.identifiers.add(i); this.identifiers.add(i);
} }
/**
* Removes an identifier from the list of identifiers.
*
* @param i the identifier to remove
*/
public synchronized void removeIdentifier(Identifier i) {
this.identifiers.remove(i);
}
/** /**
* Adds the maven artifact as evidence. * Adds the maven artifact as evidence.
* *
@@ -386,15 +397,17 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
} }
if (mavenArtifact.getArtifactUrl() != null && !mavenArtifact.getArtifactUrl().isEmpty()) { if (mavenArtifact.getArtifactUrl() != null && !mavenArtifact.getArtifactUrl().isEmpty()) {
boolean found = false; boolean found = false;
for (Identifier i : this.getIdentifiers()) { synchronized (this) {
if ("maven".equals(i.getType()) && i.getValue().equals(mavenArtifact.toString())) { for (Identifier i : this.identifiers) {
found = true; if ("maven".equals(i.getType()) && i.getValue().equals(mavenArtifact.toString())) {
i.setConfidence(Confidence.HIGHEST); found = true;
final String url = "http://search.maven.org/#search|ga|1|1%3A%22" + this.getSha1sum() + "%22"; i.setConfidence(Confidence.HIGHEST);
i.setUrl(url); final String url = "http://search.maven.org/#search|ga|1|1%3A%22" + this.getSha1sum() + "%22";
//i.setUrl(mavenArtifact.getArtifactUrl()); i.setUrl(url);
LOGGER.debug("Already found identifier {}. Confidence set to highest", i.getValue()); //i.setUrl(mavenArtifact.getArtifactUrl());
break; LOGGER.debug("Already found identifier {}. Confidence set to highest", i.getValue());
break;
}
} }
} }
if (!found) { if (!found) {
@@ -410,26 +423,17 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* *
* @param identifier the identifier to add * @param identifier the identifier to add
*/ */
public void addIdentifier(Identifier identifier) { public synchronized void addIdentifier(Identifier identifier) {
this.identifiers.add(identifier); this.identifiers.add(identifier);
} }
/** /**
* Get the value of suppressedIdentifiers. * Get the unmodifiable set of suppressedIdentifiers.
* *
* @return the value of suppressedIdentifiers * @return the value of suppressedIdentifiers
*/ */
public Set<Identifier> getSuppressedIdentifiers() { public synchronized Set<Identifier> getSuppressedIdentifiers() {
return suppressedIdentifiers; return Collections.unmodifiableSet(new HashSet<>(suppressedIdentifiers));
}
/**
* Set the value of suppressedIdentifiers.
*
* @param suppressedIdentifiers new value of suppressedIdentifiers
*/
public void setSuppressedIdentifiers(Set<Identifier> suppressedIdentifiers) {
this.suppressedIdentifiers = suppressedIdentifiers;
} }
/** /**
@@ -437,26 +441,17 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* *
* @param identifier an identifier that was suppressed. * @param identifier an identifier that was suppressed.
*/ */
public void addSuppressedIdentifier(Identifier identifier) { public synchronized void addSuppressedIdentifier(Identifier identifier) {
this.suppressedIdentifiers.add(identifier); this.suppressedIdentifiers.add(identifier);
} }
/** /**
* Get the value of suppressedVulnerabilities. * Get an unmodifiable sorted set of suppressedVulnerabilities.
* *
* @return the value of suppressedVulnerabilities * @return the unmodifiable sorted set of suppressedVulnerabilities
*/ */
public SortedSet<Vulnerability> getSuppressedVulnerabilities() { public synchronized SortedSet<Vulnerability> getSuppressedVulnerabilities() {
return suppressedVulnerabilities; return Collections.unmodifiableSortedSet(new TreeSet<>(suppressedVulnerabilities));
}
/**
* Set the value of suppressedVulnerabilities.
*
* @param suppressedVulnerabilities new value of suppressedVulnerabilities
*/
public void setSuppressedVulnerabilities(SortedSet<Vulnerability> suppressedVulnerabilities) {
this.suppressedVulnerabilities = suppressedVulnerabilities;
} }
/** /**
@@ -464,7 +459,7 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* *
* @param vulnerability the vulnerability that was suppressed * @param vulnerability the vulnerability that was suppressed
*/ */
public void addSuppressedVulnerability(Vulnerability vulnerability) { public synchronized void addSuppressedVulnerability(Vulnerability vulnerability) {
this.suppressedVulnerabilities.add(vulnerability); this.suppressedVulnerabilities.add(vulnerability);
} }
@@ -505,21 +500,12 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
} }
/** /**
* Get the list of vulnerabilities. * Get the unmodifiable sorted set of vulnerabilities.
* *
* @return the list of vulnerabilities * @return the unmodifiable sorted set of vulnerabilities
*/ */
public SortedSet<Vulnerability> getVulnerabilities() { public synchronized SortedSet<Vulnerability> getVulnerabilities() {
return vulnerabilities; return Collections.unmodifiableSortedSet(new TreeSet<>(vulnerabilities));
}
/**
* Set the value of vulnerabilities.
*
* @param vulnerabilities new value of vulnerabilities
*/
public void setVulnerabilities(SortedSet<Vulnerability> vulnerabilities) {
this.vulnerabilities = vulnerabilities;
} }
/** /**
@@ -550,39 +536,48 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
/** /**
* Adds a vulnerability to the dependency. * Adds a vulnerability to the dependency.
* *
* @param vulnerability a vulnerability outlining a vulnerability. * @param vulnerability a vulnerability
*/ */
public void addVulnerability(Vulnerability vulnerability) { public synchronized void addVulnerability(Vulnerability vulnerability) {
this.vulnerabilities.add(vulnerability); this.vulnerabilities.add(vulnerability);
} }
/** /**
* Get the value of {@link #relatedDependencies}. This field is used to * Adds a list of vulnerabilities to the dependency.
* collect other dependencies which really represent the same dependency,
* and may be presented as one item in reports.
* *
* @return the value of relatedDependencies * @param vulnerabilities a list of vulnerabilities
*/ */
public Set<Dependency> getRelatedDependencies() { public synchronized void addVulnerabilities(List<Vulnerability> vulnerabilities) {
return relatedDependencies; this.vulnerabilities.addAll(vulnerabilities);
} }
/** /**
* Get the value of projectReferences. * Removes the given vulnerability from the list.
* *
* @return the value of projectReferences * @param v the vulnerability to remove
*/ */
public Set<String> getProjectReferences() { public synchronized void removeVulnerability(Vulnerability v) {
return projectReferences; this.vulnerabilities.remove(v);
} }
/** /**
* Set the value of projectReferences. * Get the unmodifiable set of {@link #relatedDependencies}. This field is
* used to collect other dependencies which really represent the same
* dependency, and may be presented as one item in reports.
* *
* @param projectReferences new value of projectReferences * @return the unmodifiable set of relatedDependencies
*/ */
public void setProjectReferences(Set<String> projectReferences) { public synchronized Set<Dependency> getRelatedDependencies() {
this.projectReferences = projectReferences; return Collections.unmodifiableSet(new HashSet<>(relatedDependencies));
}
/**
* Get the unmodifiable set of projectReferences.
*
* @return the unmodifiable set of projectReferences
*/
public synchronized Set<String> getProjectReferences() {
return Collections.unmodifiableSet(new HashSet<>(projectReferences));
} }
/** /**
@@ -590,7 +585,7 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* *
* @param projectReference a project reference * @param projectReference a project reference
*/ */
public void addProjectReference(String projectReference) { public synchronized void addProjectReference(String projectReference) {
this.projectReferences.add(projectReference); this.projectReferences.add(projectReference);
} }
@@ -599,19 +594,10 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* *
* @param projectReferences a set of project references * @param projectReferences a set of project references
*/ */
public void addAllProjectReferences(Set<String> projectReferences) { public synchronized void addAllProjectReferences(Set<String> projectReferences) {
this.projectReferences.addAll(projectReferences); this.projectReferences.addAll(projectReferences);
} }
/**
* Set the value of relatedDependencies.
*
* @param relatedDependencies new value of relatedDependencies
*/
public void setRelatedDependencies(Set<Dependency> relatedDependencies) {
this.relatedDependencies = relatedDependencies;
}
/** /**
* Adds a related dependency. The internal collection is normally a * Adds a related dependency. The internal collection is normally a
* {@link java.util.TreeSet}, which relies on * {@link java.util.TreeSet}, which relies on
@@ -621,7 +607,7 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* *
* @param dependency a reference to the related dependency * @param dependency a reference to the related dependency
*/ */
public void addRelatedDependency(Dependency dependency) { public synchronized void addRelatedDependency(Dependency dependency) {
if (this == dependency) { if (this == dependency) {
LOGGER.warn("Attempted to add a circular reference - please post the log file to issue #172 here " LOGGER.warn("Attempted to add a circular reference - please post the log file to issue #172 here "
+ "https://github.com/jeremylong/DependencyCheck/issues/172"); + "https://github.com/jeremylong/DependencyCheck/issues/172");
@@ -634,22 +620,22 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
} }
} }
/**
* Removes a related dependency.
*
* @param dependency the dependency to remove
*/
public synchronized void removeRelatedDependencies(Dependency dependency) {
this.relatedDependencies.remove(dependency);
}
/** /**
* Get the value of availableVersions. * Get the value of availableVersions.
* *
* @return the value of availableVersions * @return the value of availableVersions
*/ */
public List<String> getAvailableVersions() { public synchronized List<String> getAvailableVersions() {
return availableVersions; return Collections.unmodifiableList(new ArrayList<>(availableVersions));
}
/**
* Set the value of availableVersions.
*
* @param availableVersions new value of availableVersions
*/
public void setAvailableVersions(List<String> availableVersions) {
this.availableVersions = availableVersions;
} }
/** /**
@@ -657,7 +643,7 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
* *
* @param version the version to add to the list * @param version the version to add to the list
*/ */
public void addAvailableVersion(String version) { public synchronized void addAvailableVersion(String version) {
this.availableVersions.add(version); this.availableVersions.add(version);
} }
@@ -746,4 +732,13 @@ public class Dependency extends EvidenceCollection implements Serializable, Comp
return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath
+ "', filePath='" + filePath + "', packagePath='" + packagePath + "'}"; + "', filePath='" + filePath + "', packagePath='" + packagePath + "'}";
} }
/**
* Add a list of suppressed vulnerabilities to the collection.
*
* @param vulns the list of suppressed vulnerabilities to add
*/
public synchronized void addSuppressedVulnerabilities(List<Vulnerability> vulns) {
this.suppressedVulnerabilities.addAll(vulns);
}
} }

View File

@@ -119,13 +119,13 @@ class EvidenceCollection implements Serializable {
switch (type) { switch (type) {
case VENDOR: case VENDOR:
list = Collections.unmodifiableSet(vendors); list = Collections.unmodifiableSet(new HashSet<>(vendors));
break; break;
case PRODUCT: case PRODUCT:
list = Collections.unmodifiableSet(products); list = Collections.unmodifiableSet(new HashSet<>(products));
break; break;
case VERSION: case VERSION:
list = Collections.unmodifiableSet(versions); list = Collections.unmodifiableSet(new HashSet<>(versions));
break; break;
default: default:
return null; return null;
@@ -254,7 +254,7 @@ class EvidenceCollection implements Serializable {
* @return an unmodifiable set of vendor weighting strings * @return an unmodifiable set of vendor weighting strings
*/ */
public synchronized Set<String> getVendorWeightings() { public synchronized Set<String> getVendorWeightings() {
return Collections.unmodifiableSet(vendorWeightings); return Collections.unmodifiableSet(new HashSet<>(vendorWeightings));
} }
/** /**
@@ -265,7 +265,7 @@ class EvidenceCollection implements Serializable {
* @return an unmodifiable set of vendor weighting strings * @return an unmodifiable set of vendor weighting strings
*/ */
public synchronized Set<String> getProductWeightings() { public synchronized Set<String> getProductWeightings() {
return Collections.unmodifiableSet(productWeightings); return Collections.unmodifiableSet(new HashSet<>(productWeightings));
} }
/** /**
@@ -278,11 +278,11 @@ class EvidenceCollection implements Serializable {
if (null != type) { if (null != type) {
switch (type) { switch (type) {
case VENDOR: case VENDOR:
return Collections.unmodifiableSet(vendors); return Collections.unmodifiableSet(new HashSet<>(vendors));
case PRODUCT: case PRODUCT:
return Collections.unmodifiableSet(products); return Collections.unmodifiableSet(new HashSet<>(products));
case VERSION: case VERSION:
return Collections.unmodifiableSet(versions); return Collections.unmodifiableSet(new HashSet<>(versions));
default: default:
break; break;
} }

View File

@@ -278,6 +278,8 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
* @param vulnSoftware the vulnerable software * @param vulnSoftware the vulnerable software
*/ */
public void updateVulnerableSoftware(VulnerableSoftware vulnSoftware) { public void updateVulnerableSoftware(VulnerableSoftware vulnSoftware) {
//this behavior is because the vuln software being updated may have
// a value that is not included in the hash/comparison
if (vulnerableSoftware.contains(vulnSoftware)) { if (vulnerableSoftware.contains(vulnSoftware)) {
vulnerableSoftware.remove(vulnSoftware); vulnerableSoftware.remove(vulnSoftware);
} }

View File

@@ -18,8 +18,10 @@
package org.owasp.dependencycheck.xml.suppression; package org.owasp.dependencycheck.xml.suppression;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
@@ -365,9 +367,8 @@ public class SuppressionRule {
} }
if (this.hasCpe()) { if (this.hasCpe()) {
final Iterator<Identifier> itr = dependency.getIdentifiers().iterator(); Set<Identifier> removalList = new HashSet<>();
while (itr.hasNext()) { for (Identifier i : dependency.getIdentifiers()) {
final Identifier i = itr.next();
for (PropertyType c : this.cpe) { for (PropertyType c : this.cpe) {
if (identifierMatches("cpe", c, i)) { if (identifierMatches("cpe", c, i)) {
if (!isBase()) { if (!isBase()) {
@@ -376,19 +377,22 @@ public class SuppressionRule {
} }
dependency.addSuppressedIdentifier(i); dependency.addSuppressedIdentifier(i);
} }
itr.remove(); removalList.add(i);
break; break;
} }
} }
} }
for (Identifier i : removalList) {
dependency.removeIdentifier(i);
}
} }
if (hasCve() || hasCwe() || hasCvssBelow()) { if (hasCve() || hasCwe() || hasCvssBelow()) {
final Iterator<Vulnerability> itr = dependency.getVulnerabilities().iterator(); Set<Vulnerability> removeVulns = new HashSet<>();
while (itr.hasNext()) { for (Vulnerability v : dependency.getVulnerabilities()) {
boolean remove = false; boolean remove = false;
final Vulnerability v = itr.next();
for (String entry : this.cve) { for (String entry : this.cve) {
if (entry.equalsIgnoreCase(v.getName())) { if (entry.equalsIgnoreCase(v.getName())) {
removeVulns.add(v);
remove = true; remove = true;
break; break;
} }
@@ -400,6 +404,7 @@ public class SuppressionRule {
final String toTest = v.getCwe().substring(0, toMatch.length()).toUpperCase(); final String toTest = v.getCwe().substring(0, toMatch.length()).toUpperCase();
if (toTest.equals(toMatch)) { if (toTest.equals(toMatch)) {
remove = true; remove = true;
removeVulns.add(v);
break; break;
} }
} }
@@ -409,6 +414,7 @@ public class SuppressionRule {
for (float cvss : this.cvssBelow) { for (float cvss : this.cvssBelow) {
if (v.getCvssScore() < cvss) { if (v.getCvssScore() < cvss) {
remove = true; remove = true;
removeVulns.add(v);
break; break;
} }
} }
@@ -420,9 +426,11 @@ public class SuppressionRule {
} }
dependency.addSuppressedVulnerability(v); dependency.addSuppressedVulnerability(v);
} }
itr.remove();
} }
} }
for (Vulnerability v : removeVulns) {
dependency.removeVulnerability(v);
}
} }
} }

View File

@@ -177,7 +177,7 @@ public class DependencyTest extends BaseTest {
public void testSetIdentifiers() { public void testSetIdentifiers() {
Set<Identifier> identifiers = new HashSet<>(); Set<Identifier> identifiers = new HashSet<>();
Dependency instance = new Dependency(); Dependency instance = new Dependency();
instance.setIdentifiers(identifiers); instance.addIdentifiers(identifiers);
assertNotNull(instance.getIdentifiers()); assertNotNull(instance.getIdentifiers());
} }
@@ -220,7 +220,7 @@ public class DependencyTest extends BaseTest {
MavenArtifact mavenArtifact = new MavenArtifact("group", "artifact", "version", "url"); MavenArtifact mavenArtifact = new MavenArtifact("group", "artifact", "version", "url");
instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH); instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH);
assertTrue(instance.contains(EvidenceType.VENDOR, Confidence.HIGH)); assertTrue(instance.contains(EvidenceType.VENDOR, Confidence.HIGH));
assertTrue(instance.size()>1); assertTrue(instance.size() > 1);
assertFalse(instance.getIdentifiers().isEmpty()); assertFalse(instance.getIdentifiers().isEmpty());
} }
@@ -233,7 +233,7 @@ public class DependencyTest extends BaseTest {
MavenArtifact mavenArtifact = new MavenArtifact(null, null, null, null); MavenArtifact mavenArtifact = new MavenArtifact(null, null, null, null);
instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH); instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH);
assertFalse(instance.getEvidence(EvidenceType.VENDOR).contains(Confidence.HIGH)); assertFalse(instance.getEvidence(EvidenceType.VENDOR).contains(Confidence.HIGH));
assertTrue(instance.size()==0); assertTrue(instance.size() == 0);
assertTrue(instance.getIdentifiers().isEmpty()); assertTrue(instance.getIdentifiers().isEmpty());
} }
} }