Safeguard
Security Guides•

Why does Maven pick an old library version, and does it matter?

Maven uses the nearest definition, not the newest version. That can keep a vulnerable transitive library on your classpath. Here is how to see and fix it.

Safeguard Security Team
4 min read

Maven resolves conflicts with a rule called nearest definition: the version closest to your project in the dependency tree wins, not the highest. So a patched library can sit in your tree while an older, vulnerable copy is the one that actually loads. You find it with dependency:tree and fix it with dependencyManagement.

How nearest wins works

The Maven dependency mechanism guide explains dependency mediation. When multiple versions of an artifact appear, Maven picks the nearest definition. Its example: if A depends on B, which depends on C, which depends on D 2.0, and A also depends on E, which depends on D 1.0, then D 1.0 is used, because the path through E is shorter. If two versions are at the same depth, the first declaration wins.

The guide adds that you can always guarantee a version by declaring it explicitly in your own POM.

Why this is a security issue

Imagine D 1.0 has a known flaw fixed in 2.0. You upgrade B to pull in the patched D, and assume the finding is gone because the POM looks right. But the build still resolves D 1.0, because E is nearer. The fix looks applied and is not.

The same thing happens in reverse after a patch release of a library you use directly: a nearer transitive path can hold an older copy.

Step by step: see it and fix it

  1. Print the tree and look for the artifact in question.
mvn dependency:tree -Dincludes=groupId:artifactId

The dependency plugin's tree goal shows the resolved version and where it came from.

  1. Decide the version you want, normally the fixed one.
  2. Pin it in dependencyManagement in your POM or parent POM, which sets one version regardless of what transitive paths ask for.
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>library-d</artifactId>
      <version>2.0</version>
    </dependency>
  </dependencies>
</dependencyManagement>
  1. Rebuild and run the tree again to confirm the new version.
  2. Run your tests, since you may have overridden a version a dependency was not built for.

Prevent a repeat

The Enforcer plugin's requireUpperBoundDeps rule fails the build when the resolved version is lower than the highest version required anywhere in the tree. dependencyConvergence goes further and fails whenever different versions of one artifact appear. See our guide to the Enforcer rules.

ApproachEffectCost
dependencyManagement pinForces one version everywhereYou own the choice
Exclusion on one pathRemoves a specific routeEasy to miss other paths
requireUpperBoundDepsFails when old winsMay need several fixes up front

Try it yourself

Click through it at your own pace, or open it full screen. Look at how a component can appear under more than one parent, which is the situation nearest wins quietly resolves.

Safeguard scans the dependencies your build resolves, and reachability analysis helps you prioritize what it finds. See also fixing vulnerabilities in Maven projects. Free scan: gold.safeguard.sh/scan.

Quick check

If I declare the patched version in my POM, will Maven use it?

Yes. A version declared explicitly in your project POM is the nearest definition, so it wins over transitive paths.

Is this specific to Maven?

Resolution rules differ by build tool. Gradle, for example, uses its own conflict resolution and lets you add constraints for transitive versions.

Sources

Last reviewed: 19 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.