Gradle plugin that scans the dependencies of a Gradle project using Sonatype platforms: OSS Index and Nexus IQ Server.
./gradlew clean publishToMavenLocal
If you want to save some time, skip integration tests:
./gradlew clean publishToMavenLocal -x integrationTest
./gradlew integrationTest
The plugin can be used on projects with Gradle 3.3 or higher (local installation or wrapper) and Java 8 installed locally.
Gradle can be used to build projects developed in various programming languages. This plugin supports:
- Java
- Kotlin
- Scala
- Groovy
- Create/Clone/Download any Gradle project.
- Edit its
build.gradle
file adding this:
plugins {
id 'org.sonatype.gradle.plugins.scan' version '2.7.0' // Update the version as needed
}
- Or
build.gradle.kts
:
plugins {
id ("org.sonatype.gradle.plugins.scan") version "2.7.0" // Update the version as needed
}
Some basic examples will be provided next, which we strongly advice to read :)
After doing so, specific usage on CI tools can be found at https://github.com/guillermo-varela/example-scan-gradle-plugin
OSS Index can be used without any extra configuration, but to avoid reaching the limit for anonymous queries every user is encouraged to create a free account on OSS Index and use the credentials on this plugin. Cache can also be configured optionally.
If you are using Groovy (build.gradle file):
ossIndexAudit {
username = 'email' // if not provided, an anonymous query will be made
password = 'pass'
allConfigurations = false // if true includes the dependencies in all resolvable configurations. By default is false, meaning only 'compileClasspath', 'runtimeClasspath', 'releaseCompileClasspath' and 'releaseRuntimeClasspath' are considered
useCache = true // true by default
cacheDirectory = 'some/path' // by default it uses the user data directory (according to OS)
cacheExpiration = 'PT12H' // 12 hours if omitted. It must follow the Joda Time specification at https://www.javadoc.io/doc/joda-time/joda-time/2.10.4/org/joda/time/Duration.html#parse-java.lang.String-
proxyConfiguration { // extra configuration when running behind a proxy without direct internet access
protocol = 'http' // can be 'http' (default) or 'https'
host = 'proxy-host' // hostname for the proxy
port = 8080 // port for the proxy
authConfiguration.username = 'username' // username for the proxy (if credentials are required)
authConfiguration.password = 'password' // password for the proxy (if credentials are required)
}
modulesIncluded = ['module-1', 'module-2'] // Optional. For multi-module projects, the names of the sub-modules to include for auditing. If not specified all modules are included.
modulesExcluded = ['module-1', 'module-2'] // Optional. For multi-module projects, the names of the sub-modules to exclude from auditing. If not specified no modules are excluded. This value is processed after 'modulesIncluded' if both are specified.
// For projects using multiple custom variants for the release distribution, a Map can be set with the attributes names and values to match the specific variant. See more at the section "How to Deal with Multiple Release Variants" below in this doc.
variantAttributes = ['com.android.build.api.attributes.ProductFlavor:version': 'prod', 'other.attribute': 'other value'] // Optional, use it only when the plugin can't match a variant on its own
// ossIndexAudit can be configured to exclude vulnerabilities from matching
excludeVulnerabilityIds = ['39d74cc8-457a-4e57-89ef-a258420138c5'] // list containing ids of vulnerabilities to be ignored
excludeCoordinates = ['commons-fileupload:commons-fileupload:1.3'] // list containing coordinate of components which if vulnerable should be ignored
// Output options
outputFormat = 'DEFAULT' // Optional, other values are: 'DEPENDENCY_GRAPH' prints dependency graph showing direct/transitive dependencies, 'JSON_CYCLONE_DX_1_4' prints a CycloneDX 1.4 SBOM in JSON format.
cycloneDxComponentType = 'LIBRARY' // Optional, only used when outputFormat = 'JSON_CYCLONE_DX_1_4' to define the type of component this project is for the BOM metadata with possible values: 'LIBRARY' (default), 'APPLICATION', 'FRAMEWORK', 'CONTAINER', 'OPERATING_SYSTEM', 'DEVICE', 'FIRMWARE' and 'FILE'.
isColorEnabled = false // if true (and outputFormat = "DEFAULT") prints vulnerability description in color. By default is true.
showAll = false // if true prints all dependencies. By default is false, meaning only dependencies with vulnerabilities will be printed.
printBanner = true // if true will print ASCII text banner. By default is true.
}
Or if you are using Kotlin (build.gradle.kts file):
ossIndexAudit {
username = "email" // if not provided, an anonymous query will be made
password = "pass"
isAllConfigurations =
false // if true includes the dependencies in all resolvable configurations. By default is false, meaning only "compileClasspath", "runtimeClasspath", "releaseCompileClasspath" and "releaseRuntimeClasspath" are considered
isUseCache = true // true by default
cacheDirectory = "some/path" // by default it uses the user data directory (according to OS)
cacheExpiration =
"PT12H" // 12 hours if omitted. It must follow the Joda Time specification at https://www.javadoc.io/doc/joda-time/joda-time/2.10.4/org/joda/time/Duration.html#parse-java.lang.String-
proxyConfiguration { // extra configuration when running behind a proxy without direct internet access
protocol = "http" // can be "http" (default) or "https"
host = "proxy-host" // hostname for the proxy
port = 8080 // port for the proxy
authConfiguration.username = "username" // username for the proxy (if credentials are required)
authConfiguration.password = "password" // password for the proxy (if credentials are required)
}
modulesIncluded = listOf("module-1", "module-2") // Optional. For multi-module projects, the names of the sub-modules to include for auditing. If not specified all modules are included.
modulesExcluded = listOf("module-1", "module-2") // Optional. For multi-module projects, the names of the sub-modules to exclude from auditing. If not specified no modules are excluded. This value is processed after 'modulesIncluded' if both are specified.
// For projects using multiple custom variants for the release distribution, a Map can be set with the attributes names and values to match the specific variant. See more at the section "How to Deal with Multiple Release Variants" below in this doc.
variantAttributes = mapOf("com.android.build.api.attributes.ProductFlavor:version" to "prod", "other.attribute" to "other value") // Optional, use it only when the plugin can't match a variant on its own
// ossIndexAudit can be configured to exclude vulnerabilities from matching
excludeVulnerabilityIds =
listOf("39d74cc8-457a-4e57-89ef-a258420138c5") // list containing ids of vulnerabilities to be ignored
excludeCoordinates =
listOf("commons-fileupload:commons-fileupload:1.3") // list containing coordinate of components which if vulnerable should be ignored
// Output options
outputFormat = "DEFAULT" // Optional, other values are: "DEPENDENCY_GRAPH" prints dependency graph showing direct/transitive dependencies, "JSON_CYCLONE_DX_1_4" prints a CycloneDX 1.4 SBOM in JSON format.
cycloneDxComponentType = "LIBRARY" // Optional, only used when outputFormat = "JSON_CYCLONE_DX_1_4" to define the type of component this project is for the BOM metadata with possible values: "LIBRARY" (default), "APPLICATION", "FRAMEWORK", "CONTAINER", "OPERATING_SYSTEM", "DEVICE", "FIRMWARE" and "FILE".
isColorEnabled = false // if true (and outputFormat = "DEFAULT") prints vulnerability description in color. By default is true.
isShowAll = false // if true prints all dependencies. By default is false, meaning only dependencies with vulnerabilities will be printed.
isPrintBanner = true // if true will print ASCII text banner. By default is true.
}
- Open Terminal on the project's root and run
./gradlew ossIndexAudit
- You should see the audit result on Terminal.
- Start a local instance of IQ Server, or get the URL and credentials of a remote one.
- Configure IQ Server settings inside the
nexusIQScan
configuration on the filebuild.gradle
e.g.
Groovy:
nexusIQScan {
username = 'admin' // Make sure to use an user with the role 'Application Evaluator' in the given IQ Server application
password = 'pass'
serverUrl = 'http://localhost:8070'
applicationId = 'app'
organizationId = 'orgId' // Optional. If provided, a validation will be done to check if the given application ID exists under the organization ID (please note this is different than the organization name). If the application doesn't exists, then it will be created under the organization.
stage = 'build' // build is used if omitted
allConfigurations = false // if true includes the dependencies in all resolvable configurations. By default is false, meaning only 'compileClasspath', 'runtimeClasspath', 'releaseCompileClasspath' and 'releaseRuntimeClasspath' are considered
resultFilePath = 'results.json' // Optional. JSON file containing results of the evaluation
modulesExcluded = ['module-1', 'module-2'] // Optional. For multi-module projects, the names of the sub-modules to exclude from scanning and evaluation.
dirExcludes = 'some-ant-pattern' // Optional. Comma separated ant-like glob patterns to select directories/archives that should be excluded. For Android projects we suggest using '**/classes.jar,**/annotations.zip,**/lint.jar,**/internal_impl-*.jar'
dirIncludes = 'some-ant-pattern' // Optional. Comma separated ant-like glob patterns to select directories/archives that should be examined
// For projects using multiple custom variants for the release distribution, a Map can be set with the attributes names and values to match the specific variant. See more at the section "How to Deal with Multiple Release Variants" below in this doc.
variantAttributes = ['com.android.build.api.attributes.ProductFlavor:version': 'prod', 'other.attribute': 'other value'] // Optional, use it only when the plugin can't match a variant on its own
scanTargets = ['package-lock.json', '**/*.lock'] // Optional. Ant-like glob patterns for relative paths (to the project's folder) to select additional files to be scanned and evaluated.
}
Kotlin:
nexusIQScan {
username = "admin" // Make sure to use an user with the role "Application Evaluator" in the given IQ Server application
password = "pass"
serverUrl = "http://localhost:8070"
applicationId = "app"
organizationId = "orgId" // Optional. If provided, a validation will be done to check if the given application ID exists under the organization ID (please note this is different than the organization name). If the application doesn"t exists, then it will be created under the organization.
stage = "build" // build is used if omitted
isAllConfigurations = false // if true includes the dependencies in all resolvable configurations. By default is false, meaning only "compileClasspath", "runtimeClasspath", "releaseCompileClasspath" and "releaseRuntimeClasspath" are considered
resultFilePath = "results.json" // Optional. JSON file containing results of the evaluation
modulesExcluded = listOf("module-1", "module-2") // Optional. For multi-module projects, the names of the sub-modules to exclude from scanning and evaluation.
dirExcludes = "some-ant-pattern" // Optional. Comma separated ant-like glob patterns to select directories/archives that should be excluded. For Android projects we suggest using "**/classes.jar,**/annotations.zip,**/lint.jar,**/internal_impl-*.jar"
dirIncludes = "some-ant-pattern" // Optional. Comma separated ant-like glob patterns to select directories/archives that should be examined
// For projects using multiple custom variants for the release distribution, a Map can be set with the attributes names and values to match the specific variant. See more at the section "How to Deal with Multiple Release Variants" below in this doc.
variantAttributes = mapOf("com.android.build.api.attributes.ProductFlavor:version" to "prod", "other.attribute" to "other value") // Optional, use it only when the plugin can't match a variant on its own
scanTargets = listOf("package-lock.json", "**/*.lock") // Optional. Ant-like glob patterns for relative paths (to the project's folder) to select additional files to be scanned and evaluated.
}
- Open Terminal on the project's root and run
./gradlew nexusIQScan
- You should see the scan report URL report on Terminal.
Allows you to save information about the dependencies of a project into module information (module.xml
) files that Sonatype CI tools can use to include these dependencies in a scan.
- Open Terminal on the project's root and run
./gradlew nexusIQIndex
For multi-module projects, you can configure a list of sub-modules to exclude from indexing. Groovy:
nexusIQIndex {
modulesExcluded = ['module-1', 'module-2'] // Optional. For multi-module projects, the names of the sub-modules to exclude from indexing.
}
Kotlin:
nexusIQIndex {
modulesExcluded = listOf("module-1", "module-2") // Optional. For multi-module projects, the names of the sub-modules to exclude from indexing.
}
Sometimes it's not desirable to keep sensitive data stored on build.gradle
. For such cases it's possible to use project properties (-P arguments) or system properties (-D arguments or injected from a tool) from command line or environment variables when running the nexusIQScan
or ossIndexAudit
tasks.
Here is an example using project properties for the credentials, Groovy
nexusIQScan {
username = project['username']
password = project['password']
serverUrl = 'http://localhost:8070'
applicationId = 'app'
}
ossIndexAudit {
username = project['username']
password = project['password']
}
Kotlin:
nexusIQScan {
username = project["username"]
password = project["password"]
serverUrl = "http://localhost:8070"
applicationId = "app"
}
ossIndexAudit {
username = project["username"]
password = project["password"]
}
On command line:
./gradlew nexusIQScan -Pusername=admin -Ppassword=pass
./gradlew ossIndexAudit -Pusername=admin -Ppassword=pass
Each property name can be set as needed.
Here is an example using system properties for the credentials (Groovy):
nexusIQScan {
username = System.properties['username']
password = System.properties['password']
serverUrl = 'http://localhost:8070'
applicationId = 'app'
}
ossIndexAudit {
username = System.properties['username']
password = System.properties['password']
}
As mentioned above the values can be set on command line using -D arguments or injected via a tool (CI/CD for instance).
Finally this is how environment variables can be used (usually values are injected from the local environment or by a CI tool, Groovy):
nexusIQScan {
username = System.getenv('username')
password = System.getenv('password')
serverUrl = 'http://localhost:8070'
applicationId = 'app'
}
ossIndexAudit {
username = System.getenv('username')
password = System.getenv('password')
}
Kotlin version:
nexusIQScan {
username = System.getenv("username")
password = System.getenv("password")
serverUrl = "http://localhost:8070"
applicationId = "app"
}
ossIndexAudit {
username = System.getenv("username")
password = System.getenv("password")
}
Just apply the plugin on the root project and all sub-modules will be processed and the output will be a single report with all components found in each module. This includes Android projects.
This plugin makes its best effort to find the release (production) configuration and variant to get the dependencies to analyze.
However, a Gradle project can have multiple custom release variants and the plugin might not be able to tell Gradle which one to pick, resulting in an error like this:
> Could not resolve all dependencies for configuration 'sonatypeCopyConfiguration0'.
> Could not resolve project :common-lib.
Required by:
project :app
> The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release'. However we cannot choose between the following variants of project :baseapp:
- ciReleaseRuntimeElements
- prodReleaseRuntimeElements
All of them match the consumer attributes:
- Variant 'ciReleaseRuntimeElements' capability common-lib:1.0.0 declares a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release':
- Unmatched attributes:
- Provides attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.2.2' but the consumer didn't ask for it
- Provides attribute 'com.android.build.api.attributes.ProductFlavor:version' with value 'ci' but the consumer didn't ask for it
- Provides attribute 'com.android.build.gradle.internal.attributes.VariantAttr' with value 'ciRelease' but the consumer didn't ask for it
- Provides a library but the consumer didn't ask for it
- Provides attribute 'org.gradle.jvm.environment' with value 'android' but the consumer didn't ask for it
- Variant 'prodReleaseRuntimeElements' capability common-lib:1.0.0 declares a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release':
- Unmatched attributes:
- Provides attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.2.2' but the consumer didn't ask for it
- Provides attribute 'com.android.build.api.attributes.ProductFlavor:version' with value 'prod' but the consumer didn't ask for it
- Provides attribute 'com.android.build.gradle.internal.attributes.VariantAttr' with value 'prodRelease' but the consumer didn't ask for it
- Provides a library but the consumer didn't ask for it
- Provides attribute 'org.gradle.jvm.environment' with value 'android' but the consumer didn't ask for it
From that output we can see the value of the attribute com.android.build.api.attributes.ProductFlavor:version
can be used to distinguish between the available variants.
Since attribute names and values can be customized on each project, this plugin allows to set the attributes needed to match the right variant using the property variantAttributes
.
In the example above, the following configuration would allow the plugin to choose the prodReleaseRuntimeElements
variant:
Groovy
nexusIQScan {
variantAttributes = ['com.android.build.api.attributes.ProductFlavor:version': 'prod']
}
ossIndexAudit {
variantAttributes = ['com.android.build.api.attributes.ProductFlavor:version': 'prod']
}
Kotlin
nexusIQScan {
variantAttributes = mapOf("com.android.build.api.attributes.ProductFlavor:version" to "prod")
}
ossIndexAudit {
variantAttributes = mapOf("com.android.build.api.attributes.ProductFlavor:version" to "prod")
}
See more information about attributes matching for variant selection see https://docs.gradle.org/current/userguide/variant_model.html#sec:variant-select-errors
We care a lot about making the world a safer place, and that's why we created this scan-gradle-plugin
. If you as well want to speed up the pace of software development by working on this project, jump on in! Before you start work, create a new issue, or comment on an existing issue, to let others know you are!
Check the full contrubuting guidelines at: CONTRIBUTING.md
It is worth noting that this is NOT SUPPORTED by Sonatype, and is a contribution of ours to the open source community (read: you!)
Remember:
- Use this contribution at the risk tolerance that you have
- Do NOT file Sonatype support tickets related to
scan-gradle-plugin
support in regard to this project - DO file issues here on GitHub, so that the community can pitch in
Phew, that was easier than I thought. Last but not least of all:
Have fun creating and using scan-gradle-plugin
, we are glad to have you here!
Looking to contribute to our code but need some help? There's a few ways to get information:
- Chat with us on Gitter