Skip to main content

How to find third-party vulnerabilities in your Java code

Learn four ways to check your Java projects for vulnerable dependencies.
Image
Hands typing on keyboard

In a previous article, I showed you how to detect third-party vulnerabilities in Python. This article shows how to scan your Java code for the same issues.

Example 1: Scan the libraries of an open source project

Software is complex, but thanks to open source, I can quickly develop new applications by leveraging the efforts of people who choose to share their work. Unfortunately, new software functionality also introduces new bugs that malicious attackers could exploit.

To illustrate the problem, I will download a vulnerable version of a well-known open source application server:

$ curl --location --fail --output ~/Downloads/XXXX-A.B.C-zzzz.zip https://XXX.ZZZZ.org/dist/XXX/server/A.B.C/XXXX-A.B.C-zzzz.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 88.8M  100 88.8M    0     0  5142k      0  0:00:17  0:00:17 --:--:-- 1194k

cd ~/Downloads
unzip XXXX-A.B.C-zzzz.zip

There are many tools out there to scan for application vulnerabilities. I will demonstrate how to do this with OWASP dependency analyzer by Jeremy Long, so grab a copy:

$ curl --fail --output ~/Downloads/dependency-check-6.5.3-release.zip --location https://github.com/jeremylong/DependencyCheck/releases/download/v6.5.3/dependency-check-6.5.3-release.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   669  100   669    0     0   4430      0 --:--:-- --:--:-- --:--:--  4430
100 23.0M  100 23.0M    0     0  7653k      0  0:00:03  0:00:03 --:--:-- 8899k

Use it to check vulnerabilities on this application:

$ ~/Downloads/dependency-check/bin/dependency-check.sh --prettyPrint --format HTML -scan /home/josevnz/Downloads/XXXX-A.B.C-zzzz/lib/
[INFO] Checking for updates
[INFO] Download Started for NVD CVE - Modified
[INFO] Download Complete for NVD CVE - Modified  (278 ms)
[INFO] Processing Started for NVD CVE - Modified
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/josevnz/Downloads/dependency-check/lib/jackson-module-afterburner-2.13.1.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String)
WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Processing Complete for NVD CVE - Modified  (5150 ms)
[INFO] Begin database maintenance
[INFO] Updated the CPE ecosystem on 116793 NVD records
[INFO] Cleaned up 2 orphaned NVD records
[INFO] End database maintenance (24739 ms)
[INFO] Begin database defrag
[INFO] End database defrag (4660 ms)
[INFO] Check for updates complete (39967 ms)
[INFO] 

Dependency-Check is an open source tool performing a best effort analysis of 3rd party dependencies; false positives and false negatives may exist in the analysis performed by the tool. Use of the tool and the reporting provided constitutes acceptance for use in an AS IS condition, and there are NO warranties, implied or otherwise, with regard to the analysis or its use. Any use of the tool and the reporting provided is at the user's risk. In no event shall the copyright holder or OWASP be held liable for any damages whatsoever arising out of or in connection with the use of this tool, the analysis performed, or the resulting report.

   About ODC: https://jeremylong.github.io/DependencyCheck/general/internals.html
   False Positives: https://jeremylong.github.io/DependencyCheck/general/suppression.html

💖 Sponsor: https://github.com/sponsors/jeremylong


