Safeguard
Software Supply Chain•

What are the best practices for managing Java dependencies safely?

Java dependency risk hides in transitive libraries. Pin versions, control mediation in Maven or Gradle, lock resolved versions and patch on a schedule.

Safeguard Security Team
4 min read

Manage Java dependencies by controlling transitive versions explicitly, locking resolved versions, removing what you do not use, and checking the tree for known vulnerable libraries on every build. Maven and Gradle both give you the controls, but you have to switch them on.

Why do transitive dependencies cause trouble?

Maven's documentation explains that transitive dependency discovery means you do not need to list the libraries your dependencies need. The cost is that the graph can grow quite large. Most of what ships in your application is code you never chose directly.

Maven decides which version of a library wins with dependency mediation. Its default is the "nearest definition": the version closest to your project in the tree is used, and when two are at the same depth, the first declaration wins. The docs give an example where A depends on B, which depends on C, which depends on D 2.0, while A also depends on E, which depends on D 1.0. Version 1.0 wins because it is nearer. That can silently pick an older library than the one a component was tested with, or a vulnerable one.

How do you take control in Maven?

The documentation names several tools. The dependencyManagement section lets a project directly specify the versions of artifacts used when they appear in transitive dependencies or where no version is given. In the docs' example, project A can add D to its dependencyManagement section to control which version is used, even if D is never referenced directly.

Dependency scope limits which dependencies are included for the current build stage, so test libraries do not ship. Exclusions let you remove a dependency that a library pulls in but you do not need.

What does Gradle add?

Gradle's dependency locking saves resolved versions to a lock file so later builds use the same versions. Its documentation says this helps avoid unexpected changes in the dependency graph. Teams that use dynamic versions during development can lock them for stability, and no longer need to rely on snapshot or changing dependencies that can cause surprise failures.

Lock files turn "whatever resolved today" into a reviewable change in a pull request, which is where a security review belongs.

How do you respond when a library is vulnerable?

OWASP's vulnerable dependency management guidance recommends analyzing dependencies from the start of a project, since adding the task late can create a huge burden. Its approach: update the dependency version, confirm automated tests cover the affected feature, and handle the case where a transitive dependency pins the old version. It also warns that blocking one provided exploit proves nothing about the library, so test alternate inputs and all reachable paths.

A constructed example: a scan reports a vulnerable JSON library at version 2.9, pulled in by a reporting tool. The team uses dependencyManagement to force 2.15, runs the test suite, and finds one failure from a changed default. They fix it, commit the lock file and note the reason in the pull request.

Where does Safeguard fit?

Safeguard scans dependencies and builds an SBOM across ecosystems, and its reachability analysis helps separate libraries that are present from vulnerable code your application can actually call. The free in-browser scan at gold.safeguard.sh/scan covers up to 100 dependencies if you want a quick look at a project.

How to check or apply this

  1. Print the full dependency tree and look for libraries you never chose.
  2. Use dependencyManagement in Maven to set versions for risky shared libraries.
  3. Use Gradle dependency locking, and review lock file changes in pull requests.
  4. Remove unused dependencies and narrow scopes so test code stays out of production.
  5. Run a vulnerability scan on every build and on a weekly schedule.
  6. Keep automated tests around features that use critical libraries so upgrades are safe.
  7. Decide an update cadence, such as monthly for routine and immediate for exploited flaws.
  8. Record any exclusion or pin with a reason, so it can be removed later.

Quick check

Does Maven always pick the newest version of a library?

No. Its default mediation picks the nearest definition in the tree, not the highest version.

What does a Gradle lock file do?

It records resolved versions so later builds use the same ones, until you deliberately update the lock.

Sources

  1. Apache Maven, Introduction to the Dependency Mechanism
  2. Gradle, Dependency locking
  3. OWASP, Vulnerable Dependency Management Cheat Sheet

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.