diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java index 11eaefc6a..3075ea142 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java @@ -20,7 +20,12 @@ package org.owasp.dependencycheck.dependency; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import javax.annotation.concurrent.ThreadSafe; + import org.apache.commons.lang3.StringUtils; import org.owasp.dependencycheck.data.cpe.IndexEntry; import org.slf4j.Logger; @@ -187,6 +192,35 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp return "VulnerableSoftware{" + name + "[" + previousVersion + "]}"; } + /** + * Method that split versions for '.', '|' and '-". Then if a token start + * with a number and then contains letters, it will split it too. For + * example "12a" is splitted in ["12", "a"]. This is done to support correct + * comparison of "5.0.3a", "5.0.9" and "5.0.30". + * + * @return an Array of String containing the tokens to be compared + */ + private String[] split(String s) { + String[] splitted = s.split("(\\.|-)"); + + ArrayList res = new ArrayList<>(); + for (String token : splitted) { + if (token.matches("^[\\d]+?[A-z]+")) { + Pattern pattern = Pattern.compile("^([\\d]+?)(.*)$"); + Matcher matcher = pattern.matcher(token); + matcher.find(); + String g1 = matcher.group(1); + String g2 = matcher.group(2); + + res.add(g1); + res.add(g2); + continue; + } + res.add(token); + } + return res.toArray(new String[res.size()]); + } + /** * Implementation of the comparable interface. * @@ -201,8 +235,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp final int max = (left.length <= right.length) ? left.length : right.length; if (max > 0) { for (int i = 0; result == 0 && i < max; i++) { - final String[] subLeft = left[i].split("(\\.|-)"); - final String[] subRight = right[i].split("(\\.|-)"); + final String[] subLeft = split(left[i]); + final String[] subRight = split(right[i]); final int subMax = (subLeft.length <= subRight.length) ? subLeft.length : subRight.length; if (subMax > 0) { for (int x = 0; result == 0 && x < subMax; x++) { diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/VulnerableSoftwareTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/VulnerableSoftwareTest.java index e0d527393..9acc20330 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/VulnerableSoftwareTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/VulnerableSoftwareTest.java @@ -174,4 +174,25 @@ public class VulnerableSoftwareTest extends BaseTest { assertFalse(VulnerableSoftware.isPositiveInteger("01")); assertFalse(VulnerableSoftware.isPositiveInteger("00")); } + + @Test + public void testVersionsWithLettersComparison() { + VulnerableSoftware a = new VulnerableSoftware(); + a.setName("cpe:/a:mysql:mysql:5.0.3a"); + + VulnerableSoftware b = new VulnerableSoftware(); + b.setName("cpe:/a:mysql:mysql:5.0.9"); + + VulnerableSoftware c = new VulnerableSoftware(); + c.setName("cpe:/a:mysql:mysql:5.0.30"); + + assertTrue(a.compareTo(b) < 0); + assertTrue(a.compareTo(c) < 0); + + assertTrue(b.compareTo(a) > 0); + assertTrue(b.compareTo(c) < 0); + + assertTrue(c.compareTo(a) > 0); + assertTrue(c.compareTo(b) > 0); + } }