Stop Vulnerable Java Code Before Production: SpotBugs, OWASP Dependency-Check, and Sonatype Guide
Stop Vulnerable Java Code Before Production: SpotBugs, OWASP Dependency-Check, and Sonatype Guide AI coding assistants have changed the way we write software. They help us generate code faster, understand unfamiliar AP
Stop Vulnerable Java Code Before Production: SpotBugs, OWASP Dependency-Check, and Sonatype Guide
AI coding assistants have changed the way we write software. They help us generate code faster, understand unfamiliar APIs, and implement features in minutes.
But there is another side to this productivity: AI-generated code can introduce bugs, misunderstand API contracts, use insecure coding patterns, or select vulnerable dependencies.
And let's be honest: developers have been making these mistakes long before AI existed!
The solution is not to stop using AI. It is to introduce automated security and code-quality checks into our development workflow.
In this article, we'll look at three tools and how to integrate them into a Java Maven project:
- SpotBugs Maven Plugin: Detect potential bugs and unsafe coding patterns in compiled Java code.
- OWASP Dependency-Check Maven Plugin: Identify known vulnerabilities in third-party dependencies.
- Sonatype OSS Index through Sonatype Guide: Add another source of component vulnerability intelligence to dependency analysis.
The objective is straightforward: make the Maven build fail when configured security or quality rules are violated, before the application reaches production.
All the configurations below use official project documentation and the following versions:
| Component | Version |
|---|---|
| SpotBugs Maven Plugin | 4.10.5.0 |
| OWASP Dependency-Check Maven | 13.0.0 |
1. SpotBugs: Find bugs in your Java code
What is SpotBugs?
SpotBugs is a static analysis tool that examines compiled Java bytecode to identify potential bugs.
Instead of executing your application, SpotBugs analyzes the generated .class files and searches for known bug patterns.
These patterns can result from:
- Misunderstanding Java language features.
- Incorrect usage of API methods.
- Violating assumptions when modifying existing code.
- Common programming mistakes, such as incorrect boolean conditions.
For example, it can detect potential null-pointer dereferences, suspicious code patterns, and other programming mistakes.
This is particularly useful when working with AI-generated code. The implementation might compile and look perfectly reasonable while still containing a potential defect.
SpotBugs helps identify these problems before the application is executed.
Add SpotBugs to your Maven project
Add the following configuration to your pom.xml:
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.10.5.0</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<xmlOutput>true</xmlOutput>
<includeTests>false</includeTests>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
Let's understand the important configuration options.
effort
Controls the analysis effort used by SpotBugs.
Available values:
MinDefaultMax
Using Max requests the highest analysis effort. It can increase execution time, but is a reasonable choice for a production verification gate.
threshold
Controls the minimum bug severity reported by SpotBugs.
Available values:
HighDefaultLowIgnoreExp
Using Low allows the report to include lower-priority findings instead of limiting it to more severe ones.
xmlOutput
Enables XML report generation. This is useful for CI systems and other reporting integrations.
includeTests
Controls whether test classes are included in the analysis. Its default is false, which is generally appropriate when the primary objective is protecting production application code.
check goal
This is particularly important.
The spotbugs:check goal executes SpotBugs analysis and fails the Maven build when violations meet the configured failure criteria.
It is bound by default to the Maven verify lifecycle phase.
This means we can use mvn verify to enforce the analysis rather than simply generating a report for someone to inspect.
Optional configuration recommendations
As your project grows, consider:
-
Include and exclude filters: Use
includeFilterFileandexcludeFilterFileto control which findings are reported. This is preferable to broadly disabling analysis when dealing with known false positives. -
Failing on specific priorities: The check goal supports
failThreshold, withHigh,Medium, andLowvalues. For a strict gate, useLow. -
Violation allowance:
maxAllowedViolationsdefaults to0. Keep it at zero if you want the build to fail when matching violations are found. - Multi-module projects: Configure the plugin centrally in the parent POM so modules follow the same analysis policy.
For a new project, I would start with maximum analysis effort, a low reporting threshold, and zero allowed violations. For an existing project with many findings, introduce the policy progressively rather than hiding existing problems with broad exclusions.
Official documentation:
- SpotBugs Maven Plugin – Usage
- SpotBugs Maven Plugin – Check Goal
- SpotBugs Maven Plugin – Violation Checking
2. OWASP Dependency-Check: Find vulnerable dependencies
What is OWASP Dependency-Check?
Finding bugs in our own code is only part of the problem.
Most Java applications depend on third-party libraries through Maven dependencies.
For example:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>...</version>
</dependency>
Even if our implementation is perfectly written, a vulnerable dependency can introduce a security risk into the application.
This is where OWASP Dependency-Check becomes useful.
Dependency-Check is an open-source software composition analysis (SCA) tool. It examines project dependencies and compares identified components against vulnerability information from sources such as the National Vulnerability Database (NVD).
It helps identify dependencies associated with known vulnerabilities, including transitive dependencies.
A transitive dependency is particularly important because you might never explicitly declare the vulnerable library in your pom.xml. Another dependency may introduce it into your application.
Add Dependency-Check to Maven
Add the following configuration:
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>13.0.0</version>
<configuration>
<failBuildOnCVSS>7.0</failBuildOnCVSS>
<failOnError>true</failOnError>
<autoUpdate>true</autoUpdate>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
Let's examine the important configuration options.
failBuildOnCVSS
This is one of the most important properties.
CVSS (Common Vulnerability Scoring System) provides a numerical score between 0 and 10 to represent vulnerability severity.
Dependency-Check uses this threshold to decide whether a vulnerability should fail the build.
For example:
<failBuildOnCVSS>7.0</failBuildOnCVSS>
This configuration fails the build when a detected vulnerability has a CVSS score equal to or greater than 7.0.
You can adjust the threshold according to your project's risk tolerance.
For a public-facing application handling sensitive data, a stricter policy may be appropriate.
failOnError
Controls whether an error during Dependency-Check execution should fail the build.
Keep this enabled.
A scanner that cannot execute successfully should not silently allow the build to pass.
autoUpdate
Allows Dependency-Check to update its vulnerability data automatically.
Keep it enabled. The project documentation explicitly advises against disabling automatic updates for normal operation.
Understanding the generated report
By default, Dependency-Check generates an HTML report:
target/dependency-check-report.html
The report provides information about:
- Dependencies scanned.
- Identified vulnerabilities.
- CVE identifiers.
- Vulnerability severity.
- Dependency coordinates.
- Analysis evidence.
This is useful when investigating why a build failed and deciding whether to upgrade, replace, or otherwise address a vulnerable dependency.
Optional configuration recommendations
There are several useful configurations worth considering.
1. Generate multiple report formats
Dependency-Check supports multiple formats, including:
- HTML
- XML
- JSON
- CSV
- JUNIT
- SARIF
- GITLAB
- JENKINS
For example, SARIF can be useful when integrating security findings with supported CI reporting systems.
2. Scan Maven build plugins
By default, scanPlugins is disabled.
You can enable it:
<scanPlugins>true</scanPlugins>
This extends scanning to Maven plugins and their dependencies, which can be useful when you also want visibility into your build tooling.
3. Manage false positives with suppression files
Dependency analysis is not perfect. Sometimes a vulnerability can be incorrectly associated with a component.
Dependency-Check supports XML suppression files through suppressionFiles.
The important point is to suppress findings only after investigating them. Document why a suppression is necessary and review it periodically.
You can also enable:
<failBuildOnUnusedSuppressionRule>true</failBuildOnUnusedSuppressionRule>
This makes the build fail when unused suppression rules are detected, helping prevent obsolete exceptions from accumulating.
4. Keep the Known Exploited Vulnerabilities analyzer enabled
Dependency-Check includes the Known Exploited Vulnerabilities analyzer, which uses CISA's KEV catalog.
It is enabled by default. Unless you have a specific reason to change it, keep it enabled.
5. Configure an NVD API key in CI
Dependency-Check uses NVD vulnerability data.
The initial execution can take 20 minutes or more because the vulnerability database needs to be downloaded and processed.
The project recommends keeping the database updated regularly and recommends mirroring NVD data for operational integrations.
When using an NVD API key in CI, Dependency-Check recommends passing it through an environment variable rather than putting it directly into the Maven configuration:
<nvdApiKeyEnvironmentVariable>NVD_API_KEY</nvdApiKeyEnvironmentVariable>
Then provide NVD_API_KEY through your CI secret-management mechanism.
This helps avoid exposing credentials in the project configuration or Maven debug output.
Official documentation:
- Dependency-Check Maven – Usage
- Dependency-Check Maven – Configuration
- Dependency-Check Maven – Check Goal
3. Sonatype OSS Index through Sonatype Guide
What is OSS Index?
Sonatype OSS Index provides vulnerability intelligence for open-source components.
It helps identify known security issues affecting the software components used by an application.
For Java projects, this provides another useful source of vulnerability information alongside the NVD-based analysis performed by Dependency-Check.
However, there is an important integration detail to understand.
The old Sonatype OSS Index Maven plugin (ossindex:audit) is now a legacy tool.
According to Sonatype's current documentation, it is no longer supported and does not receive fixes.
Sonatype recommends migrating to maintained integrations, including OWASP Dependency-Check Maven.
Therefore, rather than introducing another unsupported Maven plugin, we can integrate Sonatype's current Guide service directly into Dependency-Check.
This gives us a single Maven dependency-analysis workflow with vulnerability information from both Dependency-Check's configured sources and Sonatype's component intelligence.
Configure Sonatype Guide
Sonatype has migrated OSS Index API authentication to Guide Personal Access Tokens (PATs).
The process is straightforward.
Step 1: Generate a personal access token
- Go to Sonatype Guide.
- Sign in.
- Open Settings from the user menu.
- Navigate to Personal Access Tokens.
- Generate a new token.
- Copy the token immediately.
The token will not be displayed again after creation.
Step 2: Configure Maven credentials
Instead of putting the token directly into your pom.xml, configure it in your Maven settings.xml.
For example:
<settings>
<servers>
<server>
<id>sonatype-guide</id>
<password>YOUR_GUIDE_PAT</password>
</server>
</servers>
</settings>
The file is normally located at:
~/.m2/settings.xml
For production CI environments, store the token in your CI secret manager and provide it to Maven securely. You can also use Maven's password encryption facilities.
Notice that the Guide PAT is configured as the password. Sonatype documents that the username is not required when authenticating with a Guide PAT.
Step 3: Connect Dependency-Check to Sonatype Guide
Now add these properties to your Dependency-Check configuration:
<ossIndexAnalyzerEnabled>true</ossIndexAnalyzerEnabled>
<ossIndexAnalyzerUrl>
https://api.guide.sonatype.com
</ossIndexAnalyzerUrl>
<ossIndexServerId>sonatype-guide</ossIndexServerId>
Here is what each property does:
-
ossIndexAnalyzerEnabled: Enables the Sonatype OSS Index analyzer. -
ossIndexAnalyzerUrl: Specifies the Sonatype Guide API endpoint. -
ossIndexServerId: References the Maven server credentials configured insettings.xml.
The Maven server ID must match the ID configured in your settings.xml.
This integration uses the existing Dependency-Check analyzer, so we don't need to introduce an additional Maven plugin just to communicate with Sonatype.
Optional configuration recommendations
The OSS Index analyzer supports caching:
<ossIndexAnalyzerUseCache>true</ossIndexAnalyzerUseCache>
<ossIndexAnalyzerCacheValidForHours>24</ossIndexAnalyzerCacheValidForHours>
Caching helps reduce repeated requests for the same components.
The default cache validity is 24 hours, which is a reasonable starting point.
Keep the default remote-error behavior unless you have deliberately chosen a different failure policy. By default, remote errors from OSS Index can fail the execution rather than being reduced to warnings.
This is important: do not configure the scanner to ignore remote errors without understanding the consequences. A failed vulnerability lookup should not automatically be interpreted as a clean security result.
Official documentation:
- Sonatype – Integrate Build Tools with Sonatype Guide
- Sonatype – Using Guide Personal Access Tokens with OSS Index API Integrations
4. Putting everything together in one Maven configuration
Now let's combine the three configurations.
The following is a practical configuration for a Java Maven application:
<build>
<plugins>
<!-- Static Java Code Analysis -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.10.5.0</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<xmlOutput>true</xmlOutput>
<includeTests>false</includeTests>
<failOnError>true</failOnError>
<failThreshold>Low</failThreshold>
<maxAllowedViolations>0</maxAllowedViolations>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Dependency Vulnerability Analysis -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>13.0.0</version>
<configuration>
<failBuildOnCVSS>7.0</failBuildOnCVSS>
<failOnError>true</failOnError>
<autoUpdate>true</autoUpdate>
<!-- Sonatype Guide integration -->
<ossIndexAnalyzerEnabled>true</ossIndexAnalyzerEnabled>
<ossIndexAnalyzerUrl>
https://api.guide.sonatype.com
</ossIndexAnalyzerUrl>
<ossIndexServerId>sonatype-guide</ossIndexServerId>
<!-- Optional cache configuration -->
<ossIndexAnalyzerUseCache>true</ossIndexAnalyzerUseCache>
<ossIndexAnalyzerCacheValidForHours>24</ossIndexAnalyzerCacheValidForHours>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
A few important details:
-
spotbugs:checkruns SpotBugs analysis and checks its findings. -
dependency-check:checkexamines dependencies and generates its vulnerability report. - Both checks are associated with Maven's
verifyphase. - The SpotBugs configuration uses a strict failure policy.
- Dependency-Check fails on vulnerabilities with CVSS scores of at least 7.0.
- Sonatype Guide credentials are retrieved through Maven settings, not hardcoded in the POM.
For multi-module projects, define plugin versions and configuration centrally in the parent POM. For Dependency-Check, its aggregate goal is also available when you prefer a consolidated report across child modules.
5. Make it part of your CI/CD pipeline
Having these plugins in your POM is useful, but the real value comes from enforcing their results.
Run:
mvn clean verify
The Maven build will compile and test the application before executing the verification checks.
If SpotBugs identifies a finding matching the configured failure policy, the build fails.
If Dependency-Check identifies a vulnerability exceeding the configured CVSS threshold, the build fails.
Your CI/CD pipeline should then prevent the merge or deployment from proceeding until the checks pass.
For example:
Developer / AI-generated code
|
v
Pull Request
|
v
Maven Build
|
v
Compile + Tests
|
v
SpotBugs
|
v
Dependency-Check
|
v
Build Result
/ \
/ \
Failure Success
| |
v v
Block merge Allow merge
|
v
Deploy
Make the CI job a required status check on protected branches, and ensure the deployment pipeline cannot bypass the verification stage.
This distinction matters: Maven plugins alone do not prevent someone from pushing a Git commit. Branch protection and deployment rules enforce the gate.
6. What do we actually gain?
We now have three complementary security and quality layers.
| Tool | What it examines | Main objective |
|---|---|---|
| SpotBugs | Compiled Java bytecode | Detect potential bugs |
| Dependency-Check | Project dependencies | Identify known vulnerable components |
| Sonatype OSS Index | Dependency component intelligence | Add another vulnerability information source |
The main advantage is that these tools address different classes of problems.
For example, an AI coding assistant might generate a method that uses an unsafe API incorrectly. SpotBugs may identify the problem.
Alternatively, it might introduce an outdated dependency with a known vulnerability. Dependency-Check, with Sonatype Guide integration, can help identify that risk.
And the same protection applies when the code is written entirely by a human developer.
7. Final thoughts
You don't need an expensive enterprise security platform just to introduce meaningful security checks into a Java development workflow.
SpotBugs and OWASP Dependency-Check are open-source tools that can provide a useful baseline for application code analysis and dependency vulnerability detection.
Sonatype Guide adds an external vulnerability intelligence service to the dependency analysis workflow. It is important to distinguish the open-source scanners from the hosted Sonatype service and check the applicable account and usage terms.
Of course, no automated scanner can guarantee that an application is secure. Static analysis and vulnerability databases have limitations, including false positives and false negatives. They do not replace code reviews, security testing, threat modelling, or regular maintenance.
But they give us something extremely valuable: a repeatable and automated security gate that runs on every build, regardless of whether the code was written by a developer or generated by AI.
And that is a great starting point for building more secure Java applications.
Official documentation and references
All implementation details in this article are based on the following official documentation:
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.