Run the Maven dependency plugin's analyze goal. It classifies libraries as used and declared, used but undeclared, or unused but declared. Remove the unused ones, declare the undeclared ones, and your build carries fewer components that can attract advisories. Treat results as leads to verify, not orders.
Why removal is a security task
Every library on your classpath is something to patch, track and answer questions about. A dependency nobody calls still shows up in scans and SBOMs, and still costs triage time when an advisory lands. The cheapest vulnerability to fix is the one in a library you removed.
What the goal reports
The plugin documentation describes dependency:analyze as analyzing the dependencies of a project and determining which are used and declared, used and undeclared, and unused and declared. By default it uses the maven-dependency-analyzer, which works at the bytecode level, and the page notes this brings limitations.
It always runs the test-compile phase, because it is meant to be used standalone. For use inside your normal lifecycle, the same page points to the analyze-only goal.
Steps
- Run the analysis.
mvn dependency:analyze
- Read each group in the output.
- For unused but declared, check why it is there before deleting: runtime reflection, service loading, annotation processors and plugins are loaded without a direct bytecode reference.
- For used but undeclared, add a direct declaration. Relying on a transitive path is fragile and hides what you really depend on.
- Rebuild, run tests, and run the analysis again.
What the analyzer cannot see
Because it inspects bytecode, it can flag a library as unused when it is loaded by name at runtime, or needed only at runtime, such as a database driver or logging backend. The plugin offers options to handle this: ignore runtime scope in the unused check, ignore specific dependencies, or exclude class patterns.
| Option | Use |
|---|---|
| ignoreUnusedRuntime | Skip runtime scope in the unused check |
| ignoredDependencies | List libraries to leave alone |
| failOnWarning | Fail the build when a warning is found (default false) |
| ignoreNonCompile | Skip runtime, provided, test and system scopes in the unused check |
Turning on failOnWarning in CI keeps the list from growing back, once the existing warnings are cleared.
Unused is not the same as unreachable
This is a different question from reachability. dependency:analyze asks whether your code references a library at all. Reachability analysis asks whether a vulnerable function in a library you do use can be called from your code. Both reduce noise and they complement each other. Also, trimming a dependency can change the transitive tree, so check why Maven picks older versions after you remove one.
Try it yourself
Click through it at your own pace, or open it full screen. See how findings are sorted by whether the vulnerable code can be reached, the next step after you have removed what is unused.
Safeguard's dependency scanning and reachability analysis work together on Java projects, and its SBOMs show what remains after cleanup. Free scan: gold.safeguard.sh/scan.
Quick check
Is it safe to delete everything flagged as unused?
No. Libraries used through reflection or loaded at runtime can be flagged. Check each one and run your tests.
Does this replace a vulnerability scan?
No. It reduces what there is to scan. You still need scanning for what remains.
Sources
Last reviewed: 19 October 2026.