Snyk Documentation

CLI - Advanced Gradle testing

Intro

Gradle is a very powerful and flexible build system. As a result, analyzing Gradle projects in order to detect its dependencies is not an easy task: the exact set of dependencies is influenced by a number of parameters you need to take into account.

Sub-projects

Each Gradle build can consist of several sub-projects (each sub-project has build.gradle, the root project is the one that has settings.gradle in it). By default, sub-projects depend on the root project (e.g. "apps" depend on "base"), but it can be configured otherwise.

By default, Snyk CLI scans only the current project (either the one in the current folder or the one that is specified by --file=path/to/build.gradle). To scan all subprojects at once (recommended), use the --all-sub-projects flag (in case of snyk monitor, they will be uploaded as separate projects to the Registry).

To scan only a specific project myapp, specify the --gradle-sub-project=myapp option (or --file=myapp/build.gradle).

Build Configurations

A Gradle build produces several build "configurations": there's a compileOnly configuration for development dependencies, compile configuration that includes compile and runtime dependencies, test... configurations that include test dependencies; Android builds can have "debug" and "release" configurations of all kinds).

By default, the snyk command line tool merges all the configurations and gets the sum total of the dependencies. Sometimes this is either undesired (you might want to exclude test and service tasks) or does not work at all (see below about Android build variants).

A --configuration-matching option can help you to select only certain configurations. It takes a Java regular expression (case-insensitive) as a parameter. If only one configuration matches, it will be used to scan the project. If several configurations match, they all will be merged and resolved together.

Examples:

  • --configuration-matching=compile will match compile, testCompile, compileOnly etc;
  • --configuration-matching=^compile$ will match only compile;
  • --configuration-matching='^(debug|release)compile$' will match debugCompile and releaseCompile

Notes:

  • different sub-projects might have different configurations, so in some cases you will have to scan them individually, instead of using the --all-sub-projects flag;
  • in older versions of the snyk command line tool, you could specify the desired configuration like this: snyk test -- --configuration=myconf. This syntax is now deprecated and might be removed in future versions.

Android Build Variants

Android Gradle builds allow you to configure several "variants": https://developer.android.com/studio/build/build-variants. This becomes a problem if some of your sub-projects depend on other sub-project that have variants, like this:

dependencies {
implementation project(':mymodulewithvariants')
}

Then snyk test or snyk monitor command will fail with an error from Gradle, which will contain a message like:

  • Cannot choose between the following configurations of project :mymodulewithvariants
  • Cannot choose between the following variants of project :mymodulewithvariants
  • Could not select value from candidates

The most common variant conflicts are due to different build types (debug vs release) or "product flavors" (which are specific to your project). Those conflicts can be solved in one of the following ways:

Suggest configuration attributes: a Gradle configuration would select the dependency variants using its attributes. In the error message, the snyk command line tool will tell you about available attribute values, and the error details from Gradle will tell you about which dependency variants would match which attributes. You can then add the attribute filter option. E.g. --configuration-attributes=buildtype:release,usage:java-runtime,mode:demowould match the variants using:

  • com.android.build.api.attributes.BuildTypeAttr=release
  • org.gradle.usage=java-runtime
  • flavor dimension mode **selecting the flavor demo

Use a specific configuration(s): if you know of a build configuration that has all the required attributes, you can specify it instead, e.g. --configuration-matching=prodReleaseRuntimeClasspath . However, this method will fail if not all of the tested subprojects have the same configurations.

Explicitly specify the dependency configuration: finally, you can modify intra-project dependencies in your build.gradle file(s) to use a specific configuration:

dependencies {
implementation project(path: ':mymodulewithvariants', configuration: 'default')
}