Dependencycheck: Comparison method violates its general contract!

Created on 11 Oct 2017  路  4Comments  路  Source: jeremylong/DependencyCheck

When launching a mvn site:site on my project, I get this error

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-site-plugin:3.3:site (default-cli) on project upc-demo: Execution default-cli of goal org.apache.maven.plugins:maven-site-plugin:3.3:site failed: Invocation of method 'getVulnerableSoftware' in class org.owasp.dependencycheck.dependency.Vulnerability threw exception java.lang.IllegalArgumentException: Comparison method violates its general contract! at templates/htmlReport.vsl[line 886, column 59] -> [Help 1]

The plugin is configured in this way

<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>2.1.1</version>
    <configuration>
        <pyDistributionAnalyzerEnabled>false</pyDistributionAnalyzerEnabled>
        <pyPackageAnalyzerEnabled>false</pyPackageAnalyzerEnabled>
        <rubygemsAnalyzerEnabled>false</rubygemsAnalyzerEnabled>
        <cmakeAnalyzerEnabled>false</cmakeAnalyzerEnabled>
        <autoconfAnalyzerEnabled>false</autoconfAnalyzerEnabled>
        <nodeAnalyzerEnabled>false</nodeAnalyzerEnabled>
        <nspAnalyzerEnabled>false</nspAnalyzerEnabled>
        <nuspecAnalyzerEnabled>false</nuspecAnalyzerEnabled>
        <cocoapodsAnalyzerEnabled>false</cocoapodsAnalyzerEnabled>
        <bundleAuditAnalyzerEnabled>false</bundleAuditAnalyzerEnabled>
        <swiftPackageManagerAnalyzerEnabled>false</swiftPackageManagerAnalyzerEnabled>
        <assemblyAnalyzerEnabled>false</assemblyAnalyzerEnabled>
    </configuration>
</plugin>
...
<reporting>
    <plugins>
        <plugin>
            <groupId>org.owasp</groupId>
            <artifactId>dependency-check-maven</artifactId>
            <reportSets>
                <reportSet>
                    <reports>
                        <report>aggregate</report>
                    </reports>
                </reportSet>
            </reportSets>
        </plugin>
    </plugins>
</reporting>

The problem seem on the implementation of org.owasp.dependencycheck.dependency.VulnerableSoftware compare method.

@Override
public int compareTo(VulnerableSoftware vs) {
    int result = 0;
    final String[] left = StringUtils.split(this.name, ':');
    final String[] right = StringUtils.split(vs.getName(), ':');
    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 int subMax = (subLeft.length <= subRight.length) ? subLeft.length : subRight.length;
            if (subMax > 0) {
                for (int x = 0; result == 0 && x < subMax; x++) {
                    if (isPositiveInteger(subLeft[x]) && isPositiveInteger(subRight[x])) {
                        try {
                            result = Long.valueOf(subLeft[x]).compareTo(Long.valueOf(subRight[x]));
                        } catch (NumberFormatException ex) {
                            //ignore the exception - they obviously aren't numbers
                            if (!subLeft[x].equalsIgnoreCase(subRight[x])) {
                                result = subLeft[x].compareToIgnoreCase(subRight[x]);
                            }
                        }
                    } else {
                        result = subLeft[x].compareToIgnoreCase(subRight[x]);
                    }
                }
                if (result == 0) {
                    if (subLeft.length > subRight.length) {
                        result = 2;
                    }
                    if (subRight.length > subLeft.length) {
                        result = -2;
                    }
                }
            } else {
                result = left[i].compareToIgnoreCase(right[i]);
            }
        }
        if (result == 0) {
            if (left.length > right.length) {
                result = 2;
            }
            if (right.length > left.length) {
                result = -2;
            }
        }
    } else {
        result = this.getName().compareToIgnoreCase(vs.getName());
    }
    return result;
}

I found this issue with Oracle JDK 8 but the check (made by java.util.Arrays.sort) is implemented in JDK7 too (see this thread on stackoverflow)

The code is quite convoluted, I will try to offer a pull request and some tests.

bug

Most helpful comment

Here the pull request for the fix #925

All 4 comments

Finally a test that fail:

@Test
public void testVersionsComparison() {
    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");

    Assert.assertTrue(a.compareTo(b) > 0);
    Assert.assertTrue(a.compareTo(c) > 0);

    Assert.assertTrue(b.compareTo(a) < 0);
    Assert.assertTrue(b.compareTo(c) > 0);

    Assert.assertTrue(c.compareTo(a) < 0);
    Assert.assertTrue(c.compareTo(b) < 0);
}

The compare give those result:

a<b=true
a>c=true
b>a=true
b<c=true
c<a=true
c>b=true

So we find this two impossible situations:

  • a>c and a b > a > c
  • b > a and b < c -> c > b > a

Here the pull request for the fix #925

Thanks for the PR - I truly appreciate it.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings