Sec in your DevOps: Adding the OWASP Dependency Check to your Jenkins pipeline

January 23, 2020
Written by: Eivind Utnes
Back to blog

This blog post aims to help DevOps practitioners and security professionals to take a first step towards adding security testing to an existing CI/CD pipeline. We will install the OWASP Dependency Check plugin in a Jenkins instance, verify that it gives us the expected output, and create a suppression file to deal with false positives.

The Dependency Check project has a simple purpose: To detect known vulnerabilities in a project’s dependencies (also see the OWASP 2017 Top 10, which lists “Using Components with Known Vulnerabilities” as number nine.). It does this by scanning the project to detect references to components that have been assigned CVE (Common Vulnerability and Exposure) entries, and to report these known vulnerabilities to the developers. The project supports multiple technology stacks, including Ant, Maven and Gradle, but for this demonstration we will be focusing on the Jenkins plugin.

Adding the OWASP Dependency Check plugin to your pipeline is a simple first step to reduce your attack surface which is easy to implement, as it requires no server reconfigurations or additional servers in order to work. In its most basic form, it is simply to install the plugin and roll out.

Installing and running the plugin

Step one in the process is to install the OWASP Dependency-Check plugin from the Update Center, the built in Jenkins plugin repository. Go to Manage Jenkins > Manage Plugins > Available and filter for the OWASP Dependency-Check plugin to get started.

Note: If needed, a test instance of Jenkins can be set up in minutes by using the Jenkins/Jenkins Docker image.

After installation, the plugin needs some basic configuration in order to become operative. Go to Manage Jenkins > Global Tool Configuration and scroll down to Dependency-Check and click “Add Dependency-Check”.

We can name the Dependency-Check installation anything we want, but if we add multiple installations, descriptive names help distinguish between them. To install the plugin, we can use our own installation files or use the default files hosted by Bintray.

Click “Save” at the bottom of the page when you are finished. The Installation will complete the first time you run a build with the plugin activated.

To start using the plugin, you need to edit the configuration of your project. First, we need to add the plugin as a build step. Go to {Your project name} > Configure and add “Invoke Dependency-Check” to the build process.

Note: If you want to verify that the plugin works before adding it to an existing project, then you can use my test project from Github. This project generates two medium severity findings.

Choose the installation that you created earlier. For now, you can leave the Arguments field blank, we will revisit this later.

For the plugin to be able to fail a build, we need to process the dependency-check results as a post-build action. Add “Publish Dependency-Check results” to the post-build process.

We do not need to edit the XML Report field. If left blank, if will grab the default file from the project, “dependency-check-report.xml“.

If you click the “Risk gate Thresholds” button, then you can set thresholds for what should cause a build to fail. If no thresholds are set, the plugin will only report the number of findings, but not fail the build.

Note: I recommend starting with the plugin in report only mode to determine which thresholds would be appropriate for your application.

After installing the plugin, the first build will be slower than usual. The plugin will be downloading the necessary files to finalize the installation, as well as the NVD CVE database. On subsequent builds the plugin will connect to the NVD data feeds and update the local database with new CVEs.

After running your first build, you will get a new graph on the main project page. This graph will show you the number of findings that the plugin has discovered in the project and allows you to see trends over time.

For a detailed view of the findings, go to the build and click on the “Dependency Check” link in the sidebar. This will present you with a report of the findings from the plugin, including the affected file, path, and a description of the vulnerability.

The results page is parsing the XML file that the plugin has generated during the build process. The raw xml version of the report is stored in the projects workspace and can be downloaded from there.

Dealing with false positives

The findings from our project will most likely include some false positives, and we do not want to deal with them every time we run a build.

Thus, enter the suppression file. This file tells the plugin which findings we are not interested in.

In order to build our suppression file, it is helpful to use the HTML report as a starting point. In order to generate this report, we need to go back to our project configuration, and add the --format HTML --format XML arguments to our Dependency-Check invocation.

These arguments tell the plugin to generate both an XML report and a human readable HTML report in the project workspace.

Because of the security settings of Jenkins, this HTML file will not work properly when opened directly on the Jenkins server. Thankfully, it is self-contained, so we can download it locally and review it.

The reason we want this file is that it provides us with a “Suppress” button for each finding, which displays the required XML configuration for suppressing the finding from future scans.

Note: I recommend clicking the “Complete XML Doc” button the first time. This will generate the full XML page.

The full XML for suppressing the CVE-2015-9251 finding in my application looks like this:

Filename: suppression.xml

<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
    <suppress>
        <notes><![CDATA[
        file name: jquery-2.1.4.min.js
        ]]></notes>
        <packageUrl regex="true">^pkg:javascript/jquery@.*$</packageUrl>
        <cve>CVE-2015-9251</cve>
    </suppress>
</suppressions>

In order to use this file, we must again return to the project configuration page and add a new argument to our build step. This time, we add the --suppression suppression.xml (substitute your filename and path) argument to our Dependency-Check invocation. This file can be located anywhere, even on a different server or a remote source repository, as long as it is reachable by the Jenkins server.

If we build the application again, then we can verify that our suppression file is working as intended.

Note: My suppression.xml is checked into the git repository. This means that the current version is always pulled when the application is being built. In a larger project, this also means that you can read the commit log to see who suppressed a finding.

Congratulations! You have added your first security check to your pipeline.

If you have comments or questions to this guide, please contact me on Twitter @0xprime. Alternatively, you can get in contact with me through the contact form.

Header picture by tian kuan on Unsplash