Safeguard
Supply Chain•

Can a Gradle included build silently replace a published dependency?

Yes. Gradle swaps an external dependency for an included build with matching group and name. Learn the risk and how to keep substitutions deliberate.

Safeguard Security Team
3 min read

Yes. By default, Gradle inspects the group and name of projects in an included build and substitutes them for any external dependency with the same coordinates. If an unexpected included build declares matching coordinates, your build uses its source instead of the published artifact. Keep includes explicit and reviewed.

What composite builds are

A composite build is a build that includes other builds, called included builds. It is similar to a multi-project build, except that entire builds are included instead of subprojects. You add one in settings with includeBuild, or on the command line with the include build flag. Gradle's documentation lists three uses: developing a library and its consumer together, organizing large codebases, and sharing build logic.

How substitution works

Gradle configures each included build to find which dependencies it can provide, then replaces an external dependency matching project group and name with a project dependency at execution time. Explicit substitution rules are possible when defaults do not fit, and the main build is not substituted unless you self include it.

That behavior is exactly what you want in co-development. It is also a trust decision, because the replacement happens without a version check against the published artifact.

Where the risk comes from

SituationRisk
Included build path points outside the repositorySource you did not review can ship
Absolute path in a settings fileWorks on one machine, differs on another, and is discouraged by the docs
Command line include in a CI scriptChanges what builds without a code change in the repo
Included build from a submodule or sibling checkoutIts commit is whatever is checked out
Matching group and name from a forked copyA fork silently stands in for the original

Not every one of these is a vulnerability. Each is a way for what you build to differ from what you reviewed.

Reducing the risk

  1. Keep includeBuild entries in version controlled settings files and review changes to them like dependency changes.
  2. Use relative paths to builds inside the same repository. The docs discourage absolute paths.
  3. Pin submodules or sibling checkouts by commit, and verify the commit in CI.
  4. Avoid command line includes in CI unless the pipeline is itself reviewed.
  5. For release builds, add a check that no included build supplies a coordinate you expect to come from a published artifact.
  6. Resolve a configuration without substitution when you need to compare published and local JARs. Gradle documents turning off global substitution rules on a configuration's resolution strategy for this.

Plugins and build logic

Included builds can also provide plugins, including settings plugins through the plugin management block. Build logic is code that runs with your credentials, so apply the same review you give to convention plugins and buildSrc.

Isolation notes

Included builds do not share repositories, plugins or properties with each other or with the root. That limits some accidents, but it also means each included build needs its own repository and verification settings.

Quick check

Are substitutions transitive across included builds?

No. Explicitly declared substitutions are active only for the build that defines them.

Is a composite build a security feature?

No. It is a structuring tool. Its substitution behavior needs governance, like any mechanism that changes where code comes from.

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.