View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.dependency;
19  
20  import io.github.jeremylong.openvulnerability.client.nvd.CvssV2;
21  import io.github.jeremylong.openvulnerability.client.nvd.CvssV3;
22  import io.github.jeremylong.openvulnerability.client.nvd.CvssV4;
23  import java.io.Serializable;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Set;
29  import javax.annotation.concurrent.NotThreadSafe;
30  
31  import org.apache.commons.lang3.builder.CompareToBuilder;
32  import org.apache.commons.lang3.builder.EqualsBuilder;
33  import org.apache.commons.lang3.builder.HashCodeBuilder;
34  import org.jetbrains.annotations.NotNull;
35  import org.owasp.dependencycheck.utils.SeverityUtil;
36  
37  /**
38   * Contains the information about a vulnerability.
39   *
40   * @author Jeremy Long
41   */
42  @NotThreadSafe
43  public class Vulnerability implements Serializable, Comparable<Vulnerability> {
44  
45      /**
46       * An enumeration for the source of vulnerability.
47       */
48      public enum Source {
49          /**
50           * National Vulnerability Database.
51           */
52          NVD,
53          /**
54           * NPM Public Advisory.
55           */
56          NPM,
57          /**
58           * RetireJS.
59           */
60          RETIREJS,
61          /**
62           * Sonatype OSS Index.
63           */
64          OSSINDEX,
65          /**
66           * Vulnerability from Bundle Audit.
67           */
68          BUNDLEAUDIT,
69          /**
70           * Vulnerability from Mix Audit.
71           */
72          MIXAUDIT
73      }
74  
75      /**
76       * The serial version uid.
77       */
78      private static final long serialVersionUID = 307319490326651053L;
79  
80      /**
81       * The name of the vulnerability.
82       */
83      private String name;
84      /**
85       * the description of the vulnerability.
86       */
87      private String description;
88      /**
89       * Data if the vulnerability is a known exploited vulnerability.
90       */
91      private org.owasp.dependencycheck.data.knownexploited.json.Vulnerability knownExploitedVulnerability;
92      /**
93       * References for this vulnerability.
94       */
95      private final Set<Reference> references = Collections.synchronizedSet(new HashSet<>());
96      /**
97       * A set of vulnerable software.
98       */
99      private final Set<VulnerableSoftware> vulnerableSoftware = new HashSet<>();
100     /**
101      * The CWE(s) for the vulnerability.
102      */
103     private final CweSet cwes = new CweSet();
104     /**
105      * The severity a {@link Source} has assigned for which a CVSS score is not
106      * available. Severity could be anything ranging from 'critical', 'high',
107      * 'medium', and 'low', to non-traditional labels like 'major', 'minor', and
108      * 'important'.
109      */
110     private String unscoredSeverity;
111     /**
112      * The CVSS V2 scoring information.
113      */
114     private CvssV2 cvssV2;
115 
116     /**
117      * The CVSS V3 scoring information.
118      */
119     private CvssV3 cvssV3;
120 
121     /**
122      * The CVSS V4 scoring information.
123      */
124     private CvssV4 cvssV4;
125 
126     /**
127      * The Vulnerable Software that caused this vulnerability to be flagged.
128      */
129     private VulnerableSoftware matchedVulnerableSoftware;
130     /**
131      * Notes about the vulnerability. Generally used for suppression
132      * information.
133      */
134     private String notes;
135 
136     /**
137      * The source that identified the vulnerability.
138      */
139     private Source source = null;
140 
141     /**
142      * Default constructor.
143      */
144     public Vulnerability() {
145         //empty
146     }
147 
148     /**
149      * Constructs a new Vulnerability by its name.
150      *
151      * @param name the name of the vulnerability
152      */
153     public Vulnerability(String name) {
154         this.name = name;
155     }
156 
157     /**
158      * Get the value of name.
159      *
160      * @return the value of name
161      */
162     public String getName() {
163         return name;
164     }
165 
166     /**
167      * Set the value of name.
168      *
169      * @param name new value of name
170      */
171     public void setName(String name) {
172         this.name = name;
173     }
174 
175     /**
176      * Get the value of description.
177      *
178      * @return the value of description
179      */
180     public String getDescription() {
181         return description;
182     }
183 
184     /**
185      * Set the value of description.
186      *
187      * @param description new value of description
188      */
189     public void setDescription(String description) {
190         this.description = description;
191     }
192 
193     /**
194      * Get the value of references.
195      *
196      * @return the value of references
197      */
198     public Set<Reference> getReferences() {
199         return references;
200     }
201 
202     /**
203      * Returns the list of references. This is primarily used within the
204      * generated reports.
205      *
206      * @param sorted whether the returned list should be sorted
207      * @return the list of references
208      */
209     public List<Reference> getReferences(boolean sorted) {
210         final List<Reference> sortedRefs = new ArrayList<>(this.references);
211         if (sorted) {
212             Collections.sort(sortedRefs);
213         }
214         return sortedRefs;
215     }
216 
217     /**
218      * Adds the references to the collection.
219      *
220      * @param references a collection of references to add
221      */
222     public void addReferences(Set<Reference> references) {
223         this.references.addAll(references);
224     }
225 
226     /**
227      * Adds a reference to the references collection.
228      *
229      * @param ref a reference for the vulnerability
230      */
231     public void addReference(Reference ref) {
232         this.references.add(ref);
233     }
234 
235     /**
236      * Adds a reference.
237      *
238      * @param referenceSource the source of the reference
239      * @param referenceName the referenceName of the reference
240      * @param referenceUrl the url of the reference
241      */
242     public void addReference(String referenceSource, String referenceName, String referenceUrl) {
243         final Reference ref = new Reference();
244         ref.setSource(referenceSource);
245         ref.setName(referenceName);
246         ref.setUrl(referenceUrl);
247         this.references.add(ref);
248     }
249 
250     /**
251      * Adds information about known exploited vulnerabilities.
252      *
253      * @param kev the known exploited vulnerability information
254      */
255     public void setKnownExploitedVulnerability(org.owasp.dependencycheck.data.knownexploited.json.Vulnerability kev) {
256         this.knownExploitedVulnerability = kev;
257     }
258 
259     /**
260      * Get the value of knownExploitedVulnerability.
261      *
262      * @return the value of knownExploitedVulnerability
263      */
264     public org.owasp.dependencycheck.data.knownexploited.json.Vulnerability getKnownExploitedVulnerability() {
265         return knownExploitedVulnerability;
266     }
267 
268     /**
269      * Get the value of vulnerableSoftware.
270      *
271      * @return the value of vulnerableSoftware
272      */
273     public Set<VulnerableSoftware> getVulnerableSoftware() {
274         return vulnerableSoftware;
275     }
276 
277     /**
278      * Returns a sorted list of vulnerable software. This is primarily used for
279      * display within reports.
280      *
281      * @param sorted whether or not the list should be sorted
282      * @return the list of vulnerable software
283      */
284     @SuppressWarnings("unchecked")
285     public List<VulnerableSoftware> getVulnerableSoftware(boolean sorted) {
286         synchronized (vulnerableSoftware) {
287             final List<VulnerableSoftware> sortedVulnerableSoftware = new ArrayList<>(this.vulnerableSoftware);
288             if (sorted) {
289                 Collections.sort(sortedVulnerableSoftware);
290             }
291             return sortedVulnerableSoftware;
292         }
293     }
294 
295     /**
296      * Adds the vulnerableSoftware to the collection.
297      *
298      * @param vulnerableSoftware a collection of vulnerable software
299      */
300     public void addVulnerableSoftware(Set<VulnerableSoftware> vulnerableSoftware) {
301         this.vulnerableSoftware.addAll(vulnerableSoftware);
302     }
303 
304     /**
305      * Adds an entry for vulnerable software.
306      *
307      * @param software the vulnerable software reference to add
308      */
309     public void addVulnerableSoftware(VulnerableSoftware software) {
310         vulnerableSoftware.add(software);
311     }
312 
313     /**
314      * Get the CVSS V2 scoring information.
315      *
316      * @return the CVSS V2 scoring information
317      */
318     public CvssV2 getCvssV2() {
319         return cvssV2;
320     }
321 
322     /**
323      * Sets the CVSS V2 scoring information.
324      *
325      * @param cvssV2 the CVSS V2 scoring information
326      */
327     public void setCvssV2(CvssV2 cvssV2) {
328         this.cvssV2 = cvssV2;
329     }
330 
331     /**
332      * Get the CVSS V3 scoring information.
333      *
334      * @return the CVSS V3 scoring information
335      */
336     public CvssV3 getCvssV3() {
337         return cvssV3;
338     }
339 
340     /**
341      * Sets the CVSS V3 scoring information.
342      *
343      * @param cvssV3 the CVSS V3 scoring information
344      */
345     public void setCvssV3(CvssV3 cvssV3) {
346         this.cvssV3 = cvssV3;
347     }
348 
349     /**
350      * Get the CVSS V3 scoring information.
351      *
352      * @return the CVSS V3 scoring information
353      */
354     public CvssV4 getCvssV4() {
355         return cvssV4;
356     }
357 
358     /**
359      * Sets the CVSS V4 scoring information.
360      *
361      * @param cvssV4 the CVSS V4 scoring information
362      */
363     public void setCvssV4(CvssV4 cvssV4) {
364         this.cvssV4 = cvssV4;
365     }
366 
367     /**
368      * Get the set of CWEs.
369      *
370      * @return the set of CWEs
371      */
372     public CweSet getCwes() {
373         return cwes;
374     }
375 
376     /**
377      * Adds a CWE to the set.
378      *
379      * @param cwe new CWE to add
380      */
381     public void addCwe(String cwe) {
382         this.cwes.addCwe(cwe);
383     }
384 
385     /**
386      * Retrieves the severity a {@link Source} has assigned for which a CVSS
387      * score is not available. Severity could be anything ranging from
388      * 'critical', 'high', 'medium', and 'low', to non-traditional labels like
389      * 'major', 'minor', and 'important'.
390      *
391      * @return the un-scored severity
392      */
393     public String getUnscoredSeverity() {
394         return unscoredSeverity;
395     }
396 
397     /**
398      * Sets the severity a {@link Source} has assigned for which a CVSS score is
399      * not available. Severity could be anything ranging from 'critical',
400      * 'high', 'medium', and 'low', to non-traditional labels like 'major',
401      * 'minor', and 'important'.
402      *
403      * @param unscoredSeverity the un-scored severity
404      */
405     public void setUnscoredSeverity(String unscoredSeverity) {
406         this.unscoredSeverity = unscoredSeverity;
407     }
408 
409     /**
410      * Get the value of notes from suppression notes.
411      *
412      * @return the value of notes
413      */
414     public String getNotes() {
415         return notes;
416     }
417 
418     /**
419      * Set the value of notes.
420      *
421      * @param notes new value of notes
422      */
423     public void setNotes(String notes) {
424         this.notes = notes;
425     }
426 
427     @Override
428     public boolean equals(Object obj) {
429         if (obj == null || !(obj instanceof Vulnerability)) {
430             return false;
431         }
432         if (this == obj) {
433             return true;
434         }
435         final Vulnerability other = (Vulnerability) obj;
436         return new EqualsBuilder()
437                 .append(name, other.name)
438                 .isEquals();
439     }
440 
441     @Override
442     public int hashCode() {
443         return new HashCodeBuilder(3, 73)
444                 .append(name)
445                 .toHashCode();
446     }
447 
448     @Override
449     public String toString() {
450         final StringBuilder sb = new StringBuilder("Vulnerability ");
451         sb.append(this.name);
452         sb.append("\nReferences:\n");
453         for (Reference reference : getReferences(true)) {
454             sb.append("=> ");
455             sb.append(reference);
456             sb.append("\n");
457         }
458         sb.append("\nSoftware:\n");
459 
460         for (VulnerableSoftware software : getVulnerableSoftware(true)) {
461             sb.append("=> ");
462             sb.append(software);
463             sb.append("\n");
464         }
465         return sb.toString();
466     }
467 
468     /**
469      * Compares two vulnerabilities.<br>
470      * Natural order of vulnerabilities is defined as decreasing in severity and
471      * alphabetically by name for equal severity. This way the most severe
472      * issues are listed first in a sorted list.
473      * <br>
474      * This uses a
475      * {@link #bestEffortSeverityLevelForSorting() best-effort ordering} for
476      * severity as the variety of sources do not guarantee a consistent
477      * availability of standardized severity scores. The bestEffort severity
478      * level estimation will use CVSSv3 baseScore for comparison when available
479      * on both sides. If any of the vulnerabilities does not have a CVSSv3 score
480      * the sort order may be off, but it will be consistent.
481      * <br>
482      * The ranking (high to low) of severity can be informally represented as      {@code &lt;CVSSv3 critical> >> &lt;Unscored recognized critical> >>
483      *     &lt;Unscored unrecognized (assumed Critical)> >> &lt;Score-based comparison for high-or-lower scoring severities with
484      *     recognized unscored severities taking the lower bound of the comparable CVSSv3 range>
485      * }
486      *
487      * @param o a vulnerability to be compared
488      * @return a negative integer, zero, or a positive integer as this object is
489      * less than , equal to, or greater than the specified vulnerability
490      * @see #bestEffortSeverityLevelForSorting()
491      */
492     @Override
493     public int compareTo(@NotNull Vulnerability o) {
494         return new CompareToBuilder()
495                 .append(o.bestEffortSeverityLevelForSorting(), this.bestEffortSeverityLevelForSorting())
496                 .append(this.name, o.name)
497                 .toComparison();
498     }
499 
500     /**
501      * Compute a best-effort score for the severity of a vulnerability for the
502      * purpose of sorting.
503      * <br>
504      * Note that CVSSv2 and CVSSv3 scores are essentially uncomparable. For the
505      * purpose of sorting we nevertheless treat them comparable, with an
506      * exception for the 9.0-10.0 range. For that entire range CVSSv3 is scoring
507      * more severe than CVSSv2, so that the 'CRITICAL' severity is retained to
508      * be reported as the highest severity after sorting on descending severity.
509      * <br>
510      * For vulnerabilities not scored with a CVSS score we estimate a score from
511      * the severity text. For textual severities assumed or semantically
512      * confirmed to be of a critical nature we assign a value in between the
513      * highest CVSSv2 HIGH and the lowest CVSSv3 CRITICAL severity level.
514      *
515      * @see SeverityUtil#estimatedSortAdjustedCVSSv3(String)
516      * @see SeverityUtil#sortAdjustedCVSSv3BaseScore(float)
517      * @return A float value that allows for best-effort sorting on
518      * vulnerability severity
519      */
520     private Double bestEffortSeverityLevelForSorting() {
521         if (this.cvssV3 != null) {
522             return SeverityUtil.sortAdjustedCVSSv3BaseScore(this.cvssV3.getCvssData().getBaseScore());
523         }
524         if (this.cvssV2 != null) {
525             return this.cvssV2.getCvssData().getBaseScore();
526         }
527         return SeverityUtil.estimatedSortAdjustedCVSSv3(this.unscoredSeverity);
528     }
529 
530     /**
531      * The report text to use for highest severity when this issue is ranked
532      * highest.
533      *
534      * @return The string to display in the report, clarifying for unrecognized
535      * unscored severities that critical is assumed.
536      */
537     public String getHighestSeverityText() {
538         if (this.cvssV3 != null) {
539             return this.cvssV3.getCvssData().getBaseSeverity().value().toUpperCase();
540         }
541         if (this.cvssV2 != null) {
542             return this.cvssV2.getCvssData().getBaseSeverity().toUpperCase();
543         }
544         return SeverityUtil.unscoredToSeveritytext(this.unscoredSeverity).toUpperCase();
545     }
546 
547     /**
548      * Sets the CPE that caused this vulnerability to be flagged.
549      *
550      * @param software a Vulnerable Software identifier
551      */
552     public void setMatchedVulnerableSoftware(VulnerableSoftware software) {
553         matchedVulnerableSoftware = software;
554     }
555 
556     /**
557      * Get the value of matchedVulnerableSoftware.
558      *
559      * @return the value of matchedVulnerableSoftware
560      */
561     public VulnerableSoftware getMatchedVulnerableSoftware() {
562         return matchedVulnerableSoftware;
563     }
564 
565     /**
566      * Returns the source that identified the vulnerability.
567      *
568      * @return the source
569      */
570     public Source getSource() {
571         return source;
572     }
573 
574     /**
575      * Sets the source that identified the vulnerability.
576      *
577      * @param source the source
578      */
579     public void setSource(Source source) {
580         this.source = source;
581     }
582 }