Threat model
Security boundaries and triage guidance for the minimal Gradle Wrapper bootstrap.
IMPORTANT: Developer documentation for the current development branch. This content is unreleased, may change without notice, and must not be treated as Buildish release documentation.
This page documents the implemented minimal bootstrap in the unreleased development tree. No component release has been published; acquire the three source files from an exact reviewed revision.
The blueprint lets a project keep one or both ordinary gradlew and
gradlew.bat entry points without tracking gradle/wrapper/gradle-wrapper.jar in Git. A
missing JAR is downloaded to ignored project-local state and checked against a
committed SHA-256 before it becomes executable.
No compiled Wrapper executable is tracked in Git. Exclusion from a source release remains an objective. Release publication and any unconditional source-release claim remain blocked until the real component archive assembler proves that exclusion with a local ignored Wrapper JAR present.
There is intentionally no stable release selector yet. Obtain a full
<reviewed-commit> SHA through a channel you trust, then clone and select
exactly that revision without executing project content:
1git clone https://github.com/buildish-tooling/buildish-no-gradle-wrapper-jar.git
2cd buildish-no-gradle-wrapper-jar
3reviewed_commit=REPLACE_WITH_FULL_REVIEWED_COMMIT_SHA
4git checkout --detach "$reviewed_commit"
5test "$(git rev-parse HEAD)" = "$reviewed_commit"
Review the selected commit and complete source tree before copying any product file into a consumer project. The commit SHA identifies bytes; deciding that the revision is trusted remains an adopter responsibility.
Acquire the init script and the bootstrap helper for each launcher the project
maintains from an exact reviewed revision of this component. Copy them
basename-for-basename into the consumer project’s gradle/ directory:
| Reviewed component source | Consumer destination |
|---|---|
buildish-wrapper-bootstrap.sh |
gradle/buildish-wrapper-bootstrap.sh |
buildish-wrapper-bootstrap.ps1 |
gradle/buildish-wrapper-bootstrap.ps1 |
buildish-wrapper.init.gradle.kts |
gradle/buildish-wrapper.init.gradle.kts |
The first implementation has no automatic installer, release bootstrap, or download-and-execute shortcut. Acquiring, authenticating, and reviewing these executable source files is an adopter responsibility outside the runtime JAR download guarantee.
The init script makes the native top-level :wrapper task the single path that
patches the maintained launcher set and writes Wrapper JAR metadata. A
POSIX-only or Windows-only project remains that way; if neither launcher exists
before initial adoption, the normal Gradle pair is retained. Do not copy
launcher patches from examples or calculate the Buildish properties by hand.
After successful adoption, gradle/wrapper/gradle-wrapper.properties contains
exactly one canonical line for each Buildish-owned property:
1buildishWrapperJarVersion=<canonical three-component stable Gradle version>
2buildishWrapperJarSha256Sum=<lowercase 64-character Wrapper JAR SHA-256>
The enhanced Wrapper task derives this pair from the Gradle process and JAR that generated the launchers. The pair is independent from the target distribution settings:
1distributionUrl=https\://services.gradle.org/distributions/gradle-<version>-bin.zip
2distributionSha256Sum=<lowercase 64-character distribution ZIP SHA-256>
The init script accepts canonical stable Gradle versions and official binary distribution URLs with a canonical SHA-256. It records the digest of the JAR actually generated by the executing Gradle process. The Wrapper JAR version may temporarily differ from the target distribution version during a normal Gradle Wrapper upgrade or downgrade.
Perform adoption from a clean, reviewable project state. Keep the existing Wrapper JAR available until the enhanced Wrapper task succeeds. If that JAR is already untracked and cannot be recovered from version control, make a collision-safe project-local backup first.
Confirm that distributionSha256Sum is already present for the selected
distribution, or obtain the reviewed value before running the task.
From the project root, explicitly attach the newly copied init script and force the native task to execute:
1./gradlew \
2 --init-script gradle/buildish-wrapper.init.gradle.kts \
3 --rerun-tasks \
4 :wrapper
If the properties file does not already contain the reviewed distribution checksum, also pass:
1--gradle-distribution-sha256-sum <reviewed-distribution-sha256>
Use a same-directory stable copy because the Wrapper task may replace
gradlew.bat while cmd.exe is still executing it. Save this as a temporary
PowerShell script in the project root, for example
run-stable-wrapper.ps1:
1$wrapperExitCode = 1
2$stableLauncher = Join-Path $PWD (
3 '.gradlew-buildish-update-{0}.bat' -f [System.Guid]::NewGuid().ToString('N')
4)
5try {
6 Copy-Item -LiteralPath .\gradlew.bat -Destination $stableLauncher `
7 -ErrorAction Stop
8 & $stableLauncher @args
9 $wrapperExitCode = $LASTEXITCODE
10} finally {
11 Remove-Item -LiteralPath $stableLauncher -Force -ErrorAction SilentlyContinue
12}
13exit $wrapperExitCode
Run that script with the adoption arguments:
1powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass `
2 -File .\run-stable-wrapper.ps1 `
3 --init-script gradle\buildish-wrapper.init.gradle.kts `
4 --rerun-tasks `
5 :wrapper
Pass the reviewed --gradle-distribution-sha256-sum value as an additional
argument when it is not already configured. The GUID-named copy preserves
APP_HOME, propagates success or any native nonzero exit code, and is removed
in finally. An interrupted update may leave an ignored copy; remove it only
after confirming that no update still uses it. Remove the temporary
run-stable-wrapper.ps1 driver after the reviewed operation as well.
An independently installed stable Gradle may establish or recover the integration. Run it from the project root and supply every target choice explicitly:
1gradle \
2 --init-script gradle/buildish-wrapper.init.gradle.kts \
3 --rerun-tasks \
4 :wrapper \
5 --gradle-version <reviewed-version> \
6 --distribution-type bin \
7 --gradle-distribution-sha256-sum <reviewed-distribution-sha256>
The installed Gradle runtime, its user-home state, and ambient init scripts are
trusted generation inputs. A bare gradle wrapper command does not attach the
project-local script and may replace the integrated launchers.
After the task succeeds:
Compare the generated Wrapper JAR version and digest with the reviewed configuration and tested-version evidence.
Review the complete changes to every maintained launcher and
gradle-wrapper.properties.
Add these exact root-scoped ignore rules:
/gradle/wrapper/gradle-wrapper.jar
/.gradlew-buildish-update-*.bat
Add these exact root-scoped checkout-attribute rules:
/gradlew text eol=lf
/gradlew.bat -text
/gradle/buildish-wrapper-bootstrap.sh text eol=lf
/gradle/buildish-wrapper-bootstrap.ps1 text eol=lf
/gradle/buildish-wrapper.init.gradle.kts text eol=lf
These rules keep clean checkouts usable across Git core.autocrlf settings.
Stop tracking the JAR without deleting the reviewed warm copy:
1git rm --cached gradle/wrapper/gradle-wrapper.jar
Run the repository invariant check and a cold --version smoke test.
Investigate every change outside the selected source files, maintained launchers, properties, and root-scoped ignore and checkout-attribute rules before committing the adoption.
When gradle/wrapper/gradle-wrapper.jar is absent, the bootstrap requires
exactly one canonical stable Gradle Wrapper JAR version and lowercase digest.
It derives that version’s fixed raw-tagged Gradle Wrapper JAR URL; membership in
the finite test manifest is not required. The response is downloaded to a
unique sibling temporary file with bounded time and a 10 MiB limit. Only bytes
whose SHA-256 equals the committed pin are moved into the executable JAR path.
The bootstrap does not download checksum or signature sidecars and does not
invoke GPG. Gradle separately applies distributionSha256Sum when it
provisions a missing target distribution; the presence of that property is not
evidence that an already cached distribution was reverified.
An existing local JAR is trusted checkout state and is not hashed again. The
POSIX launcher still sources its small helper to attach the project-local init
script. The Windows launcher performs the existence check in cmd.exe and
starts neither PowerShell nor network or hashing work on the warm path.
This blueprint does not defend against an actor who can modify the checkout, committed pin, helper sources, build logic, or existing ignored JAR. That actor already controls executable project state. See the canonical threat model for the full boundary and reporting guidance.
Run the ordinary enhanced Wrapper task with the reviewed target distribution checksum:
1./gradlew wrapper \
2 --gradle-version <canonical-stable-target-version> \
3 --distribution-type bin \
4 --gradle-distribution-sha256-sum <reviewed-distribution-sha256>
If Wrapper JAR A launches target distribution B, this pass may correctly leave
the Buildish Wrapper JAR pair at A while moving distributionUrl and
distributionSha256Sum to B. That state is complete and supported; the two
versions are intentionally independent.
On native Windows, the ordinary first A-to-B pass is supported directly when the active and generated launcher control flow is unchanged. Use the stable copy whenever a pass may cross launcher generations.
A later ./gradlew wrapper invocation runs under distribution B and can refresh
the generated Wrapper implementation and Buildish JAR pair to B. This pass is
optional. On native Windows it must run through the GUID-named stable-copy
script above when it may replace one launcher generation with another.
Always review every maintained launcher, the independent Wrapper JAR pair, the
target URL, and distributionSha256Sum together.
The supported Renovate contract uses Renovate 44.7.0, the gradle-wrapper
manager, binarySource="global", and administrator configuration
allowedUnsafeExecutions=["gradleWrapper"]. It executes the project launcher
and root :wrapper task, allowing the init script to restore both launcher
integrations and the two Buildish properties in the files Renovate collects.
A one-pass Renovate update may validly retain Wrapper JAR A while selecting distribution B. CI must validate each value generically instead of requiring version equality or membership in the repository’s finite test matrix. An updater that directly replaces generated launchers without executing the enhanced task is unsupported and should fail the repository invariant check.
gradle/wrapper/gradle-wrapper.jar and rerun the command to take the verified
cold path.:wrapper from a known-good
stable launcher with the init script attached explicitly.gradle wrapper removes the integration, rerun the
explicit installed-Gradle recovery command above.The helpers do not repair arbitrary launcher changes outside the enhanced Wrapper task and do not verify an existing ignored JAR on every invocation.
Product behavior is not restricted to an enumerated version table. Any canonical three-component stable Gradle version uses the fixed upstream Wrapper JAR path derived from that version and the committed digest authorizes the downloaded bytes. Wrapper generation accepts official binary distribution URLs and canonical SHA-256 values; upgrades and downgrades are both valid.
tests/fixtures/compatibility-manifest.json is a finite test matrix, not a
product allowlist. It records exact JAR digests, distribution checksums,
launcher fixtures, and lifecycle cases for the versions exercised in this
repository. Newer versions are supported when their generated launchers retain
the required structural invocation shape. Unfamiliar launcher templates fail
closed so that support can be reviewed without adding a version table to the
product.
Custom distributions, mirrors, -all archives, prereleases, snapshots, and
Gradle forks remain outside the supported contract.
| Scenario | Required environment |
|---|---|
| Source acquisition and adoption review | Git and the normal tools used to review the selected revision and project diff |
| POSIX runtime | POSIX shell, curl 8.4.0 or newer, and either sha256sum or shasum |
| Native Windows runtime | cmd.exe, Windows PowerShell 5.1 or later, and the required .NET HTTP and SHA-256 APIs |
| Wrapper generation | An existing Wrapper or independently installed stable Gradle plus its required JDK |
| Repository validation | The tools listed in the component’s contributing and test documentation |
Nothing is installed under $GRADLE_USER_HOME/init.d/, $GRADLE_HOME/init.d/,
or another user-global location. The project-local init script is attached
explicitly through the launchers.
Security boundaries and triage guidance for the minimal Gradle Wrapper bootstrap.