Safeguard
Supply Chain•

How do you stop Gradle fetching internal groups from public repos?

Use exclusiveContent in Gradle so internal groups resolve only from your private repository. It blocks lookups that leak names and swapped packages.

Safeguard Security Team
4 min read

Declare your private repository with exclusiveContent and a filter that includes your internal group. Gradle will then fetch those artifacts from that repository alone and will not search any other repository for them. This cuts off name leakage and substitution by a public package with the same coordinates.

The problem in plain words

Gradle searches repositories in the order you declare them. If a public repository is listed before your private one, a request for your internal library goes to the public server first. That leaks the name of an internal project, and if someone publishes an artifact with the same coordinates publicly, your build may pick it up.

Gradle's documentation lists the use cases for repository filtering: performance, reliability, and security, by avoiding leaking which dependencies a private project uses. It also notes that the declared order of repositories matters.

Two kinds of filter

A plain content filter on a repository says what that repository contains. By default, a repository includes everything and excludes nothing. If you declare an include, it excludes everything else. The catch is that these filters are not exclusive. Saying one repository includes a group does not stop other repositories from also supplying it.

exclusiveContent changes that. The documentation says that with it, an artifact declared in one repository cannot be found in any other exclusive repository, and it describes using exclusive filters as a recommended Gradle practice.

How to set it up

repositories {
    exclusiveContent {
        forRepository {
            maven { url = uri("https://repo.mycompany.com/maven2") }
        }
        filter {
            includeGroup("my.company")
        }
    }
    mavenCentral()
}

In this setup, the public repository is not searched for artifacts in my.company, even though it is declared in a normal way too. You can filter by group, module or version, and by regular expression.

Steps to roll it out

  1. List the groups and modules you publish internally.
  2. Add one exclusiveContent block per internal repository, with those groups.
  3. For the public repository, optionally add the matching exclude so it is explicit.
  4. Run a build with an empty cache on CI and confirm internal artifacts still resolve.
  5. Review the dependency tree for any group you forgot.

Watch the transitives and plugins

The documentation gives a warning: when you set up exclusive filters, consider which repository will supply transitive dependencies. If your private repository is exclusive for a group, anything that group pulls in must come from somewhere declared.

A second gotcha applies to plugins. If you use exclusive content in the pluginManagement section of settings, then adding more repositories through a project's buildscript repositories becomes illegal and the build fails. Either declare all repositories in settings or use non-exclusive filtering.

ChoiceEffect
Non-exclusive includeDocuments contents, other repositories may still serve the group
exclusiveContentGroup is resolved only from the named repository
Exclude on public repoPublic repo never asked for that group

Why this is a supply chain control

Dependency confusion relies on a build asking the wrong place for a name. Making the right place the only place removes the opening. It pairs well with checksum checks, covered in how to enable Gradle dependency verification.

Safeguard's dependency scanning and SBOMs show what your Gradle builds actually resolved, which helps confirm that internal groups come from where you expect. Free scan: gold.safeguard.sh/scan.

Quick check

Is a normal content filter enough to stop dependency confusion?

No. It describes what a repository holds but does not stop other repositories from also supplying that group. Use exclusiveContent for that.

Do I need to change every subproject?

Not if you centralize repositories in settings. See how to centralize Gradle repositories.

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.