A Java vulnerability scanner analyzes your application's dependencies, source or bytecode, and sometimes its running behavior to surface known CVEs and insecure patterns before they reach production. For most Java teams the highest-value scan is dependency analysis, because the JVM ecosystem's deep transitive graphs mean the majority of your risk sits in libraries you never explicitly added — Log4Shell being the canonical example.
There is no single tool that does everything well. Understanding the categories helps you assemble a workflow that actually covers your risk instead of one that scans a lot and catches little.
The three kinds of Java scanning
Software composition analysis (SCA) reads your dependency graph — pom.xml, build.gradle, or the resolved lockfile — and matches each artifact and version against vulnerability databases like the NVD and GitHub Advisory Database. This is where you catch a vulnerable log4j-core, an old aws-java-sdk-s3 carrying CVE-2022-31159, or a bc-fips build with a known use-after-free. Because Java dependencies pull in other dependencies several levels deep, the scanner has to resolve the full transitive tree, not just what you declared.
Static application security testing (SAST) reads your own code (source or bytecode) for insecure patterns: SQL built by string concatenation, unsafe deserialization, XXE-prone XML parsing, hardcoded secrets. It finds bugs you wrote, whereas SCA finds bugs you inherited.
Dynamic application security testing (DAST) exercises the running application from the outside to find runtime and configuration flaws. It complements the other two by catching things that only appear when the app is live.
Most known-CVE exposure in Java lives in the first bucket, which is why SCA is usually the place to start.
How dependency scanning works under the hood
A good SCA scanner does three things. It resolves the complete dependency tree, including transitive pulls and version conflicts. It identifies each component precisely — group, artifact, version — often by hash so a renamed or shaded JAR is still recognized. Then it matches those identities against advisory data and reports the affected ones with a fixed version.
You can see the resolution step yourself:
mvn dependency:tree
That output is exactly what a scanner consumes, plus the matching against CVE data. The reason to use a tool rather than eyeball the tree is scale — a mid-sized service easily has hundreds of transitive dependencies, and the advisory data changes daily.
Free and open-source options
You do not need to buy anything to start:
- OWASP Dependency-Check scans Maven and Gradle projects against the NVD and produces an HTML/JSON report. It is the well-known open-source baseline. It can generate false positives (its CPE matching is fuzzy), so expect to tune a suppression file.
- grype scans SBOMs and container images and is fast to wire into CI.
- Maven Versions Plugin is not a security tool, but
mvn versions:display-dependency-updatesshows you which dependencies are behind, which pairs well with scanning.
Running Dependency-Check in a build looks like:
mvn org.owasp:dependency-check-maven:check \
-DfailBuildOnCVSS=7
-DfailBuildOnCVSS=7 turns the scan into a gate: a High-or-above finding fails the build.
Wiring a scanner into CI
The scan is only useful if it runs automatically and can block a risky change. A minimal CI job:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Scan dependencies
run: mvn org.owasp:dependency-check-maven:check -DfailBuildOnCVSS=7
Set a severity threshold you can hold, and add a suppression file for the false positives rather than lowering the bar for everyone. A managed SCA tool reduces that tuning burden by validating findings against reachability and known-good version data, which is the main thing you pay for over the free baseline.
Reading and acting on results
A finding is only worth as much as the action it drives. For each real issue:
- Confirm the resolved version in your tree, not just the declared one.
- Find the fixed version from the advisory.
- Upgrade the direct dependency, or pin the transitive one via
dependencyManagement. - Re-scan to confirm the finding cleared.
Prioritize by severity and, where your tooling supports it, by reachability — a Critical CVE in code your app never calls is lower priority than a High in a hot path. Chasing every finding equally is how teams burn out on security scanning. If you want the end-to-end version-pinning walkthrough, the log4j Maven dependency guide shows it concretely.
FAQ
What does a Java vulnerability scanner actually scan?
Depending on type: dependency scanners (SCA) analyze your resolved Maven/Gradle tree against CVE databases; SAST scanners read your source or bytecode for insecure patterns; DAST scanners test the running app. Most Java CVE exposure lives in dependencies, so SCA is the usual starting point.
Is OWASP Dependency-Check good enough?
It is a solid open-source baseline that scans against the NVD and can gate your build on CVSS. Its main downside is false positives from fuzzy component matching, so budget time for a suppression file. Commercial tools mainly buy you lower noise and reachability analysis.
How do I scan transitive dependencies?
Use a scanner that resolves the full dependency tree rather than only your declared dependencies — that is standard for SCA tools. You can preview the tree yourself with mvn dependency:tree.
Does a Java scanner replace penetration testing?
No. Scanners catch known CVEs and common patterns automatically; a pen test finds logic flaws and creative chains that automation misses. They are layers, not substitutes.