Skip to content

Creates CycloneDX Software Bill of Materials (SBOM) from Gradle projects

License

Notifications You must be signed in to change notification settings

CycloneDX/cyclonedx-gradle-plugin

Build Status Gradle Plugin License Website Slack Invite Group Discussion Twitter

CycloneDX Gradle Plugin

The CycloneDX Gradle plugin creates an aggregate of all direct and transitive dependencies of a project and creates a valid CycloneDX SBOM. CycloneDX is a lightweight software bill of materials (SBOM) specification designed for use in application security contexts and supply chain component analysis.

Usage

Execution:

gradle cyclonedxBom

Output CycloneDX Generation Info:

gradle cyclonedxBom --info

build.gradle (excerpt)

To generate BOM for a single project add the plugin to the build.gradle.

plugins {
    id 'org.cyclonedx.bom' version '1.10.0'
}

Once a BOM is generated, by default it will reside at ./build/reports/bom.xml and ./build/reports/bom.json

Configuration:

You can add the following configuration to build.gradle to control various options in generating a BOM:

cyclonedxBom {
    // includeConfigs is the list of configuration names to include when generating the BOM (leave empty to include every configuration), regex is supported
    includeConfigs = ["runtimeClasspath"]
    // skipConfigs is a list of configuration names to exclude when generating the BOM, regex is supported
    skipConfigs = ["compileClasspath", "testCompileClasspath"]
    // skipProjects is a list of project names to exclude when generating the BOM
    skipProjects = [rootProject.name, "yourTestSubProject"]
    // Specified the type of project being built. Defaults to 'library'
    projectType = "application"
    // Specified the version of the CycloneDX specification to use. Defaults to '1.5'
    schemaVersion = "1.6"
    // Boms destination directory. Defaults to 'build/reports'
    destination = file("build/reports")
    // The file name for the generated BOMs (before the file format suffix). Defaults to 'bom'
    outputName = "bom"
    // The file format generated, can be xml, json or all for generating both. Defaults to 'all'
    outputFormat = "json"
    // Include BOM Serial Number. Defaults to 'true'
    includeBomSerialNumber = false
    // Include License Text. Defaults to 'true'
    includeLicenseText = false
    // Include resolution of full metadata for components including licenses. Defaults to 'true'
    includeMetadataResolution = true
    // Override component version. Defaults to the project version
    componentVersion = "2.0.0"
    // Override component name. Defaults to the project name
    componentName = "my-component"
}

If you are using the Kotlin DSL, the plugin can be configured as following:

tasks.cyclonedxBom {
    setIncludeConfigs(listOf("runtimeClasspath"))
    setSkipConfigs(listOf("compileClasspath", "testCompileClasspath"))
    setSkipProjects(listOf(rootProject.name, "yourTestSubProject"))
    setProjectType("application")
    setSchemaVersion("1.6")
    setDestination(project.file("build/reports"))
    setOutputName("bom")
    setOutputFormat("json")
    setIncludeBomSerialNumber(false)
    setIncludeLicenseText(true)
    setIncludeMetadataResolution(true)
    setComponentVersion("2.0.0")
    setComponentName("my-component")
}

Run Gradle with info logging (-i option) to see which configurations add to the BOM.

Generate BOM for multiple projects:

You can also build the BOM for multiple projects using the --init-script option:

gradle --init-script <path-to-init.gradle> cyclonedxBom --info

where the init.gradle can look like this:

initscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "org.cyclonedx:cyclonedx-gradle-plugin:1.10.0"
  }
}

allprojects{
  apply plugin:org.cyclonedx.gradle.CycloneDxPlugin
  cyclonedxBom {
    includeConfigs = ["runtimeClasspath"]
    skipConfigs = ["compileClasspath", "testCompileClasspath"]
    skipProjects = [rootProject.name, "yourTestSubProject"]
    projectType = "application"
    schemaVersion = "1.6"
    destination = file("build/reports")
    outputName = "bom"
    outputFormat = "json"
    includeBomSerialNumber = false
    includeLicenseText = true
    componentVersion = "2.0.0"
    componentName = "my-component"
  }
}

How to manually modify Metadata

The Plugin makes it possible to manually add Manufacture-Data and Licenses-Data to the Metadata of the BOM.
The structure of the Metadata is shown on https://cyclonedx.org/docs/1.6/json/#metadata.
The editing of the Manufacture and Licenses-Data is optional. If the Manufacture/Licenses-Date isn't edited, then the respective structure won't appear in the BOM.

To enable the modification of the metadata the cyclonedx-core-java plugin must be implemented in the build.gradle.


Adding Manufacture-Data

In order to be able to define the Manufacture-Data you must import org.cyclonedx.model.*; into the build.gradle.
You can add the Manufacture-Data by passing an Object of the Type OrganizationalEntity to the Plugin.

