jeremylong / DependencyCheck

OWASP dependency-check is a software composition analysis utility that detects publicly disclosed vulnerabilities in application dependencies.
https://owasp.org/www-project-dependency-check/
Apache License 2.0
6.32k stars 1.26k forks source link

How are compile dependencies handled in dependency management? #4036

Open Lars5678 opened 2 years ago

Lars5678 commented 2 years ago

I'm not sure if I found a bug or if the behavior of the ODC is correct.

I have a Maven parent project that defines JUnit Jupiter Vintage 5.7.2 in Dependency Management. I've set the skipDependencyManagement flag and skipTestScope to false and I also see that the JUnit libs are being scanned. A CVE is not found.

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
                <version>5.7.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Result in Sonar: grafik

But if I now use this parent in a pom and use the JUnit Jupiter Library explicitly, it tells me that there is a CVE-2020-15250 in JUnit 4.13 which is used by the Jupiter 5.7.2 as a compile dependency.

<dependencies>
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

https://search.maven.org/artifact/org.junit.vintage/junit-vintage-engine/5.7.2/jar

grafik

So I can understand why he reported this to me. What I don't understand is why this is not recognized in my parent, although the Jupiter 5.7.2 was also scanned.

Or are compile dependencies only evaluated once they are actually used? Or to put it another way: Why doesn't it recognize the CVE if the Jupiter Lib is "only" in Dependency Management?

mprins commented 2 years ago

