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