Example (groovy):

cyclonedxBom {
    //declaration of the Object from OrganizationalContact
    OrganizationalContact organizationalContact = new OrganizationalContact()
    //setting the Name[String], Email[String] and Phone[String] of the Object
    organizationalContact.setName("Max_Mustermann")
    organizationalContact.setEmail("max.mustermann@test.org")
    organizationalContact.setPhone("0000 99999999")
    //passing Data to the plugin
    organizationalEntity {oe->
        oe.name = 'Test'
        oe.url = ['www.test1.com', 'www.test2.com']
        oe.addContact(organizationalContact)
    }
}

Example (Kotlin):

cyclonedxBom {
    //declaration of the Object from OrganizationalContact
    var organizationalContact1 = OrganizationalContact()
    //setting the Name[String], Email[String] and Phone[String] of the Object
    organizationalContact1.setName("Max_Mustermann")
    organizationalContact1.setEmail("max.mustermann@test.org")
    organizationalContact1.setPhone("0000 99999999")
    //passing Data to the plugin
    setOrganizationalEntity{oe->
        oe.name = "Test";
        oe.urls = listOf("www.test1.com", "www.test2.com")
        oe.addContact(organizationalContact1)
    }
}

It should be noted that some Data like OrganizationalContact, Url, Name,... can be left out.
OrganizationalEntity can also include multiple OrganizationalContact.

For details look at https://cyclonedx.org/docs/1.6/json/#metadata.

Adding Licenses-Data

In order to be able to define the Manufacture-Data you must import org.cyclonedx.model.*; into the build.gradle.

You can add the Licenses-Data by passing an Object of the Type LicenseChoice to the Plugin. The Object from LicenseChoice includes either License or Expression. It can't include both.

License

Example (groovy):

cyclonedxBom {
    //declaration of the Object from AttachmentText -> Needed for the setting of LicenseText
    AttachmentText attachmentText = new AttachmentText()
    attachmentText.setText("This is a Licenses-Test")
    //declaration of the Object from License
    License license = new License()
    //setting the Name[String], LicenseText[AttachmentText] and Url[String]
    license.setName("XXXX XXXX Software")
    //license.setId("Mup")     // either id or name -> both not possible
    license.setLicenseText(attachmentText);
    license.setUrl("https://www.test-Url.org/")
    //passing Data to Plugin
    licenseChoice {lc->
        lc.addLicense(license)
    }
}

Example (Kotlin):

cyclonedxBom {
    //declaration of the Object from AttachmentText -> Needed for the setting of LicenseText
    val attachmentText = AttachmentText()
    attachmentText.setText("This is a Licenses-Test")
    //declaration of the Object from License
    val license = License()
    //setting the Name[String], LicenseText[AttachmentText] and Url[String]
    license.setName("XXXX XXXX Software")
    //license.setId("Mup")     // either id or name -> both not possible
    license.setLicenseText(attachmentText)
    license.setUrl("https://www.test-Url.org/")
    //passing Data to Plugin
    setLicenseChoice{lc->
        lc.addLicense(license)
    }
}

It should be noted that License requires either Id or Name, but both can't be included at the same time.

Text and Url are optional for inclusion and multiple License can be added to LicenseChoice.


Expression

Example (groovy):

cyclonedxBom {
    //passing Expression to Plugin
    licenseChoice {lc->
        lc.setExpression("This is a Test Expression")
    }
}

Example (Kotlin):

cyclonedxBom {
    //passing Expression to Plugin
    setLicenseChoice{lc->
        lc.setExpression("This is a Test Expression")
    }
}

For details of the BOM structure look at https://cyclonedx.org/docs/1.6/json/#metadata.

CycloneDX Schema Support

The following table provides information on the version of this Gradle plugin, the CycloneDX schema version supported, as well as the output format options. Use the latest possible version of this plugin that is the compatible with the CycloneDX version supported by the target system.

Version Schema Version Format(s)
1.10.x CycloneDX v1.6 XML/JSON
1.9.x CycloneDX v1.6 XML/JSON
1.8.x CycloneDX v1.5 XML/JSON
1.7.x CycloneDX v1.4 XML/JSON
1.6.x CycloneDX v1.4 XML/JSON
1.5.x CycloneDX v1.3 XML/JSON
1.4.x CycloneDX v1.3 XML/JSON
1.2.x CycloneDX v1.2 XML/JSON
1.1.x CycloneDX v1.1 XML
1.0x CycloneDX v1.0 XML

Copyright & License

CycloneDX Gradle Plugin is Copyright (c) OWASP Foundation. All Rights Reserved.

Permission to modify and redistribute is granted under the terms of the Apache 2.0 license. See the LICENSE file for the full license.