dependencyManagement "dependencies" are not actual dependencies for Maven but a recipe to describe the version; if your parent does not define dependencies (or does not define junit-vintage-engine as a dependency) and a child project defines junit-vintage-engine the child will automatically depend on the transitives of junit-vintage-engine, if you don;t want junit 4.13 (but eg. 4.13.2 you could/should add that to the parent in the dependencyManagement section to override the transitive dependency version (or you can add the override in your child project)

running mvn dependency:tree will show this

Lars5678 commented 2 years ago

Thanks for the explanation @mprins.

So ODC finds vulnerable "dependencies" in DependencyManagement (because of skipDependencyManagement = false) but not CVEs in transitive dependencies of a "dependency" in DependencyManagement? So a CVE in junit-vintage-engine would have found ODC but not in junit since it's just a transitive dependency of junit-vintage-engine?

aikebah commented 2 years ago

No, it doesn't find any vulnerable "dependencies" in DependencyManagement by default (not even the 'direct' ones. Were you to dependency-manage JUnit 4.13 instead of junit-vintage-engine it still would not show up for your parent. And that is logical, because when you ask maven about your project's dependencies (mvn dependency:tree) it's also not there.

By default dependendency-check maven plugin only scans the libraries that are expected to be used at runtime (scopes provided, compile, system, runtime) as represented in the output of mvn dependency:tree

You can configure with flags that it also scans your testDependencies This you apparently did, or you made some mistake in your child-project that made the junit libraries get promoted by maven from test to one of the default-scanned scopes. Maven transforms any compile/runtime-scoped transitive dependency of a test-scoped dependency to test-scoped transitive dependency for your project. So if you configured the projects correct junit 4.13 will be a test-scoped dependency of the project where you used your parent.

And similarly (typically for the cases of scanning bill-of-material projects and standalone dependency-managing parent projects) you can configure dependencycheck to check your project also for any managed dependencies that your project doesn't depend on by itself.

If dependency-check would scan all managed dependencies by default and you would for example build a project on the chronicle framework (which has their different components individually versioned, so for a consistent compatible set of libraries you definitely would want to use their bill-of-materials in dependency-management):

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>net.openhft</groupId>
                <artifactId>chronicle-bom</artifactId>
                <!--
                  Normally, this is the only place where the Chronicle version is specified.
                  It may always be easily bumped to the latest.
                -->
                <version>2.17.141</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Your project would get flagged for Log4Shell even if you opted to use one of the other chronicle logging bundles listed in that same bill-of-materials.

Picked this project, with an outdated version as an example of why you don't normally want to have dependencyManagement analysed as it may well contain vulnerable non-dependencies, as I knew that they are using independently versioned subprojects, so the only reliable way as a user of the framework to know that you depend on internally compatible versions is by the use of their bill-of-materials that contains all the sub-projects, even those that you opted not to use.

Lars5678 commented 2 years ago

Thanks @aikebah for the explanation. Either I don't understand or we're talking past each other.

I use dependency management to define versions that a child project should use when it declares the lib. I don't want to overwrite a lib here, I just want to understand why ODC doesn't recognize JUnit as vulnerable in this case, but does if I integrate Jupiter Vintage directly as dependency.

What I want to aim for here is that I get a hint when building the parent pom if I offer a vulnerable lib in dependency management. I don't want the child project to realize this until it declares the lib.

You said: No, it doesn't find any vulnerable "dependencies" in DependencyManagement by default

I'm aware. That's why I set skipDependencyManagement=false in the parent project. I want the libs to be scanned in dependency management. I understood that there is a parameter for exactly that.

I also see that junit-vintage is being scanned. This is because I set skipTestScope=false.

So everything works as expected. It scans the libs in Dependency Management and also those in TestScope. I just wonder why he then does not recognize that the Lib is affected.

Here is an example. The following example detects the vulnerable lib.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>de.xx.odc</groupId>
    <artifactId>odc-test-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.release>11</maven.compiler.release>

        <depencency-check-maven.version>6.5.3</depencency-check-maven.version>

        <junit-vintage.version>5.7.2</junit-vintage.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <version>${junit-vintage.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.owasp</groupId>
                <artifactId>dependency-check-maven</artifactId>
                <version>${depencency-check-maven.version}</version>
                <configuration>
                    <skipTestScope>false</skipTestScope>
                    <skipDependencyManagement>false</skipDependencyManagement>
                    <formats>
                        <format>html</format>
                        <format>json</format>
                    </formats>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

grafik

Unfortunately, the following example does not recognize the vulnerable lib. But I see that Jupiter Vintage is scanned.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>de.xx.odc</groupId>
    <artifactId>odc-test-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.release>11</maven.compiler.release>

        <depencency-check-maven.version>6.5.3</depencency-check-maven.version>

        <junit-vintage.version>5.7.2</junit-vintage.version>
    </properties>

    <dependencyManagement>
   <dependencies>
            <dependency>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
                <version>${junit-vintage.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.owasp</groupId>
                <artifactId>dependency-check-maven</artifactId>
                <version>${depencency-check-maven.version}</version>
                <configuration>
                    <skipTestScope>false</skipTestScope>
                    <skipDependencyManagement>false</skipDependencyManagement>
                    <formats>
                        <format>html</format>
                        <format>json</format>
                    </formats>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

grafik

I'm really only concerned here with the parent. I would like to see in the parent whether I have defined a version in Dependency Management that is affected. My understanding is that I can achieve this with skipDependencyManagement=false.

But maybe I don't really understand what the parameter skipDependencyManagement=false is supposed to do.

Lars5678 commented 2 years ago

I have now written JUnit directly into the Dependency Management section myself. The result: ODC recognizes it as vulnerable.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>de.xx.odc</groupId>
    <artifactId>odc-test-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.release>11</maven.compiler.release>

        <depencency-check-maven.version>6.5.3</depencency-check-maven.version>

    </properties>

    <dependencyManagement>
   <dependencies>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.13</version>
           <scope>test</scope>
       </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.owasp</groupId>
                <artifactId>dependency-check-maven</artifactId>
                <version>${depencency-check-maven.version}</version>
                <configuration>
                    <skipTestScope>false</skipTestScope>
                    <skipDependencyManagement>false</skipDependencyManagement>
                    <formats>
                        <format>html</format>
                        <format>json</format>
                    </formats>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

So ODC detects a vulnerable lib in Dependency Management. However, not if it comes along as a transitive dependency.

So my question is whether this is known and simply cannot be done differently or whether it is a bug.

aikebah commented 2 years ago

Given the configuration contents in your pom this is indeed a bug. With the DependencyCheck configuration in that pom (skipTestScope and skipDependencyManagement set to false) it should find the transitive vulnerability in JUnit for the dependencyManaged junit-vintage-engine on the parent project.

My remarks were based on the defaults of DependencyCheck (to skip dependencyManagement as well as test-scoped dependencies) and no indication from your side that those defaults were overridden in your project.

Lars5678 commented 2 years ago

No problem. The main thing is that we were able to clarify it ;)

Can you change the label to bug? Unfortunately I can't. Or should I create a new BugReport and close the question?