[INFO] Analysis Started
[INFO] Finished Archive Analyzer (1 seconds)
[INFO] Finished File Name Analyzer (0 seconds)
[INFO] Finished Jar Analyzer (1 seconds)
[INFO] Finished Central Analyzer (9 seconds)
[ERROR] ----------------------------------------------------
[ERROR] .NET Assembly Analyzer could not be initialized and at least one ''exe'' or ''dll'' was scanned. The ''dotnet'' executable could not be found on the path; either disable the Assembly Analyzer or add the path to dotnet core in the configuration.
[ERROR] ----------------------------------------------------
[INFO] Finished Dependency Merging Analyzer (0 seconds)
[INFO] Finished Version Filter Analyzer (0 seconds)
[INFO] Finished Hint Analyzer (0 seconds)
[INFO] Created CPE Index (2 seconds)
[INFO] Finished CPE Analyzer (6 seconds)
[INFO] Finished False Positive Analyzer (0 seconds)
[INFO] Finished NVD CVE Analyzer (0 seconds)
[INFO] Finished Sonatype OSS Index Analyzer (3 seconds)
[INFO] Finished Vulnerability Suppression Analyzer (0 seconds)
[INFO] Finished Dependency Bundling Analyzer (0 seconds)
[INFO] Analysis Complete (23 seconds)
[INFO] Writing report to: /home/josevnz/Downloads/./dependency-check-report.html
Image
OWASP scan results
(Jose Nunez, CC BY-SA 4.0)

The report shows that the version I chose has several issues. The vendor fixed all of them in the latest release.

Not everything is perfect, and the tool could generate false positives, but this is still a great start.

Next, I'll show a different project that you can fix yourself.

Example 2: Analyze a homegrown Covid-19 town statistics reporter

This project downloads Covid-19 data from the State of Connecticut government portal and performs basic filtering before displaying the results.

Download and compile with Gradle:

git clone   https://github.com/josevnz/Covid19Informer.git
$ gradle test jar 
$ gradle distTar
$ /bin/tar --directory $HOME --extract --verbose --file build/distributions/Covid19Informer-0.0.1.tar

NOTE: You could also install by hand like this (I like the Gradle wrapper enough to avoid doing this):

/bin/mkdir --parent --verbose $HOME/Covid19Informer-0.0.1
/bin/cp --verbose build/libs/Covid19Informer-0.0.1.jar $HOME/Covid19Informer-0.0.1
/bin/curl --fail --location --output $HOME/Covid19Informer-0.0.1/lanterna-3.1.1.jar --url https://repo1.maven.org/maven2/com/googlecode/lanterna/lanterna/3.1.1/lanterna-3.1.1.jar
/bin/curl --fail --location --output $HOME/Covid19Informer-0.0.1/commons-io-2.11.0.jar --url https://repo1.maven.org/maven2/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar

Next, run Covid19Informer to get an idea of what this small application does. I usually won't use an Uber .jar in production, but instead will deploy the Java application and its dependencies (jars) in a deployment directory:

$ $HOME/Covid19Informer-0.0.1/bin/Covid19Informer
...
INFO: Covid19DataPerTown[lastUpdateDate=2022-01-19, townNumber=135, town='Stamford', totalCases=26239, confirmedCases=24448, probableCases=1791, caseRate=135.0, totalDeaths=349, confirmedDeaths=308, probableDeaths=41, peopleTested=121435, rateTestedPer100k=93672.0, numberOfTests=383071, numberOfPositives=33471, numberOfNegatives=308, numberOfIndeterminates=422]
Jan 20, 2022 7:39:13 PM com.kodegeek.covid19.towndata.TownDataRetriever$Covid19DataPerTown lambda$printCovidData$1
INFO: Covid19DataPerTown[lastUpdateDate=2022-01-19, townNumber=151, town='Waterbury', totalCases=29064, confirmedCases=25500, probableCases=3564, caseRate=151.0, totalDeaths=444, confirmedDeaths=378, probableDeaths=66, peopleTested=99611, rateTestedPer100k=92603.0, numberOfTests=423805, numberOfPositives=36882, numberOfNegatives=378, numberOfIndeterminates=787]

Analyze the dependencies:

$ ~/Downloads/dependency-check/bin/dependency-check.sh --prettyPrint --format HTML -scan $HOME/Covid19Informer-0.0.1/lib
[INFO] Checking for updates
[INFO] Skipping NVD check since last check was within 4 hours.
[INFO] Skipping RetireJS update since last update was within 24 hours.
[INFO] Check for updates complete (62 ms)
[INFO] 

