Vulnerability.java
/*
* This file is part of dependency-check-core.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.dependency;
import io.github.jeremylong.openvulnerability.client.nvd.CvssV2;
import io.github.jeremylong.openvulnerability.client.nvd.CvssV3;
import io.github.jeremylong.openvulnerability.client.nvd.CvssV4;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.jetbrains.annotations.NotNull;
import org.owasp.dependencycheck.utils.SeverityUtil;
/**
* Contains the information about a vulnerability.
*
* @author Jeremy Long
*/
@NotThreadSafe
public class Vulnerability implements Serializable, Comparable<Vulnerability> {
/**
* An enumeration for the source of vulnerability.
*/
public enum Source {
/**
* National Vulnerability Database.
*/
NVD,
/**
* NPM Public Advisory.
*/
NPM,
/**
* RetireJS.
*/
RETIREJS,
/**
* Sonatype OSS Index.
*/
OSSINDEX,
/**
* Vulnerability from Bundle Audit.
*/
BUNDLEAUDIT,
/**
* Vulnerability from Mix Audit.
*/
MIXAUDIT
}
/**
* The serial version uid.
*/
private static final long serialVersionUID = 307319490326651053L;
/**
* The name of the vulnerability.
*/
private String name;
/**
* the description of the vulnerability.
*/
private String description;
/**
* Data if the vulnerability is a known exploited vulnerability.
*/
private org.owasp.dependencycheck.data.knownexploited.json.Vulnerability knownExploitedVulnerability;
/**
* References for this vulnerability.
*/
private final Set<Reference> references = Collections.synchronizedSet(new HashSet<>());
/**
* A set of vulnerable software.
*/
private final Set<VulnerableSoftware> vulnerableSoftware = new HashSet<>();
/**
* The CWE(s) for the vulnerability.
*/
private final CweSet cwes = new CweSet();
/**
* The severity a {@link Source} has assigned for which a CVSS score is not
* available. Severity could be anything ranging from 'critical', 'high',
* 'medium', and 'low', to non-traditional labels like 'major', 'minor', and
* 'important'.
*/
private String unscoredSeverity;
/**
* The CVSS V2 scoring information.
*/
private CvssV2 cvssV2;
/**
* The CVSS V3 scoring information.
*/
private CvssV3 cvssV3;
/**
* The CVSS V4 scoring information.
*/
private CvssV4 cvssV4;
/**
* The Vulnerable Software that caused this vulnerability to be flagged.
*/
private VulnerableSoftware matchedVulnerableSoftware;
/**
* Notes about the vulnerability. Generally used for suppression
* information.
*/
private String notes;
/**
* The source that identified the vulnerability.
*/
private Source source = null;
/**
* Default constructor.
*/
public Vulnerability() {
//empty
}
/**
* Constructs a new Vulnerability by its name.
*
* @param name the name of the vulnerability
*/
public Vulnerability(String name) {
this.name = name;
}
/**
* Get the value of name.
*
* @return the value of name
*/
public String getName() {
return name;
}
/**
* Set the value of name.
*
* @param name new value of name
*/
public void setName(String name) {
this.name = name;
}
/**
* Get the value of description.
*
* @return the value of description
*/
public String getDescription() {
return description;
}
/**
* Set the value of description.
*
* @param description new value of description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Get the value of references.
*
* @return the value of references
*/
public Set<Reference> getReferences() {
return references;
}
/**
* Returns the list of references. This is primarily used within the
* generated reports.
*
* @param sorted whether the returned list should be sorted
* @return the list of references
*/
public List<Reference> getReferences(boolean sorted) {
final List<Reference> sortedRefs = new ArrayList<>(this.references);
if (sorted) {
Collections.sort(sortedRefs);
}
return sortedRefs;
}
/**
* Adds the references to the collection.
*
* @param references a collection of references to add
*/
public void addReferences(Set<Reference> references) {
this.references.addAll(references);
}
/**
* Adds a reference to the references collection.
*
* @param ref a reference for the vulnerability
*/
public void addReference(Reference ref) {
this.references.add(ref);
}
/**
* Adds a reference.
*
* @param referenceSource the source of the reference
* @param referenceName the referenceName of the reference
* @param referenceUrl the url of the reference
*/
public void addReference(String referenceSource, String referenceName, String referenceUrl) {
final Reference ref = new Reference();
ref.setSource(referenceSource);
ref.setName(referenceName);
ref.setUrl(referenceUrl);
this.references.add(ref);
}
/**
* Adds information about known exploited vulnerabilities.
*
* @param kev the known exploited vulnerability information
*/
public void setKnownExploitedVulnerability(org.owasp.dependencycheck.data.knownexploited.json.Vulnerability kev) {
this.knownExploitedVulnerability = kev;
}
/**
* Get the value of knownExploitedVulnerability.
*
* @return the value of knownExploitedVulnerability
*/
public org.owasp.dependencycheck.data.knownexploited.json.Vulnerability getKnownExploitedVulnerability() {
return knownExploitedVulnerability;
}
/**
* Get the value of vulnerableSoftware.
*
* @return the value of vulnerableSoftware
*/
public Set<VulnerableSoftware> getVulnerableSoftware() {
return vulnerableSoftware;
}
/**
* Returns a sorted list of vulnerable software. This is primarily used for
* display within reports.
*
* @param sorted whether or not the list should be sorted
* @return the list of vulnerable software
*/
@SuppressWarnings("unchecked")
public List<VulnerableSoftware> getVulnerableSoftware(boolean sorted) {
synchronized (vulnerableSoftware) {
final List<VulnerableSoftware> sortedVulnerableSoftware = new ArrayList<>(this.vulnerableSoftware);
if (sorted) {
Collections.sort(sortedVulnerableSoftware);
}
return sortedVulnerableSoftware;
}
}
/**
* Adds the vulnerableSoftware to the collection.
*
* @param vulnerableSoftware a collection of vulnerable software
*/
public void addVulnerableSoftware(Set<VulnerableSoftware> vulnerableSoftware) {
this.vulnerableSoftware.addAll(vulnerableSoftware);
}
/**
* Adds an entry for vulnerable software.
*
* @param software the vulnerable software reference to add
*/
public void addVulnerableSoftware(VulnerableSoftware software) {
vulnerableSoftware.add(software);
}
/**
* Get the CVSS V2 scoring information.
*
* @return the CVSS V2 scoring information
*/
public CvssV2 getCvssV2() {
return cvssV2;
}
/**
* Sets the CVSS V2 scoring information.
*
* @param cvssV2 the CVSS V2 scoring information
*/
public void setCvssV2(CvssV2 cvssV2) {
this.cvssV2 = cvssV2;
}
/**
* Get the CVSS V3 scoring information.
*
* @return the CVSS V3 scoring information
*/
public CvssV3 getCvssV3() {
return cvssV3;
}
/**
* Sets the CVSS V3 scoring information.
*
* @param cvssV3 the CVSS V3 scoring information
*/
public void setCvssV3(CvssV3 cvssV3) {
this.cvssV3 = cvssV3;
}
/**
* Get the CVSS V3 scoring information.
*
* @return the CVSS V3 scoring information
*/
public CvssV4 getCvssV4() {
return cvssV4;
}
/**
* Sets the CVSS V4 scoring information.
*
* @param cvssV4 the CVSS V4 scoring information
*/
public void setCvssV4(CvssV4 cvssV4) {
this.cvssV4 = cvssV4;
}
/**
* Get the set of CWEs.
*
* @return the set of CWEs
*/
public CweSet getCwes() {
return cwes;
}
/**
* Adds a CWE to the set.
*
* @param cwe new CWE to add
*/
public void addCwe(String cwe) {
this.cwes.addCwe(cwe);
}
/**
* Retrieves the severity a {@link Source} has assigned for which a CVSS
* score is not available. Severity could be anything ranging from
* 'critical', 'high', 'medium', and 'low', to non-traditional labels like
* 'major', 'minor', and 'important'.
*
* @return the un-scored severity
*/
public String getUnscoredSeverity() {
return unscoredSeverity;
}
/**
* Sets the severity a {@link Source} has assigned for which a CVSS score is
* not available. Severity could be anything ranging from 'critical',
* 'high', 'medium', and 'low', to non-traditional labels like 'major',
* 'minor', and 'important'.
*
* @param unscoredSeverity the un-scored severity
*/
public void setUnscoredSeverity(String unscoredSeverity) {
this.unscoredSeverity = unscoredSeverity;
}
/**
* Get the value of notes from suppression notes.
*
* @return the value of notes
*/
public String getNotes() {
return notes;
}
/**
* Set the value of notes.
*
* @param notes new value of notes
*/
public void setNotes(String notes) {
this.notes = notes;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Vulnerability)) {
return false;
}
if (this == obj) {
return true;
}
final Vulnerability other = (Vulnerability) obj;
return new EqualsBuilder()
.append(name, other.name)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(3, 73)
.append(name)
.toHashCode();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Vulnerability ");
sb.append(this.name);
sb.append("\nReferences:\n");
for (Reference reference : getReferences(true)) {
sb.append("=> ");
sb.append(reference);
sb.append("\n");
}
sb.append("\nSoftware:\n");
for (VulnerableSoftware software : getVulnerableSoftware(true)) {
sb.append("=> ");
sb.append(software);
sb.append("\n");
}
return sb.toString();
}
/**
* Compares two vulnerabilities.<br>
* Natural order of vulnerabilities is defined as decreasing in severity and
* alphabetically by name for equal severity. This way the most severe
* issues are listed first in a sorted list.
* <br>
* This uses a
* {@link #bestEffortSeverityLevelForSorting() best-effort ordering} for
* severity as the variety of sources do not guarantee a consistent
* availability of standardized severity scores. The bestEffort severity
* level estimation will use CVSSv3 baseScore for comparison when available
* on both sides. If any of the vulnerabilities does not have a CVSSv3 score
* the sort order may be off, but it will be consistent.
* <br>
* The ranking (high to low) of severity can be informally represented as {@code <CVSSv3 critical> >> <Unscored recognized critical> >>
* <Unscored unrecognized (assumed Critical)> >> <Score-based comparison for high-or-lower scoring severities with
* recognized unscored severities taking the lower bound of the comparable CVSSv3 range>
* }
*
* @param o a vulnerability to be compared
* @return a negative integer, zero, or a positive integer as this object is
* less than , equal to, or greater than the specified vulnerability
* @see #bestEffortSeverityLevelForSorting()
*/
@Override
public int compareTo(@NotNull Vulnerability o) {
return new CompareToBuilder()
.append(o.bestEffortSeverityLevelForSorting(), this.bestEffortSeverityLevelForSorting())
.append(this.name, o.name)
.toComparison();
}
/**
* Compute a best-effort score for the severity of a vulnerability for the
* purpose of sorting.
* <br>
* Note that CVSSv2 and CVSSv3 scores are essentially uncomparable. For the
* purpose of sorting we nevertheless treat them comparable, with an
* exception for the 9.0-10.0 range. For that entire range CVSSv3 is scoring
* more severe than CVSSv2, so that the 'CRITICAL' severity is retained to
* be reported as the highest severity after sorting on descending severity.
* <br>
* For vulnerabilities not scored with a CVSS score we estimate a score from
* the severity text. For textual severities assumed or semantically
* confirmed to be of a critical nature we assign a value in between the
* highest CVSSv2 HIGH and the lowest CVSSv3 CRITICAL severity level.
*
* @see SeverityUtil#estimatedSortAdjustedCVSSv3(String)
* @see SeverityUtil#sortAdjustedCVSSv3BaseScore(float)
* @return A float value that allows for best-effort sorting on
* vulnerability severity
*/
private Double bestEffortSeverityLevelForSorting() {
if (this.cvssV3 != null) {
return SeverityUtil.sortAdjustedCVSSv3BaseScore(this.cvssV3.getCvssData().getBaseScore());
}
if (this.cvssV2 != null) {
return this.cvssV2.getCvssData().getBaseScore();
}
return SeverityUtil.estimatedSortAdjustedCVSSv3(this.unscoredSeverity);
}
/**
* The report text to use for highest severity when this issue is ranked
* highest.
*
* @return The string to display in the report, clarifying for unrecognized
* unscored severities that critical is assumed.
*/
public String getHighestSeverityText() {
if (this.cvssV3 != null) {
return this.cvssV3.getCvssData().getBaseSeverity().value().toUpperCase();
}
if (this.cvssV2 != null) {
return this.cvssV2.getCvssData().getBaseSeverity().toUpperCase();
}
return SeverityUtil.unscoredToSeveritytext(this.unscoredSeverity).toUpperCase();
}
/**
* Sets the CPE that caused this vulnerability to be flagged.
*
* @param software a Vulnerable Software identifier
*/
public void setMatchedVulnerableSoftware(VulnerableSoftware software) {
matchedVulnerableSoftware = software;
}
/**
* Get the value of matchedVulnerableSoftware.
*
* @return the value of matchedVulnerableSoftware
*/
public VulnerableSoftware getMatchedVulnerableSoftware() {
return matchedVulnerableSoftware;
}
/**
* Returns the source that identified the vulnerability.
*
* @return the source
*/
public Source getSource() {
return source;
}
/**
* Sets the source that identified the vulnerability.
*
* @param source the source
*/
public void setSource(Source source) {
this.source = source;
}
}