DEV Community

loading...

Quick guide to safelisting repository artifacts in Gradle

Rob R
Updated on ・3 min read

The background

The typical build.gradle files list repositories in a way similar to this:

repositories {
   google()
   mavenCentral()
}
Enter fullscreen mode Exit fullscreen mode

This is a quick way to specify that dependencies we're using should be grabbed from one of the repositories above.

This approach has a few potential issues:

  • Security, e.g: if a malicious actor placed a dependency impersonating the one we need, the script could download it and we wouldn't know.
  • Slowness: the script doesn't know the exact coordinates of each dependency and would be searching in each repository starting from the top one.

A better way

A few years ago, Gradle added a functionality that allows to specify where each of the dependencies is precisely located.
These APIs include: includeModule, includeGroup, includeGroupByRegex.

The good news is that we can gradually migrate to these APIs and make the dependency resolution more secure and faster straight away.

1st approach

The easiest way is to replace each repository with a set of includeGroupByRegex entries.

Let's imagine that our project is using Retrofit and OkHttp.

Step 1: Remove mavenCentral() from the repositories section.

Step 2: Force refresh dependencies in one of the following ways:

  • sync the project,
  • execute ./gradlew --refresh-dependencies,
  • run the app/tests,
  • remove .m2 & .gradle/caches folders from the machine

In Android Studio, we will see errors similar to these:

   > Could not find com.squareup.okhttp3:okhttp:3.12.1.
     Required by:
         project :app
   > Could not find com.squareup.okhttp3:okhttp-urlconnection:.
     Required by:
         project :app
   > Could not find com.squareup.retrofit2:retrofit:2.9.0.
Enter fullscreen mode Exit fullscreen mode

Step 3: Notice that all these missing files have the same group id: com.squareup

Step 4: In place of mavenCentral(), add the below and refresh dependenices again (see step 2):

   mavenCentral {
      content {
         includeGroupByRegex("com.squareup.*")
      }
  }
Enter fullscreen mode Exit fullscreen mode

Step 5: Continue the same way till every dependency has been safe-listed. Do the same for google().

We could stop at this stage. The dependency resolution is safer and faster.

Going a bit further with includeGroup

includeGroup is a more precise version of includeGroupByRegex.

Let's remove includeGroupByRegex("com.squareup.*") and inspect the errors again. We can see the group names. Let's use them and replace:

includeGroupByRegex("com.squareup.*")
Enter fullscreen mode Exit fullscreen mode

with

 includeGroup("com.squareup.retrofit2")
 includeGroup("com.squareup.okhttp3")
Enter fullscreen mode Exit fullscreen mode

Repeat that for every includeGroupByRegex.

The dependency resolution is yet safer and faster. We could stop here.

An ultimate version - using: includeModule

Now, start removing each includeGroup one by one and inspect the errors again. The groups and modules names shown in the error console are all we need to use includeModule(). Let's use these and replace:

includeGroup("com.squareup.retrofit2")
Enter fullscreen mode Exit fullscreen mode

with

includeModule("com.squareup.retrofit2", "retrofit")
includeModule("com.squareup.retrofit2", "converter-gson")
includeModule("com.squareup.retrofit2", "retrofit-mock")
Enter fullscreen mode Exit fullscreen mode

Repeat that for every includeGroup.
The resulting build script files will be quite large but we'll have full control over the repositories and what gets included in the app.

Conclusion

We can migrate at our own pace. We can either jump right into using includeModule or gradually transition from general repositories to includeGroupByRegex to includeGroup and then finally includeModule.

We can also use a mix of the includes above.

As long as we replace general repositories with a set of includes - our dependency resolution will be faster and safer. And we will know exactly what goes into our apps.

Further reading:

Discussion (0)