skip to content

How do you set up gradle/wrapper-validation in CI, and where in the pipeline should it run?

level: middleimportance: must knowfreq 50%

answer

  1. gradle/actions/wrapper-validation (was wrapper-validation-action)
  2. separate early job, build needs it
  3. trigger on pull_request incl. forks
  4. scans every gradle-wrapper.jar recursively
  5. pin action version/SHA

basics

~20 s

Add the official action (gradle/actions/wrapper-validation, formerly gradle/wrapper-validation-action) as one of the first jobs in your CI workflow, running on every push and pull request. It fails the build if any wrapper jar's checksum is unknown.

solid answer

~50 s

Use the official `gradle/actions/wrapper-validation` action (the modern home of the old `gradle/wrapper-validation-action`). Add it as an **early, standalone job** — ideally before any job that runs `./gradlew` — so a poisoned jar is caught before it executes with repo secrets in scope. It must trigger on **pull_request** as well as push, including PRs from forks, since the attack vector is precisely a malicious PR adding a tampered jar. The action recursively scans the checkout for every `gradle-wrapper.jar` and fails if any does not match a Gradle-published SHA-256. Keep it independent from the build job (a separate `validation` job that the build `needs`) so the build never starts unless validation passed. Pin the action to a major version (e.g. `@v3`) or a commit SHA. Outside GitHub Actions you replicate this with the `gradle-wrapper-validator` CLI or by scripting the checksum comparison.

code

groovy · 12 lines
groovy
jobs:
  validation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: gradle/actions/wrapper-validation@v3
  build:
    needs: validation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./gradlew build --no-daemon

go deeper

for a junior

Know there's an official GitHub Action you add to CI to check the wrapper jar.

for a middle

Wire it as an early job triggered on push and pull_request, with the build depending on it, and know it scans every wrapper jar in the repo.

for a senior

Reason about placement and the fork-PR threat, pin the action, and cover non-GitHub CI via the standalone validator or scripted checksum comparison.

for a principal

Mandate it as a required status check org-wide, integrate with branch-protection policy, and treat the validator action itself as a pinned, audited dependency.

## The official tooling The canonical control is GitHub's official action. Historically it was `gradle/wrapper-validation-action`; it has since been consolidated into the `gradle/actions` repository as `gradle/actions/wrapper-validation`. Both do the same thing: recursively find every `gradle-wrapper.jar` in the checkout and verify each against the set of SHA-256 checksums Gradle publishes for its releases. ## A minimal workflow ```yaml name: ci on: push: pull_request: jobs: validation: name: Validate Gradle wrapper runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: gradle/actions/wrapper-validation@v3 build: needs: validation # build only runs if validation passed runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: ./gradlew build ``` ## Why placement matters The whole value is **catching the jar before it runs**. If you validate after `./gradlew build` (or in the same job, after the wrapper has already bootstrapped), a malicious jar has already executed with whatever the runner exposes — secrets, tokens, cache write access. So: - Make validation its **own job** that the build `needs`. - Trigger on `pull_request`, not only `push` — the realistic attack is a contributor opening a PR that swaps the jar. Forked-PR runs in particular must validate, because that's untrusted input. ## What it does and doesn't cover - **Covers:** every `gradle-wrapper.jar` in the repo, including nested/sub-project wrappers. It fails on *any* unknown checksum. - **Doesn't cover:** the `distributionUrl` / the Gradle distribution zip (use `distributionSha256Sum`), and your dependencies (use dependency verification). It also doesn't fix anything — it's a gate. ## Outside GitHub Actions On other CI systems you don't have the action, so either: - run the standalone `gradle-wrapper-validator` tool, or - script it: download Gradle's published checksum list and compare `sha256sum gradle/wrapper/gradle-wrapper.jar` against it, failing the pipeline on mismatch. ## Hardening the action itself Pin third-party actions to a major tag (`@v3`) or, for stricter supply-chain hygiene, to an immutable commit SHA, so the validator you rely on can't itself be silently swapped.

  • Why must validation run on pull_request events, not just push?
    The primary attack is a malicious PR that swaps the wrapper jar. If you only validate on push to your branches, a poisoned jar can run during PR CI before you ever merge — so you must validate untrusted PRs, including forked ones.
  • How do you do wrapper validation on a CI system that isn't GitHub Actions?
    Use the standalone gradle-wrapper-validator tool, or script the check: fetch Gradle's published wrapper checksums and compare the SHA-256 of each gradle-wrapper.jar, failing the pipeline on any mismatch.

saying these in an interview costs you the question

  • Running validation only after the build, when the wrapper has already executed.
  • Validating only on push and skipping pull_request / forked PRs — that's exactly the unprotected path.

context