Dependency-Check is an open source tool performing a best effort analysis of 3rd party dependencies; false positives and false negatives may exist in the analysis performed by the tool. Use of the tool and the reporting provided constitutes acceptance for use in an AS IS condition, and there are NO warranties, implied or otherwise, with regard to the analysis or its use. Any use of the tool and the reporting provided is at the user'suser's risk. In no event shall the copyright holder or OWASP be held liable for any damages whatsoever arising out of or in connection with the use of this tool, the analysis performed, or the resulting report.

   About ODC: https://jeremylong.github.io/DependencyCheck/general/internals.html
   False Positives: https://jeremylong.github.io/DependencyCheck/general/suppression.html

💖 Sponsor: https://github.com/sponsors/jeremylong

[INFO] Analysis Started
[INFO] Finished Archive Analyzer (0 seconds)
[INFO] Finished File Name Analyzer (0 seconds)
[INFO] Finished Jar Analyzer (0 seconds)
[INFO] Finished Central Analyzer (0 seconds)
[INFO] Finished Dependency Merging Analyzer (0 seconds)
[INFO] Finished Version Filter Analyzer (0 seconds)
[INFO] Finished Hint Analyzer (0 seconds)
[INFO] Created CPE Index (1 seconds)
[INFO] Finished CPE Analyzer (2 seconds)
[INFO] Finished False Positive Analyzer (0 seconds)
[INFO] Finished NVD CVE Analyzer (0 seconds)
[INFO] Finished Sonatype OSS Index Analyzer (0 seconds)
[INFO] Finished Vulnerability Suppression Analyzer (0 seconds)
[INFO] Finished Dependency Bundling Analyzer (0 seconds)
[INFO] Analysis Complete (2 seconds)
[INFO] Writing report to: /home/josevnz/Documents/Covid19Informer/./dependency-check-report.html

The application is clean, but that doesn't mean I caught all the issues. Let me elaborate:

  • I didn't check the test or integration dependencies, if any. I checked only the runtime dependencies (which is good enough for most cases).
  • This approach is reactive, meaning I'll catch a problem after it happens, not during the development cycle.

The situation improves when I have access to the source code.

[ Download the eBook to Get ready for your Red Hat remote exam. ]

Example 3: Scan Covid19Informer while compiling the code

To use the tool as part of continuous integration, add it into the build.gradle.kts.

plugins {
    `java-library`
    application
    id("org.owasp.dependencycheck") version "6.5.3"
}

Using it is just a matter of calling Gradle, like this:

$ gradle dependencyCheckAnalyze --info
...
One or more dependencies were identified with known vulnerabilities in Covid19Informer:

h2-1.4.199.jar (pkg:maven/com.h2database/h2@1.4.199, cpe:2.3:a:h2database:h2:1.4.199:*:*:*:*:*:*:*) : CVE-2021-23463, CVE-2021-42392

See the dependency-check report for more details.

Element event queue destroyed: org.apache.commons.jcs.engine.control.event.ElementEventQueue@12440215
In DISPOSE, [NODEAUDIT] fromRemote [false]
In DISPOSE, [NODEAUDIT] auxiliary [NODEAUDIT]
...
BUILD SUCCESSFUL in 4s
2 actionable tasks: 2 executed
Some of the file system contents retained in the virtual file system are on file systems that Gradle doesn't support watching. The relevant state was discarded to ensure changes to these locations are properly detected. You can override this by explicitly enabling file system watching.
Watching 24 directories to track changes

A few things to note:

  • The build went through, but there was a warning: A vulnerable .jar was found in my Gradle cache (file path: /home/josevnz/.gradle/caches/modules-2/files-2.1/com.h2database/h2/1.4.199/7bf08152984ed8859740ae3f97fae6c72771ae45/h2-1.4.199.jar). It is there from a previous test, so keep this in mind if you run into some false positives when using this tool.
  • This Gradle scanner downloads a lot of data the first time. After that, it stabilizes using the local cache content.

For that reason, I'll show you a different Gradle plugin to scan for vulnerabilities.

[ Learn the benefits of modernizing your network in the eBook Network automation for everyone. ]

