On 7 October 2026 Gradle published GHSA-j5m7-59rp-24f5, a high severity advisory about dependency resolution. When a repository failed with a TLS error such as SSLException or SSLHandshakeException, Gradle didn't treat it as fatal. It moved on and tried the next repository in the list. An attacker who can break the TLS connection to one repository, and who controls a repository declared later, could get the build to resolve a malicious artifact.
It is the third advisory of this shape. In January 2026 Gradle fixed the same behaviour for repositories that didn't answer and for unknown hosts. This one closes the TLS case.
What's affected
- Package:
org.gradle:gradle-core - Affected: versions below 8.14.6, and 9.0.0 through 9.8.0
- Fixed (open source): 9.8.1 and 8.14.6. Backports for older 9.x lines and 7.6.7 are listed through Gradle's Security Subscription.
- Severity: High, CVSS v4 8.6
- CVE: none assigned
- Weaknesses: CWE-494 (download of code without integrity check), CWE-829
The advisory explains the intended design. Gradle searches repositories in declaration order, and on a connection error it should disable that repository and fail the resolution, so a build never silently picks up an artifact from somewhere else. Some exceptions weren't classified as either fatal or transient, so Gradle skipped them. The fix makes Gradle "fail hard on any failure that is not transient."
The attack needs two things: the ability to disrupt TLS to an earlier repository, and control over a later one. That later repository might be a public repository where anyone can register a group ID, a stale internal mirror nobody maintains, or a repository on a domain that has lapsed.
The earlier related advisories are GHSA-mqwm-5m85-gmcv (repositories failing to answer) and GHSA-w78c-w6vf-rw82 (unknown host), both published 16 January 2026.
How to check if you're exposed
Version. Read the wrapper version in each project:
grep -rH "distributionUrl" --include=gradle-wrapper.properties .
Repository list. The risk grows with every repository a build declares. List them in build and settings scripts:
grep -rnE "maven\s*\{|mavenCentral\(\)|google\(\)|gradlePluginPortal\(\)|jcenter\(\)|url\s*=?\s*(uri\()?['\"]https?://" \
--include=*.gradle --include=*.gradle.kts .
For each project, write the repositories down in order. Any repository after your primary one that you can't name an owner for is a candidate for the "later repository" in this attack.
Mitigations already in place. The advisory says builds using strict repository content filtering or dependency verification are safe from these vulnerabilities. Check for either:
ls gradle/verification-metadata.xml 2>/dev/null
grep -rnE "exclusiveContent|includeGroup|excludeGroup" --include=*.gradle --include=*.gradle.kts .
What to do now
- Upgrade to Gradle 9.8.1 or 8.14.6:
./gradlew :wrapper --gradle-version=9.8.1 && ./gradlew :wrapper - Turn on dependency verification. If you can't upgrade, the advisory calls this the best way to ensure your build resolves only expected dependencies. Bootstrap it, then review the generated file before committing:
This writes./gradlew --write-verification-metadata sha256,pgp helpgradle/verification-metadata.xml. Gradle's docs note that bootstrapping trusts whatever the repositories serve at that moment, so run it from a clean, known-good state. - Pin groups to repositories. Use content filtering so internal groups can only come from your internal repository:
repositories { mavenCentral() exclusiveContent { forRepository { maven { url = uri("https://repo.internal.example/maven") } } filter { includeGroup("com.example.internal") } } } - Remove repositories you don't need. The advisory explicitly recommends reviewing declared repositories and removing unused or outdated ones.
- Centralise repositories in
settings.gradle(.kts)withdependencyResolutionManagementso individual modules can't add new ones quietly.
Why it matters
Repository ordering is a trust decision that most builds make by accident. Someone adds a repository to fix one missing artifact, and from then on it sits in the search path for every dependency. As long as resolution fails closed, that is mostly a performance problem. When it fails open, the least trusted repository in the list decides what you build with.
Three advisories in one year about the same fallback suggest a broader lesson: error handling is security logic. Any resolver that treats some failures as "try the next source" needs those failures to be the narrow, transient kind, and everything else should stop the build.
Try it yourself
Pick one Gradle project and comment out every repository except your primary one. Run ./gradlew dependencies --refresh-dependencies and note what breaks. Everything that still resolves didn't need the extra repositories. Everything that breaks tells you exactly which groups each extra repository is for, which is the list to put in an exclusiveContent filter.
Click through it at your own pace, or open it full screen.
Quick check
Your build uses dependency verification with a reviewed verification-metadata.xml. Does this advisory affect you?
The advisory states that builds using dependency verification or strict repository content filtering are safe from these vulnerabilities. Upgrading is still recommended.
What two capabilities does an attacker need?
The ability to disrupt the TLS connection to a repository, and control over a repository declared later in the search order.
Which earlier Gradle advisories describe the same fallback for other error types?
GHSA-mqwm-5m85-gmcv (repositories failing to answer) and GHSA-w78c-w6vf-rw82 (unknown host), both from January 2026.