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