Example 4: Use a different tool to scan Covid19Informer after it is compiled

The folks from Sonatype created a Gradle plugin to scan your project called Scan Gradle Plugin, which is baked in by the OSS Index catalog.

By now, you can probably see where this is going. By having this check within your Java compilation toolset, your continuous integration tool can run this scan every time the code changes, reporting any anomalies back to you before the code is deployed into production.

To make this happen, add the following to the build.gradle.kts file:

plugins {
    `java-library`
    id ("org.sonatype.gradle.plugins.scan") version "2.2.2"
}

ossIndexAudit {
    username = System.getenv("ossindexaudit_user")
    password = System.getenv("ossindexaudit_password")
}

And then scan for vulnerabilities (Sonatype recommends creating a free account with no limits on the number of times you call the service):

$ gradlew test jar
# These 2 variables will be ''injected'' on your continuous integration environment
$ read -r -p "Please enter your Sonatype user (like myemail@example.com): " ossindexaudit_user
$ read -r -p -s ""Please enter your Sonatype account password"" ossindexaudit_user
$ export ossindexaudit_user ossindexaudit_user
$ gradle ossIndexAudit 

> Task :ossIndexAudit
 ________  ________  ________  ________  ___       _______           ________  ________  ________  ________
|\   ____\|\   __  \|\   __  \|\   ___ \|\  \     |\  ___ \         |\   ____\|\   ____\|\   __  \|\   ___  \
\ \  \___|\ \  \|\  \ \  \|\  \ \  \_|\ \ \  \    \ \   __/|        \ \  \___|\ \  \___|\ \  \|\  \ \  \\ \  \
 \ \  \  __\ \   _  _\ \   __  \ \  \ \\ \ \  \    \ \  \_|/__       \ \_____  \ \  \    \ \   __  \ \  \\ \  \
  \ \  \|\  \ \  \\  \\ \  \ \  \ \  \_\\ \ \  \____\ \  \_|\ \       \|____|\  \ \  \____\ \  \ \  \ \  \\ \  \
   \ \_______\ \__\\ _\\ \__\ \__\ \_______\ \_______\ \_______\        ____\_\  \ \_______\ \__\ \__\ \__\\ \__\
    \|_______|\|__|\|__|\|__|\|__|\|_______|\|_______|\|_______|       |\_________\|_______|\|__|\|__|\|__| \|__|
                                                                       \|_________|


  _      _                       _   _
 /_)    /_`_  _  _ _/_   _  _   (/  /_`_._  _   _/ _
/_)/_/ ._//_// //_|/ /_//_//_' (_X /  ///_'/ //_/_\
   _/                _//

Gradle Scan version: 2.2.2
------------------------------------------------------------------------------------------------------------------------------------------------------

Checking vulnerabilities in 1 dependencies
No vulnerabilities found!

BUILD SUCCESSFUL in 758ms
1 actionable task: 1 executed

So everything is good, right?

Not quite. Take a closer look at my project again:

dependencies {
    implementation("com.googlecode.lanterna:lanterna:3.1.1")
    implementation("commons-cli:commons-cli:20040117.000000")
    testImplementation("junit:junit:4.13")
}

The scanner found my compilation dependencies but ignored my testing dependencies—in this case, Junit. It turns out the version I'm using on my build file has a vulnerability from 2020: JUnit CVE-2020-15250.

Why did that happen? You need to have the isAllConfigurations = true property set to true on the build.gradle.kts:

ossIndexAudit {
    username = System.getenv("ossindexaudit_user")
    password = System.getenv("ossindexaudit_password")
    isAllConfigurations = true
}

I'll try again:

$ gradle jar; gradle ossIndexAudit 

BUILD SUCCESSFUL in 2s
3 actionable tasks: 3 up-to-date

> Task :ossIndexAudit FAILED
 ________  ________  ________  ________  ___       _______           ________  ________  ________  ________
