A Java security scanner is not one tool but two working together: a static analysis engine that inspects the code you wrote, and a dependency scanner that inspects the libraries you pulled in. Most real-world Java risk lives in the second category, so any serious approach has to cover both. This guide walks through what each layer catches, the open-source options worth knowing, and how to run a Java security scanner without turning your pipeline into a slow, noisy mess.
What a Java Security Scanner Actually Inspects
Split the problem into first-party and third-party code. First-party is the code your team authors: controllers, services, serialization logic, SQL access. Third-party is everything in pom.xml or build.gradle plus the transitive graph underneath it.
Static application security testing (SAST) tools parse your source or bytecode and trace data flow to find patterns like unsanitized input reaching a SQL query, unsafe deserialization, or hardcoded secrets. Software composition analysis (SCA) tools resolve your full dependency tree and match each artifact against known vulnerability databases.
The reason both matter: a clean SAST report tells you nothing about the Log4Shell-class bug sitting three levels deep in your dependency graph, and a clean SCA report says nothing about the injection flaw in the endpoint you shipped last week.
SAST for Java Code
The most widely used open-source SAST option for Java is SpotBugs with the Find Security Bugs plugin. It works on compiled bytecode, so you point it at your build output:
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.6.4</version>
<configuration>
<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.13.0</version>
</plugin>
</plugins>
</configuration>
</plugin>
Find Security Bugs ships detectors for SQL injection, command injection, XXE, weak cryptography, and unsafe deserialization. Semgrep is a strong alternative with a rule syntax that is easier to extend when you need a project-specific pattern, and it runs on source rather than bytecode.
Expect false positives from any taint-tracking tool. Budget time to triage and suppress with justification rather than blanket-disabling rules.
SCA for Maven and Gradle Dependencies
This is where a Java security scanner earns its keep. OWASP Dependency-Check is the classic open-source entry point. It builds a bill of materials from your resolved dependencies and cross-references the National Vulnerability Database:
mvn org.owasp:dependency-check-maven:check
Trivy is faster and covers container layers too, which helps if you ship Java in Docker images. Whichever engine you use, the discipline is the same: fail the build on newly introduced high-severity findings, and track existing ones on a backlog with owners. An SCA tool such as Safeguard can flag a vulnerable transitive artifact and show you which direct dependency dragged it in, which is usually the fastest route to a fix.
Reading a Transitive Dependency Report
The single most useful skill is reading the dependency path. When a scanner flags commons-collections, you rarely declared it yourself. Run:
mvn dependency:tree -Dincludes=commons-collections:commons-collections
That prints the chain from your direct dependency down to the flagged artifact. You fix it by bumping the direct dependency that carries it, or by pinning a safe version in dependencyManagement. Bumping the transitive artifact directly is a last resort because it can drift out of sync with what the parent expects.
Running It in CI Without the Pain
Two failure modes kill scanner adoption: it is too slow, or it is too loud. Address both deliberately.
For speed, cache the vulnerability database between runs and scan incrementally where the tool supports it. Dependency-Check in particular benefits from a persistent data directory. For noise, separate the gate from the report. The gate blocks merges only on new, high-severity, reachable findings. Everything else goes to a dashboard the team reviews weekly.
- name: Dependency scan
run: mvn org.owasp:dependency-check-maven:check -DfailBuildOnCVSS=7
Setting failBuildOnCVSS=7 means the build fails on CVSS 7.0 and above, which keeps the gate focused on serious issues while lower-severity items still appear in the report.
Picking a Combination
For a small team on a budget, Find Security Bugs plus OWASP Dependency-Check covers both layers at zero license cost. For larger organizations, the deciding factors are usually triage workflow, reachability analysis to cut false positives, and how well findings map to real fix actions. If you want to see how a commercial SCA workflow compares before committing, the pricing page lays out where hosted scanning makes sense versus rolling your own.
Whatever you choose, treat the scanner as one input to a review process, not an oracle. A finding is a hypothesis about risk; your team decides what to do with it.
FAQ
Is a Java security scanner the same as a linter?
No. A linter enforces style and catches simple bugs. A security scanner traces data flow to find exploitable patterns (SAST) or matches your dependencies against known CVEs (SCA). They are complementary, not interchangeable.
Does SCA need my source code?
Not usually. Most SCA tools work from the resolved dependency graph, so a built pom.xml or gradle lockfile is enough. SAST, by contrast, needs source or compiled bytecode.
How often should I run the scan?
Run SCA on every build so new vulnerabilities in existing dependencies surface quickly, since the vulnerability database changes even when your code does not. Run SAST on every pull request against changed code.
Can one tool replace both SAST and SCA?
Some platforms bundle both, but they are still doing two distinct analyses under the hood. Bundling helps with a single dashboard and unified triage; it does not remove the need for both techniques.