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) 2023 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.utils;
19  
20  import io.github.jeremylong.openvulnerability.client.nvd.CvssV2;
21  import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data;
22  import io.github.jeremylong.openvulnerability.client.nvd.CvssV3;
23  import io.github.jeremylong.openvulnerability.client.nvd.CvssV3Data;
24  
25  import java.util.Arrays;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import io.github.jeremylong.openvulnerability.client.nvd.CvssV4;
31  import io.github.jeremylong.openvulnerability.client.nvd.CvssV4Data;
32  import org.sonatype.ossindex.service.api.cvss.Cvss3Severity;
33  
34  /**
35   * Utility class to create CVSS Objects.
36   *
37   * @author Jeremy Long
38   */
39  public final class CvssUtil {
40  
41      private CvssUtil() {
42          //empty constructor for utility class.
43      }
44  
45      /**
46       * The CVSS v3 Base Metrics (that are required by the spec for any CVSS v3
47       * Vector String)
48       */
49      private static final List<String> BASE_METRICS_V3 = Arrays.asList("AV", "AC", "PR", "UI", "S", "C", "I", "A");
50  
51      /**
52       * The CVSS V2 Metrics required for the vector string to be complete.
53       */
54      private static final List<String> BASE_METRICS_V2 = Arrays.asList("AV", "AC", "Au", "C", "I", "A");
55      /**
56       * ZERO.
57       */
58      private static final Double ZERO = 0.0;
59      /**
60       * ONE.
61       */
62      private static final Double ONE = 1.0;
63      /**
64       * FOUR.
65       */
66      private static final Double FOUR = 4.0;
67      /**
68       * SEVEN.
69       */
70      private static final Double SEVEN = 7.0;
71      /**
72       * NINE.
73       */
74      private static final Double NINE = 9.0;
75      /**
76       * TEN.
77       */
78      private static final Double TEN = 10.0;
79      /**
80       * UNKNOWN.
81       */
82      private static final String UNKNOWN = "UNKNOWN";
83      /**
84       * HIGH.
85       */
86      private static final String HIGH = "HIGH";
87      /**
88       * MEDIUM.
89       */
90      private static final String MEDIUM = "MEDIUM";
91      /**
92       * LOW.
93       */
94      private static final String LOW = "LOW";
95  
96      /**
97       * Convert a CVSSv2 vector String into a CvssV3 Object.
98       *
99       * @param vectorString the vector string
100      * @param baseScore the base score
101      * @return the CVSSv2 object
102      */
103     public static CvssV2 vectorToCvssV2(String vectorString, Double baseScore) {
104         if (vectorString.startsWith("CVSS:")) {
105             throw new IllegalArgumentException("Not a valid CVSSv2 vector string: " + vectorString);
106         }
107         final String[] metricStrings = vectorString.substring(vectorString.indexOf('/') + 1).split("/");
108         final HashMap<String, String> metrics = new HashMap<>();
109         for (int i = 0; i < metricStrings.length; i++) {
110             final String[] metricKeyVal = metricStrings[i].split(":");
111             if (metricKeyVal.length != 2) {
112                 throw new IllegalArgumentException(
113                         String.format("Not a valid CVSSv2 vector string '%s', invalid metric component '%s'",
114                                 vectorString, metricStrings[i]));
115             }
116             metrics.put(metricKeyVal[0], metricKeyVal[1]);
117         }
118         if (!metrics.keySet().containsAll(BASE_METRICS_V2)) {
119             throw new IllegalArgumentException(
120                     String.format("Not a valid CVSSv2 vector string '%s'; missing one or more required Metrics;",
121                             vectorString));
122         }
123 
124         //"AV:L/AC:L/Au:N/C:N/I:N/A:C"
125         final CvssV2Data.AccessVectorType accessVector = CvssV2Data.AccessVectorType.fromValue(metrics.get("AV"));
126         final CvssV2Data.AccessComplexityType attackComplexity = CvssV2Data.AccessComplexityType.fromValue(metrics.get("AC"));
127         final CvssV2Data.AuthenticationType authentication = CvssV2Data.AuthenticationType.fromValue(metrics.get("Au"));
128         final CvssV2Data.CiaType confidentialityImpact = CvssV2Data.CiaType.fromValue(metrics.get("C"));
129         final CvssV2Data.CiaType integrityImpact = CvssV2Data.CiaType.fromValue(metrics.get("I"));
130         final CvssV2Data.CiaType availabilityImpact = CvssV2Data.CiaType.fromValue(metrics.get("A"));
131 
132         final String baseSeverity = cvssV2ScoreToSeverity(baseScore);
133         final CvssV2Data data = new CvssV2Data(CvssV2Data.Version._2_0, vectorString, accessVector, attackComplexity,
134                 authentication, confidentialityImpact, integrityImpact, availabilityImpact, baseScore, baseSeverity,
135                 null, null, null, null, null, null, null, null, null, null);
136         final CvssV2 cvss = new CvssV2(null, null, data, baseSeverity, null, null, null, null, null, null, null);
137         return cvss;
138 
139     }
140 
141     /**
142      * Determines the severity from the score.
143      *
144      * @param score the score
145      * @return the severity
146      */
147     public static String cvssV2ScoreToSeverity(Double score) {
148         if (score != null) {
149             if (ZERO.compareTo(score) <= 0 && FOUR.compareTo(score) > 0) {
150                 return LOW;
151             } else if (FOUR.compareTo(score) <= 0 && SEVEN.compareTo(score) > 0) {
152                 return MEDIUM;
153             } else if (SEVEN.compareTo(score) <= 0 && TEN.compareTo(score) >= 0) {
154                 return HIGH;
155             }
156         }
157         return UNKNOWN;
158     }
159 
160     /**
161      * Determines the severity from the score.
162      *
163      * @param score the score
164      * @return the severity
165      */
166     public static CvssV3Data.SeverityType cvssV3ScoreToSeverity(Double score) {
167         if (score != null) {
168             if (ZERO.compareTo(score) == 0) {
169                 return CvssV3Data.SeverityType.NONE;
170             } else if (ZERO.compareTo(score) <= 0 && FOUR.compareTo(score) > 0) {
171                 return CvssV3Data.SeverityType.LOW;
172             } else if (FOUR.compareTo(score) <= 0 && SEVEN.compareTo(score) > 0) {
173                 return CvssV3Data.SeverityType.MEDIUM;
174             } else if (SEVEN.compareTo(score) <= 0 && NINE.compareTo(score) > 0) {
175                 return CvssV3Data.SeverityType.HIGH;
176             } else if (NINE.compareTo(score) <= 0 && TEN.compareTo(score) >= 0) {
177                 return CvssV3Data.SeverityType.CRITICAL;
178             }
179         }
180         return null;
181     }
182 
183     /**
184      * Convert a CVSSv3 vector String into a CvssV3 Object.
185      *
186      * @param vectorString the vector string
187      * @param baseScore the base score
188      * @return the CVSSv3 object
189      */
190     public static CvssV3 vectorToCvssV3(String vectorString, Double baseScore) {
191         if (!vectorString.startsWith("CVSS:3")) {
192             throw new IllegalArgumentException("Not a valid CVSSv3 vector string: " + vectorString);
193         }
194         final String versionString = vectorString.substring(5, vectorString.indexOf('/'));
195         final String[] metricStrings = vectorString.substring(vectorString.indexOf('/') + 1).split("/");
196         final HashMap<String, String> metrics = new HashMap<>();
197         for (int i = 0; i < metricStrings.length; i++) {
198             final String[] metricKeyVal = metricStrings[i].split(":");
199             if (metricKeyVal.length != 2) {
200                 throw new IllegalArgumentException(
201                         String.format("Not a valid CVSSv3 vector string '%s', invalid metric component '%s'",
202                                 vectorString, metricStrings[i]));
203             }
204             metrics.put(metricKeyVal[0], metricKeyVal[1]);
205         }
206         if (!metrics.keySet().containsAll(BASE_METRICS_V3)) {
207             throw new IllegalArgumentException(
208                     String.format("Not a valid CVSSv3 vector string '%s'; missing one or more required Base Metrics;",
209                             vectorString));
210         }
211 
212         final CvssV3Data.Version version = CvssV3Data.Version.fromValue(versionString);
213         //"CVSS:3.1\/AV:L\/AC:L\/PR:L\/UI:N\/S:U\/C:N\/I:N\/A:H"
214         final CvssV3Data.AttackVectorType attackVector = CvssV3Data.AttackVectorType.fromValue(metrics.get("AV"));
215         final CvssV3Data.AttackComplexityType attackComplexity = CvssV3Data.AttackComplexityType.fromValue(metrics.get("AC"));
216         final CvssV3Data.PrivilegesRequiredType privilegesRequired = CvssV3Data.PrivilegesRequiredType.fromValue(metrics.get("PR"));
217         final CvssV3Data.UserInteractionType userInteraction = CvssV3Data.UserInteractionType.fromValue(metrics.get("UI"));
218         final CvssV3Data.ScopeType scope = CvssV3Data.ScopeType.fromValue(metrics.get("S"));
219         final CvssV3Data.CiaType confidentialityImpact = CvssV3Data.CiaType.fromValue(metrics.get("C"));
220         final CvssV3Data.CiaType integrityImpact = CvssV3Data.CiaType.fromValue(metrics.get("I"));
221         final CvssV3Data.CiaType availabilityImpact = CvssV3Data.CiaType.fromValue(metrics.get("A"));
222 
223         final String baseSeverityString = Cvss3Severity.of(baseScore.floatValue()).name();
224         final CvssV3Data.SeverityType baseSeverity = CvssV3Data.SeverityType.fromValue(baseSeverityString);
225         final CvssV3Data data = new CvssV3Data(version, vectorString, attackVector, attackComplexity,
226                 privilegesRequired, userInteraction, scope, confidentialityImpact, integrityImpact, availabilityImpact, baseScore,
227                 baseSeverity, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
228         final CvssV3 cvss = new CvssV3(null, null, data, null, null);
229         return cvss;
230     }
231 
232     public static CvssV4Data.SeverityType cvssV4ScoreToSeverity(double baseScore) {
233         if (baseScore == 0.0) {
234             return CvssV4Data.SeverityType.NONE;
235         } else if (baseScore > 0.0 && baseScore < 4.0) {
236             return CvssV4Data.SeverityType.LOW;
237         } else if (baseScore >= 4.0 && baseScore < 7.0) {
238             return CvssV4Data.SeverityType.MEDIUM;
239         } else if (baseScore >= 7.0 && baseScore < 9.0) {
240             return CvssV4Data.SeverityType.HIGH;
241         } else if (baseScore >= 9.0 && baseScore <= 10.0) {
242             return CvssV4Data.SeverityType.CRITICAL;
243         } else {
244             throw new IllegalArgumentException("Invalid CVSS base score: " + baseScore);
245         }
246     }
247 
248     /**
249      * Convert a CVSSv4 vector String into a CvssV4 Object.
250      *
251      * @param source the source of the CVSS data
252      * @param type the type of CVSS data (primary or secondary)
253      * @param baseScore the base score
254      * @param vectorString the vector string
255      * @return the CVSSv4 object
256      */
257     public static CvssV4 vectorToCvssV4(String source, CvssV4.Type type, Double baseScore, String vectorString) {
258         // Remove "CVSS:" prefix and split by "/"
259         String[] parts = vectorString.replaceFirst("^CVSS:", "").split("/");
260         Map<String, String> values = new HashMap<>();
261         for (String part : parts) {
262             String[] kv = part.split(":");
263             if (kv.length == 2) {
264                 values.put(kv[0], kv[1]);
265             }
266         }
267 
268         CvssV4Data.Version version = CvssV4Data.Version.fromValue(values.getOrDefault("4.0", "4.0"));
269 
270         CvssV4Data.AttackVectorType attackVector = values.containsKey("AV") ? CvssV4Data.AttackVectorType.fromValue(values.get("AV")) : null;
271         CvssV4Data.AttackComplexityType attackComplexity = values.containsKey("AC") ? CvssV4Data.AttackComplexityType.fromValue(values.get("AC")) : null;
272         CvssV4Data.AttackRequirementsType attackRequirements = values.containsKey("AT") ? CvssV4Data.AttackRequirementsType.fromValue(values.get("AT")) : null;
273         CvssV4Data.PrivilegesRequiredType privilegesRequired = values.containsKey("PR") ? CvssV4Data.PrivilegesRequiredType.fromValue(values.get("PR")) : null;
274         CvssV4Data.UserInteractionType userInteraction = values.containsKey("UI") ? CvssV4Data.UserInteractionType.fromValue(values.get("UI")) : null;
275         CvssV4Data.CiaType vulnConfidentialityImpact = values.containsKey("VC") ? CvssV4Data.CiaType.fromValue(values.get("VC")) : null;
276         CvssV4Data.CiaType vulnIntegrityImpact = values.containsKey("VI") ? CvssV4Data.CiaType.fromValue(values.get("VI")) : null;
277         CvssV4Data.CiaType vulnAvailabilityImpact = values.containsKey("VA") ? CvssV4Data.CiaType.fromValue(values.get("VA")) : null;
278         CvssV4Data.CiaType subConfidentialityImpact = values.containsKey("SC") ? CvssV4Data.CiaType.fromValue(values.get("SC")) : null;
279         CvssV4Data.CiaType subIntegrityImpact = values.containsKey("SI") ? CvssV4Data.CiaType.fromValue(values.get("SI")) : null;
280         CvssV4Data.CiaType subAvailabilityImpact = values.containsKey("SA") ? CvssV4Data.CiaType.fromValue(values.get("SA")) : null;
281         CvssV4Data.ExploitMaturityType exploitMaturity = values.containsKey("E") ? CvssV4Data.ExploitMaturityType.fromValue(values.get("E")) : CvssV4Data.ExploitMaturityType.NOT_DEFINED;
282         CvssV4Data.CiaRequirementType confidentialityRequirement = values.containsKey("CR") ? CvssV4Data.CiaRequirementType.fromValue(values.get("CR")) : CvssV4Data.CiaRequirementType.NOT_DEFINED;
283         CvssV4Data.CiaRequirementType integrityRequirement = values.containsKey("IR") ? CvssV4Data.CiaRequirementType.fromValue(values.get("IR")) : CvssV4Data.CiaRequirementType.NOT_DEFINED;
284         CvssV4Data.CiaRequirementType availabilityRequirement = values.containsKey("AR") ? CvssV4Data.CiaRequirementType.fromValue(values.get("AR")) : CvssV4Data.CiaRequirementType.NOT_DEFINED;
285         CvssV4Data.ModifiedAttackVectorType modifiedAttackVector = values.containsKey("MAV") ? CvssV4Data.ModifiedAttackVectorType.fromValue(values.get("MAV")) : CvssV4Data.ModifiedAttackVectorType.NOT_DEFINED;
286         CvssV4Data.ModifiedAttackComplexityType modifiedAttackComplexity = values.containsKey("MAC") ? CvssV4Data.ModifiedAttackComplexityType.fromValue(values.get("MAC")) : CvssV4Data.ModifiedAttackComplexityType.NOT_DEFINED;
287         CvssV4Data.ModifiedAttackRequirementsType modifiedAttackRequirements = values.containsKey("MAT") ? CvssV4Data.ModifiedAttackRequirementsType.fromValue(values.get("MAT")) : CvssV4Data.ModifiedAttackRequirementsType.NOT_DEFINED;
288         CvssV4Data.ModifiedPrivilegesRequiredType modifiedPrivilegesRequired = values.containsKey("MPR") ? CvssV4Data.ModifiedPrivilegesRequiredType.fromValue(values.get("MPR")) : CvssV4Data.ModifiedPrivilegesRequiredType.NOT_DEFINED;
289         CvssV4Data.ModifiedUserInteractionType modifiedUserInteraction = values.containsKey("MUI") ? CvssV4Data.ModifiedUserInteractionType.fromValue(values.get("MUI")) : CvssV4Data.ModifiedUserInteractionType.NOT_DEFINED;
290         CvssV4Data.ModifiedCiaType modifiedVulnConfidentialityImpact = values.containsKey("MVC") ? CvssV4Data.ModifiedCiaType.fromValue(values.get("MVC")) : CvssV4Data.ModifiedCiaType.NOT_DEFINED;
291         CvssV4Data.ModifiedCiaType modifiedVulnIntegrityImpact = values.containsKey("MVI") ? CvssV4Data.ModifiedCiaType.fromValue(values.get("MVI")) : CvssV4Data.ModifiedCiaType.NOT_DEFINED;
292         CvssV4Data.ModifiedCiaType modifiedVulnAvailabilityImpact = values.containsKey("MVA") ? CvssV4Data.ModifiedCiaType.fromValue(values.get("MVA")) : CvssV4Data.ModifiedCiaType.NOT_DEFINED;
293         CvssV4Data.ModifiedSubCType modifiedSubConfidentialityImpact = values.containsKey("MSC") ? CvssV4Data.ModifiedSubCType.fromValue(values.get("MSC")) : CvssV4Data.ModifiedSubCType.NOT_DEFINED;
294         CvssV4Data.ModifiedSubIaType modifiedSubIntegrityImpact = values.containsKey("MSI") ? CvssV4Data.ModifiedSubIaType.fromValue(values.get("MSI")) : CvssV4Data.ModifiedSubIaType.NOT_DEFINED;
295         CvssV4Data.ModifiedSubIaType modifiedSubAvailabilityImpact = values.containsKey("MSA") ? CvssV4Data.ModifiedSubIaType.fromValue(values.get("MSA")) : CvssV4Data.ModifiedSubIaType.NOT_DEFINED;
296         CvssV4Data.SafetyType safety = values.containsKey("S") ? CvssV4Data.SafetyType.fromValue(values.get("S")) : CvssV4Data.SafetyType.NOT_DEFINED;
297         CvssV4Data.AutomatableType automatable = values.containsKey("AU") ? CvssV4Data.AutomatableType.fromValue(values.get("AU")) : CvssV4Data.AutomatableType.NOT_DEFINED;
298         CvssV4Data.RecoveryType recovery = values.containsKey("R") ? CvssV4Data.RecoveryType.fromValue(values.get("R")) : CvssV4Data.RecoveryType.NOT_DEFINED;
299         CvssV4Data.ValueDensityType valueDensity = values.containsKey("V") ? CvssV4Data.ValueDensityType.fromValue(values.get("V")) : CvssV4Data.ValueDensityType.NOT_DEFINED;
300         CvssV4Data.VulnerabilityResponseEffortType vulnerabilityResponseEffort = values.containsKey("RE") ? CvssV4Data.VulnerabilityResponseEffortType.fromValue(values.get("RE")) : CvssV4Data.VulnerabilityResponseEffortType.NOT_DEFINED;
301         CvssV4Data.ProviderUrgencyType providerUrgency = values.containsKey("U") ? CvssV4Data.ProviderUrgencyType.fromValue(values.get("U")) : CvssV4Data.ProviderUrgencyType.NOT_DEFINED;
302 
303         CvssV4Data.SeverityType baseSeverity = cvssV4ScoreToSeverity(baseScore);
304         // Scores and severities are not present in the vector string, set to null/defaults
305         Double threatScore = null;
306         CvssV4Data.SeverityType threatSeverity = null;
307         Double environmentalScore = null;
308         CvssV4Data.SeverityType environmentalSeverity = null;
309 
310         CvssV4Data cvssData = new CvssV4Data(
311                 version,
312                 vectorString,
313                 attackVector,
314                 attackComplexity,
315                 attackRequirements,
316                 privilegesRequired,
317                 userInteraction,
318                 vulnConfidentialityImpact,
319                 vulnIntegrityImpact,
320                 vulnAvailabilityImpact,
321                 subConfidentialityImpact,
322                 subIntegrityImpact,
323                 subAvailabilityImpact,
324                 exploitMaturity,
325                 confidentialityRequirement,
326                 integrityRequirement,
327                 availabilityRequirement,
328                 modifiedAttackVector,
329                 modifiedAttackComplexity,
330                 modifiedAttackRequirements,
331                 modifiedPrivilegesRequired,
332                 modifiedUserInteraction,
333                 modifiedVulnConfidentialityImpact,
334                 modifiedVulnIntegrityImpact,
335                 modifiedVulnAvailabilityImpact,
336                 modifiedSubConfidentialityImpact,
337                 modifiedSubIntegrityImpact,
338                 modifiedSubAvailabilityImpact,
339                 safety,
340                 automatable,
341                 recovery,
342                 valueDensity,
343                 vulnerabilityResponseEffort,
344                 providerUrgency,
345                 baseScore,
346                 baseSeverity,
347                 threatScore,
348                 threatSeverity,
349                 environmentalScore,
350                 environmentalSeverity
351         );
352 
353         return new CvssV4(source, type, cvssData);
354     }
355 }