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 }
541 }
542 if (remove && !isBase()) {
543 matched = true;
544 if (this.notes != null) {
545 v.setNotes(this.notes);
546 }
547 dependency.addSuppressedVulnerability(v);
548 }
549 }
550 removeVulns.forEach(dependency::removeVulnerability);
551 }
552 }
553
554
555
556
557
558
559
560
561
562 protected boolean cpeHasNoVersion(PropertyType c) {
563 return !c.isRegex() && countCharacter(c.getValue(), ':') <= 3;
564 }
565
566
567
568
569
570
571
572
573
574 private int countCharacter(String str, char c) {
575 int count = 0;
576 int pos = str.indexOf(c) + 1;
577 while (pos > 0) {
578 count += 1;
579 pos = str.indexOf(c, pos) + 1;
580 }
581 return count;
582 }
583
584
585
586
587
588
589
590
591
592 protected boolean purlMatches(PropertyType suppressionEntry, Identifier identifier) {
593 if (identifier instanceof PurlIdentifier) {
594 final PurlIdentifier purl = (PurlIdentifier) identifier;
595 return suppressionEntry.matches(purl.toString());
596 }
597 return false;
598 }
599
600
601
602
603
604
605
606
607
608 protected boolean identifierMatches(PropertyType suppressionEntry, Identifier identifier) {
609 if (identifier instanceof PurlIdentifier) {
610 final PurlIdentifier purl = (PurlIdentifier) identifier;
611 return suppressionEntry.matches(purl.toGav());
612 } else if (identifier instanceof CpeIdentifier) {
613
614 final Cpe cpeId = ((CpeIdentifier) identifier).getCpe();
615 if (suppressionEntry.isRegex()) {
616 try {
617 return suppressionEntry.matches(cpeId.toCpe22Uri());
618 } catch (CpeEncodingException ex) {
619 LOGGER.debug("Unable to convert CPE to 22 URI?" + cpeId);
620 }
621 } else if (suppressionEntry.isCaseSensitive()) {
622 try {
623 return cpeId.toCpe22Uri().startsWith(suppressionEntry.getValue());
624 } catch (CpeEncodingException ex) {
625 LOGGER.debug("Unable to convert CPE to 22 URI?" + cpeId);
626 }
627 } else {
628 final String id;
629 try {
630 id = cpeId.toCpe22Uri().toLowerCase();
631 } catch (CpeEncodingException ex) {
632 LOGGER.debug("Unable to convert CPE to 22 URI?" + cpeId);
633 return false;
634 }
635 final String check = suppressionEntry.getValue().toLowerCase();
636 return id.startsWith(check);
637 }
638 }
639 return suppressionEntry.matches(identifier.getValue());
640 }
641
642
643
644
645
646
647 @Override
648 public String toString() {
649 final StringBuilder sb = new StringBuilder(64);
650 sb.append("SuppressionRule{");
651 if (until != null) {
652 final String dt = DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.format(until);
653 sb.append("until=").append(dt).append(',');
654 }
655 if (filePath != null) {
656 sb.append("filePath=").append(filePath).append(',');
657 }
658 if (sha1 != null) {
659 sb.append("sha1=").append(sha1).append(',');
660 }
661 if (packageUrl != null) {
662 sb.append("packageUrl=").append(packageUrl).append(',');
663 }
664 if (gav != null) {
665 sb.append("gav=").append(gav).append(',');
666 }
667 if (cpe != null && !cpe.isEmpty()) {
668 sb.append("cpe={");
669 cpe.forEach((pt) -> sb.append(pt).append(','));
670 sb.append('}');
671 }
672 if (cwe != null && !cwe.isEmpty()) {
673 sb.append("cwe={");
674 cwe.forEach((s) -> sb.append(s).append(','));
675 sb.append('}');
676 }
677 if (cve != null && !cve.isEmpty()) {
678 sb.append("cve={");
679 cve.forEach((s) -> sb.append(s).append(','));
680 sb.append('}');
681 }
682 if (vulnerabilityNames != null && !vulnerabilityNames.isEmpty()) {
683 sb.append("vulnerabilityName={");
684 vulnerabilityNames.forEach((pt) -> sb.append(pt).append(','));
685 sb.append('}');
686 }
687 if (cvssBelow != null && !cvssBelow.isEmpty()) {
688 sb.append("cvssBelow={");
689 cvssBelow.forEach((s) -> sb.append(s).append(','));
690 sb.append('}');
691 }
692 sb.append('}');
693 return sb.toString();
694 }
695 }