1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.xml.suppression;
19
20 import java.util.ArrayList;
21 import java.util.Calendar;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Set;
26 import javax.annotation.concurrent.NotThreadSafe;
27 import org.apache.commons.lang3.time.DateFormatUtils;
28 import org.owasp.dependencycheck.dependency.Dependency;
29 import org.owasp.dependencycheck.dependency.Vulnerability;
30 import org.owasp.dependencycheck.dependency.naming.CpeIdentifier;
31 import org.owasp.dependencycheck.dependency.naming.Identifier;
32 import org.owasp.dependencycheck.dependency.naming.PurlIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import us.springett.parsers.cpe.Cpe;
36 import us.springett.parsers.cpe.exceptions.CpeEncodingException;
37
38
39
40
41
42 @NotThreadSafe
43 public class SuppressionRule {
44
45
46
47
48 private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionRule.class);
49
50
51
52 private PropertyType filePath;
53
54
55
56
57 private String sha1;
58
59
60
61 private List<PropertyType> cpe = new ArrayList<>();
62
63
64
65 private List<Double> cvssBelow = new ArrayList<>();
66
67
68
69 private List<String> cwe = new ArrayList<>();
70
71
72
73 private List<String> cve = new ArrayList<>();
74
75
76
77 private final List<PropertyType> vulnerabilityNames = new ArrayList<>();
78
79
80
81 private PropertyType gav = null;
82
83
84
85 private PropertyType packageUrl = null;
86
87
88
89
90 private String notes;
91
92
93
94
95
96
97 private boolean base;
98
99
100
101
102
103
104 private Calendar until;
105
106
107
108
109 private boolean matched = false;
110
111
112
113
114
115
116 public boolean isMatched() {
117 return matched;
118 }
119
120
121
122
123
124
125 public void setMatched(boolean matched) {
126 this.matched = matched;
127 }
128
129
130
131
132
133
134 public Calendar getUntil() {
135 return until;
136 }
137
138
139
140
141
142
143 public void setUntil(Calendar until) {
144 this.until = until;
145 }
146
147
148
149
150
151
152 public PropertyType getFilePath() {
153 return filePath;
154 }
155
156
157
158
159
160
161 public void setFilePath(PropertyType filePath) {
162 this.filePath = filePath;
163 }
164
165
166
167
168
169
170 public String getSha1() {
171 return sha1;
172 }
173
174
175
176
177
178
179 public void setSha1(String sha1) {
180 this.sha1 = sha1;
181 }
182
183
184
185
186
187
188 public List<PropertyType> getCpe() {
189 return cpe;
190 }
191
192
193
194
195
196
197 public void setCpe(List<PropertyType> cpe) {
198 this.cpe = cpe;
199 }
200
201
202
203
204
205
206 public void addCpe(PropertyType cpe) {
207 this.cpe.add(cpe);
208 }
209
210
211
212
213
214
215 public void addVulnerabilityName(PropertyType name) {
216 this.vulnerabilityNames.add(name);
217 }
218
219
220
221
222
223
224 public boolean hasCpe() {
225 return !cpe.isEmpty();
226 }
227
228
229
230
231
232
233 public List<Double> getCvssBelow() {
234 return cvssBelow;
235 }
236
237
238
239
240
241
242 public void setCvssBelow(List<Double> cvssBelow) {
243 this.cvssBelow = cvssBelow;
244 }
245
246
247
248
249
250
251 public void addCvssBelow(Double cvss) {
252 this.cvssBelow.add(cvss);
253 }
254
255
256
257
258
259
260 public boolean hasCvssBelow() {
261 return !cvssBelow.isEmpty();
262 }
263
264
265
266
267
268
269 public String getNotes() {
270 return notes;
271 }
272
273
274
275
276
277
278 public void setNotes(String notes) {
279 this.notes = notes;
280 }
281
282
283
284
285
286
287 public boolean hasNotes() {
288 return !notes.isEmpty();
289 }
290
291
292
293
294
295
296 public List<String> getCwe() {
297 return cwe;
298 }
299
300
301
302
303
304
305 public void setCwe(List<String> cwe) {
306 this.cwe = cwe;
307 }
308
309
310
311
312
313
314 public void addCwe(String cwe) {
315 this.cwe.add(cwe);
316 }
317
318
319
320
321
322
323 public boolean hasCwe() {
324 return !cwe.isEmpty();
325 }
326
327
328
329
330
331
332 public List<String> getCve() {
333 return cve;
334 }
335
336
337
338
339
340
341 public void setCve(List<String> cve) {
342 this.cve = cve;
343 }
344
345
346
347
348
349
350 public void addCve(String cve) {
351 this.cve.add(cve);
352 }
353
354
355
356
357
358
359 public boolean hasCve() {
360 return !cve.isEmpty();
361 }
362
363
364
365
366
367
368 public boolean hasVulnerabilityName() {
369 return !vulnerabilityNames.isEmpty();
370 }
371
372
373
374
375
376
377 public PropertyType getGav() {
378 return gav;
379 }
380
381
382
383
384
385
386 public void setGav(PropertyType gav) {
387 this.gav = gav;
388 }
389
390
391
392
393
394
395 public boolean hasGav() {
396 return gav != null;
397 }
398
399
400
401
402
403
404 public void setPackageUrl(PropertyType purl) {
405 this.packageUrl = purl;
406 }
407
408
409
410
411
412
413 public boolean hasPackageUrl() {
414 return packageUrl != null;
415 }
416
417
418
419
420
421
422 public boolean isBase() {
423 return base;
424 }
425
426
427
428
429
430
431 public void setBase(boolean base) {
432 this.base = base;
433 }
434
435
436
437
438
439
440
441
442 public void process(Dependency dependency) {
443 if (filePath != null && !filePath.matches(dependency.getFilePath())) {
444 return;
445 }
446 if (sha1 != null && !sha1.equalsIgnoreCase(dependency.getSha1sum())) {
447 return;
448 }
449 if (hasGav()) {
450 final Iterator<Identifier> itr = dependency.getSoftwareIdentifiers().iterator();
451 boolean found = false;
452 while (itr.hasNext()) {
453 final Identifier i = itr.next();
454 if (identifierMatches(this.gav, i)) {
455 found = true;
456 break;
457 }
458 }
459 if (!found) {
460 return;
461 }
462 }
463 if (hasPackageUrl()) {
464 final Iterator<Identifier> itr = dependency.getSoftwareIdentifiers().iterator();
465 boolean found = false;
466 while (itr.hasNext()) {
467 final Identifier i = itr.next();
468 if (purlMatches(this.packageUrl, i)) {
469 found = true;
470 break;
471 }
472 }
473 if (!found) {
474 return;
475 }
476 }
477
478 if (this.hasCpe()) {
479 final Set<Identifier> removalList = new HashSet<>();
480 for (Identifier i : dependency.getVulnerableSoftwareIdentifiers()) {
481 for (PropertyType c : this.cpe) {
482 if (identifierMatches(c, i)) {
483 if (!isBase()) {
484 matched = true;
485 if (this.notes != null) {
486 i.setNotes(this.notes);
487 }
488 dependency.addSuppressedIdentifier(i);
489 }
490 removalList.add(i);
491 break;
492 }
493 }
494 }
495 removalList.forEach(dependency::removeVulnerableSoftwareIdentifier);
496 }
497 if (hasCve() || hasVulnerabilityName() || hasCwe() || hasCvssBelow()) {
498 final Set<Vulnerability> removeVulns = new HashSet<>();
499 for (Vulnerability v : dependency.getVulnerabilities()) {
500 boolean remove = false;
501 for (String entry : this.cve) {
502 if (entry.equalsIgnoreCase(v.getName())) {
503 removeVulns.add(v);
504 remove = true;
505 break;
506 }
507 }
508 if (!remove && this.cwe != null && !v.getCwes().isEmpty()) {
509 for (String entry : this.cwe) {
510 final String toMatch = String.format("CWE-%s", entry);
511 if (v.getCwes().stream().anyMatch(toTest -> toMatch.regionMatches(0, toTest, 0, toMatch.length()))) {
512 remove = true;
513 removeVulns.add(v);
514 break;
515 }
516 }
517 }
518 if (!remove && v.getName() != null) {
519 for (PropertyType entry : this.vulnerabilityNames) {
520 if (entry.matches(v.getName())) {
521 remove = true;
522 removeVulns.add(v);
523 break;
524 }
525 }
526 }
527 if (!remove) {
528 for (Double cvss : this.cvssBelow) {
529
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 }
545 }
546 }
547 if (remove && !isBase()) {
548 matched = true;
549 if (this.notes != null) {
550 v.setNotes(this.notes);
551 }
552 dependency.addSuppressedVulnerability(v);
553 }
554 }
555 removeVulns.forEach(dependency::removeVulnerability);
556 }
557 }
558
559
560
561
562
563
564
565
566
567 protected boolean cpeHasNoVersion(PropertyType c) {
568 return !c.isRegex() && countCharacter(c.getValue(), ':') <= 3;
569 }
570
571
572
573
574
575
576
577
578
579 private int countCharacter(String str, char c) {
580 int count = 0;
581 int pos = str.indexOf(c) + 1;
582 while (pos > 0) {
583 count += 1;
584 pos = str.indexOf(c, pos) + 1;
585 }
586 return count;
587 }
588
589
590
591
592
593
594
595
596
597 protected boolean purlMatches(PropertyType suppressionEntry, Identifier identifier) {
598 if (identifier instanceof PurlIdentifier) {
599 final PurlIdentifier purl = (PurlIdentifier) identifier;
600 return suppressionEntry.matches(purl.toString());
601 }
602 return false;
603 }
604
605
606
607
608
609
610
611
612
613 protected boolean identifierMatches(PropertyType suppressionEntry, Identifier identifier) {
614 if (identifier instanceof PurlIdentifier) {
615 final PurlIdentifier purl = (PurlIdentifier) identifier;
616 return suppressionEntry.matches(purl.toGav());
617 } else if (identifier instanceof CpeIdentifier) {
618
619 final Cpe cpeId = ((CpeIdentifier) identifier).getCpe();
620 if (suppressionEntry.isRegex()) {
621 try {
622 return suppressionEntry.matches(cpeId.toCpe22Uri());
623 } catch (CpeEncodingException ex) {
624 LOGGER.debug("Unable to convert CPE to 22 URI?" + cpeId);
625 }
626 } else if (suppressionEntry.isCaseSensitive()) {
627 try {
628 return cpeId.toCpe22Uri().startsWith(suppressionEntry.getValue());
629 } catch (CpeEncodingException ex) {
630 LOGGER.debug("Unable to convert CPE to 22 URI?" + cpeId);
631 }
632 } else {
633 final String id;
634 try {
635 id = cpeId.toCpe22Uri().toLowerCase();
636 } catch (CpeEncodingException ex) {
637 LOGGER.debug("Unable to convert CPE to 22 URI?" + cpeId);
638 return false;
639 }
640 final String check = suppressionEntry.getValue().toLowerCase();
641 return id.startsWith(check);
642 }
643 }
644 return suppressionEntry.matches(identifier.getValue());
645 }
646
647
648
649
650
651
652 @Override
653 public String toString() {
654 final StringBuilder sb = new StringBuilder(64);
655 sb.append("SuppressionRule{");
656 if (until != null) {
657 final String dt = DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.format(until);
658 sb.append("until=").append(dt).append(',');
659 }
660 if (filePath != null) {
661 sb.append("filePath=").append(filePath).append(',');
662 }
663 if (sha1 != null) {
664 sb.append("sha1=").append(sha1).append(',');
665 }
666 if (packageUrl != null) {
667 sb.append("packageUrl=").append(packageUrl).append(',');
668 }
669 if (gav != null) {
670 sb.append("gav=").append(gav).append(',');
671 }
672 if (cpe != null && !cpe.isEmpty()) {
673 sb.append("cpe={");
674 cpe.forEach((pt) -> sb.append(pt).append(','));
675 sb.append('}');
676 }
677 if (cwe != null && !cwe.isEmpty()) {
678 sb.append("cwe={");
679 cwe.forEach((s) -> sb.append(s).append(','));
680 sb.append('}');
681 }
682 if (cve != null && !cve.isEmpty()) {
683 sb.append("cve={");
684 cve.forEach((s) -> sb.append(s).append(','));
685 sb.append('}');
686 }
687 if (vulnerabilityNames != null && !vulnerabilityNames.isEmpty()) {
688 sb.append("vulnerabilityName={");
689 vulnerabilityNames.forEach((pt) -> sb.append(pt).append(','));
690 sb.append('}');
691 }
692 if (cvssBelow != null && !cvssBelow.isEmpty()) {
693 sb.append("cvssBelow={");
694 cvssBelow.forEach((s) -> sb.append(s).append(','));
695 sb.append('}');
696 }
697 sb.append('}');
698 return sb.toString();
699 }
700 }