1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.dependency;
19
20 import java.io.Serializable;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23
24 import javax.annotation.concurrent.ThreadSafe;
25
26 import org.apache.commons.lang3.builder.CompareToBuilder;
27 import org.apache.commons.lang3.builder.EqualsBuilder;
28 import org.apache.commons.lang3.builder.HashCodeBuilder;
29 import org.jetbrains.annotations.NotNull;
30 import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException;
31 import org.owasp.dependencycheck.dependency.naming.CpeIdentifier;
32 import org.owasp.dependencycheck.utils.DependencyVersion;
33 import us.springett.parsers.cpe.Cpe;
34 import us.springett.parsers.cpe.ICpe;
35 import us.springett.parsers.cpe.exceptions.CpeValidationException;
36 import us.springett.parsers.cpe.util.Convert;
37 import us.springett.parsers.cpe.values.LogicalValue;
38 import us.springett.parsers.cpe.values.Part;
39
40
41
42
43
44
45
46 @ThreadSafe
47 public class VulnerableSoftware extends Cpe implements Serializable {
48
49
50
51
52 private static final long serialVersionUID = 605319412326651052L;
53
54
55
56
57
58 private final String versionEndExcluding;
59
60
61
62
63 private final String versionEndIncluding;
64
65
66
67
68 private final String versionStartExcluding;
69
70
71
72
73 private final String versionStartIncluding;
74
75
76
77 private final boolean vulnerable;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 public VulnerableSoftware(Part part, String vendor, String product, String version,
114 String update, String edition, String language, String swEdition,
115 String targetSw, String targetHw, String other,
116 String versionEndExcluding, String versionEndIncluding, String versionStartExcluding,
117 String versionStartIncluding, boolean vulnerable) throws CpeValidationException {
118 super(part, vendor, product, version, update, edition, language, swEdition, targetSw, targetHw, other);
119 this.versionEndExcluding = versionEndExcluding;
120 this.versionEndIncluding = versionEndIncluding;
121 this.versionStartExcluding = versionStartExcluding;
122 this.versionStartIncluding = versionStartIncluding;
123 this.vulnerable = vulnerable;
124 }
125
126
127
128
129
130
131
132 private static String normalizeForComparison(String s) {
133 return (s == null || s.isEmpty()) ? null : s;
134 }
135
136 @Override
137 public int compareTo(@NotNull ICpe o) {
138 if (o instanceof VulnerableSoftware) {
139 final VulnerableSoftware other = (VulnerableSoftware) o;
140 return new CompareToBuilder()
141 .appendSuper(super.compareTo(other))
142 .append(normalizeForComparison(versionStartIncluding), normalizeForComparison(other.versionStartIncluding))
143 .append(normalizeForComparison(versionStartExcluding), normalizeForComparison(other.versionStartExcluding))
144 .append(normalizeForComparison(versionEndIncluding), normalizeForComparison(other.versionEndIncluding))
145 .append(normalizeForComparison(versionEndExcluding), normalizeForComparison(other.versionEndExcluding))
146 .append(this.vulnerable, other.vulnerable)
147 .build();
148 } else if (o instanceof Cpe) {
149 return super.compareTo(o);
150 }
151 throw new UnexpectedAnalysisException("Unable to compare " + o.getClass().getCanonicalName());
152 }
153
154 @Override
155 public int hashCode() {
156
157
158 return new HashCodeBuilder(13, 59)
159 .appendSuper(super.hashCode())
160 .append(versionEndExcluding)
161 .append(versionEndIncluding)
162 .append(versionStartExcluding)
163 .append(versionStartIncluding)
164 .toHashCode();
165 }
166
167 @Override
168 public boolean equals(Object obj) {
169 if (obj == null || !(obj instanceof VulnerableSoftware)) {
170 return false;
171 }
172 if (this == obj) {
173 return true;
174 }
175 final VulnerableSoftware rhs = (VulnerableSoftware) obj;
176 return new EqualsBuilder()
177 .appendSuper(super.equals(obj))
178 .append(versionEndExcluding, rhs.versionEndExcluding)
179 .append(versionEndIncluding, rhs.versionEndIncluding)
180 .append(versionStartExcluding, rhs.versionStartExcluding)
181 .append(versionStartIncluding, rhs.versionStartIncluding)
182 .isEquals();
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 @Override
201 public boolean matches(ICpe target) {
202 boolean result = this.vulnerable;
203 result &= compareAttributes(this.getPart(), target.getPart());
204 result &= compareAttributes(this.getVendor(), target.getVendor());
205 result &= compareAttributes(this.getProduct(), target.getProduct());
206
207
208 result &= compareVersionRange(target.getVersion());
209
210
211
212 result &= compareUpdateAttributes(this.getUpdate(), target.getUpdate());
213 result &= compareAttributes(this.getEdition(), target.getEdition());
214 result &= compareAttributes(this.getLanguage(), target.getLanguage());
215 result &= compareAttributes(this.getSwEdition(), target.getSwEdition());
216 result &= compareAttributes(this.getTargetSw(), target.getTargetSw());
217 result &= compareAttributes(this.getTargetHw(), target.getTargetHw());
218 result &= compareAttributes(this.getOther(), target.getOther());
219 return result;
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233 protected static boolean compareUpdateAttributes(String left, String right) {
234
235
236
237
238 if (left.equalsIgnoreCase(right)) {
239
240 return true;
241 } else if (LogicalValue.ANY.getAbbreviation().equals(left)) {
242
243 return true;
244 } else if (LogicalValue.NA.getAbbreviation().equals(left)
245 && LogicalValue.ANY.getAbbreviation().equals(right)) {
246
247 return true;
248 } else if (LogicalValue.NA.getAbbreviation().equals(left)) {
249
250 return false;
251 } else if (LogicalValue.NA.getAbbreviation().equals(right)) {
252
253 return false;
254 } else if (LogicalValue.ANY.getAbbreviation().equals(right)) {
255
256 return true;
257 }
258 final String leftValue = left.replace("-", "").replace("_", "");
259 final String rightValue = right.replace("-", "").replace("_", "");
260 if (leftValue.equalsIgnoreCase(rightValue)) {
261
262 return true;
263 }
264
265 boolean results = false;
266
267 if (containsSpecialCharacter(left)) {
268 final Pattern p = Convert.wellFormedToPattern(left.toLowerCase());
269 final Matcher m = p.matcher(right.toLowerCase());
270 results = m.matches();
271 }
272 if (!results && rightValue.matches("^[abu]\\d.*") && leftValue.matches("^(update|alpha|beta).*")) {
273 switch (right.charAt(0)) {
274 case 'u':
275 results = compareUpdateAttributes(leftValue, "update" + rightValue.substring(1));
276 break;
277 case 'a':
278 results = compareUpdateAttributes(leftValue, "alpha" + rightValue.substring(1));
279 break;
280 case 'b':
281 results = compareUpdateAttributes(leftValue, "beta" + rightValue.substring(1));
282 break;
283 default:
284 break;
285 }
286 }
287 if (!results && leftValue.matches("^[abu]\\d.*") && rightValue.matches("^(update|alpha|beta).*")) {
288 switch (left.charAt(0)) {
289 case 'u':
290 results = compareUpdateAttributes("update" + leftValue.substring(1), rightValue);
291 break;
292 case 'a':
293 results = compareUpdateAttributes("alpha" + leftValue.substring(1), rightValue);
294 break;
295 case 'b':
296 results = compareUpdateAttributes("beta" + leftValue.substring(1), rightValue);
297 break;
298 default:
299 break;
300 }
301 }
302 return results;
303 }
304
305
306
307
308
309
310
311
312 private static boolean containsSpecialCharacter(String value) {
313 for (int x = 0; x < value.length(); x++) {
314 final char c = value.charAt(x);
315 if (c == '?' || c == '*') {
316 return true;
317 } else if (c == '\\') {
318
319 x += 1;
320 }
321 }
322 return false;
323 }
324
325
326
327
328
329
330
331
332
333 public static boolean testMatch(ICpe left, ICpe right) {
334 boolean result = true;
335 result &= compareAttributes(left.getPart(), right.getPart());
336 result &= compareAttributes(left.getWellFormedVendor(), right.getWellFormedVendor());
337 result &= compareAttributes(left.getWellFormedProduct(), right.getWellFormedProduct());
338
339 if (right instanceof VulnerableSoftware) {
340 final VulnerableSoftware vs = (VulnerableSoftware) right;
341 result &= vs.vulnerable;
342 result &= compareVersions(vs, left.getVersion());
343 } else if (left instanceof VulnerableSoftware) {
344 final VulnerableSoftware vs = (VulnerableSoftware) left;
345 result &= vs.vulnerable;
346 result &= compareVersions(vs, right.getVersion());
347 } else {
348 result &= compareAttributes(left.getWellFormedVersion(), right.getWellFormedVersion());
349 }
350
351
352
353 result &= compareUpdateAttributes(left.getWellFormedUpdate(), right.getWellFormedUpdate());
354 result &= compareAttributes(left.getWellFormedEdition(), right.getWellFormedEdition());
355 result &= compareAttributes(left.getWellFormedLanguage(), right.getWellFormedLanguage());
356 result &= compareAttributes(left.getWellFormedSwEdition(), right.getWellFormedSwEdition());
357 result &= compareAttributes(left.getWellFormedTargetSw(), right.getWellFormedTargetSw());
358 result &= compareAttributes(left.getWellFormedTargetHw(), right.getWellFormedTargetHw());
359 result &= compareAttributes(left.getWellFormedOther(), right.getWellFormedOther());
360 return result;
361 }
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378 @Override
379 public boolean matchedBy(ICpe target) {
380 return testMatch(target, this);
381 }
382
383
384
385
386
387
388
389
390
391
392 protected boolean compareVersionRange(String targetVersion) {
393 return compareVersions(this, targetVersion);
394 }
395
396
397
398
399
400
401
402
403
404
405
406 protected static boolean compareVersions(VulnerableSoftware vs, String targetVersion) {
407 if (LogicalValue.NA.getAbbreviation().equals(vs.getVersion())) {
408 return false;
409 }
410
411 boolean result = (vs.versionEndExcluding != null && !vs.versionEndExcluding.isEmpty())
412 || (vs.versionStartExcluding != null && !vs.versionStartExcluding.isEmpty())
413 || (vs.versionEndIncluding != null && !vs.versionEndIncluding.isEmpty())
414 || (vs.versionStartIncluding != null && !vs.versionStartIncluding.isEmpty());
415
416 if (!result && compareAttributes(vs.getVersion(), targetVersion)) {
417 return true;
418 }
419
420 final DependencyVersion target = new DependencyVersion(targetVersion);
421 if (target.getVersionParts().isEmpty()) {
422 return false;
423 }
424 if (result && vs.versionEndExcluding != null && !vs.versionEndExcluding.isEmpty()) {
425 final DependencyVersion endExcluding = new DependencyVersion(vs.versionEndExcluding);
426 result = endExcluding.compareTo(target) > 0;
427 }
428 if (result && vs.versionStartExcluding != null && !vs.versionStartExcluding.isEmpty()) {
429 final DependencyVersion startExcluding = new DependencyVersion(vs.versionStartExcluding);
430 result = startExcluding.compareTo(target) < 0;
431 }
432 if (result && vs.versionEndIncluding != null && !vs.versionEndIncluding.isEmpty()) {
433 final DependencyVersion endIncluding = new DependencyVersion(vs.versionEndIncluding);
434 result &= endIncluding.compareTo(target) >= 0;
435 }
436 if (result && vs.versionStartIncluding != null && !vs.versionStartIncluding.isEmpty()) {
437 final DependencyVersion startIncluding = new DependencyVersion(vs.versionStartIncluding);
438 result &= startIncluding.compareTo(target) <= 0;
439 }
440 return result;
441 }
442
443
444
445
446
447
448 public String getVersionEndExcluding() {
449 return versionEndExcluding;
450 }
451
452
453
454
455
456
457 public String getVersionEndIncluding() {
458 return versionEndIncluding;
459 }
460
461
462
463
464
465
466 public String getVersionStartExcluding() {
467 return versionStartExcluding;
468 }
469
470
471
472
473
474
475 public String getVersionStartIncluding() {
476 return versionStartIncluding;
477 }
478
479
480
481
482
483
484 public boolean isVulnerable() {
485 return vulnerable;
486 }
487
488 @Override
489 public String toString() {
490 final StringBuilder sb = new StringBuilder();
491 sb.append(this.toCpe23FS());
492 boolean textAdded = false;
493 if (versionStartIncluding != null && !versionStartIncluding.isEmpty()) {
494 sb.append(" versions from (including) ")
495 .append(versionStartIncluding);
496 textAdded = true;
497 }
498 if (versionStartExcluding != null && !versionStartExcluding.isEmpty()) {
499 if (textAdded) {
500 sb.append(";");
501 }
502 sb.append(" versions from (excluding) ")
503 .append(versionStartExcluding);
504 textAdded = true;
505 }
506 if (versionEndIncluding != null && !versionEndIncluding.isEmpty()) {
507 if (textAdded) {
508 sb.append(";");
509 }
510 sb.append(" versions up to (including) ")
511 .append(versionEndIncluding);
512 textAdded = true;
513 }
514 if (versionEndExcluding != null && !versionEndExcluding.isEmpty()) {
515 if (textAdded) {
516 sb.append(";");
517 }
518 sb.append(" versions up to (excluding) ")
519 .append(versionEndExcluding);
520 textAdded = true;
521 }
522 if (!vulnerable) {
523 if (textAdded) {
524 sb.append(";");
525 }
526 sb.append(" version is NOT VULNERABLE");
527 }
528 return sb.toString();
529 }
530
531 public String toNvdSearchUrl() {
532 return CpeIdentifier.nvdSearchUrlFor(this);
533 }
534 }