|\   ____\|\   __  \|\   __  \|\   ___ \|\  \     |\  ___ \         |\   ____\|\   ____\|\   __  \|\   ___  \
\ \  \___|\ \  \|\  \ \  \|\  \ \  \_|\ \ \  \    \ \   __/|        \ \  \___|\ \  \___|\ \  \|\  \ \  \\ \  \
 \ \  \  __\ \   _  _\ \   __  \ \  \ \\ \ \  \    \ \  \_|/__       \ \_____  \ \  \    \ \   __  \ \  \\ \  \
  \ \  \|\  \ \  \\  \\ \  \ \  \ \  \_\\ \ \  \____\ \  \_|\ \       \|____|\  \ \  \____\ \  \ \  \ \  \\ \  \
   \ \_______\ \__\\ _\\ \__\ \__\ \_______\ \_______\ \_______\        ____\_\  \ \_______\ \__\ \__\ \__\\ \__\
    \|_______|\|__|\|__|\|__|\|__|\|_______|\|_______|\|_______|       |\_________\|_______|\|__|\|__|\|__| \|__|
                                                                       \|_________|


  _      _                       _   _
 /_)    /_`_  _  _ _/_   _  _   (/  /_`_._  _   _/ _
/_)/_/ ._//_// //_|/ /_//_//_' (_X /  ///_'/ //_/_\
   _/                _//

Gradle Scan version: 2.2.2
------------------------------------------------------------------------------------------------------------------------------------------------------

Checking vulnerabilities in 2 dependencies
Found vulnerabilities in 1 dependencies
[1/1] - pkg:maven/junit/junit@4.7 - 1 vulnerability found!

   Vulnerability Title:  [CVE-2020-15250] In JUnit4 from version 4.7 and before 4.13.1, the test rule TemporaryFolder cont...
   ID:  7ea56ad4-8a8b-4e51-8ed9-5aad83d8efb1
   Description:  In JUnit4 from version 4.7 and before 4.13.1, the test rule TemporaryFolder contains a local information disclosure vulnerability. On Uni...
   CVSS Score:  (5.5/10, Medium)
   CVSS Vector:  CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
   CVE:  CVE-2020-15250
   Reference:  https://ossindex.sonatype.org/vulnerability/7ea56ad4-8a8b-4e51-8ed9-5aad83d8efb1?component-type=maven&component-name=junit.junit&utm_source=ossindex-client&utm_medium=integration&utm_content=1.7.0


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':ossIndexAudit'.
> Vulnerabilities detected, check log output to review them

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.
Use ''--warning-mode all'' to show the individual deprecation warnings.
See https://docs.gradle.org/7.0/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 1s
1 actionable task: 1 executed

Great! Fixing this is trivial—just upgrade Junit to the latest coordinates (at this time junit:junit:4.13.2) in build.gradle.kts and compile it again:

dependencies {
    implementation("com.googlecode.lanterna:lanterna:3.1.1")
    implementation("commons-io:commons-io:2.11.0")
    testImplementation("junit:junit:4.13.2")
}
Checking vulnerabilities in 3 dependencies
No vulnerabilities found!

Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.
Use ''--warning-mode all'' to show the individual deprecation warnings.
See https://docs.gradle.org/7.0/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

Will an IDE tell if you are using an older version of a library?

Most integrated development environments (IDEs) will report these issues, and you should not ignore those warnings. For example, the Community Edition of IntelliJ caught the old Junit and marked my build.gradle.kts file with a warning:

Image
intellij_gradle_warning
(Jose Nunez, CC BY-SA 4.0)

Other IDEs—like VS Code—do the same.

What you've learned

  • How to analyze projects using OWASP Dependency check.
  • How to fix projects if a vulnerable dependency is found (in this case by fixing the build.gradle.kts file).
  • How to add vulnerability checks to your continuous integration using the sonatype-scan-gradle-plugin.

Now you are more prepared to check your Java projects for third-party vulnerabilities.

Topics:   Java   Security   Troubleshooting  
Author’s photo

Jose Vicente Nunez

Proud dad and husband, software developer and sysadmin. Recreational runner and geek. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.