Safeguard
Open Source Security

Maven and Gradle dependency supply chain attacks in the J...

How attackers exploit Maven Central and the Gradle Plugin Portal — dependency confusion, malicious artifacts, and plugin takeovers — and how to defend Java builds.

Aman Khan
AppSec Engineer
7 min read

Every pom.xml and build.gradle file is a trust contract. It tells your build system to reach out to Maven Central, a corporate Nexus instance, or the Gradle Plugin Portal, download code written by strangers, and execute it — often with full access to your filesystem, environment variables, and CI/CD secrets. A java maven gradle supply chain attack exploits exactly that trust: it doesn't break your code, it becomes part of your code. Attackers have used dependency confusion, typosquatted artifact names, hijacked abandoned packages, and malicious Gradle plugins to slip payloads into builds at companies ranging from startups to Apple and Microsoft. With Maven Central alone serving hundreds of billions of download requests a year, even a small percentage of poisoned packages translates into massive blast radius. This post breaks down how these attacks actually work, the real incidents behind the headlines, and what engineering teams can do about it.

What is a Java Maven Gradle supply chain attack?

It's an attack that compromises your software not by breaching your servers, but by poisoning something your build already trusts and pulls in automatically. Rather than targeting your application directly, the attacker targets the dependency resolution process — Maven's pom.xml transitive dependency graph or Gradle's build.gradle/build.gradle.kts resolution — so that malicious code gets downloaded, compiled, and often executed with build-time or runtime privileges before anyone reviews a single line. The category covers several distinct techniques: dependency confusion (tricking the resolver into pulling a public package instead of an internal one), typosquatting (publishing commons-collction instead of commons-collections), namespace or maintainer takeover, and malicious build plugins that run arbitrary code the moment gradle build executes. What makes Java's ecosystem particularly exposed is scale: Maven Central hosts well over 700,000 unique artifacts, and a typical Spring Boot or Android project can easily pull in 100-300 transitive dependencies from a two-line declaration you wrote yourself.

How does java dependency confusion actually work against Maven and Gradle builds?

It works by exploiting how build tools decide which repository wins when the same package name exists in more than one place. In February 2021, security researcher Alex Birsan published research showing he could register public packages under internal-sounding names used by companies like Apple, Microsoft, PayPal, Shopify, and Netflix, then get those public versions installed instead of the intended private ones — earning over $130,000 in bug bounties across the affected organizations. The Java-specific version of this attack targets Maven and Gradle repository resolution order: if your settings.xml or build.gradle repositories block checks public Maven Central before (or in addition to) your internal Artifactory/Nexus instance, and an attacker publishes a package with the same groupId:artifactId and a higher version number, the build will happily pull the attacker's artifact instead of your internal one. This is especially dangerous for internal libraries with generic names — com.company.utils or internal-auth-client — that were never intended to be public but also were never reserved on Maven Central.

What does a malicious Maven artifact look like once it's published?

It usually looks almost identical to the real thing, differing by a character, a maintainer handle, or a version bump that resets to a suspiciously low number. Researchers and Sonatype's ongoing supply chain monitoring have repeatedly found malicious Maven artifacts using typosquatted names of popular libraries — think jackson-databind variants or lookalikes of common logging and HTTP client packages — that execute code in a static initializer block or constructor so the payload runs the moment the class loads, no method call required. A separate and more insidious pattern surfaced in January 2024 when researchers at Oversecured published the "MavenGate" findings: they discovered that many groupId namespaces on Maven Central and the Gradle Plugin Portal are tied to domain names that publishers have since let expire. Because Maven's namespace verification for some legacy groups relied on domain ownership, an attacker who simply re-registers that expired domain can, in some cases, claim ownership of the namespace and push a "legitimate" update to an existing, trusted package — no typosquatting or new artifact required, just patience and a $12 domain registration.

Can a Gradle plugin compromise your build without anyone noticing?

Yes, and more easily than a compromised library, because Gradle plugins execute during the build itself rather than only at runtime. When you add a plugin via plugins { id("some-plugin") version "1.2.3" }, Gradle downloads and runs that plugin's code as part of evaluating your build script — with the same permissions as the Gradle daemon, including network access, environment variables, and read/write access to your CI runner's filesystem. This makes gradle plugin security a distinct risk category from ordinary dependency vetting: a malicious or compromised plugin can exfiltrate GITHUB_TOKEN, npm/Maven publish credentials, or cloud IAM keys straight out of your CI environment before your tests even run, and it can do so on every single build rather than only in production. Because the Gradle Plugin Portal has historically had lighter publishing controls than Maven Central's groupId ownership verification, plugin takeover — where a maintainer's account or an abandoned plugin ID is compromised — has been flagged repeatedly by researchers as one of the higher-risk, lower-scrutiny corners of the JVM ecosystem.

Why did Log4Shell matter for supply chain security, not just vulnerability management?

Log4Shell mattered because it proved that a single transitive dependency, four directory levels deep in a build graph nobody was actively reading, could put a critical remote-code-execution flaw into an enormous share of enterprise Java applications overnight. Disclosed on December 9, 2021, as CVE-2021-44228 with a maximum CVSS score of 10.0, the vulnerability lived in Log4j, a logging library so ubiquitous that most engineering teams that used it had never deliberately added it — it arrived as a transitive dependency of Spring Boot starters, Elasticsearch, and dozens of other frameworks. Teams spent weeks discovering they were affected precisely because Maven and Gradle dependency trees obscure depth: mvn dependency:tree or gradle dependencies can reveal it, but almost nobody runs that audit until an incident forces them to. Log4Shell wasn't a malicious package planted by an attacker, but it demonstrated the exact mechanism a deliberate java maven gradle supply chain attack would exploit — silent, deep, widely-trusted inclusion — and it directly accelerated adoption of SBOM generation and dependency scanning across the Java ecosystem.

What can Java teams actually do to reduce this risk?

The most effective defenses target the resolution process itself, not just the code inside each artifact. Concretely: configure Maven's settings.xml and Gradle's repository declarations to check your internal registry first and fail closed rather than falling back to public repositories for internal-looking groupIds; pin exact versions instead of version ranges (+ in Gradle or unbounded ranges in Maven) so an attacker publishing a "newer" malicious version can't get auto-resolved into your build; enable dependency lock files (Gradle's dependencies.lock or Maven's dependency:lock) so any change to the resolved graph shows up as a reviewable diff; and generate an SBOM on every build so you have a queryable record of exactly what shipped, not just what your pom.xml declared. None of this is exotic — it's closer to hygiene than heroics — but it requires tooling that actually enforces it in CI rather than living in a wiki page nobody reads before merging.

How Safeguard Helps

Safeguard is built for exactly this gap between "we wrote a dependency policy" and "our CI actually enforces it." For Java teams, Safeguard continuously analyzes your Maven and Gradle dependency graphs — including transitive dependencies and Gradle plugins — to flag namespace confusion risks, typosquatted or newly-published artifacts with suspicious version jumps, and packages whose maintainer or publishing domain has recently changed hands, the exact pattern behind MavenGate-style takeovers. It generates verifiable SBOMs on every build so you have provenance evidence for every artifact that reached production, not just the ones you remembered to check. And because build-time execution is where Gradle plugin security risks and malicious Maven artifacts do their real damage, Safeguard integrates directly into your CI/CD pipeline to catch a poisoned dependency before it's compiled, tested, and shipped — turning supply chain security from a post-incident forensic exercise into a gate your build has to pass first.

Never miss an update

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