Skip to content

Commit d560a5a

Browse files
committed
feat: support multple cvssBelow thesholds per version (#2563)
1 parent adaa264 commit d560a5a

File tree

7 files changed

+453
-23
lines changed

7 files changed

+453
-23
lines changed

core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ protected String getAnalyzerEnabledSettingKey() {
7676

7777
@Override
7878
public boolean filter(SuppressionRule rule) {
79-
return rule.hasCve() || rule.hasCvssBelow() || rule.hasCwe() || rule.hasVulnerabilityName();
79+
return rule.hasCve() || rule.hasCvssBelow() || rule.hasCvssV2Below() || rule.hasCvssV3Below() || rule.hasCvssV4Below() || rule.hasCwe() || rule.hasVulnerabilityName();
8080
}
8181

8282
@Override

core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ public class SuppressionHandler extends DefaultHandler {
9494
* The cvssBelow element name.
9595
*/
9696
public static final String CVSS_BELOW = "cvssBelow";
97+
/**
98+
* The cvssV2Below element name.
99+
*/
100+
public static final String CVSS_V2_BELOW = "cvssV2Below";
101+
/**
102+
* The cvssV3Below element name.
103+
*/
104+
public static final String CVSS_V3_BELOW = "cvssV3Below";
105+
/**
106+
* The cvssV4Below element name.
107+
*/
108+
public static final String CVSS_V4_BELOW = "cvssV4Below";
97109
/**
98110
* A list of suppression rules.
99111
*/
@@ -232,6 +244,18 @@ public void endElement(String uri, String localName, String qName) throws SAXExc
232244
final Double cvss = Double.valueOf(currentText.toString().trim());
233245
rule.addCvssBelow(cvss);
234246
break;
247+
case CVSS_V2_BELOW:
248+
final Double cvssV2 = Double.valueOf(currentText.toString().trim());
249+
rule.addCvssV2Below(cvssV2);
250+
break;
251+
case CVSS_V3_BELOW:
252+
final Double cvssV3 = Double.valueOf(currentText.toString().trim());
253+
rule.addCvssV3Below(cvssV3);
254+
break;
255+
case CVSS_V4_BELOW:
256+
final Double cvssV4 = Double.valueOf(currentText.toString().trim());
257+
rule.addCvssV4Below(cvssV4);
258+
break;
235259
default:
236260
break;
237261
}

core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ public class SuppressionParser {
5555
*/
5656
private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionParser.class);
5757

58+
/**
59+
* The suppression schema file location for v 1.4.
60+
*/
61+
public static final String SUPPRESSION_SCHEMA_1_5 = "schema/dependency-suppression.1.5.xsd";
5862
/**
5963
* The suppression schema file location for v 1.4.
6064
*/
@@ -106,6 +110,7 @@ public List<SuppressionRule> parseSuppressionRules(File file) throws Suppression
106110
public List<SuppressionRule> parseSuppressionRules(InputStream inputStream)
107111
throws SuppressionParseException, SAXException {
108112
try (
113+
InputStream schemaStream15 = FileUtils.getResourceAsStream(SUPPRESSION_SCHEMA_1_5);
109114
InputStream schemaStream14 = FileUtils.getResourceAsStream(SUPPRESSION_SCHEMA_1_4);
110115
InputStream schemaStream13 = FileUtils.getResourceAsStream(SUPPRESSION_SCHEMA_1_3);
111116
InputStream schemaStream12 = FileUtils.getResourceAsStream(SUPPRESSION_SCHEMA_1_2);
@@ -118,7 +123,7 @@ public List<SuppressionRule> parseSuppressionRules(InputStream inputStream)
118123
final String charsetName = bom == null ? defaultEncoding : bom.getCharsetName();
119124

120125
final SuppressionHandler handler = new SuppressionHandler();
121-
final SAXParser saxParser = XmlUtils.buildSecureSaxParser(schemaStream14, schemaStream13, schemaStream12, schemaStream11, schemaStream10);
126+
final SAXParser saxParser = XmlUtils.buildSecureSaxParser(schemaStream15, schemaStream14, schemaStream13, schemaStream12, schemaStream11, schemaStream10);
122127
final XMLReader xmlReader = saxParser.getXMLReader();
123128
xmlReader.setErrorHandler(new SuppressionErrorHandler());
124129
xmlReader.setContentHandler(handler);

core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java

Lines changed: 177 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ public class SuppressionRule {
6363
* The list of cvssBelow scores.
6464
*/
6565
private List<Double> cvssBelow = new ArrayList<>();
66+
/**
67+
* The list of cvssV2Below scores.
68+
*/
69+
private List<Double> cvssV2Below = new ArrayList<>();
70+
/**
71+
* The list of cvssV3Below scores.
72+
*/
73+
private List<Double> cvssV3Below = new ArrayList<>();
74+
/**
75+
* The list of cvssV4Below scores.
76+
*/
77+
private List<Double> cvssV4Below = new ArrayList<>();
6678
/**
6779
* The list of CWE entries to suppress.
6880
*/
@@ -261,6 +273,114 @@ public boolean hasCvssBelow() {
261273
return !cvssBelow.isEmpty();
262274
}
263275

276+
/**
277+
* Get the value of cvssV2Below.
278+
*
279+
* @return the value of cvssV2Below
280+
*/
281+
public List<Double> getCvssV2Below() {
282+
return cvssV2Below;
283+
}
284+
285+
/**
286+
* Set the value of cvssV2Below.
287+
*
288+
* @param cvssV2Below new value of cvssV2Below
289+
*/
290+
public void setCvssV2Below(List<Double> cvssV2Below) {
291+
this.cvssV2Below = cvssV2Below;
292+
}
293+
294+
/**
295+
* Adds the CVSS to the cvssV2Below list.
296+
*
297+
* @param cvss the CVSS to add
298+
*/
299+
public void addCvssV2Below(Double cvss) {
300+
this.cvssV2Below.add(cvss);
301+
}
302+
303+
/**
304+
* Returns whether or not this suppression rule has CVSS v2 suppression criteria.
305+
*
306+
* @return whether or not this suppression rule has CVSS v2 suppression criteria.
307+
*/
308+
public boolean hasCvssV2Below() {
309+
return !cvssV2Below.isEmpty();
310+
}
311+
312+
/**
313+
* Get the value of cvssV3Below.
314+
*
315+
* @return the value of cvssV3Below
316+
*/
317+
public List<Double> getCvssV3Below() {
318+
return cvssV3Below;
319+
}
320+
321+
/**
322+
* Set the value of cvssV3Below.
323+
*
324+
* @param cvssV3Below new value of cvssV3Below
325+
*/
326+
public void setCvssV3Below(List<Double> cvssV3Below) {
327+
this.cvssV3Below = cvssV3Below;
328+
}
329+
330+
/**
331+
* Adds the CVSS to the cvssV3Below list.
332+
*
333+
* @param cvss the CVSS to add
334+
*/
335+
public void addCvssV3Below(Double cvss) {
336+
this.cvssV3Below.add(cvss);
337+
}
338+
339+
/**
340+
* Returns whether or not this suppression rule has CVSS v3 suppression criteria.
341+
*
342+
* @return whether or not this suppression rule has CVSS v3 suppression criteria.
343+
*/
344+
public boolean hasCvssV3Below() {
345+
return !cvssV3Below.isEmpty();
346+
}
347+
348+
/**
349+
* Get the value of cvssV4Below.
350+
*
351+
* @return the value of cvssV4Below
352+
*/
353+
public List<Double> getCvssV4Below() {
354+
return cvssV4Below;
355+
}
356+
357+
/**
358+
* Set the value of cvssV4Below.
359+
*
360+
* @param cvssV4Below new value of cvssV4Below
361+
*/
362+
public void setCvssV4Below(List<Double> cvssV4Below) {
363+
this.cvssV4Below = cvssV4Below;
364+
}
365+
366+
/**
367+
* Adds the CVSS to the cvssV4Below list.
368+
*
369+
* @param cvss the CVSS to add
370+
*/
371+
public void addCvssV4Below(Double cvss) {
372+
this.cvssV4Below.add(cvss);
373+
}
374+
375+
/**
376+
* Returns whether or not this suppression rule has CVSS v4 suppression criteria.
377+
*
378+
* @return whether or not this suppression rule has CVSS v4 suppression criteria.
379+
*/
380+
public boolean hasCvssV4Below() {
381+
return !cvssV4Below.isEmpty();
382+
}
383+
264384
/**
265385
* Get the value of notes.
266386
*
@@ -494,7 +614,7 @@ public void process(Dependency dependency) {
494614
}
495615
removalList.forEach(dependency::removeVulnerableSoftwareIdentifier);
496616
}
497-
if (hasCve() || hasVulnerabilityName() || hasCwe() || hasCvssBelow()) {
617+
if (hasCve() || hasVulnerabilityName() || hasCwe() || hasCvssBelow() || hasCvssV2Below() || hasCvssV3Below() || hasCvssV4Below()) {
498618
final Set<Vulnerability> removeVulns = new HashSet<>();
499619
for (Vulnerability v : dependency.getVulnerabilities()) {
500620
boolean remove = false;
@@ -525,23 +645,9 @@ public void process(Dependency dependency) {
525645
}
526646
}
527647
if (!remove) {
528-
for (Double cvss : this.cvssBelow) {
529-
//TODO validate this comparison
530-
if (v.getCvssV2() != null && v.getCvssV2().getCvssData().getBaseScore().compareTo(cvss) < 0) {
531-
remove = true;
532-
removeVulns.add(v);
533-
break;
534-
}
535-
if (v.getCvssV3() != null && v.getCvssV3().getCvssData().getBaseScore().compareTo(cvss) < 0) {
536-
remove = true;
537-
removeVulns.add(v);
538-
break;
539-
}
540-
if (v.getCvssV4() != null && v.getCvssV4().getCvssData().getBaseScore().compareTo(cvss) < 0) {
541-
remove = true;
542-
removeVulns.add(v);
543-
break;
544-
}
648+
if (suppressedBasedOnScore(v)) {
649+
remove = true;
650+
removeVulns.add(v);
545651
}
546652
}
547653
if (remove && !isBase()) {
@@ -556,6 +662,44 @@ public void process(Dependency dependency) {
556662
}
557663
}
558664

665+
boolean suppressedBasedOnScore(Vulnerability v) {
666+
if (!cvssBelow.isEmpty()) {
667+
for (Double cvss : this.cvssBelow) {
668+
//TODO validate this comparison
669+
if (v.getCvssV2() != null && v.getCvssV2().getCvssData().getBaseScore().compareTo(cvss) < 0) {
670+
return true;
671+
}
672+
if (v.getCvssV3() != null && v.getCvssV3().getCvssData().getBaseScore().compareTo(cvss) < 0) {
673+
return true;
674+
}
675+
if (v.getCvssV4() != null && v.getCvssV4().getCvssData().getBaseScore().compareTo(cvss) < 0) {
676+
return true;
677+
}
678+
}
679+
return false;
680+
}
681+
682+
if (hasCvssV2Below() || hasCvssV3Below() || hasCvssV4Below()) {
683+
Double v2SuppressionThreshold = this.cvssV2Below.stream().max(Double::compare).orElse(11.0);
684+
Double v3SuppressionThreshold = this.cvssV3Below.stream().max(Double::compare).orElse(11.0);
685+
Double v4SuppressionThreshold = this.cvssV4Below.stream().max(Double::compare).orElse(11.0);
686+
687+
Double v2Score = v.getCvssV2() != null ? v.getCvssV2().getCvssData().getBaseScore() : null;
688+
Double v3Score = v.getCvssV3() != null ? v.getCvssV3().getCvssData().getBaseScore() : null;
689+
Double v4Score = v.getCvssV4() != null ? v.getCvssV4().getCvssData().getBaseScore() : null;
690+
691+
// only if all version indicate suppression will the vulnerability be suppressed
692+
// so if we are missing data (score or threshold) for a specific version we assume suppression
693+
boolean cvssV2CheckSuppressing = v2Score == null || v2Score < v2SuppressionThreshold;
694+
boolean cvssV3CheckSuppressing = v3Score == null || v3Score < v3SuppressionThreshold;
695+
boolean cvssV4CheckSuppressing = v4Score == null || v4Score < v4SuppressionThreshold;
696+
697+
return cvssV2CheckSuppressing && cvssV3CheckSuppressing && cvssV4CheckSuppressing;
698+
}
699+
700+
return false;
701+
}
702+
559703
/**
560704
* Identifies if the cpe specified by the cpe suppression rule does not
561705
* specify a version.
@@ -694,6 +838,21 @@ public String toString() {
694838
cvssBelow.forEach((s) -> sb.append(s).append(','));
695839
sb.append('}');
696840
}
841+
if (cvssV2Below != null && !cvssV2Below.isEmpty()) {
842+
sb.append("cvssV2Below={");
843+
cvssV2Below.forEach((s) -> sb.append(s).append(','));
844+
sb.append('}');
845+
}
846+
if (cvssV3Below != null && !cvssV3Below.isEmpty()) {
847+
sb.append("cvssV3Below={");
848+
cvssV3Below.forEach((s) -> sb.append(s).append(','));
849+
sb.append('}');
850+
}
851+
if (cvssV4Below != null && !cvssV4Below.isEmpty()) {
852+
sb.append("cvssV4Below={");
853+
cvssV4Below.forEach((s) -> sb.append(s).append(','));
854+
sb.append('}');
855+
}
697856
sb.append('}');
698857
return sb.toString();
699858
}

0 commit comments

Comments
 (0)