Recent major data losses and security vulnerabilities in open source frameworks (and the applications that use them) have caused the companies that use those frameworks to have elevated concerns regarding vulnerabilities. The elevated awareness is for good reason, too. After all, no one wants to be the next one to lose sensitive data, be the punching bag of others, or be the example of what not to do security-wise.
Dealing with application vulnerabilities is not only a developer concern but a concern at all levels of organizations. Companies cannot fool others by blaming only the developers when bad things do happen.
If you happen to be in a group that doesn’t have any open source vulnerability reporting, OWASP Dependency-Check may be your short-term answer to get at least something in place. Adding OWASP Dependency-Check into your build process takes a relatively low effort. Other than not having the technology that stack Dependency-Check can help you with, there isn’t a reason not to at least add Dependency-Check to give a little insight into your open source dependencies.
TL;DR: Add OWASP Dependency-Check to your build process to get insight into your dependency vulnerabilities.
Dependency-Check Project: https://www.owasp.org/index.php/OWASP_Dependency_Check
Example Project: https://github.com/jhoestje/FooWorld
In this example, I’ll use a Java project with Maven. I don’t have any code in the project since Dependency-Check only cares about dependences and not source code under
/src’test. The important parts are what exists in the POM.XML and
For a quick description, Dependency-Check uses a variety of analyzers to build a list of Common Platform Enumeration (CPE) entries. The JAR and Archive analyzers are the two analyzers most commonly used for Java projects. The JAR analyzer analyzes each GAV in the
POM.XML file and builds the CPE for the dependency. The analyzer can also inspect JARs managed outside of Maven by utilizing the archive manifest metadata. The Archive analyzer targets other archive formats such as the ZIP archive format and then uses the other analyzers for the archive contents.
The Archive analyzer seems to catch archive files in most project locations, but I haven’t tried them all. The analyzer is a little funky when a JAR (same CPE) that contains a vulnerability was found in several locations or archives. The JAR is reported once, even though it can reside in several locations within the project.
Dependency-Check uses the CPE as the lookup identifier in its database of associated Common Vulnerability and Exposure (CVE) entries. We’ll go over the drawbacks of the CPE method later. When using Dependency-Check with Maven, Dependency-Check downloads the NIST NVD Data Feeds to the default configuration location which is in the local Maven M2 repository (
org/owasp/…./data). The location is the same for the local environment and Jenkins.
When the Dependency-Check Maven plugin executes, Dependency-Check downloads the NVD Data Feed if done for the first time or has been longer than the default four hours. The initial download can take 10 – 20+ minutes. The data is stored in an H2 database located in the local M2 repo. Once the vulnerability data has been downloaded, Dependency-Check uses the CPE to retrieve the CVE information from the H2 database. The report is then generated with this information and add to the default
Dependency-Check can be configured to use other databases.
The Basic Setup With Most Defaults
OWASP makes it very easy to get a report generated out of the box. To get started, add Dependency-Check to your
The plugin can be configured as a build or reporting plugin.
Maven Build Plugin:
<build> <finalName>fooworld</finalName> <plugins> <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> <version>3.2.1</version> <!-- https://jeremylong.github.io/DependencyCheck/dependency-check-maven/configuration.html --> <configuration> <!-- Generate all report formats --> <format>ALL</format> <!-- Don't use Nexus Analyzer --> <centralAnalyzerEnabled>false</centralAnalyzerEnabled> <!-- Am I the latest version? --> <versionCheckEnabled>true</versionCheckEnabled> </configuration> <!-- add an execution to be automatically ran as part of the 'verify' build lifecycle phase --> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Maven Reporting Plugin:
<reporting> <plugins> <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> <version>3.2.1</version> <configuration> <format>ALL</format> <centralAnalyzerEnabled>false</centralAnalyzerEnabled> <versionCheckEnabled>true</versionCheckEnabled> </configuration> </plugin> </plugins> </reporting>
Each plugin configuration is attached to a Maven lifecycle phase by default. The build plugin configuration is attached to the
verify phase and the report plugin configuration is attached to the
If you don’t want the build plugin to be attached to a Maven phase then remove the execution block. To run without the
execution block, add
dependency-check:check as a Maven goal.
//Style somehow RUN BUILD PLUGIN -> mvn verify RUN REPORT PLUGIN -> mvn site RUN WITH IT’S OWN GOAL -> mvn dependency-check:check // /Style somehow
ALL as the format value so XML, CSV, JSON, and HTML based reports will be generated. If you’re not publishing the findings to other products like SonarQube, then
HTML will work.
If you have a Maven multi-module project, Dependency-Check provides an
aggregate goal along with the
check goal. This will allow the child project results to get rolled-up into the parent project report. Some of the details of the child project in which the CVE exists is lost as a result.
Generated Vulnerability Log
... One or more dependencies were identified with known vulnerabilities in fooworld Maven Webapp: log4j-core-2.0.1.jar (cpe:/a:apache:log4j:2.0.1, org.apache.logging.log4j:log4j-core:2.0.1) : CVE-2017-5645 struts2-core-18.104.22.168.jar (cpe:/a:apache:struts:22.214.171.124, org.apache.struts:struts2-core:126.96.36.199) : CVE-2018-1327 spring-core-3.2.5.RELEASE.jar (cpe:/a:springsource:spring_framework:3.2.5, cpe:/a:pivotal_software:spring_framework:3.2.5, cpe:/a:pivotal:spring_framework:3.2.5, org.springframework:spring-core:3.2.5.RELEASE) : CVE-2014-1904, CVE-2014-0054, CVE-2018-1271, CVE-2018-1270, CVE-2014-0225, CVE-2015-5211, CVE-2015-3192, CVE-2016-5007, CVE-2016-9878, CVE-2018-1272, CVE-2014-3578, CVE-2014-3625 additional.jars.zip: taglibs-standard-impl-1.2.1.jar (org.apache.taglibs:taglibs-standard-impl:1.2.1, cpe:/a:apache:standard_taglibs:1.2.1) : CVE-2015-0254 additional.lib.jars.zip: struts-1.2.9.jar (cpe:/a:apache:struts:1.2.9, struts:struts:1.2.9) : CVE-2016-1182, CVE-2016-1181, CVE-2014-0114, CVE-2015-0899
Generated Vulnerability Reports
Application Vulnerability Summary:
List of vulnerabilities by CVE:
Make Everyone Responsible
Unless you already have the
site phase publishing the reports to a highly visible location, the default
/target report location isn’t going to cut it if you want the proper eyeballs viewing the vulnerabilities. The whole idea is to get the vulnerabilities in front of the proper eyeballs so the vulnerabilities can get remediated.
Publishing the (XML and HTML) results to a good code quality platform like SonarQube will really help. The SonarQube Dependency-Check plugin is still fairly static, but it has the effect and it is improving.
Get the report in front of developers, IT leaders, and business leaders. Use the CVE information to help create an understanding of the risk associated by now remediating the vulnerability. Make it everyone’s responsibility to have zero vulnerabilities. Make it a quality control gate as part of the build process.
Configuring Dependency-Check to fail the build when a vulnerability is identified (
failBuildOnCVSS) is a valid option.
The list of all the configuration options is located here.
Extra Setup For Performance
There are a few configuration defaults that can be overridden to make sure each build job isn’t slowed down too much by the Dependency-Check plugin. The analyzers are fairly quick but there could be a penalty if a data update is performed. As mentioned before, the initial database download is a drag but each incremental update is minor assuming Dependency-Check run a few times a day.
The reduce the time and distance it takes to reach the data, the NVD database can be mirrored on an internal server. The internal pipeline to the data will be better equipped to handle the requests rather than the pipeline to nvd.nist.gov.
Dependency-Check also provides an
update-only goal that updates the local cache.
We ended up not doing these overrides since jobs always run frequently and data updates are quick enough when accessing external URLs.
Extra Setup for Better Enterprise Environments
Switch the database!
The default database location is
With each Dependency-Check access to the default H2 database, a lock is made on the database. This lock will block other reads from being executed. This isn’t good for your continuous integration environment.
“Dependency-check does not currently use file hashes for identification. If the dependency was built from source the hash likely will not match the “published” hash. While the evidence-based mechanism currently used can also be unreliable the design decision was to avoid maintaining a hash database of known vulnerable libraries. A future enhancement may add some hash matching for very common well-known libraries (Spring, Struts, etc.).”
In other words, you may need to utilize the suppression and hint files due to the higher rate of false positives and false negatives. The dependency identification errors are minimal if the dependency is listed in a Maven POM file.
Be careful with the default behavior of these options. The Nexus Analyzer require Sonatype Nexus Pro. If the
centralAnalyzerEnabled options were set to
true without a backend service present, a service not found exception is thrown. The exception handling behavior changes across plugin versions. Older versions of the plugin hide the exception and newer versions ones would fail the build. It appears the very latest (v3.2.1) will automatically disable the analyzer if the Nexus service cannot be reached.
Why Paid Solutions Are Worth It
If you really want to stay ahead of the vulnerability curve, paying for a solution is the way to go. Granted, the solutions are not cheap, but neither is the penalty if data gets leaked due to a vulnerability.
Dependency-Check reports on vulnerabilities that have made it into the NVD database. While this is a legit database of vulnerabilities, the downside is the length of time it takes for vulnerabilities to be proven and through the verification process. This means a vulnerability could be in the wild for a while.
Paid solutions (Snyk, Black Duck, AquaSec, JFrog, Sonatype, ..) offer teams that are proactively researching potential issues in various sources that allow their databases be way ahead of what the NVD database can offer. They also offer other important dependency data points like licensing, popularity, and age.
The Wrap Up
OWASP Dependency-Check provides a solution to get a basic dependency vulnerability analyzer in place for every development shop. Use the reports Dependency-Check generates to get the list of vulnerabilities and their known risks in front of everyone’s eyes so it forces the issue of remediation.
A dependency vulnerability analyzer should be mandatory in every build process. You owe it to your users.