A Gradle plugin that verifies dependency integrity at build time to guard against supply chain attacks.
Retrial was created to address the unresolved issues in Open Whisper Systems' gradle-witness plugin, and to provide a more convenient means of storing dependency checksums.
SUPPORT NOTICE: This library is now STABLE. It is no longer under active development, however pull requests from others are still being accepted.
Using remote dependencies is unavoidable for any non-trivial project, but doing so creates an opportunity for supply chain attacks. By acting as a MITM or by gaining direct access to a remote repository, an adversary could replace a legitimate dependency with their own compromised version. If used correctly, such an attack would allow an adversary to inject vulnerabilities into your project at build time.
Retrial guards against this problem by keeping a cryptographically secure checksum of each dependency, and comparing the saved checksums to the current checksums at build time. If any dependency has changed in any way, Retrial fails the build and describes the change. Since dependencies may have dependencies of their own, Retrial transitively checks the entire dependency graph to ensure integrity throughout.
There are three steps to using the plugin:
- Add the plugin to your project
- Record the dependency checksums
- Verify the dependency checksums
Retrial cannot be published as an artifact because doing so would create a bootstrap problem. Instead, it must be built from the source and included in your project as a jar.
Start by cloning the repository:
git clone https://github.com/matthewtamlin/retrial.git
Then build the plugin using Gradle:
cd retrial
# On MacOS and Linux
./gradlew buildRelease
# On Windows
gradlew buildRelease
Next copy the jar from retrial/build/libs/retrial.jar
to the libs
folder of your project.
cp build/libs/retrial.jar yourproject/app/libs/retrial.jar
# Or just use Finder/explorer to copy the files...
Finally add the following to your Gradle build file:
buildscript {
dependencies {
classpath files('libs/retrial.jar')
}
}
apply plugin: 'retrial'
That’s it! You've successfully added Retrial to your project.
To create the checksum record, run the recordDependencyChecksums
task:
# On MacOS/Linux
./gradlew recordDependencyChecksums
# On Windows
gradlew recordDependencyChecksums
This task creates the retrial-checksums.json
file in your project directory and writes the checksums to it. You can generally disregard this file, but make sure its checked in to source control and avoid manually editing it.
Whenever you intentionally update/add/remove a dependency, you’ll need to run the record dependencies task again to update the record.
To verify your dependencies against the record, run the verifyDependencyChecksums
task:
# On MacOS/Linux
./gradlew verifyDependencyChecksums
# On Windows
gradlew verifyDependencyChecksums
This task compares the saved checksums against the current checksums and fails the build if:
- There are any dependency in the build that are not in the record.
- There are any dependency in the record that are not in the build.
- There are any checksum mismatches.
By default, the task only runs when manually invoked. To automatically run the task every time the project is built, add the following to your gradle build file:
build.finalizedBy(verifyDependencyChecksums)
Its important to recognise that Retrial doesn't provide any assurance that your dependencies are actually free from vulnerabilities. All it does is ensure that the remote dependencies haven’t changed since you added them. Retrial will not save you if you include a dependency that already has a vulnerability and then record the checksums. Depending on your circumstances and the acceptable level of risk, you may want to perform a full audit of your dependencies prior to using Retrial.
Gradle has built in support for dependency locking, but it doesn't offer any protection against supply chain attacks. Dependency locking makes builds reproducible when using dynamic versioning, but it trusts the version declared by the repository and never performs any kind of integrity check. As such, dependency locking and Retrial serve entirely different purposes.
Retrial and dependency locking can be used at the same time with no conflicts. In fact, you probably want to use dependency locking if you’re using Retrial and dynamic dependency ranges, or else your build may fail spontaneously.
If you wish to contribute, please read the contributing guidelines.