Skip to content
← Blog

Technical explainer

Gradle supply chain security: build scripts, the plugin portal, and two-host gating

3 min read

Gradle has the most code-execution-heavy build model of any mainstream ecosystem and the most interesting supply-chain topology: it pulls from two registries, not one. Understanding both is the key to securing a gradle build.

This post covers Gradle's build-script execution surface, the two-host problem (Maven Central plus the Gradle Plugin Portal), and how a dual-origin gate routes both.

The build script is code, evaluated eagerly

A build.gradle (Groovy) or build.gradle.kts (Kotlin) is a program. Gradle evaluates it during the configuration phase of every build — before any task runs. Applied plugins contribute their own configuration logic, which also runs. So gradle build executes the build script and every applied plugin's code, with the developer's privileges, as a matter of course.

That puts Gradle's install-time surface closer to npm's postinstall than to Maven's plugin-binding model: the act of configuring the build runs third-party code.

Two registries, two hosts

Here's what makes Gradle distinct. A typical build resolves from two different places:

  • Dependencies — from Maven Central (repo1.maven.org) or other Maven repos declared in repositories { }.
  • Plugins — from the Gradle Plugin Portal (plugins.gradle.org), a separate Maven-layout registry, when a build declares plugins { id '…' version '…' }.

A gate that only fronts Maven Central can route dependencies but not plugins. Under a sandbox that restricts network to the gate, a build that applies any external plugin — Spring Boot, Android, Kotlin, Shadow, most real projects — would have its plugin resolution blocked and fail. Securing Gradle for real means routing both hosts.

Lockfiles and verification

Gradle's dependency locking (gradle.lockfile) pins resolved versions, and its dependency verification (verification-metadata.xml) records SHA-256 checksums per artifact. Both are opt-in but well-supported, and together they give reproducibility plus tamper-evidence. As everywhere, a checksum proves the bytes are the pinned bytes, not that the plugin or library is safe to run.

How a dual-origin gate routes both

The mechanism has two parts:

  1. An init script (placed in an isolated GRADLE_USER_HOME) that forces all repositories through the gate — clearing project-declared repos so even a mavenCentral() in the build is overridden — and points plugin-management repositories at a distinct gate path.
  2. A dual-origin gate that routes dependency requests to Maven Central and plugin requests (under that distinct path) to the Gradle Plugin Portal — stripping the routing marker before scoring so the vulnerability and threat checks see the real coordinate.

With both in place, every artifact — dependency and plugin — flows through one gate:

  • OSV lookup against the Maven ecosystem (Gradle and the Plugin Portal both use Maven coordinates).
  • Threat-feed match for known-malicious coordinates.

And because the build script and plugins execute during configuration, the gate pairs with an OS sandbox: the build runs contained, network restricted to the gate, so a hostile plugin can't reach a credential store or an exfiltration endpoint.

With Veln, veln safe gradle build writes an isolated GRADLE_USER_HOME init script that forces dependencies and plugins through a dual-origin gate (Maven Central + Plugin Portal), scores every artifact, and runs the build inside an OS sandbox. veln verify reads gradle.lockfile and verification-metadata.xml for CI and pre-commit gating.

The practical takeaway

Gradle's risk is front-loaded: the dangerous code runs at configuration time, and it comes from two registries, not one. Adopt dependency locking and verification metadata for integrity, but don't stop there — a plugin from the portal is as much an attack surface as a library from Central, and only routing both through the same gate (plus a contained build) covers the way Gradle actually resolves and executes code.